diff -ruN linux-2.5.68-rmk1-pxa1/Makefile linux-2.5.68-rmk1-pxa1-cerf1/Makefile --- linux-2.5.68-rmk1-pxa1/Makefile Mon Jun 23 13:11:28 2003 +++ linux-2.5.68-rmk1-pxa1-cerf1/Makefile Mon Jun 23 11:31:49 2003 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 5 SUBLEVEL = 68 -EXTRAVERSION =-rmk1-pxa1 +EXTRAVERSION =-rmk1-pxa1-cerf1 # *DOCUMENTATION* # To see a list of typical targets execute "make help" diff -ruN linux-2.5.68-rmk1-pxa1/arch/arm/Makefile linux-2.5.68-rmk1-pxa1-cerf1/arch/arm/Makefile --- linux-2.5.68-rmk1-pxa1/arch/arm/Makefile Mon Jun 23 13:11:28 2003 +++ linux-2.5.68-rmk1-pxa1-cerf1/arch/arm/Makefile Mon Jun 23 11:39:43 2003 @@ -37,7 +37,7 @@ # # Note - GCC does accept -march=armv5te, but someone messed up the assembler or the # gcc specs file - this needs fixing properly - ie in gcc and/or binutils. -arch-$(CONFIG_CPU_32v5) :=-D__LINUX_ARM_ARCH__=5 -march=armv5t +arch-$(CONFIG_CPU_32v5) :=-D__LINUX_ARM_ARCH__=5 -march=armv4 #5t arch-$(CONFIG_CPU_32v4) :=-D__LINUX_ARM_ARCH__=4 -march=armv4 arch-$(CONFIG_CPU_32v3) :=-D__LINUX_ARM_ARCH__=3 -march=armv3 diff -ruN linux-2.5.68-rmk1-pxa1/arch/arm/def-configs/cerfcube_pxa linux-2.5.68-rmk1-pxa1-cerf1/arch/arm/def-configs/cerfcube_pxa --- linux-2.5.68-rmk1-pxa1/arch/arm/def-configs/cerfcube_pxa Wed Dec 31 16:00:00 1969 +++ linux-2.5.68-rmk1-pxa1-cerf1/arch/arm/def-configs/cerfcube_pxa Mon Jun 23 13:31:21 2003 @@ -0,0 +1,720 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# General setup +# +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_LOG_BUF_SHIFT=14 + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# System Type +# +# CONFIG_ARCH_ADIFCC is not set +# CONFIG_ARCH_ANAKIN is not set +# CONFIG_ARCH_ARCA5K is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +CONFIG_ARCH_PXA=y +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_SHARK is not set + +# +# Archimedes/A5000 Implementations +# + +# +# Archimedes/A5000 Implementations (select only ONE) +# + +# +# CLPS711X/EP721X Implementations +# + +# +# Epxa10db +# + +# +# Footbridge Implementations +# + +# +# IOP3xx Implementation Options +# +# CONFIG_ARCH_IOP310 is not set +# CONFIG_ARCH_IOP321 is not set + +# +# IOP3xx Chipset Features +# + +# +# Intel PXA250/210 Implementations +# +# CONFIG_ARCH_LUBBOCK is not set +# CONFIG_ARCH_PXA_IDP is not set +CONFIG_ARCH_PXA_CERF=y + +# +# SA11x0 Implementations +# + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_XSCALE=y +CONFIG_CPU_32v5=y + +# +# Processor Features +# +# CONFIG_ARM_THUMB is not set +CONFIG_XSCALE_PMU=y + +# +# General setup +# +# CONFIG_ZBOOT_ROM is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +# CONFIG_HOTPLUG is not set + +# +# MMC device drivers +# +# CONFIG_MMC is not set + +# +# At least one math emulation must be selected +# +# CONFIG_FPE_NWFPE is not set +CONFIG_FPE_FASTFPE=y +CONFIG_KCORE_ELF=y +# CONFIG_KCORE_AOUT is not set +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PM is not set +# CONFIG_PREEMPT is not set +# CONFIG_ARTHUR is not set +CONFIG_CMDLINE="root=/dev/mtdblock3 rw mem=64M console=ttyS0,38400 init=/linuxrc rootfstype=jffs2" +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_REDBOOT_PARTS=y +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_NOSWAP=y +# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set +# CONFIG_MTD_CFI_GEOMETRY is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_NORA is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +CONFIG_MTD_PXA_CERF=y +# CONFIG_MTD_PXA is not set +# CONFIG_MTD_EDB7312 is not set +# CONFIG_MTD_UCLINUX is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC1000 is not set +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_XFRM_USER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +CONFIG_IPV6_SCTP__=y +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_LLC is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MII is not set +CONFIG_NET_VENDOR_SMC=y +CONFIG_SMC91X=y + +# +# Ethernet (1000 Mbit) +# +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices (depends on LLC=y) +# +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y + +# +# IDE, ATA and ATAPI Block devices +# +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD is not set +CONFIG_BLK_DEV_IDEDISK=m +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_IDEDISK_STROKE is not set +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set + +# +# IDE chipset support/bugfixes +# + +# +# SCSI support +# +CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_REPORT_LUNS is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_ACARD is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_MEGARAID is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GDTH is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set +# CONFIG_SCSI_PCI2000 is not set +# CONFIG_SCSI_PCI2220I is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_NSP32 is not set +# CONFIG_SCSI_DEBUG is not set + +# +# I2O device support +# + +# +# ISDN subsystem +# +# CONFIG_ISDN_BOOL is not set + +# +# Input device support +# +# CONFIG_INPUT is not set + +# +# Userland interfaces +# + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +# CONFIG_SERIO is not set + +# +# Input Device Drivers +# + +# +# Character devices +# +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_DZ is not set +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_SERIAL_CORE_COUNT=16 +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=32 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# I2C Hardware Sensors Mainboard support +# + +# +# I2C Hardware Sensors Chip support +# +# CONFIG_I2C_SENSOR is not set + +# +# L3 serial bus support +# +# CONFIG_L3 is not set + +# +# Mice +# +CONFIG_BUSMOUSE=y +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_HANGCHECK_TIMER is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +CONFIG_DEVPTS_FS=y +CONFIG_TMPFS=y +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_NAND is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_GSS is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_NEC98_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# Misc devices +# + +# +# Multimedia Capabilities Port drivers +# +# CONFIG_MCP is not set + +# +# Console Switches +# +# CONFIG_SWITCHES is not set + +# +# USB support +# + +# +# Bluetooth support +# +# CONFIG_BT is not set + +# +# Kernel hacking +# +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_INFO is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SLAB is not set +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_WAITQ is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_ERRORS=y +CONFIG_KALLSYMS=y +CONFIG_DEBUG_LL=y + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_CRC32=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y diff -ruN linux-2.5.68-rmk1-pxa1/arch/arm/mach-pxa/Kconfig linux-2.5.68-rmk1-pxa1-cerf1/arch/arm/mach-pxa/Kconfig --- linux-2.5.68-rmk1-pxa1/arch/arm/mach-pxa/Kconfig Sat Apr 19 19:48:55 2003 +++ linux-2.5.68-rmk1-pxa1-cerf1/arch/arm/mach-pxa/Kconfig Mon Jun 23 11:33:32 2003 @@ -9,5 +9,9 @@ bool "Accelent Xscale IDP" depends on ARCH_PXA +config ARCH_PXA_CERF + bool "CerfBoard PXA Reference Board" + depends on ARCH_PXA + endmenu diff -ruN linux-2.5.68-rmk1-pxa1/arch/arm/mach-pxa/Makefile linux-2.5.68-rmk1-pxa1-cerf1/arch/arm/mach-pxa/Makefile --- linux-2.5.68-rmk1-pxa1/arch/arm/mach-pxa/Makefile Sat Apr 19 19:49:34 2003 +++ linux-2.5.68-rmk1-pxa1-cerf1/arch/arm/mach-pxa/Makefile Mon Jun 23 11:33:51 2003 @@ -8,6 +8,7 @@ # Specific board support obj-$(CONFIG_ARCH_LUBBOCK) += lubbock.o obj-$(CONFIG_ARCH_PXA_IDP) += idp.o +obj-$(CONFIG_ARCH_PXA_CERF) += cerf.o # Support for blinky lights led-y := leds.o diff -ruN linux-2.5.68-rmk1-pxa1/arch/arm/mach-pxa/cerf.c linux-2.5.68-rmk1-pxa1-cerf1/arch/arm/mach-pxa/cerf.c --- linux-2.5.68-rmk1-pxa1/arch/arm/mach-pxa/cerf.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.68-rmk1-pxa1-cerf1/arch/arm/mach-pxa/cerf.c Mon Jun 23 13:29:12 2003 @@ -0,0 +1,98 @@ +/* + * linux/arch/arm/mach-pxa/cerf.c + * + * Support for Intrinsyc's CerfBoard (pxa25x) + * + * Copyright: Intrinsyc Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +//#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "generic.h" + +static void __init cerf_init_irq(void) +{ + int irq; + + pxa_init_irq(); + +// set_irq_type(IRQ_GPIO(0), IRQT_FALLING); +// set_irq_type(IRQ_GPIO(21), IRQT_RISING); /* Ethernet */ +} + +static int __init cerf_init(void) +{ + return 0; +} + +__initcall(cerf_init); + +static struct map_desc cerf_io_desc[] __initdata = { + /* virtual physical length type */ + { CERF_FLASH_BASE, CERF_FLASH_PHYS, CERF_FLASH_SIZE, MT_DEVICE }, /* Flash */ + { CERF_ETH_BASE , CERF_ETH_PHYS , CERF_ETH_SIZE , MT_DEVICE }, /* Ethernet */ + { CERF_ETH2_BASE , CERF_ETH2_PHYS , CERF_ETH2_SIZE , MT_DEVICE }, /* Ethernet DB */ +}; + +static void __init cerf_map_io(void) +{ + pxa_map_io(); + iotable_init(cerf_io_desc, ARRAY_SIZE(cerf_io_desc)); + + /* setup memory timing for CS0/1 */ + MSC0 = MSC_CS(0, CERF_MSC_CS0) | MSC_CS(1, CERF_MSC_CS1); + printk(KERN_INFO "MCS0 = 0x%08x\n", MSC0); + + /* setup memory timing for CS2/3 */ + MSC1 = MSC_CS(2, CERF_MSC_CS2) | MSC_CS(3, CERF_MSC_CS3); + printk(KERN_INFO "MCS1 = 0x%08x\n", MSC1); + + /* setup memory timing for CS4/5 */ + MSC2 = MSC_CS(4, CERF_MSC_CS4) | MSC_CS(5, CERF_MSC_CS5); + printk(KERN_INFO "MCS2 = 0x%08x\n", MSC2); + +#if 0 + /* This enables the BTUART */ + CKEN |= CKEN7_BTUART; + pxa_gpio_mode(GPIO42_BTRXD_MD); + pxa_gpio_mode(GPIO43_BTTXD_MD); + pxa_gpio_mode(GPIO44_BTCTS_MD); + pxa_gpio_mode(GPIO45_BTRTS_MD); + + /* setup sleep mode values */ + PWER = 0x00000002; + PFER = 0x00000000; + PRER = 0x00000002; + PGSR0 = 0x00008000; + PGSR1 = 0x003F0202; + PGSR2 = 0x0001C000; + PCFR |= PCFR_OPDE; +#endif +} + +MACHINE_START(PXA_CERF, "CerfBoard PXA Reference Board") + MAINTAINER("Intrinsyc Software Inc.") + BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000)) + BOOT_PARAMS(0xa0000100) + MAPIO(cerf_map_io) + INITIRQ(cerf_init_irq) +MACHINE_END diff -ruN linux-2.5.68-rmk1-pxa1/drivers/mtd/maps/Kconfig linux-2.5.68-rmk1-pxa1-cerf1/drivers/mtd/maps/Kconfig --- linux-2.5.68-rmk1-pxa1/drivers/mtd/maps/Kconfig Mon Jun 23 13:11:28 2003 +++ linux-2.5.68-rmk1-pxa1-cerf1/drivers/mtd/maps/Kconfig Mon Jun 23 11:36:01 2003 @@ -279,6 +279,14 @@ 21285 bridge used with Intel's StrongARM processors. More info at . +config MTD_PXA_CERF + tristate "CFI Flash device mapped on XScale CerfBoard" + depends on ARM && MTD_CFI && ARCH_PXA_CERF + help + This enables access routines for the flash chips on Intrinsyc's XScale + CerfBoard. If you have one of these boards and would like to use the + flash chips on it, say 'Y'. + config MTD_IQ80310 tristate "CFI Flash device mapped on the XScale IQ80310 board" depends on ARM && MTD_CFI && ARCH_IQ80310 diff -ruN linux-2.5.68-rmk1-pxa1/drivers/mtd/maps/Makefile linux-2.5.68-rmk1-pxa1-cerf1/drivers/mtd/maps/Makefile --- linux-2.5.68-rmk1-pxa1/drivers/mtd/maps/Makefile Mon Jun 23 13:11:28 2003 +++ linux-2.5.68-rmk1-pxa1-cerf1/drivers/mtd/maps/Makefile Mon Jun 23 11:36:17 2003 @@ -25,6 +25,7 @@ obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o obj-$(CONFIG_MTD_PXA) += pxa.o +obj-$(CONFIG_MTD_PXA_CERF) += cerf_pxa.o obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o obj-$(CONFIG_MTD_NETSC520) += netsc520.o diff -ruN linux-2.5.68-rmk1-pxa1/drivers/mtd/maps/cerf_pxa.c linux-2.5.68-rmk1-pxa1-cerf1/drivers/mtd/maps/cerf_pxa.c --- linux-2.5.68-rmk1-pxa1/drivers/mtd/maps/cerf_pxa.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.68-rmk1-pxa1-cerf1/drivers/mtd/maps/cerf_pxa.c Mon Jun 23 11:36:35 2003 @@ -0,0 +1,171 @@ +/* + * $Id: + * + * Map driver for the CerfBoard developer platform. + * + * Author: Nicolas Pitre + * Copyright: (C) 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + + +#define WINDOW_ADDR 0 +#define WINDOW_SIZE 32*1024*1024 +#define BUSWIDTH 4 + +static __u8 cerf_pxa_read8(struct map_info *map, unsigned long ofs) +{ + return *(__u8 *)(map->map_priv_1 + ofs); +} + +static __u16 cerf_pxa_read16(struct map_info *map, unsigned long ofs) +{ + return *(__u16 *)(map->map_priv_1 + ofs); +} + +static __u32 cerf_pxa_read32(struct map_info *map, unsigned long ofs) +{ + return *(__u32 *)(map->map_priv_1 + ofs); +} + +static void cerf_pxa_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy(to, (void *)(map->map_priv_1 + from), len); +} + +static void cerf_pxa_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + *(__u8 *)(map->map_priv_1 + adr) = d; +} + +static void cerf_pxa_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + *(__u16 *)(map->map_priv_1 + adr) = d; +} + +static void cerf_pxa_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + *(__u32 *)(map->map_priv_1 + adr) = d; +} + +static void cerf_pxa_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy((void *)(map->map_priv_1 + to), from, len); +} + +static struct map_info cerf_pxa_map = { + name: "PXA CerfBoard flash", + size: WINDOW_SIZE, + buswidth: BUSWIDTH, + read8: cerf_pxa_read8, + read16: cerf_pxa_read16, + read32: cerf_pxa_read32, + copy_from: cerf_pxa_copy_from, + write8: cerf_pxa_write8, + write16: cerf_pxa_write16, + write32: cerf_pxa_write32, + copy_to: cerf_pxa_copy_to +}; + +static struct mtd_partition cerf_pxa_partitions[] = { + { + name: "Bootloader", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE /* force read-only */ + },{ + name: "Partition Tables", + size: 0x00080000, + offset: 0x00040000, + },{ + name: "Kernel", + size: 0x00100000, + offset: 0x000C0000, + },{ + name: "Filesystem", + size: WINDOW_SIZE-0x001C0000, + offset: 0x001C0000 + } +}; + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +static struct mtd_info *mymtd; +static struct mtd_partition *parsed_parts; + +extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); + +static int __init init_cerf_pxa(void) +{ + struct mtd_partition *parts; + int nb_parts = 0; + int parsed_nr_parts = 0; + char *part_type = "static"; + + printk("Probing CerfBoard flash at physical address 0x%08x\n", WINDOW_ADDR); + cerf_pxa_map.map_priv_1 = ioremap(WINDOW_ADDR, WINDOW_SIZE); + if (!cerf_pxa_map.map_priv_1) { + printk("Failed to ioremap\n"); + return -EIO; + } + mymtd = do_map_probe("cfi_probe", &cerf_pxa_map); + if (!mymtd) { + iounmap((void *)cerf_pxa_map.map_priv_1); + return -ENXIO; + } + mymtd->module = THIS_MODULE; + +#ifdef CONFIG_MTD_REDBOOT_PARTS + if (parsed_nr_parts == 0) { + int ret = parse_redboot_partitions(mymtd, &parsed_parts); + + if (ret > 0) { + part_type = "RedBoot"; + parsed_nr_parts = ret; + } + } +#endif + + if (parsed_nr_parts > 0) { + parts = parsed_parts; + nb_parts = parsed_nr_parts; + } else { + parts = cerf_pxa_partitions; + nb_parts = NB_OF(cerf_pxa_partitions); + } + if (nb_parts) { + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(mymtd, parts, nb_parts); + } else { + add_mtd_device(mymtd); + } + return 0; +} + +static void __exit cleanup_cerf_pxa(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + if (parsed_parts) + kfree(parsed_parts); + } + if (cerf_pxa_map.map_priv_1) + iounmap((void *)cerf_pxa_map.map_priv_1); + return 0; +} + +module_init(init_cerf_pxa); +module_exit(cleanup_cerf_pxa); + diff -ruN linux-2.5.68-rmk1-pxa1/drivers/net/smc91x.c linux-2.5.68-rmk1-pxa1-cerf1/drivers/net/smc91x.c --- linux-2.5.68-rmk1-pxa1/drivers/net/smc91x.c Mon Jun 23 13:11:29 2003 +++ linux-2.5.68-rmk1-pxa1-cerf1/drivers/net/smc91x.c Mon Jun 23 13:26:24 2003 @@ -1,1462 +1,1210 @@ -/*------------------------------------------------------------------------ - . smc91x.c - . This is a driver for SMSC's 91C9x/91C1xx single-chip Ethernet devices. - . - . Copyright (C) 1996 by Erik Stahlman - . Copyright (C) 2001 Standard Microsystems Corporation - . Developed by Simple Network Magic Corporation - . Copyright (C) 2003 Monta Vista Software, Inc. - . Unified SMC91x driver by Nicolas Pitre - . - . This program is free software; you can redistribute it and/or modify - . it under the terms of the GNU General Public License as published by - . the Free Software Foundation; either version 2 of the License, or - . (at your option) any later version. - . - . This program is distributed in the hope that it will be useful, - . but WITHOUT ANY WARRANTY; without even the implied warranty of - . MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - . GNU General Public License for more details. - . - . You should have received a copy of the GNU General Public License - . along with this program; if not, write to the Free Software - . Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - . - . Arguments: - . io = for the base address - . irq = for the IRQ - . nowait = 0 for normal wait states, 1 eliminates additional wait states - . - . original author: - . Erik Stahlman - . - . hardware multicast code: - . Peter Cammaert - . - . contributors: - . Daris A Nevil - . Nicolas Pitre - . - . History: - . 08/20/00 Arnaldo Melo fix kfree(skb) in smc_hardware_send_packet - . 12/15/00 Christian Jullien fix "Warning: kfree_skb on hard IRQ" - . 03/16/01 Daris A Nevil modified smc9194.c for use with LAN91C111 - . 08/22/01 Scott Anderson merge changes from smc9194 to smc91111 - . 08/21/01 Pramod B Bhardwaj added support for RevB of LAN91C111 - . 12/20/01 Jeff Sutherland initial port to Xscale PXA with DMA support - . 04/07/03 Nicolas Pitre unified SMC91x driver, killed irq races, - . more bus abstraction, big cleanup, etc. - ----------------------------------------------------------------------------*/ - -static const char version[] = - "smc91x.c: v1.0, mar 07 2003 by Nicolas Pitre \n"; - -/* Debugging level */ -#ifndef SMC_DEBUG -#define SMC_DEBUG 0 -#endif - - +/* ---------------------------------------------------------------------- + * smc91x.c + * + * Copyright (C) 2003 Intrinsyc Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Derived from smc91111.c. Too many cooks to list... + * + * 18-Feb-2003 : Initial version [FB] + * 10-Mar-2003 : Basic functionality [FB] + * 15-Apr-2003 : Autoneg, carrier detect [FB] + * 21-Apr-2003 : Some cleanup [FB] + * 23-Apr-2003 : RCS version tag, timeout code, misc fixes, + * simple ethtool support [FB] + * 25-Apr-2003 : Added multicast hardware assisted filtering, + * moved remaining ins/outs to _io.h [FB] + * + * TODO: + * - graceful stop: see smc91x_stop + * - topology hotswapping (10mbps/hd -> 100mbps/fd etc.) + * see smc91x_phy_interrupt + * - ethtool needs locking for some operations. + * use only when interface is down for now + * - make use of drivers/net/mii.c for ethtool? + ------------------------------------------------------------------------*/ #include -#include #include +#include #include -#include -#include -#include -#include -#include +#include #include +#include +#include +#include #include #include #include +#include #include -#include #include +#include +#include +#include #include "smc91x.h" +#include "smc91x_io.h" +#include "smc91x_cfg.h" +#define SMC_DEBUG 0 -#ifdef CONFIG_ISA -/* - . the LAN91C111 can be at any of the following port addresses. To change, - . for a slightly different card, you can add it to the array. Keep in - . mind that the array must end in zero. -*/ -static unsigned int smc_portlist[] __initdata = { - 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0, - 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0, 0 -}; -#endif /* CONFIG_ISA */ - -#ifndef SMC_IOADDR -# define SMC_IOADDR -1 -#endif -static int io = SMC_IOADDR; - -#ifndef SMC_IRQ -# define SMC_IRQ -1 +#ifdef SMC_DEBUG +static unsigned int smc_debug = SMC_DEBUG; +#else +#define smc_debug 0 #endif -static int irq = SMC_IRQ; -#ifndef SMC_NOWAIT -# define SMC_NOWAIT 0 -#endif -static int nowait = SMC_NOWAIT; +#define DRIVER_NAME "SMC91x" +#define DRIVER_VERSION "$Revision: 1.6 $" -MODULE_PARM(io, "i"); -MODULE_PARM(irq, "i"); -MODULE_PARM(nowait, "i"); -MODULE_PARM_DESC(io, "I/O base address"); -MODULE_PARM_DESC(irq, "IRQ number"); -MODULE_PARM_DESC(nowait, "set to 1 for no wait state"); - - -/*------------------------------------------------------------------------ - . - . The internal workings of the driver. If you are changing anything - . here with the SMC stuff, you should have the datasheet and know - . what you are doing. - . - -------------------------------------------------------------------------*/ -#define CARDNAME "LAN91x" - -// Use power-down feature of the chip -#define POWER_DOWN 1 - -/* - . Wait time for memory to be free. This probably shouldn't be - . tuned that much, as waiting for this means nothing else happens - . in the system -*/ -#define MEMORY_WAIT_TIME 16 - -/* - . This selects whether TX packets are sent one by one to the SMC91x internal - . memory ans throttled until transmission completes. This may prevent - . RX overruns a litle by keeping much of the memory free for RX packets - . but to the expense of reduced TX throughput and increased IRQ overhead. - . Note this is not a cure for a too slow data bus or too high IRQ latency. - */ -#define THROTTLE_TX_PKTS 0 - - -/* store this information for the driver.. */ -struct smc_local { - - // If I have to wait until memory is available to send - // a packet, I will store the skbuff here, until I get the - // desired memory. Then, I'll send it out and free it. - struct sk_buff *saved_skb; - - // these are things that the kernel wants me to keep, so users - // can find out semi-useless statistics of how well the card is - // performing - struct net_device_stats stats; - - // version/revision of the SMC91x chip - int version; - - // Set to true during the auto-negotiation sequence - int autoneg_active; - - // Address of our PHY port - int phyaddr; - - // Type of PHY - int phytype; - - // Last contents of PHY Register 18 - int lastPhy18; - - // Contains the current active transmission mode - int tcr_cur_mode; - - // Contains the current active receive mode - int rcr_cur_mode; - - // Contains the current active receive/phy mode - int rpc_cur_mode; - int ctl_autoneg; - int ctl_rfduplx; - int ctl_rspeed; +static int smc91x_probe (struct net_device *dev); +static int smc91x_hard_start_xmit (struct sk_buff *skb,struct net_device *dev); +static smc91x_config_t smc91x_config[] = SMC91X_INTERFACES; +static struct net_device smc91x_dev[ ARRAY_SIZE(smc91x_config)]; + +static const char * smc91x_chip_id[] = { + "?", "?", "?", + /* 3 */ "SMC91C90/91C92", + /* 4 */ "SMC91C94", + /* 5 */ "SMC91C95", + /* 6 */ "SMC91C96", + /* 7 */ "SMC91C100", + /* 8 */ "SMC91C100FD", + /* 9 */ "SMC91C11xFD", + "?", "?", "?", + "?", "?", "?" }; +/* --- PHY MII --- */ -#if SMC_DEBUG > 2 -#define PRINTK3(args...) printk(args) -#else -#define PRINTK3(args...) do { } while(0) -#endif - -#if SMC_DEBUG > 1 -#define PRINTK2(args...) printk(args) -#else -#define PRINTK2(args...) do { } while(0) -#endif +static inline void smc91x_phy_sync_bits( u8 *bits, int *idx) +{ + int i; + /* 32 consecutive ones on MDO to establish sync */ + for (i = 0; i < 32; ++i) + bits[(*idx)++] = MII_MDOE | MII_MDO; -#if SMC_DEBUG > 0 -#define PRINTK1(args...) printk(args) -#define PRINTK(args...) printk(args) -#else -#define PRINTK1(args...) do { } while(0) -#define PRINTK(args...) printk(KERN_DEBUG args) -#endif + /* Start code <01> */ + bits[(*idx)++] = MII_MDOE; + bits[(*idx)++] = MII_MDOE | MII_MDO; +} -#if SMC_DEBUG > 3 -static void PRINT_PKT(u_char *buf, int length) +static inline void smc91x_phy_set_bits( u32 val, int numbits, u8 *bits, int *idx) { int i; - int remainder; - int lines; + u32 mask = 1<<(numbits-1); - lines = length / 16; - remainder = length % 16; + /* Output the PHY address, msb first */ + for (i = 0; i < numbits; ++i) { + if (val & mask) + bits[(*idx)++] = MII_MDOE | MII_MDO; + else + bits[(*idx)++] = MII_MDOE; - for (i = 0; i < lines ; i ++) { - int cur; - for (cur = 0; cur < 8; cur++) { - u_char a, b; - a = *buf++; - b = *buf++; - printk("%02x%02x ", a, b); - } - printk("\n"); - } - for (i = 0; i < remainder/2 ; i++) { - u_char a, b; - a = *buf++; - b = *buf++; - printk("%02x%02x ", a, b ); + /* Shift to next lowest bit */ + mask >>= 1; } - printk("\n"); } -#else -#define PRINT_PKT(x...) do { } while(0) -#endif - - -/* this enables an interrupt in the interrupt mask register */ -#define SMC_ENABLE_INT(x) do { \ - unsigned long flags; \ - unsigned char mask; \ - local_irq_save(flags); \ - mask = SMC_GET_INT_MASK(); \ - mask |= (x); \ - SMC_SET_INT_MASK(mask); \ - local_irq_restore(flags); \ -} while (0) - -/* this disables an interrupt from the interrupt mask register */ -#define SMC_DISABLE_INT(x) do { \ - unsigned long flags; \ - unsigned char mask; \ - local_irq_save(flags); \ - mask = SMC_GET_INT_MASK(); \ - mask &= ~(x); \ - SMC_SET_INT_MASK(mask); \ - local_irq_restore(flags); \ -} while (0) - -/* wait while MMU is busy */ -#define SMC_WAIT_MMU_BUSY() do { \ - if (SMC_GET_MMU_CMD() & MC_BUSY) { \ - unsigned long timeout = jiffies + 2; \ - while (SMC_GET_MMU_CMD() & MC_BUSY) { \ - if (time_after(jiffies, timeout)) { \ - printk("%s: timeout %s line %d\n", \ - dev->name, __FILE__, __LINE__); \ - break; \ - } \ - } \ - } \ -} while (0) - - -/* this does a soft reset on the device */ -static void -smc_reset(struct net_device *dev) -{ - unsigned long ioaddr = dev->base_addr; - - PRINTK2("%s: %s\n", dev->name, __FUNCTION__); - - /* This resets the registers mostly to defaults, but doesn't - affect EEPROM. That seems unnecessary */ - SMC_SELECT_BANK( 0 ); - SMC_SET_RCR( RCR_SOFTRST ); - - /* Setup the Configuration Register */ - /* This is necessary because the CONFIG_REG is not affected */ - /* by a soft reset */ - SMC_SELECT_BANK( 1 ); - SMC_SET_CONFIG( CONFIG_DEFAULT ); - - /* Setup for fast accesses if requested */ - /* If the card/system can't handle it then there will */ - /* be no recovery except for a hard reset or power cycle */ - if (nowait) - SMC_SET_CONFIG( SMC_GET_CONFIG() | CONFIG_NO_WAIT ); - -#ifdef POWER_DOWN - /* Release from possible power-down state */ - /* Configuration register is not affected by Soft Reset */ - SMC_SELECT_BANK( 1 ); - SMC_SET_CONFIG( SMC_GET_CONFIG() | CONFIG_EPH_POWER_EN ); -#endif - - /* this should pause enough for the chip to be happy */ - mdelay(1); - - /* Disable transmit and receive functionality */ - SMC_SELECT_BANK( 0 ); - SMC_SET_RCR( RCR_CLEAR ); - SMC_SET_TCR( TCR_CLEAR ); - - /* set the control register to automatically - release successfully transmitted packets, to make the best - use out of our limited memory */ - SMC_SELECT_BANK( 1 ); -#if ! THROTTLE_TX_PKTS - SMC_SET_CTL( SMC_GET_CTL() | CTL_AUTO_RELEASE ); -#else - SMC_SET_CTL( SMC_GET_CTL() & ~CTL_AUTO_RELEASE ); -#endif - /* Disable all interrupts */ - SMC_SELECT_BANK( 2 ); - SMC_SET_INT_MASK( 0 ); +static inline void smc91x_phy_set_address( u8 phyaddr, u8 *bits, int *idx) +{ + smc91x_phy_set_bits( phyaddr, 5, bits, idx); +} - /* Reset the MMU */ - SMC_SET_MMU_CMD( MC_RESET ); - SMC_WAIT_MMU_BUSY(); +static inline void smc91x_phy_set_register( u8 phyreg, u8 *bits, int *idx) +{ + smc91x_phy_set_bits( phyreg, 5, bits, idx); } -/* Enable Interrupts, Receive, and Transmit */ -static void -smc_enable(struct net_device *dev) +static inline void smc91x_phy_write_bits( unsigned long base, u8 *bits, int numbits) { - unsigned long ioaddr = dev->base_addr; - struct smc_local *lp = (struct smc_local *)dev->priv; - int mask; + int i; + int oldBank; + u16 mii_reg; - PRINTK2("%s: %s\n", dev->name, __FUNCTION__); + /* Save the current bank */ + oldBank = smc91x_get_bank(base); - /* see the header file for options in TCR/RCR DEFAULT*/ - SMC_SELECT_BANK( 0 ); - SMC_SET_TCR( lp->tcr_cur_mode ); - SMC_SET_RCR( lp->rcr_cur_mode ); + /* Select bank 3 */ + smc91x_set_bank(base, 3); - /* now, enable interrupts */ - mask = IM_EPH_INT|IM_RX_OVRN_INT|IM_RCV_INT; - if (lp->version >= 0x70) - mask |= IM_MDINT; - SMC_SELECT_BANK( 2 ); - SMC_SET_INT_MASK( mask ); -} + /* Get the current MII register value */ + mii_reg = smc91x_get_mii(base); -/* this puts the device in an inactive state */ -static void -smc_shutdown(unsigned long ioaddr) -{ - PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__); + /* Turn off all MII Interface bits */ + mii_reg &= ~(MII_MDOE | MII_MCLK | MII_MDI | MII_MDO); - /* no more interrupts for me */ - SMC_SELECT_BANK( 2 ); - SMC_SET_INT_MASK( 0 ); + /* Clock all 64 cycles */ + for (i = 0; i < numbits; ++i) { + /* Clock Low - output data */ + smc91x_set_mii( mii_reg | bits[i], base); - /* and tell the card to stay away from that nasty outside world */ - SMC_SELECT_BANK( 0 ); - SMC_SET_RCR( RCR_CLEAR ); - SMC_SET_TCR( TCR_CLEAR ); + /* Clock Hi - input data */ + smc91x_set_mii( mii_reg | bits[i] | MII_MCLK, base); + bits[i] |= (smc91x_get_mii(base) & MII_MDI); + } -#ifdef POWER_DOWN - /* finally, shut the chip down */ - SMC_SELECT_BANK( 1 ); - SMC_SET_CONFIG( SMC_GET_CONFIG() & ~CONFIG_EPH_POWER_EN ); -#endif -} + /* Return to idle state */ + /* Set clock to low, data to low, and output tristated */ + smc91x_set_mii( mii_reg, base); -/* This is the procedure to handle the receipt of a packet. */ -static inline void -smc_rcv(struct net_device *dev) -{ - struct smc_local *lp = (struct smc_local *)dev->priv; - unsigned long ioaddr = dev->base_addr; - unsigned int packet_number, status, packet_len; - - PRINTK3("%s: %s\n", dev->name, __FUNCTION__); - - packet_number = SMC_GET_RXFIFO(); - if (packet_number & RXFIFO_REMPTY) { - PRINTK("%s: smc_rcv with nothing on FIFO.\n", dev->name); - return; - } + /* Restore original bank select */ + smc91x_set_bank(base, oldBank); +} - /* read from start of packet */ - SMC_SET_PTR( PTR_READ | PTR_RCV | PTR_AUTOINC ); +static u16 smc91x_phy_reg_read(unsigned long base, u8 phyaddr, u8 phyreg) +{ + int i; + u8 bits[64]; + int clk_idx = 0; + int input_idx; + u16 phydata; - /* First two words are status and packet length */ - SMC_GET_PKT_HDR(status, packet_len); - packet_len &= 0x07ff; /* mask off top bits */ - PRINTK2("%s: RX PNR 0x%x STATUS 0x%04x LENGTH 0x%04x (%d)\n", - dev->name, packet_number, status, - packet_len, packet_len); + smc91x_phy_sync_bits( bits, &clk_idx); - if (!(status & RS_ERRORS)) { - struct sk_buff *skb; - unsigned char *data; - unsigned int data_len; + /* Read command <10> */ + bits[clk_idx++] = MII_MDOE | MII_MDO; + bits[clk_idx++] = MII_MDOE; - /* set multicast stats */ - if (status & RS_MULTICAST) - lp->stats.multicast++; + smc91x_phy_set_address( phyaddr, bits, &clk_idx); + smc91x_phy_set_register( phyreg, bits, &clk_idx); - /* - * Actual payload is packet_len - 4 (or 3 if odd byte). - * We want skb_reserve(2) and the final ctrl word - * (2 bytes, possibly containing the payload odd byte). - * Ence packet_len - 4 + 2 + 2. - */ - skb = dev_alloc_skb(packet_len); - if (skb == NULL) { - printk(KERN_NOTICE "%s: Low memory, packet dropped.\n", - dev->name); - lp->stats.rx_dropped++; - goto done; - } + /* Tristate and turnaround (2 bit times) */ + bits[clk_idx++] = 0; + /*bits[clk_idx++] = 0; */ - /* Align IP header to 32 bits */ - skb_reserve(skb, 2); + /* Input starts at this bit time */ + input_idx = clk_idx; - /* BUG: the LAN91C111 rev A never sets this bit. Force it. */ - if (lp->version == 0x90) - status |= RS_ODDFRAME; + /* Will input 16 bits */ + for (i = 0; i < 16; ++i) + bits[clk_idx++] = 0; - /* - * If odd length: packet_len - 3, - * otherwise packet_len - 4. - */ - data_len = packet_len - ((status & RS_ODDFRAME) ? 3 : 4); - data = skb_put(skb, data_len); - SMC_PULL_DATA(data, packet_len - 2); + /* Final clock bit */ + bits[clk_idx++] = 0; - PRINT_PKT(data, packet_len - 2); + smc91x_phy_write_bits( base, bits, sizeof bits); - dev->last_rx = jiffies; - skb->dev = dev; - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - lp->stats.rx_packets++; - lp->stats.rx_bytes += data_len; - } else { - /* error ... */ - lp->stats.rx_errors++; + /* Recover input data */ + phydata = 0; + for (i = 0; i < 16; ++i) { + phydata <<= 1; - if (status & RS_ALGNERR) - lp->stats.rx_frame_errors++; - if (status & (RS_TOOSHORT | RS_TOOLONG)) - lp->stats.rx_length_errors++; - if (status & RS_BADCRC) - lp->stats.rx_crc_errors++; + if (bits[input_idx++] & MII_MDI) + phydata |= 0x0001; } -done: - SMC_WAIT_MMU_BUSY(); - SMC_SET_MMU_CMD( MC_RELEASE ); + return phydata; } -/* - * This is called to actually send a packet to the chip. - * Returns non-zero when successful. - */ -static void -smc_hardware_send_packet(struct net_device *dev) -{ - struct smc_local *lp = (struct smc_local *)dev->priv; - unsigned long ioaddr = dev->base_addr; - struct sk_buff *skb = lp->saved_skb; - unsigned int packet_no, len; - unsigned char *buf; +static void smc91x_phy_reg_write( u16 phydata, unsigned long base, u8 phyaddr, u8 phyreg) +{ + u8 bits[65]; + int clk_idx = 0; - PRINTK3("%s: %s\n", dev->name, __FUNCTION__); + smc91x_phy_sync_bits( bits, &clk_idx); - if (!skb) { - printk ("%s: In XMIT with no packet to send\n", dev->name); - return; - } + /* Write command <01> */ + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; - packet_no = SMC_GET_AR(); - if (packet_no & AR_FAILED) { - printk("%s: Memory allocation failed.\n", dev->name); - lp->saved_skb = NULL; - lp->stats.tx_errors++; - lp->stats.tx_fifo_errors++; - dev_kfree_skb_any(skb); - return; - } + smc91x_phy_set_address( phyaddr, bits, &clk_idx); + smc91x_phy_set_register( phyreg, bits, &clk_idx); - /* point to the beginning of the packet */ - SMC_SET_PN( packet_no ); - SMC_SET_PTR( PTR_AUTOINC ); + /* Tristate and turnaround (2 bit times) */ + bits[clk_idx++] = 0; + bits[clk_idx++] = 0; - buf = skb->data; - len = skb->len; - PRINTK2("%s: TX PNR 0x%x lENGTH 0x%04x (%d) BUF 9x%p\n", - dev->name, packet_no, len, len, buf); - PRINT_PKT(buf, len); - - /* - * Send the packet length ( +6 for status words, length, and ctl. - * The card will pad to 64 bytes with zeroes if packet is too small. - */ - SMC_PUT_PKT_HDR(0, len + 6); + smc91x_phy_set_bits( phydata, 16, bits, &clk_idx); - /* send the actual data */ - SMC_PUSH_DATA(buf, len & ~1); + /* Final clock bit (tristate) */ + bits[clk_idx++] = 0; - /* Send final ctl word with the last byte if there is one */ - SMC_outw( ((len & 1) ? (0x2000 | buf[len-1]) : 0), ioaddr, DATA_REG ); + smc91x_phy_write_bits( base, bits, sizeof bits); +} - /* and let the chipset deal with it */ - SMC_SET_MMU_CMD( MC_ENQUEUE ); - SMC_ACK_INT( IM_TX_EMPTY_INT ); +static int smc91x_detect_phy( struct net_device *dev) +{ + smc91x_t *priv = (smc91x_t*) dev->priv; + unsigned int base = dev->base_addr; + u16 phy_id1; + u16 phy_id2; + int phyaddr; + int found = 0; - dev->trans_start = jiffies; - lp->saved_skb = NULL; - lp->stats.tx_packets++; - lp->stats.tx_bytes += len; - dev_kfree_skb_any(skb); -} + /* Scan all 32 PHY addresses if necessary */ + for (phyaddr = 0; phyaddr < 32; ++phyaddr) { + /* Read the PHY identifiers */ + phy_id1 = smc91x_phy_reg_read(base, phyaddr, PHY_ID1_REG); + phy_id2 = smc91x_phy_reg_read(base, phyaddr, PHY_ID2_REG); -/* - . Since I am not sure if I will have enough room in the chip's ram - . to store the packet, I call this routine which either sends it - . now, or set the card to generates an interrupt when ready - . for the packet. - */ -static int -smc_hard_start_xmit( struct sk_buff * skb, struct net_device * dev ) -{ - struct smc_local *lp = (struct smc_local *)dev->priv; - unsigned long ioaddr = dev->base_addr; - unsigned int numPages, poll_count, status, saved_bank; - - PRINTK3("%s: %s\n", dev->name, __FUNCTION__); - - if (lp->saved_skb) { - /* THIS SHOULD NEVER HAPPEN. */ - printk( KERN_CRIT - "%s: Bad Craziness - sent packet while busy.\n", - dev->name ); - lp->stats.tx_errors++; - lp->stats.tx_aborted_errors++; - return 1; - } - lp->saved_skb = skb; + if( smc_debug > 1) printk( KERN_INFO "%s: phy_id1=%x, phy_id2=%x\n", + dev->name, phy_id1, phy_id2); - /* - ** The MMU wants the number of pages to be the number of 256 bytes - ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) - ** - ** The 91C111 ignores the size bits, but the code is left intact - ** for backwards and future compatibility. - ** - ** Pkt size for allocating is data length +6 (for additional status - ** words, length and ctl!) - ** - ** If odd size then last byte is included in ctl word. - */ - numPages = ((skb->len & ~1) + (6 - 1)) >> 8; - if (numPages > 7) { - printk("%s: Far too big packet error.\n", dev->name); - lp->saved_skb = NULL; - lp->stats.tx_errors++; - lp->stats.tx_dropped++; - dev_kfree_skb(skb); - return 0; - } - - /* now, try to allocate the memory */ - saved_bank = SMC_CURRENT_BANK(); - SMC_SELECT_BANK( 2 ); - SMC_SET_MMU_CMD( MC_ALLOC | numPages ); - - /* - * Poll the chip for a short amount of time in case the - * allocation succeeds quickly. - */ - poll_count = MEMORY_WAIT_TIME; - do { - status = SMC_GET_INT(); - if (status & IM_ALLOC_INT) { - SMC_ACK_INT( IM_ALLOC_INT ); - break; + /* Make sure it is a valid identifier */ + if ((phy_id2 > 0x0000) && (phy_id2 < 0xffff) && + (phy_id1 > 0x0000) && (phy_id1 < 0xffff)) { + if ((phy_id1 != 0x8000) && (phy_id2 != 0x8000)) { + /* Save the PHY's address */ + priv->phyaddr = phyaddr; + found = 1; + break; + } } - } while (--poll_count); + } - if (!poll_count) { - /* oh well, wait until the chip finds memory later */ - netif_stop_queue(dev); - PRINTK2("%s: TX memory allocation deferred.\n", dev->name); - SMC_ENABLE_INT( IM_ALLOC_INT ); - } else { - /* Send current packet immediately.. */ -#if THROTTLE_TX_PKTS - netif_stop_queue(dev); -#endif - smc_hardware_send_packet(dev); - SMC_ENABLE_INT( IM_TX_INT | IM_TX_EMPTY_INT ); + if (!found) { + printk( KERN_INFO "%s: No PHY found\n", dev->name); + return -ENODEV; + } + + /* show PHY type */ + if (smc_debug && (phy_id1 == 0x0016) && ((phy_id2 & 0xFFF0) == 0xF840)) { + printk( KERN_INFO "%s: Detected PHY LAN83C183 (LAN91C111 Internal)\n", + dev->name); + } + + if (smc_debug && (phy_id1 == 0x0282) && ((phy_id2 & 0xFFF0) == 0x1C50)) { + printk( KERN_INFO "%s: Detected PHY LAN83C180\n", dev->name); } - SMC_SELECT_BANK( saved_bank ); return 0; } -/* - . This handles a TX interrupt, which is only called when an error - . relating to a packet is sent or CTL_AUTO_RELEASE is not set. -*/ -static void -smc_tx(struct net_device *dev) -{ - unsigned long ioaddr = dev->base_addr; - struct smc_local *lp = (struct smc_local *)dev->priv; - unsigned int saved_packet, packet_no, tx_status, pkt_len; - - PRINTK3("%s: %s\n", dev->name, __FUNCTION__); - - /* If the TX FIFO is empty then nothing to do */ - packet_no = SMC_GET_TXFIFO(); - if (packet_no & TXFIFO_TEMPTY) { - PRINTK("%s: smc_tx with nothing on FIFO.\n", dev->name); - return; +static void smc91x_phy_reset( struct net_device *dev) +{ + smc91x_t *priv = (smc91x_t *) dev->priv; + int timeout = 300; /* Wait up to 3 seconds */ + smc91x_phy_reg_write( PHY_CNTL_RST, dev->base_addr, priv->phyaddr, PHY_CNTL_REG); + /* Wait for the reset to complete, or time out */ + while (timeout--) { + if (!(smc91x_phy_reg_read(dev->base_addr, priv->phyaddr, PHY_CNTL_REG) + & PHY_CNTL_RST)) { + /* reset complete */ + break; + } + + mdelay(10); + } + if (timeout < 1) { + printk( KERN_ERR "%s: PHY reset timed out\n", dev->name); } - /* select packet to read from */ - saved_packet = SMC_GET_PN(); - SMC_SET_PN( packet_no ); - - /* read the first word (status word) from this packet */ - SMC_SET_PTR( PTR_AUTOINC | PTR_READ ); - SMC_GET_PKT_HDR(tx_status, pkt_len); - PRINTK2("%s: TX STATUS 0x%04x PNR 0x%02x\n", - dev->name, tx_status, packet_no); - - if (!(tx_status & TS_SUCCESS)) - lp->stats.tx_errors++; - if (tx_status & TS_LOSTCAR) - lp->stats.tx_carrier_errors++; - if (tx_status & TS_LATCOL) { - printk( KERN_DEBUG - "%s: Late collision occurred on last xmit.\n", - dev->name); - lp->stats.tx_window_errors++; - } - - /* kill the packet */ - SMC_WAIT_MMU_BUSY(); - SMC_SET_MMU_CMD( MC_FREEPKT ); - - /* Don't restore Packet Number Reg until busy bit is cleared */ - SMC_WAIT_MMU_BUSY(); - SMC_SET_PN( saved_packet ); - - /* re-enable transmit */ - SMC_SELECT_BANK( 0 ); - SMC_SET_TCR( lp->tcr_cur_mode ); - SMC_SELECT_BANK( 2 ); + /* phy sometimes ignores the first write after phy reset. Write + * default value to control register to get past this. + */ + smc91x_phy_reg_write( + PHY_CNTL_SPEED | PHY_CNTL_ANEG_EN | PHY_CNTL_MII_DIS, + dev->base_addr, priv->phyaddr, PHY_CNTL_REG); +} + +/* --- Helpers --- */ + +static void smc91x_reg_dump( struct net_device *dev, char *id) +{ + int i,b; + unsigned int base = dev->base_addr; + smc91x_t *priv = (smc91x_t *) dev->priv; + unsigned int phyaddr = priv->phyaddr; + + printk( KERN_INFO "Dump (%s)\n", id); + for( b=0; b<4; b++) { + printk( KERN_INFO "--- Bank %d\n", b); + for( i=0; i<14; i+=2) { + printk( KERN_INFO "%02x = %04x\n", + i, smc91x_reg_read( base, i, b)); + } + } + printk( KERN_INFO "--- PHY\n"); + for( i=0; i<6; i++) { + printk( KERN_INFO "%02x = %04x\n", + i, smc91x_phy_reg_read( base, phyaddr, i)); + } + for( i=16; i<20; i++) { + printk( KERN_INFO "%02x = %04x\n", + i, smc91x_phy_reg_read( base, phyaddr, i)); + } } - -//---PHY CONTROL AND CONFIGURATION----------------------------------------- - -/*------------------------------------------------------------ - . Debugging function for viewing MII Management serial bitstream - .-------------------------------------------------------------*/ -#if SMC_DEBUG > 3 -static void -PRINT_MII_STREAM(u_char *bits, int size) +static void smc91x_set_speed_and_duplex( struct net_device *dev, int speed, int dup) { - int i; + u16 val; + u16 phy_settings = 0; + u16 mac_settings = 0; + smc91x_t *priv = (smc91x_t*) dev->priv; - printk("BIT#:"); - for (i = 0; i < size; ++i) - printk("%d", i%10); - - printk("\nMDOE:"); - for (i = 0; i < size; ++i) { - if (bits[i] & MII_MDOE) - printk("1"); - else - printk("0"); + printk( KERN_DEBUG "%s: %smbps %s-duplex\n", dev->name, + speed?"100":"10", dup?"full":"half"); + + if( speed) { + phy_settings |= PHY_CNTL_SPEED; + mac_settings |= RPC_SPEED; + priv->speed = SPEED_100; + } else { + priv->speed = SPEED_10; } - printk("\nMDO :"); - for (i = 0; i < size; ++i) { - if (bits[i] & MII_MDO) - printk("1"); - else - printk("0"); + if( dup) { + phy_settings |= PHY_CNTL_DPLX; + mac_settings |= RPC_DPLX; + priv->duplex = DUPLEX_FULL; + + val = smc91x_reg_read(dev->base_addr, TCR_REG, 0) | TCR_SWFDUP; + } else { + priv->duplex = DUPLEX_HALF; + val = smc91x_reg_read(dev->base_addr, TCR_REG, 0) & ~TCR_SWFDUP; } + /* update switched full duplex mode */ + smc91x_reg_write( val, dev->base_addr, TCR_REG, 0); - printk("\nMDI :"); - for (i = 0; i < size; ++i) { - if (bits[i] & MII_MDI) - printk("1"); - else - printk("0"); + if( priv->autoneg_on) { + mac_settings |= RPC_ANEG; + phy_settings |= PHY_CNTL_ANEG_EN; } - printk("\n"); -} -#else -#define PRINT_MII_STREAM(x...) +#ifdef FIXED_LINK + val = smc91x_phy_reg_read(dev->base_addr, priv->phyaddr, PHY_CFG1_REG); + val |= PHY_CFG1_LNKDIS; + smc91x_phy_reg_write( val, dev->base_addr, priv->phyaddr, PHY_CFG1_REG); #endif -/*------------------------------------------------------------ - . Reads a register from the MII Management serial interface - .-------------------------------------------------------------*/ -static int -smc_read_phy_register(unsigned long ioaddr, int phyaddr, int phyreg) + val = phy_settings; + smc91x_phy_reg_write( val, dev->base_addr, priv->phyaddr, PHY_CNTL_REG); + + smc91x_reg_write( + mac_settings | + RPC_LEDA(RPC_LED_100_10) | RPC_LEDB(RPC_LED_TX_RX), + dev->base_addr, RPC_REG, 0); +} + +static void smc91x_autonegotiate( struct net_device *dev) { - int oldBank; - int i, mask, mii_reg; - u_char bits[64]; - int input_idx, phydata; - int clk_idx = 0; + smc91x_t *priv = (smc91x_t*) dev->priv; - // 32 consecutive ones on MDO to establish sync - for (i = 0; i < 32; ++i) - bits[clk_idx++] = MII_MDOE | MII_MDO; + u16 phy_status = smc91x_phy_reg_read(dev->base_addr, priv->phyaddr, PHY_STAT_REG); + if( priv->autoneg_on && (phy_status & PHY_STAT_CAP_ANEG)) { + u16 local_caps; + u16 advertise_caps; + u16 remote_caps; + int count; + local_caps = smc91x_phy_reg_read(dev->base_addr, priv->phyaddr, PHY_STAT_REG); - // Start code <01> - bits[clk_idx++] = MII_MDOE; - bits[clk_idx++] = MII_MDOE | MII_MDO; + advertise_caps = PHY_AD_CSMA; - // Read command <10> - bits[clk_idx++] = MII_MDOE | MII_MDO; - bits[clk_idx++] = MII_MDOE; + if (local_caps & PHY_STAT_CAP_T4) + advertise_caps |= PHY_AD_T4; - // Output the PHY address, msb first - mask = 0x10; - for (i = 0; i < 5; ++i) { - if (phyaddr & mask) - bits[clk_idx++] = MII_MDOE | MII_MDO; - else - bits[clk_idx++] = MII_MDOE; + if (local_caps & PHY_STAT_CAP_TXF) + advertise_caps |= PHY_AD_TX_FDX; - // Shift to next lowest bit - mask >>= 1; - } + if (local_caps & PHY_STAT_CAP_TXH) + advertise_caps |= PHY_AD_TX_HDX; - // Output the phy register number, msb first - mask = 0x10; - for (i = 0; i < 5; ++i) { - if (phyreg & mask) - bits[clk_idx++] = MII_MDOE | MII_MDO; - else - bits[clk_idx++] = MII_MDOE; + if (local_caps & PHY_STAT_CAP_TF) + advertise_caps |= PHY_AD_10_FDX; - // Shift to next lowest bit - mask >>= 1; + if (local_caps & PHY_STAT_CAP_TH) + advertise_caps |= PHY_AD_10_HDX; + + smc91x_phy_reg_write( advertise_caps, + dev->base_addr, priv->phyaddr, PHY_AD_REG); + + smc91x_reg_write( RPC_ANEG, dev->base_addr, RPC_REG, 0); + smc91x_phy_reg_write( PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST, + dev->base_addr, priv->phyaddr, PHY_CNTL_REG); + + count = 200; + while( count--) { + remote_caps = smc91x_phy_reg_read( + dev->base_addr, priv->phyaddr, PHY_RMT_REG); + if (remote_caps & PHY_AD_ACK) break; + mdelay(10); + } + + if( count < 1) { + printk(KERN_DEBUG "%s: PHY auto-negotiate timed out\n", + dev->name); + smc91x_set_speed_and_duplex( dev, 0, 0); + } else { + u16 phy_int; + u16 status = smc91x_phy_reg_read( + dev->base_addr, priv->phyaddr, PHY_STAT_REG); + + if (status & PHY_STAT_REM_FLT) { + printk(KERN_DEBUG "%s: PHY remote fault detected\n", dev->name); + } + + mdelay(100); + smc91x_phy_reg_read( dev->base_addr, priv->phyaddr, PHY_STAT_REG); + phy_int = smc91x_phy_reg_read( + dev->base_addr, priv->phyaddr, PHY_INT_REG); + + mdelay(100); + smc91x_phy_reg_read( dev->base_addr, priv->phyaddr, PHY_STAT_REG); + phy_int = smc91x_phy_reg_read( + dev->base_addr, priv->phyaddr, PHY_INT_REG); + + smc91x_set_speed_and_duplex( dev, + ((phy_int&PHY_INT_SPDDET)!=0), + ((phy_int&PHY_INT_DPLXDET)!=0)); + } + } else { + priv->autoneg_on = 0; + smc91x_set_speed_and_duplex( dev, + (priv->speed==SPEED_100), + (priv->duplex==DUPLEX_FULL)); } +} - // Tristate and turnaround (2 bit times) - bits[clk_idx++] = 0; - //bits[clk_idx++] = 0; +/* --- Ethtool --- */ - // Input starts at this bit time - input_idx = clk_idx; +static int smc91x_ethtool_ioctl( struct net_device *dev, void *useraddr) +{ + smc91x_t *priv = (smc91x_t*) dev->priv; + u32 ethcmd; - // Will input 16 bits - for (i = 0; i < 16; ++i) - bits[clk_idx++] = 0; + if (get_user(ethcmd, (u32 *)useraddr)) + return -EFAULT; - // Final clock bit - bits[clk_idx++] = 0; + switch (ethcmd) { + case ETHTOOL_GDRVINFO: /* get driver info */ + { + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + strcpy (info.driver, DRIVER_NAME); + strcpy (info.version, DRIVER_VERSION); + if (copy_to_user (useraddr, &info, sizeof (info))) + return -EFAULT; + } + return 0; - // Save the current bank - oldBank = SMC_CURRENT_BANK(); + case ETHTOOL_GSET: /* get settings */ + { + struct ethtool_cmd ecmd = { ETHTOOL_GSET }; + + ecmd.supported = + SUPPORTED_MII | + SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_Autoneg; + ecmd.advertising = + ADVERTISED_MII | + ADVERTISED_10baseT_Half | + ADVERTISED_10baseT_Full | + ADVERTISED_100baseT_Half | + ADVERTISED_100baseT_Full; + ecmd.speed = priv->speed; + ecmd.duplex = priv->duplex; + ecmd.port = PORT_MII; + ecmd.phy_address = priv->phyaddr; + ecmd.transceiver = XCVR_INTERNAL; + ecmd.autoneg = priv->autoneg_on; - // Select bank 3 - SMC_SELECT_BANK( 3 ); + if (copy_to_user(useraddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + } + return 0; - // Get the current MII register value - mii_reg = SMC_GET_MII(); + case ETHTOOL_SSET: /* set settings */ + { + struct ethtool_cmd ecmd; + if (copy_from_user(&ecmd, useraddr, sizeof(ecmd))) + return -EFAULT; + + if( ecmd.speed != SPEED_10 && + ecmd.speed != SPEED_100) + return -EINVAL; + else + priv->speed = ecmd.speed; + + if( ecmd.duplex != DUPLEX_HALF && + ecmd.duplex != DUPLEX_FULL) + return -EINVAL; + else + priv->duplex = ecmd.duplex; + + if( ecmd.autoneg != AUTONEG_DISABLE && + ecmd.autoneg != AUTONEG_ENABLE) + return -EINVAL; + else + priv->autoneg_on = (ecmd.autoneg==AUTONEG_ENABLE); - // Turn off all MII Interface bits - mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO); + smc91x_autonegotiate( dev); + } + return 0; - // Clock all 64 cycles - for (i = 0; i < sizeof bits; ++i) { - // Clock Low - output data - SMC_SET_MII( mii_reg | bits[i] ); - udelay(50); + case ETHTOOL_GLINK: /* get link status */ + { + struct ethtool_value edata = { ETHTOOL_GLINK }; + edata.data = netif_carrier_ok( dev); + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; - // Clock Hi - input data - SMC_SET_MII( mii_reg | bits[i] | MII_MCLK ); - udelay(50); - bits[i] |= SMC_GET_MII() & MII_MDI; - } + } + return 0; - // Return to idle state - // Set clock to low, data to low, and output tristated - SMC_SET_MII( mii_reg ); - udelay(50); + case ETHTOOL_GMSGLVL: /* get message-level */ + { + struct ethtool_value edata = { ETHTOOL_GMSGLVL }; + edata.data = smc_debug; + if (copy_to_user(useraddr, &edata, sizeof(edata))) + return -EFAULT; + } + return 0; +#ifdef SMC_DEBUG + case ETHTOOL_SMSGLVL: /* set message-level */ + { + struct ethtool_value edata; + if (copy_from_user(&edata, useraddr, sizeof(edata))) + return -EFAULT; + smc_debug = edata.data; + } + return 0; +#endif - // Restore original bank select - SMC_SELECT_BANK( oldBank ); + case ETHTOOL_NWAY_RST: /* restart autonegotiation */ + { + smc91x_autonegotiate( dev); + } + return 0; - // Recover input data - phydata = 0; - for (i = 0; i < 16; ++i) { - phydata <<= 1; +#ifdef ETHTOOL_TEST /* 2.4.19 and newer */ + case ETHTOOL_GSTRINGS: /* strings */ + { + } + return 0; - if (bits[input_idx++] & MII_MDI) - phydata |= 0x0001; + case ETHTOOL_TEST: /* test */ + { + smc91x_reg_dump( dev, "smc91x_ethtool_ioctl"); + } + return 0; +#endif + default: + break; } - PRINTK3("%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n", - __FUNCTION__, phyaddr, phyreg, phydata); - PRINT_MII_STREAM(bits, sizeof(bits)); - - return phydata; + return -EOPNOTSUPP; } -/*------------------------------------------------------------ - . Writes a register to the MII Management serial interface - .-------------------------------------------------------------*/ -static void -smc_write_phy_register( unsigned long ioaddr, int phyaddr, - int phyreg, int phydata ) -{ - int oldBank; - int i, mask, mii_reg; - u_char bits[65]; - int clk_idx = 0; +/* --- MAC --- */ - // 32 consecutive ones on MDO to establish sync - for (i = 0; i < 32; ++i) - bits[clk_idx++] = MII_MDOE | MII_MDO; +static void smc91x_enable( struct net_device *dev) +{ + smc91x_t *priv = (smc91x_t*) dev->priv; + u16 val; + int i; - // Start code <01> - bits[clk_idx++] = MII_MDOE; - bits[clk_idx++] = MII_MDOE | MII_MDO; + /* set EPH Power EN */ + val = smc91x_reg_read(dev->base_addr, CONFIG_REG, 1) | CONFIG_EPH_POWER_EN; + smc91x_reg_write( val, dev->base_addr, CONFIG_REG, 1); - // Write command <01> - bits[clk_idx++] = MII_MDOE; - bits[clk_idx++] = MII_MDOE | MII_MDO; + /* clear PDN in PHY MI */ + val = smc91x_phy_reg_read(dev->base_addr, priv->phyaddr, PHY_CNTL_REG) & ~PHY_CNTL_PDN; + smc91x_phy_reg_write( val, dev->base_addr, priv->phyaddr, PHY_CNTL_REG); - // Output the PHY address, msb first - mask = 0x10; - for (i = 0; i < 5; ++i) { - if (phyaddr & mask) - bits[clk_idx++] = MII_MDOE | MII_MDO; - else - bits[clk_idx++] = MII_MDOE; + /* MMU reset */ + smc91x_set_bank( dev->base_addr, 2); + smc91x_mmu_reset( dev->base_addr); + smc91x_mmu_wait_ready( dev->base_addr); - // Shift to next lowest bit - mask >>= 1; - } + /* enable tx */ + smc91x_reg_write( TCR_ENABLE, dev->base_addr, TCR_REG, 0); - // Output the phy register number, msb first - mask = 0x10; - for (i = 0; i < 5; ++i) { - if (phyreg & mask) - bits[clk_idx++] = MII_MDOE | MII_MDO; - else - bits[clk_idx++] = MII_MDOE; + /* enable rx */ + smc91x_reg_write( priv->rcr, dev->base_addr, RCR_REG, 0); - // Shift to next lowest bit - mask >>= 1; - } + /* enable auto-release */ + val = smc91x_reg_read(dev->base_addr, CTL_REG, 1) | CTL_AUTO_RELEASE; + smc91x_reg_write( val, dev->base_addr, CTL_REG, 1); - // Tristate and turnaround (2 bit times) - bits[clk_idx++] = 0; - bits[clk_idx++] = 0; + smc91x_phy_reset( dev); - // Write out 16 bits of data, msb first - mask = 0x8000; - for (i = 0; i < 16; ++i) { - if (phydata & mask) - bits[clk_idx++] = MII_MDOE | MII_MDO; - else - bits[clk_idx++] = MII_MDOE; + for (i = 0; i < 6; i += 2) { + u16 address; - // Shift to next lowest bit - mask >>= 1; + address = dev->dev_addr[i + 1] << 8; + address |= dev->dev_addr[i]; + smc91x_reg_write( address, dev->base_addr, ADDR0_REG+i, 1); } - // Final clock bit (tristate) - bits[clk_idx++] = 0; + smc91x_autonegotiate( dev); - // Save the current bank - oldBank = SMC_CURRENT_BANK(); + priv->last_phy_int = smc91x_phy_reg_read( + dev->base_addr, priv->phyaddr, PHY_INT_REG); + /* mask phy interrupts we are not interested in */ + smc91x_phy_reg_write( + PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD | + PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB, + dev->base_addr, priv->phyaddr, PHY_MASK_REG); - // Select bank 3 - SMC_SELECT_BANK( 3 ); + /* enable interrupts */ + smc91x_write_interrupt_mask( dev->base_addr, + IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT | IM_MDINT); +} - // Get the current MII register value - mii_reg = SMC_GET_MII(); +static void smc91x_reset( struct net_device *dev) +{ + smc91x_t *priv = (smc91x_t*) dev->priv; - // Turn off all MII Interface bits - mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO); + if( smc_debug) printk( KERN_INFO "%s: Pulling a reset!\n", dev->name); - // Clock all cycles - for (i = 0; i < sizeof bits; ++i) { - // Clock Low - output data - SMC_SET_MII( mii_reg | bits[i] ); - udelay(50); + /* disable tx */ + smc91x_reg_write( 0, dev->base_addr, TCR_REG, 0); - // Clock Hi - input data - SMC_SET_MII( mii_reg | bits[i] | MII_MCLK ); - udelay(50); - bits[i] |= SMC_GET_MII() & MII_MDI; - } + /* disable rx */ + smc91x_reg_write( 0, dev->base_addr, RCR_REG, 0); + + /* disable interrupts */ + smc91x_write_interrupt_mask( dev->base_addr, 0); + + /* soft reset */ + smc91x_reg_write( RCR_SOFTRST, dev->base_addr, RCR_REG, 0); + smc91x_reg_write( 0, dev->base_addr, RCR_REG, 0); - // Return to idle state - // Set clock to low, data to low, and output tristated - SMC_SET_MII( mii_reg ); - udelay(50); + mdelay(10); - // Restore original bank select - SMC_SELECT_BANK( oldBank ); + /* re-enable everything */ + smc91x_enable( dev); - PRINTK3("%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n", - __FUNCTION__, phyaddr, phyreg, phydata); - PRINT_MII_STREAM(bits, sizeof(bits)); + /* clear saved skb and open the queue */ + priv->saved_skb = 0; + netif_wake_queue (dev); } +static int smc91x_detect( struct net_device *dev) +{ + u16 bank; + + bank = smc91x_get_bank( dev->base_addr); + if( (bank & 0xFF00) != 0x3300) + return -ENODEV; + + smc91x_set_bank( dev->base_addr, 0); + + bank = smc91x_get_bank( dev->base_addr); + if( (bank & 0xFF00) != 0x3300) + return -ENODEV; -/*------------------------------------------------------------ - . Finds and reports the PHY address - .-------------------------------------------------------------*/ -static int smc_detect_phy(struct net_device* dev) + return smc91x_detect_phy( dev); +} + +static void smc91x_receive( struct net_device *dev) { - struct smc_local *lp = (struct smc_local *)dev->priv; - unsigned long ioaddr = dev->base_addr; - int phy_id1, phy_id2; - int phyaddr; - int found = 0; + unsigned int base = dev->base_addr; + smc91x_t *priv = (smc91x_t*) dev->priv; + u16 packet_number; + u32 val; + u16 status; + u16 packet_length; - PRINTK2("%s: %s\n", dev->name, __FUNCTION__); + if( smc_debug > 2) printk( KERN_INFO "%s: smc91x_receive\n", dev->name); - // Scan all 32 PHY addresses if necessary - for (phyaddr = 0; phyaddr < 32; ++phyaddr) { - // Read the PHY identifiers - phy_id1 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID1_REG); - phy_id2 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID2_REG); + packet_number = smc91x_read_rx_packet_number( base); - PRINTK3("%s: phy_id1=0x%x, phy_id2=0x%x\n", - dev->name, phy_id1, phy_id2); + if( packet_number & FIFO_RXREMPTY) { - // Make sure it is a valid identifier - if ((phy_id2 > 0x0000) && (phy_id2 < 0xffff) && - (phy_id1 > 0x0000) && (phy_id1 < 0xffff)) { - if ((phy_id1 != 0x8000) && (phy_id2 != 0x8000)) { - // Save the PHY's address - lp->phyaddr = phyaddr; - found = 1; - break; - } - } + /* we got called , but nothing was on the FIFO */ + printk( KERN_ERR "%s: smc91x_receive rx fifo empty.\n", + dev->name); + return; } - if (!found) { - PRINTK("%s: No PHY found\n", dev->name); - return(0); - } + smc91x_reg_write((PTR_READ | PTR_RCV | PTR_AUTOINC), base, PTR_REG, 2); - // Set the PHY type - if ( (phy_id1 == 0x0016) && ((phy_id2 & 0xFFF0) == 0xF840 ) ) { - lp->phytype = PHY_LAN83C183; - PRINTK("%s: PHY=LAN83C183 (LAN91C111 Internal)\n", dev->name); - } + val = smc91x_read_packet_header( base); + + status = (u16) val & 0xffff; + packet_length = (u16) (val >> 16) & 0x7ff; - if ( (phy_id1 == 0x0282) && ((phy_id2 & 0xFFF0) == 0x1C50) ) { - lp->phytype = PHY_LAN83C180; - PRINTK("%s: PHY=LAN83C180\n", dev->name); + if( packet_length <= 6) { + printk( KERN_ERR "%s: smc91x_receive rx packet len too small (%d)\n", + dev->name, packet_length); + goto done; } - return 1; -} + if (!(status & RS_ERRORS)) { + struct sk_buff *skb; + unsigned char *data; -/*------------------------------------------------------------ - . Waits the specified number of milliseconds - kernel friendly - .-------------------------------------------------------------*/ -static void -smc_wait_ms(unsigned int ms) -{ - if (!in_interrupt()) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1 + ms * HZ / 1000); + /* set multicast stats */ + if (status & RS_MULTICAST) + priv->stats.multicast++; + + if ( priv->odd_byte_bug || (status & RS_ODDFRAME)) + packet_length++; + + /* Allocate enough memory for entire receive frame, */ + /* plus alignment needs, to be safe */ + skb = dev_alloc_skb(packet_length + 12); + + if (skb == NULL) { + printk(KERN_ERR "%s: Low memory, packet dropped.\n", + dev->name); + priv->stats.rx_dropped++; + goto done; + } + + /* + * Apparently dev_alloc_skb is not guaranteed to return a nicely + * aligned data buffer. Make sure we get the alignment we need: + * + * BIG FAT WARNING: Some architectures (ARM Xscale for + * example) require specific alignments for DMA operations. + * In the case of the PXA250, 64 bit alignment is required + * for memory targets/sources. + */ + skb_reserve(skb, (-(u32) skb->data) & 7); /* 64 bit alignment */ + + skb->dev = dev; + data = skb_put(skb, packet_length); + + smc91x_read_packet_data( base, skb, data, packet_length); + + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + priv->stats.rx_packets++; + priv->stats.rx_bytes += packet_length; } else { - /* if this happens it must be fixed */ - printk( KERN_WARNING "%s: busy wait while in interrupt!\n", - __FUNCTION__); - mdelay(ms); + priv->stats.rx_errors++; + + if (status & RS_ALGNERR) + priv->stats.rx_frame_errors++; + if (status & (RS_TOOSHORT | RS_TOOLONG)) + priv->stats.rx_length_errors++; + if (status & RS_BADCRC) + priv->stats.rx_crc_errors++; } + +done: + smc91x_mmu_wait_ready( base); + smc91x_mmu_release( base); + + return; } -/*------------------------------------------------------------ - . Sets the PHY to a configuration as determined by the user - .-------------------------------------------------------------*/ -static int -smc_phy_fixed(struct net_device *dev) +static void smc91x_transmit( struct net_device *dev) { - unsigned long ioaddr = dev->base_addr; - struct smc_local *lp = (struct smc_local *)dev->priv; - int phyaddr = lp->phyaddr; - int my_fixed_caps, cfg1; + u16 length; + unsigned char *buf; + u16 packet_number; + smc91x_t *priv = (smc91x_t *) dev->priv; + unsigned long base = dev->base_addr; + struct sk_buff *skb = priv->saved_skb; - PRINTK3("%s: %s\n", dev->name, __FUNCTION__); + if( smc_debug > 2) printk( KERN_INFO "%s: smc91x_transmit\n", dev->name); - // Enter Link Disable state - cfg1 = smc_read_phy_register(ioaddr, phyaddr, PHY_CFG1_REG); - cfg1 |= PHY_CFG1_LNKDIS; - smc_write_phy_register(ioaddr, phyaddr, PHY_CFG1_REG, cfg1); + /* allocation done, feed chip data */ + length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + buf = skb->data; - // Set our fixed capabilities - // Disable auto-negotiation - my_fixed_caps = 0; + /*assume bank 2 */ + packet_number = smc91x_read_alloc_packet_number(base); - if (lp->ctl_rfduplx) - my_fixed_caps |= PHY_CNTL_DPLX; + if( packet_number & AR_FAILED) { + printk(KERN_ERR "%s: Memory allocation failed.\n", + dev->name); + priv->stats.tx_aborted_errors++; + priv->saved_skb = 0; + return; + } - if (lp->ctl_rspeed == 100) - my_fixed_caps |= PHY_CNTL_SPEED; + smc91x_write_packet_number(base, packet_number); + smc91x_reg_write( PTR_AUTOINC, base, PTR_REG, 2); - // Write our capabilities to the phy control register - smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, my_fixed_caps); + smc91x_write_packet_length(base, length, priv); + smc91x_write_packet_data(base, buf, length, priv); - // Re-Configure the Receive/Phy Control register - SMC_SET_RPC( lp->rpc_cur_mode ); + /* and let the chipset deal with it */ + smc91x_mmu_wait_ready( base); + smc91x_mmu_enqueue( base); + + if( smc_debug > 3) + printk( KERN_INFO "%s: Sent packet of length %d \n", + dev->name, length); + priv->tx_bytes += length; + priv->tx_packets++; + + priv->saved_skb = NULL; + dev_kfree_skb_any(skb); + dev->trans_start = jiffies; - // Success - return(1); + /* 10mbps needs throttling to reduce rcv overruns when + * using auto-release. + */ + if( priv->speed == SPEED_100) netif_wake_queue(dev); } -/*------------------------------------------------------------ - . Configures the specified PHY through the MII management interface - . using Autonegotiation. - . Calls smc_phy_fixed() if the user has requested a certain config. - .-------------------------------------------------------------*/ -static void -smc_phy_configure(struct net_device* dev) +static void smc91x_transmit_restart( struct net_device *dev) { - unsigned long ioaddr = dev->base_addr; - struct smc_local *lp = (struct smc_local *)dev->priv; - int timeout; - int phyaddr; - int my_phy_caps; // My PHY capabilities - int my_ad_caps; // My Advertised capabilities - int status; + smc91x_t *priv = (smc91x_t *) dev->priv; + unsigned long base = dev->base_addr; + u16 eph_status = smc91x_reg_read( base, EPH_STATUS_REG, 0); + u16 packet_number = smc91x_reg_read( base, FIFO_REG, 2); + + printk( KERN_DEBUG "%s: TX failed, retrying (pn=%04x,eph=%04x)\n", + dev->name, packet_number, eph_status); - PRINTK3("%s:smc_program_phy()\n", dev->name); + priv->stats.tx_errors++; + if (eph_status & ES_LOSTCARR) + priv->stats.tx_carrier_errors++; + if (eph_status & ES_LATCOL) + priv->stats.tx_window_errors++; - // Set the blocking flag - lp->autoneg_active = 1; + /* ack int, remove packet from fifo */ + smc91x_ack_interrupt( base, IM_TX_INT); - // Find the address and type of our phy - if (!smc_detect_phy(dev)) - goto smc_phy_configure_exit; + /* re-enqueue packet */ + smc91x_write_packet_number( base, packet_number); + smc91x_mmu_enqueue( base); - // Get the detected phy address - phyaddr = lp->phyaddr; + /* re-enable tx */ + smc91x_reg_write( TCR_ENABLE, base, TCR_REG, 0); +} - // Reset the PHY, setting all other bits to zero - smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, PHY_CNTL_RST); +static int smc91x_hard_start_xmit (struct sk_buff *skb,struct net_device *dev) +{ + long flags; + smc91x_t *priv = (smc91x_t *) dev->priv; + unsigned long base = dev->base_addr; - // Wait for the reset to complete, or time out - timeout = 6; // Wait up to 3 seconds - while (timeout--) { - if (!(smc_read_phy_register(ioaddr, phyaddr, PHY_CNTL_REG) - & PHY_CNTL_RST)) - // reset complete - break; - smc_wait_ms(500); // wait 500 millisecs - if (signal_pending(current)) { // Exit anyway if signaled - PRINTK("%s: PHY reset interrupted by signal\n", + if( smc_debug > 2) printk( KERN_INFO "%s: smc91x_hard_start_xmit\n", dev->name); + + if (priv->saved_skb) { + /* this should never happen */ + priv->stats.tx_aborted_errors++; + printk(KERN_ERR "%s: Unexpected send packet request. " + "Not finished with previous one.\n", dev->name); - timeout = 0; - break; - } + return 1; } + priv->saved_skb = skb; - if (timeout < 1) { - printk("%s: PHY reset timed out\n", dev->name); - goto smc_phy_configure_exit; - } + netif_stop_queue (dev); + + /* All the other mmu operations (excluding reset) are in interrupt + * context. I.e. this is the only place we have to be careful + * about really having a non-busy mmu after the wait_ready call. + * Also the ALLOC_INT cannot be cleared directly (see 8.21) so + * we want to be sure interrupts are off until we know the alloc + * request has been submitted. + */ + local_irq_save(flags); + smc91x_enable_interrupt( base, IM_ALLOC_INT); + smc91x_mmu_wait_ready( base); + smc91x_mmu_alloc( base); /* allocation request for memory page */ + local_irq_restore(flags); + + return 0; +} - // Read PHY Register 18, Status Output - lp->lastPhy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG); +static void smc91x_update_stats( unsigned long base, smc91x_t *priv) +{ + u16 card_stats; + + /* update stats */ + card_stats = smc91x_reg_read( base, COUNTER_REG, 0); + /* single collisions */ + priv->stats.collisions += card_stats & 0xF; + card_stats >>= 4; + /* multiple collisions */ + priv->stats.collisions += card_stats & 0xF; + + priv->stats.tx_packets += priv->tx_packets; + priv->stats.tx_bytes += priv->tx_bytes; + priv->tx_bytes = 0; + priv->tx_packets = 0; +} - // Enable PHY Interrupts (for register 18) - // Interrupts listed here are disabled - smc_write_phy_register(ioaddr, phyaddr, PHY_MASK_REG, - PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD | - PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB | - PHY_INT_SPDDET | PHY_INT_DPLXDET); +static void smc91x_wait_for_link(unsigned long _dev) +{ + struct net_device *dev = (struct net_device *)_dev; + smc91x_t *priv = (smc91x_t*) dev->priv; + int count; + u16 status; + + /* wait for link to come up */ + count = 200; + do { + status = smc91x_phy_reg_read( + dev->base_addr, priv->phyaddr, PHY_STAT_REG); + udelay(100); + } while ( ((status & PHY_STAT_LINK)==0) && --count); + + /* wait for remote fault to clear */ + count = 200; + do { + status = smc91x_phy_reg_read( + dev->base_addr, priv->phyaddr, PHY_STAT_REG); + udelay(100); + } while ( ((status & PHY_STAT_REM_FLT)!=0) && --count); - /* Configure the Receive/Phy Control register */ - SMC_SELECT_BANK( 0 ); - SMC_SET_RPC( lp->rpc_cur_mode ); + /* Make sure any latched bits get sucked out. + * The lnkfail bit seems to be tied to the link-status bit. + * No suck, no interrupt :( + */ + mdelay(100); + smc91x_phy_reg_read( dev->base_addr, priv->phyaddr, PHY_STAT_REG); + smc91x_phy_reg_read( dev->base_addr, priv->phyaddr, PHY_INT_REG); + mdelay(100); + smc91x_phy_reg_read( dev->base_addr, priv->phyaddr, PHY_STAT_REG); + smc91x_phy_reg_read( dev->base_addr, priv->phyaddr, PHY_INT_REG); + mdelay(100); + smc91x_phy_reg_read( dev->base_addr, priv->phyaddr, PHY_STAT_REG); + smc91x_phy_reg_read( dev->base_addr, priv->phyaddr, PHY_INT_REG); +} + +static void smc91x_phy_interrupt( struct net_device *dev) +{ + smc91x_t *priv = (smc91x_t*) dev->priv; + u16 phy_int = smc91x_phy_reg_read( + dev->base_addr, priv->phyaddr, PHY_INT_REG); + u16 changed = phy_int ^ priv->last_phy_int; + + if( smc_debug) + printk( KERN_INFO "%s: PHY MI int (last=%04x, new=%04x)\n", + dev->name, priv->last_phy_int, phy_int); + + if( changed & (PHY_INT_SPDDET | PHY_INT_DPLXDET)) { + if( smc_debug) printk( KERN_INFO "%s: PHY MI new speed/duplex\n", dev->name); + /* FIXME: detection works, but we don't fully get back on our feet. + * Reset PHY, MAC, etc. ...? + */ + if( smc_debug > 2) smc91x_reg_dump( dev, "smc91x_phy_interrupt"); - // Copy our capabilities from PHY_STAT_REG to PHY_AD_REG - my_phy_caps = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG); + smc91x_set_speed_and_duplex( dev, + ((phy_int&PHY_INT_SPDDET)!=0), + ((phy_int&PHY_INT_DPLXDET)!=0)); - // If the user requested no auto neg, then go set his request - if (!(lp->ctl_autoneg)) { - smc_phy_fixed(dev); - goto smc_phy_configure_exit; + tasklet_schedule(&priv->link_tasklet); } - if( !( my_phy_caps & PHY_STAT_CAP_ANEG)) - { - printk(KERN_INFO "Auto negotiation NOT supported\n"); - smc_phy_fixed(dev); - goto smc_phy_configure_exit; + if( phy_int&PHY_INT_LNKFAIL) { + printk( KERN_DEBUG "%s: Link down\n", dev->name); + netif_carrier_off( dev); + } else { + printk( KERN_DEBUG "%s: Link up\n", dev->name); + netif_carrier_on( dev); } - my_ad_caps = PHY_AD_CSMA; // I am CSMA capable + priv->last_phy_int = phy_int; +} - if (my_phy_caps & PHY_STAT_CAP_T4) - my_ad_caps |= PHY_AD_T4; +static void smc91x_eph_interrupt( struct net_device *dev) +{ + unsigned int base = dev->base_addr; + printk( KERN_INFO "%s: EPH int %x\n", dev->name, + smc91x_reg_read( base, EPH_STATUS_REG, 0)); +} - if (my_phy_caps & PHY_STAT_CAP_TXF) - my_ad_caps |= PHY_AD_TX_FDX; +static void smc91x_interrupt (int irq,void *id,struct pt_regs *regs) +{ + struct net_device *dev = id; + unsigned int base = dev->base_addr; + smc91x_t *priv = (smc91x_t*) dev->priv; + u16 oldBank; + u8 mask; + int timeout = 10; - if (my_phy_caps & PHY_STAT_CAP_TXH) - my_ad_caps |= PHY_AD_TX_HDX; + /* Save the current bank select */ + oldBank = smc91x_get_bank(base); + /* Get current interrupt mask */ + mask = smc91x_read_interrupt_mask( base); + /* Mask interrupts */ + smc91x_write_interrupt_mask( base, 0); - if (my_phy_caps & PHY_STAT_CAP_TF) - my_ad_caps |= PHY_AD_10_FDX; + if( smc_debug > 3) + printk( KERN_INFO "smc91x_interrupt mask %x stat %x\n", + mask, smc91x_read_interrupt_status( base)); - if (my_phy_caps & PHY_STAT_CAP_TH) - my_ad_caps |= PHY_AD_10_HDX; + do { + u8 status = smc91x_read_interrupt_status( base) & mask; - // Disable capabilities not selected by our user - if (lp->ctl_rspeed != 100) - my_ad_caps &= ~(PHY_AD_T4|PHY_AD_TX_FDX|PHY_AD_TX_HDX); + if( smc_debug > 3) + printk( KERN_INFO "smc91x_interrupt2 mask %x stat %x\n", + mask, smc91x_read_interrupt_status( base)); - if (!lp->ctl_rfduplx) - my_ad_caps &= ~(PHY_AD_TX_FDX|PHY_AD_10_FDX); + if (!status) break; - // Update our Auto-Neg Advertisement Register - smc_write_phy_register(ioaddr, phyaddr, PHY_AD_REG, my_ad_caps); + if (status & IM_RCV_INT) { + smc91x_receive( dev); + } else if (status & IM_RX_OVRN_INT) { + priv->stats.rx_errors++; + priv->stats.rx_fifo_errors++; + smc91x_ack_interrupt( base, IM_RX_OVRN_INT); + } else if (status & IM_TX_EMPTY_INT) { + smc91x_ack_interrupt( base, IM_TX_EMPTY_INT); + if( status & IM_TX_INT) { + smc91x_transmit_restart( dev); + } else { + smc91x_update_stats( base, priv); + /* 10mbps needs throttling to reduce rcv overruns + * when using auto-release. + */ + if( priv->speed == SPEED_10) netif_wake_queue(dev); + mask &= ~(IM_TX_INT | IM_TX_EMPTY_INT); + } + } else if (status & IM_TX_INT) { + u16 eph_status; + eph_status = smc91x_reg_read( base, EPH_STATUS_REG, 0); + + if( eph_status & ES_TX_SUC) { + /* Should never get here for auto-release */ + printk( KERN_INFO "TX ok\n"); + smc91x_ack_interrupt( base, IM_TX_INT); + } else { + smc91x_transmit_restart( dev); + } + } else if (status & IM_ALLOC_INT) { + mask &= ~IM_ALLOC_INT; + smc91x_transmit( dev); + /* enable tx interrupts */ + mask |= (IM_TX_INT | IM_TX_EMPTY_INT); + } else if (status & IM_MDINT) { + smc91x_ack_interrupt( base, IM_MDINT); + smc91x_phy_interrupt( dev); + } else if (status & IM_EPH_INT) { + smc91x_eph_interrupt( dev); + } + } while (timeout--); - // Read the register back. Without this, it appears that when - // auto-negotiation is restarted, sometimes it isn't ready and - // the link does not come up. - status = smc_read_phy_register(ioaddr, phyaddr, PHY_AD_REG); + /* Re-enable interrupts */ + smc91x_write_interrupt_mask( base, mask); + /* Restore original bank select */ + smc91x_set_bank(base, oldBank); +} - PRINTK2("%s: phy caps=%x\n", dev->name, my_phy_caps); - PRINTK2("%s: phy advertised caps=%x\n", dev->name, my_ad_caps); +static int smc91x_open (struct net_device *dev) +{ + int result; + smc91x_t *priv = (smc91x_t*) dev->priv; - // Restart auto-negotiation process in order to advertise my caps - smc_write_phy_register( ioaddr, phyaddr, PHY_CNTL_REG, - PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST ); +#if defined(CONFIG_ARCH_PXA) + set_irq_type(dev->irq, IRQT_RISING); +#endif - // Wait for the auto-negotiation to complete. This may take from - // 2 to 3 seconds. - // Wait for the reset to complete, or time out - timeout = 20; // Wait up to 10 seconds - while (timeout--) { - status = smc_read_phy_register(ioaddr, phyaddr, PHY_RMT_REG); - if (status & PHY_AD_ACK) - // auto-negotiate complete - break; + /* install interrupt handler */ + if ((result = request_irq (dev->irq,&smc91x_interrupt,0,dev->name,dev)) < 0) { + printk (KERN_ERR "%s: could not register interrupt %d\n",dev->name,dev->irq); + return result; + } - smc_wait_ms(500); // wait 500 millisecs - if (signal_pending(current)) { // Exit anyway if signaled - printk(KERN_DEBUG - "%s: PHY auto-negotiate interrupted by signal\n", - dev->name); - timeout = 0; - break; +#if defined(CONFIG_ARCH_PXA) + /* treat a dma addr of zero as no dma */ + if( priv->dma_addr) { + result = pxa_request_dma( + dev->name, DMA_PRIO_LOW, smc91x_dma_irq, (void *) priv); + if (result < 0) { + printk(KERN_ERR "%s: unable to acquire a DMA channel.\n", dev->name); + free_irq (dev->irq, dev); + return result; } + dev->dma = result; } - status = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG); +#endif - if (timeout < 1) { - PRINTK("%s: PHY auto-negotiate timed out\n", dev->name); - } + smc91x_enable( dev); - // Fail if we detected an auto-negotiate remote fault - if (status & PHY_STAT_REM_FLT) { - PRINTK("%s: PHY remote fault detected\n", dev->name); - } + /* start the queue */ + netif_start_queue (dev); - // Wait for link. Once the link is up, phy18 should be up to date - timeout = 200; - do - { - udelay(100); - status = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG); - } while ( ((status & PHY_STAT_LINK)==0) && --timeout); + MOD_INC_USE_COUNT; - if (status & PHY_STAT_LINK) - { - PRINTK("%s: Ethernet Link Detected\n", dev->name); - } + return 0; +} - // The smc_phy_interrupt() routine will be called to update lastPhy18 +static int smc91x_stop (struct net_device *dev) +{ + smc91x_t *priv = (smc91x_t*) dev->priv; + u16 val; - // Set our sysctl parameters to match auto-negotiation results - if ( lp->lastPhy18 & PHY_INT_SPDDET ) { - PRINTK("%s: PHY 100BaseT\n", dev->name); - lp->rpc_cur_mode |= RPC_SPEED; - } else { - PRINTK("%s: PHY 10BaseT\n", dev->name); - lp->rpc_cur_mode &= ~RPC_SPEED; - } + if( smc_debug > 3) smc91x_reg_dump( dev, "smc91x_stop"); - if ( lp->lastPhy18 & PHY_INT_DPLXDET ) { - PRINTK("%s: PHY Full Duplex\n", dev->name); - lp->rpc_cur_mode |= RPC_DPLX; - lp->tcr_cur_mode |= TCR_SWFDUP; - } else { - PRINTK("%s: PHY Half Duplex\n", dev->name); - lp->rpc_cur_mode &= ~RPC_DPLX; - lp->tcr_cur_mode &= ~TCR_SWFDUP; - } - - // Re-Configure the Receive/Phy Control register and TCR - SMC_SET_RPC( lp->rpc_cur_mode ); - SMC_SET_TCR( lp->tcr_cur_mode ); - -smc_phy_configure_exit: - // Exit auto-negotiation - lp->autoneg_active = 0; -} - -/************************************************************************* - . smc_phy_interrupt - . - . Purpose: Handle interrupts relating to PHY register 18. This is - . called from the "hard" interrupt handler. - . - ************************************************************************/ -static void -smc_phy_interrupt(struct net_device* dev) -{ - unsigned long ioaddr = dev->base_addr; - struct smc_local *lp = (struct smc_local *)dev->priv; - int phyaddr = lp->phyaddr; - int phy18; - - PRINTK2("%s: %s\n", dev->name, __FUNCTION__); - - for(;;) { - // Read PHY Register 18, Status Output - phy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG); + /* stop the queue */ + netif_stop_queue( dev); - // Exit if not more changes - if (phy18 == lp->lastPhy18) - break; + /* disable tx */ + smc91x_reg_write( 0, dev->base_addr, TCR_REG, 0); -#if SMC_DEBUG > 1 - PRINTK2("%s: phy18=0x%04x\n", dev->name, phy18); - PRINTK2("%s: lastPhy18=0x%04x\n", dev->name, lp->lastPhy18); - - // Handle events - if ((phy18 & PHY_INT_LNKFAIL) != - (lp->lastPhy18 & PHY_INT_LNKFAIL)) - PRINTK2("%s: PHY Link Fail=%x\n", dev->name, - phy18 & PHY_INT_LNKFAIL); - - if ((phy18 & PHY_INT_LOSSSYNC) != - (lp->lastPhy18 & PHY_INT_LOSSSYNC)) - PRINTK2("%s: PHY LOSS SYNC=%x\n", dev->name, - phy18 & PHY_INT_LOSSSYNC); - - if ((phy18 & PHY_INT_CWRD) != (lp->lastPhy18 & PHY_INT_CWRD)) - PRINTK2("%s: PHY INVALID 4B5B code=%x\n", dev->name, - phy18 & PHY_INT_CWRD); - - if ((phy18 & PHY_INT_SSD) != (lp->lastPhy18 & PHY_INT_SSD)) - PRINTK2("%s: PHY No Start Of Stream=%x\n", dev->name, - phy18 & PHY_INT_SSD); - - if ((phy18 & PHY_INT_ESD) != (lp->lastPhy18 & PHY_INT_ESD)) - - PRINTK2("%s: PHY No End Of Stream=%x\n", dev->name, - phy18 & PHY_INT_ESD); - - if ((phy18 & PHY_INT_RPOL) != (lp->lastPhy18 & PHY_INT_RPOL)) - PRINTK2("%s: PHY Reverse Polarity Detected=%x\n", - dev->name, phy18 & PHY_INT_RPOL); - - if ((phy18 & PHY_INT_JAB) != (lp->lastPhy18 & PHY_INT_JAB)) - PRINTK2("%s: PHY Jabber Detected=%x\n", dev->name, - phy18 & PHY_INT_JAB); - - if ((phy18 & PHY_INT_SPDDET) != - (lp->lastPhy18 & PHY_INT_SPDDET)) - PRINTK2("%s: PHY Speed Detect=%x\n", dev->name, - phy18 & PHY_INT_SPDDET); - - if ((phy18 & PHY_INT_DPLXDET) != - (lp->lastPhy18 & PHY_INT_DPLXDET)) - PRINTK2("%s: PHY Duplex Detect=%x\n", dev->name, - phy18 & PHY_INT_DPLXDET); -#endif - // Update the last phy 18 variable - lp->lastPhy18 = phy18; - } -} + /* TODO: remove and release all tx packets */ -//--- END PHY CONTROL AND CONFIGURATION------------------------------------- + /* disable rx */ + smc91x_reg_write( 0, dev->base_addr, RCR_REG, 0); + /* TODO: process and release remaining rx packets */ -/* - * This is the main routine of the driver, to handle the device when - * it needs some attention. - */ -static void -smc_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = dev_id; - unsigned long ioaddr = dev->base_addr; - struct smc_local *lp = (struct smc_local *)dev->priv; - int status, mask, timeout, card_stats; - int saved_bank, saved_pointer; + /* disable interrupts */ + smc91x_write_interrupt_mask( dev->base_addr, 0); - PRINTK3("%s: %s\n", dev->name, __FUNCTION__); + /* set PDN in PHY MI */ + val = smc91x_phy_reg_read(dev->base_addr, priv->phyaddr, PHY_CNTL_REG) | PHY_CNTL_PDN; + smc91x_phy_reg_write( val, dev->base_addr, priv->phyaddr, PHY_CNTL_REG); - saved_bank = SMC_CURRENT_BANK(); - SMC_SELECT_BANK(2); - saved_pointer = SMC_GET_PTR(); - mask = SMC_GET_INT_MASK(); - SMC_SET_INT_MASK( 0 ); + /* clear EPH Power EN */ + val = smc91x_reg_read(dev->base_addr, CONFIG_REG, 1) & ~CONFIG_EPH_POWER_EN; + smc91x_reg_write( val, dev->base_addr, CONFIG_REG, 1); - /* set a timeout value, so I don't stay here forever */ - timeout = 8; + /* uninstall interrupt handler */ + free_irq( dev->irq,dev); - do { - status = SMC_GET_INT(); +#if defined(CONFIG_ARCH_PXA) + pxa_free_dma( dev->dma); +#endif - PRINTK2("%s: IRQ 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x\n", - dev->name, status, mask, - ({ int meminfo; SMC_SELECT_BANK(0); - meminfo = SMC_GET_MIR(); - SMC_SELECT_BANK(2); meminfo; }), - SMC_GET_FIFO()); + MOD_DEC_USE_COUNT; - status &= mask; - if (!status) - break; + return 0; +} - if (status & IM_TX_INT) { - PRINTK3("%s: TX int\n", dev->name); - smc_tx(dev); - SMC_ACK_INT( IM_TX_INT ); -#if THROTTLE_TX_PKTS - netif_wake_queue(dev); -#endif - } else if (status & IM_RCV_INT) { - PRINTK3("%s: RX irq\n", dev->name); - smc_rcv(dev); - } else if (status & IM_ALLOC_INT) { - PRINTK3("%s: Allocation irq\n", dev->name); - smc_hardware_send_packet(dev); - mask |= (IM_TX_INT | IM_TX_EMPTY_INT); - mask &= ~IM_ALLOC_INT; -#if ! THROTTLE_TX_PKTS - netif_wake_queue(dev); -#endif - } else if (status & IM_TX_EMPTY_INT) { - PRINTK3("%s: TX empty\n", dev->name); - mask &= ~IM_TX_EMPTY_INT; +static void smc91x_transmit_timeout (struct net_device *dev) +{ + smc91x_t *priv = (smc91x_t *) dev->priv; - /* update stats */ - SMC_SELECT_BANK( 0 ); - card_stats = SMC_GET_COUNTER(); - SMC_SELECT_BANK( 2 ); - - /* single collisions */ - lp->stats.collisions += card_stats & 0xF; - card_stats >>= 4; + smc91x_reg_dump( dev, "smc91x_transmit_timeout"); - /* multiple collisions */ - lp->stats.collisions += card_stats & 0xF; - } else if (status & IM_RX_OVRN_INT) { - PRINTK1( "%s: RX overrun\n", dev->name); - lp->stats.rx_errors++; - lp->stats.rx_fifo_errors++; - SMC_ACK_INT( IM_RX_OVRN_INT ); - } else if (status & IM_EPH_INT) { - PRINTK("%s: UNSUPPORTED: EPH INTERRUPT\n", dev->name); - } else if (status & IM_MDINT) { - SMC_ACK_INT( IM_MDINT ); - smc_phy_interrupt(dev); - } else if (status & IM_ERCV_INT ) { - SMC_ACK_INT( IM_ERCV_INT ); - PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT \n", dev->name); - } - } while (--timeout); + priv->stats.tx_errors++; + priv->stats.tx_heartbeat_errors++; + priv->tx_bytes = 0; + priv->tx_packets = 0; - /* restore register states */ - SMC_SET_INT_MASK( mask ); - SMC_SET_PTR( saved_pointer ); - SMC_SELECT_BANK( saved_bank ); - - PRINTK3("%s: Interrupt done\n", dev->name); -} - -/* Our watchdog timed out. Called by the networking layer */ -static void -smc_timeout(struct net_device *dev) -{ - struct smc_local *lp = (struct smc_local *)dev->priv; - - PRINTK2("%s: %s\n", dev->name, __FUNCTION__); - - /* If we get here, some higher level has decided we are broken. - There should really be a "kick me" function call instead. */ - printk(KERN_WARNING "%s: transmit timed out\n", dev->name); - - /* "kick" the adaptor */ - smc_reset(dev); - smc_enable(dev); - -#if 0 - /* Reconfiguring the PHY doesn't seem like a bad idea here, but - * it introduced a problem. Now that this is a timeout routine, - * we are getting called from within an interrupt context. - * smc_phy_configure() calls smc_wait_ms() which calls - * schedule_timeout() which calls schedule(). When schedule() - * is called from an interrupt context, it prints out - * "Scheduling in interrupt" and then calls BUG(). This is - * obviously not desirable. This was worked around by removing - * the call to smc_phy_configure() here because it didn't seem - * absolutely necessary. Ultimately, if smc_wait_ms() is - * supposed to be usable from an interrupt context (which it - * looks like it thinks it should handle), it should be fixed. - */ - /* Reconfigure the PHY */ - smc_phy_configure(dev); -#endif + smc91x_reset( dev); +} - dev->trans_start = jiffies; - /* clear anything saved */ - if (lp->saved_skb != NULL) { - dev_kfree_skb (lp->saved_skb); - lp->saved_skb = NULL; - } - netif_wake_queue(dev); +static struct net_device_stats *smc91x_get_stats (struct net_device *dev) +{ + smc91x_t *priv = (smc91x_t*) dev->priv; + return & priv->stats; } -/* - * Finds the CRC32 of a set of bytes. - * (from Peter Cammaert's code) - */ -static int -crc32(char *s, int length) +static int smc91x_do_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +{ + int rc = 0; + + if (!netif_running(dev)) + return -EINVAL; + + switch (cmd) { + case SIOCETHTOOL: + return smc91x_ethtool_ioctl(dev, (void *) rq->ifr_data); + + default: + rc = -EOPNOTSUPP; + break; + } + + return rc; +} + +/* Finds the CRC32 of a set of bytes. From Peter Cammaert's code. */ +static int smc91x_crc32(char *s, int length) { /* indices */ int perByte; @@ -1466,592 +1214,252 @@ /* crc value - preinitialized to all 1's */ unsigned long crc_value = 0xffffffff; - for ( perByte = 0; perByte < length; perByte ++ ) { - unsigned char c; + for (perByte = 0; perByte < length; perByte++) { + unsigned char c; c = *(s++); - for ( perBit = 0; perBit < 8; perBit++ ) { - crc_value = (crc_value>>1)^ - (((crc_value^c)&0x01)?poly:0); + for (perBit = 0; perBit < 8; perBit++) { + crc_value = (crc_value >> 1) ^ + (((crc_value ^ c) & 0x01) ? poly : 0); c >>= 1; } } - return crc_value; + return crc_value; } -/* - . This sets the internal hardware table to filter out unwanted multicast - . packets before they take up memory. - . - . The SMC chip uses a hash table where the high 6 bits of the CRC of - . address are the offset into the table. If that bit is 1, then the - . multicast packet is accepted. Otherwise, it's dropped silently. - . - . To use the 6 bits as an offset into the table, the high 3 bits are the - . number of the 8 bit register, while the low 3 bits are the bit within - . that register. - . - . This routine is based very heavily on the one provided by Peter Cammaert. -*/ -static void -smc_setmulticast(unsigned long ioaddr, int count, struct dev_mc_list *addrs) -{ - int i; - unsigned char multicast_table[ 8 ]; - struct dev_mc_list *cur_addr; - +static void smc91x_set_multicast_filter (struct net_device *dev) +{ + int i; + unsigned char multicast_table[8]; + struct dev_mc_list *mc_addr = dev->mc_list; + int mc_count = dev->mc_count; /* table for flipping the order of 3 bits */ - static unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* start with a table of all zeros: reject all */ - memset( multicast_table, 0, sizeof( multicast_table ) ); + memset(multicast_table, 0, sizeof (multicast_table)); - cur_addr = addrs; - for ( i = 0; i < count ; i ++, cur_addr = cur_addr->next ) { + for (i = 0; i < mc_count; i++, mc_addr = mc_addr->next) { int position; /* do we have a pointer here? */ - if ( !cur_addr ) + if (!mc_addr) break; + /* make sure this is a multicast address - shouldn't this be a given if we have it here ? */ - if ( !( *cur_addr->dmi_addr & 1 ) ) + if (!(*mc_addr->dmi_addr & 1)) continue; /* only use the low order bits */ - position = crc32( cur_addr->dmi_addr, 6 ) & 0x3f; + position = smc91x_crc32(mc_addr->dmi_addr, 6) & 0x3f; /* do some messy swapping to put the bit in the right spot */ - multicast_table[invert3[position&7]] |= - (1<>3)&7]); - + multicast_table[invert3[position & 7]] |= + (1 << invert3[(position >> 3) & 7]); } - /* now, the table can be loaded into the chipset */ - SMC_SELECT_BANK( 3 ); - SMC_SET_MCAST( multicast_table ); -} - -/* - . This routine will, depending on the values passed to it, - . either make it accept multicast packets, go into - . promiscuous mode ( for TCPDUMP and cousins ) or accept - . a select set of multicast packets -*/ -static void smc_set_multicast_list(struct net_device *dev) -{ - struct smc_local *lp = (struct smc_local *)dev->priv; - unsigned long ioaddr = dev->base_addr; - - PRINTK2("%s: %s\n", dev->name, __FUNCTION__); - - SMC_SELECT_BANK(0); - if ( dev->flags & IFF_PROMISC ) { - PRINTK2("%s: RCR_PRMS\n", dev->name); - lp->rcr_cur_mode |= RCR_PRMS; - SMC_SET_RCR( lp->rcr_cur_mode ); - } - -/* BUG? I never disable promiscuous mode if multicasting was turned on. - Now, I turn off promiscuous mode, but I don't do anything to multicasting - when promiscuous mode is turned on. -*/ - - /* Here, I am setting this to accept all multicast packets. - I don't need to zero the multicast table, because the flag is - checked before the table is - */ - else if (dev->flags & IFF_ALLMULTI) { - lp->rcr_cur_mode |= RCR_ALMUL; - SMC_SET_RCR( lp->rcr_cur_mode ); - PRINTK2("%s: RCR_ALMUL\n", dev->name); - } - - /* We just get all multicast packets even if we only want them - . from one source. This will be changed at some future - . point. */ - else if (dev->mc_count ) { - /* support hardware multicasting */ - - /* be sure I get rid of flags I might have set */ - lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL); - SMC_SET_RCR( lp->rcr_cur_mode ); - /* NOTE: this has to set the bank, so make sure it is the - last thing called. The bank is set to zero at the top */ - smc_setmulticast( ioaddr, dev->mc_count, dev->mc_list ); - } else { - PRINTK2("%s: ~(RCR_PRMS|RCR_ALMUL)\n", dev->name); - lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL); - SMC_SET_RCR( lp->rcr_cur_mode ); - /* - since I'm disabling all multicast entirely, I need to - clear the multicast list - */ - SMC_SELECT_BANK( 3 ); - SMC_CLEAR_MCAST(); + for (i = 0; i < 8; i+=2) { + u16 mc_val = ((u16)multicast_table[i+1]<<8) + multicast_table[i]; + smc91x_reg_write( mc_val, dev->base_addr, MCAST_REG1+i, 3); } } +static void smc91x_set_multicast_list (struct net_device *dev) +{ + smc91x_t *priv = (smc91x_t*) dev->priv; + u16 rcr = smc91x_reg_read( dev->base_addr, RCR_REG, 0); + + if ((dev->flags & IFF_PROMISC)) { + printk( KERN_INFO "%s: Promiscuous mode enabled\n", dev->name); + rcr |= RCR_PRMS; + } else if ( dev->flags & IFF_ALLMULTI) { + if( smc_debug) printk( KERN_INFO "%s: Multicast-all enabled\n", dev->name); + rcr |= RCR_ALMUL; + } else if ( dev->mc_count) { + if( smc_debug > 2) + { + int i; + struct dev_mc_list *addr = dev->mc_list; + printk( KERN_INFO "%s: Multicast enabled (filter)\n", dev->name); + for( i=0; imc_count; i++) + { + int j; + printk( KERN_INFO "%s: addr-%d", dev->name, i); + for( j=0; j<6; j++) + { + printk( ":%02x", addr->dmi_addr[j]); + } + printk( "\n"); + addr = addr->next; + } + } -/* - * Open and Initialize the board - * - * Set up everything, reset the card, etc .. - */ -static int -smc_open(struct net_device *dev) -{ - struct smc_local *lp = (struct smc_local *)dev->priv; - unsigned long ioaddr = dev->base_addr; - - PRINTK2("%s: %s\n", dev->name, __FUNCTION__); - - /* clear out all the junk that was put here before... */ - memset(dev->priv, 0, sizeof(struct smc_local)); - - // Setup the default Register Modes - lp->tcr_cur_mode = TCR_DEFAULT; - lp->rcr_cur_mode = RCR_DEFAULT; - lp->rpc_cur_mode = RPC_DEFAULT; - - /* Set default parameters */ - lp->ctl_autoneg = 1; - lp->ctl_rfduplx = 1; - lp->ctl_rspeed = 100; - - SMC_SELECT_BANK(3); - lp->version = SMC_GET_REV() & 0xff; - - /* reset the hardware */ - smc_reset(dev); - smc_enable(dev); - - SMC_SELECT_BANK( 1 ); - SMC_SET_MAC_ADDR(dev->dev_addr); - - /* Configure the PHY */ - if (lp->version >= 0x70) - smc_phy_configure(dev); - - netif_start_queue(dev); - return 0; -} - -/*---------------------------------------------------- - . smc_close - . - . this makes the board clean up everything that it can - . and not talk to the outside world. Caused by - . an 'ifconfig ethX down' - . - -----------------------------------------------------*/ -static int -smc_close(struct net_device *dev) -{ - PRINTK2("%s: %s\n", dev->name, __FUNCTION__); - - netif_stop_queue(dev); - - /* clear everything */ - smc_shutdown(dev->base_addr); + rcr = RCR_STRIP_CRC | RCR_RXEN; - return 0; -} - -/*------------------------------------------------------------ - . Get the current statistics. - . This may be called with the card open or closed. - .-------------------------------------------------------------*/ -static struct net_device_stats * -smc_query_statistics(struct net_device *dev) -{ - struct smc_local *lp = (struct smc_local *)dev->priv; + smc91x_set_multicast_filter( dev); + } else { + rcr = RCR_STRIP_CRC | RCR_RXEN; - PRINTK2("%s: %s\n", dev->name, __FUNCTION__); + smc91x_reg_write( 0, dev->base_addr, MCAST_REG1, 3); + smc91x_reg_write( 0, dev->base_addr, MCAST_REG2, 3); + smc91x_reg_write( 0, dev->base_addr, MCAST_REG3, 3); + smc91x_reg_write( 0, dev->base_addr, MCAST_REG4, 3); + } + smc91x_reg_write( rcr, dev->base_addr, RCR_REG, 0); - return &lp->stats; + /* keep track of rcr */ + priv->rcr = rcr; } -/*---------------------------------------------------------------------- - . smc_findirq - . - . This routine has a simple purpose -- make the SMC chip generate an - . interrupt, so an auto-detect routine can detect it, and find the IRQ, - ------------------------------------------------------------------------ -*/ -int __init -smc_findirq( unsigned long ioaddr ) +static int __init smc91x_probe (struct net_device *dev) { - int timeout = 20; - unsigned long cookie; - - PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__); - - cookie = probe_irq_on(); - - /* - * What I try to do here is trigger an ALLOC_INT. This is done - * by allocating a small chunk of memory, which will give an interrupt - * when done. - */ + int result, i; + u16 rev; + u8 chip_id; + u8 chip_rev; + smc91x_t *priv; + smc91x_config_t *config_priv = (smc91x_config_t*) dev->priv; - /* enable ALLOCation interrupts ONLY */ - SMC_SELECT_BANK(2); - SMC_SET_INT_MASK( IM_ALLOC_INT ); - - /* - . Allocate 512 bytes of memory. Note that the chip was just - . reset so all the memory is available - */ - SMC_SET_MMU_CMD( MC_ALLOC | 1 ); - - /* - . Wait until positive that the interrupt has been generated - */ - do { - int int_status; - udelay(10); - int_status = SMC_GET_INT(); - if (int_status & IM_ALLOC_INT) - break; /* got the interrupt */ - } while (--timeout); - - /* there is really nothing that I can do here if timeout fails, - as autoirq_report will return a 0 anyway, which is what I - want in this case. Plus, the clean up is needed in both - cases. */ - - /* and disable all interrupts again */ - SMC_SET_INT_MASK( 0 ); - - /* and return what I found */ - return probe_irq_off(cookie); -} - -/*---------------------------------------------------------------------- - . Function: smc_probe( unsigned long ioaddr ) - . - . Purpose: - . Tests to see if a given ioaddr points to an SMC91x chip. - . Returns a 0 on success - . - . Algorithm: - . (1) see if the high byte of BANK_SELECT is 0x33 - . (2) compare the ioaddr with the base register's address - . (3) see if I recognize the chip ID in the appropriate register - . - .--------------------------------------------------------------------- - */ -/*--------------------------------------------------------------- - . Here I do typical initialization tasks. - . - . o Initialize the structure if needed - . o print out my vanity message if not done so already - . o print out what type of hardware is detected - . o print out the ethernet address - . o find the IRQ - . o set up my private data - . o configure the dev structure with my subroutines - . o actually GRAB the irq. - . o GRAB the region - .----------------------------------------------------------------- -*/ -static int __init -smc_probe(struct net_device *dev, unsigned long ioaddr) -{ - struct smc_local *lp = (struct smc_local *)dev->priv; - static int version_printed = 0; - int i, retval; - unsigned int val, revision_register; - const char *version_string; - - PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__); - - /* Grab the region so that no one else tries to probe our ioports. */ - if (!request_region(ioaddr, SMC_IO_EXTENT, dev->name)) - return -EBUSY; - - /* First, see if the high byte is 0x33 */ - val = SMC_CURRENT_BANK(); - PRINTK2("%s: bank signature probe returned 0x%04x\n", CARDNAME, val); - if ( (val & 0xFF00) != 0x3300 ) { - if ( (val & 0xFF) == 0x33 ) { - printk( KERN_WARNING - "%s: Detected possible byte-swapped interface" - " at IOADDR 0x%lx\n", CARDNAME, ioaddr); - } - retval = -ENODEV; - goto err_out; - } + ether_setup (dev); - /* The above MIGHT indicate a device, but I need to write to further - test this. */ - SMC_SELECT_BANK(0); - val = SMC_CURRENT_BANK(); - if ( (val & 0xFF00 ) != 0x3300 ) { - retval = -ENODEV; + dev->priv = kmalloc(sizeof(smc91x_t), GFP_KERNEL); + if (dev->priv == NULL) { + result = -ENOMEM; goto err_out; } + memset(dev->priv, 0, sizeof (smc91x_t)); + priv = (smc91x_t*) dev->priv; - /* well, we've already written once, so hopefully another time won't - hurt. This time, I need to switch the bank register to bank 1, - so I can access the base address register */ - SMC_SELECT_BANK(1); - val = SMC_GET_BASE(); - val = ((val & 0x1F00) >> 3) << SMC_IO_SHIFT; - if ( (ioaddr & ((PAGE_SIZE-1)<> 4) & 0xF]; - if (!version_string || (revision_register & 0xff00) != 0x3300) { - /* I don't recognize this chip, so... */ - printk( "%s: IO 0x%lx: Unrecognized revision register 0x%04x" - ", Contact author.\n", CARDNAME, - ioaddr, revision_register); + dev->base_addr = config_priv->port; + dev->irq = config_priv->irq; + priv->dma_addr = config_priv->dma_addr; + priv->use_32bit= config_priv->use_32bit; + + priv->rcr = RCR_STRIP_CRC | RCR_RXEN; + priv->autoneg_on = 1; + priv->speed = SPEED_10; + priv->duplex = DUPLEX_HALF; - retval = -ENODEV; - goto err_out; - } + tasklet_init(&priv->link_tasklet, smc91x_wait_for_link, (unsigned long)dev); - /* At this point I'll assume that the chip is an SMC91x. */ - if (version_printed++ == 0) - printk("%s", version); - - /* set the private data to zero by default */ - memset(lp, 0, sizeof(struct smc_local)); - - /* fill in some of the fields */ - dev->base_addr = ioaddr; - lp->version = revision_register & 0xff; - - /* Get the MAC address */ - SMC_SELECT_BANK( 1 ); - SMC_GET_MAC_ADDR(dev->dev_addr); - - /* now, reset the chip, and put it into a known state */ - smc_reset( dev ); - - /* - . If dev->irq is 0, then the device has to be banged on to see - . what the IRQ is. - . - . This banging doesn't always detect the IRQ, for unknown reasons. - . a workaround is to reset the chip and try again. - . - . Interestingly, the DOS packet driver *SETS* the IRQ on the card to - . be what is requested on the command line. I don't do that, mostly - . because the card that I have uses a non-standard method of accessing - . the IRQs, and because this _should_ work in most configurations. - . - . Specifying an IRQ is done with the assumption that the user knows - . what (s)he is doing. No checking is done!!!! - . - */ - if ( dev->irq < 1 ) { - int trials; - - trials = 3; - while ( trials-- ) { - dev->irq = smc_findirq( ioaddr ); - if ( dev->irq ) - break; - /* kick the card and try again */ - smc_reset( dev ); - } - } - if (dev->irq == 0 ) { - printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n", - dev->name); - retval = -ENODEV; + if( (result = smc91x_detect( dev)) < 0) goto err_out; - } - dev->irq = irq_cannonicalize(dev->irq); - - /* now, print out the card info, in a short format.. */ - printk( "%s: %s (rev %d) at %#lx IRQ %d%s%s\n", - dev->name, version_string, revision_register & 0x0f, - ioaddr, dev->irq, nowait ? " [nowait]" : "", - THROTTLE_TX_PKTS ? " [throttle_tx]" : "" ); - /* Print the Ethernet address */ - printk("%s: Ethernet addr: ", dev->name); + dev->open = smc91x_open; + dev->stop = smc91x_stop; + dev->hard_start_xmit = smc91x_hard_start_xmit; + dev->get_stats = smc91x_get_stats; + dev->do_ioctl = smc91x_do_ioctl; + dev->set_multicast_list = smc91x_set_multicast_list; + dev->tx_timeout = smc91x_transmit_timeout; + dev->watchdog_timeo = 5*HZ; + + SET_MODULE_OWNER (dev); + + request_region (dev->base_addr,SMC91X_REGION_EXTEND,dev->name); + + rev = smc91x_reg_read( dev->base_addr, REV_REG, 3); + chip_id = (rev >> 4) & 0xf; + chip_rev = rev & 0xf; + + printk( KERN_INFO "%s: %s(rev:%d) %s at %#3lx IRQ:%d", + dev->name, + smc91x_chip_id[ (rev >> 4) & 0xf], rev & 0xf, + priv->use_32bit?"32-bit":"16-bit", + dev->base_addr, dev->irq); + printk( " ADDR: "); for (i = 0; i < 5; i++) - printk("%2.2x:", dev->dev_addr[i] ); - printk("%2.2x\n", dev->dev_addr[5] ); + printk("%2.2x:", dev->dev_addr[i]); + printk("%2.2x \n", dev->dev_addr[5]); - /* Fill in the fields of the device structure with ethernet values. */ - ether_setup(dev); +#ifdef HW_TEST +{ + u16 i=0; + printk( KERN_INFO "%s: Starting SMC hardware test\n", dev->name); + while(1) { + u16 rev; + u16 gp; + smc91x_reg_write( i, dev->base_addr, GP_REG, 1); + rev = smc91x_reg_read( dev->base_addr, REV_REG, 3); + if( (rev&0xff) != 0x90) { + printk( KERN_INFO "Failed to read rev %x\n", rev); + break; + } + gp = smc91x_reg_read( dev->base_addr, GP_REG, 1); + if( gp != i) { + printk( KERN_INFO "Failed to read gp %x (!=%x)\n", gp,i); + break; + } + i++; + if( i==1) { + smc91x_reg_write( RPC_LEDA(RPC_LED_100), dev->base_addr, RPC_REG, 0); + } + if( i==32768) { + smc91x_reg_write( RPC_LEDA(RPC_LED_10), dev->base_addr, RPC_REG, 0); + } - /* Grab the IRQ */ - retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev); - if (retval) { - goto err_out; - } - - dev->open = smc_open; - dev->stop = smc_close; - dev->hard_start_xmit = smc_hard_start_xmit; - dev->tx_timeout = smc_timeout; - dev->watchdog_timeo = HZ/10; - dev->get_stats = smc_query_statistics; - dev->set_multicast_list = smc_set_multicast_list; + } + panic( "HW test failed.\n"); +} +#endif - return 0; + /* The odd byte problem has been fixed in the LAN91C111 Rev B */ + if( (chip_id == 9) && (chip_rev < 1)) + priv->odd_byte_bug = 1; + + return result; err_out: - release_region(ioaddr, SMC_IO_EXTENT); - return retval; + if( dev->priv) + kfree(dev->priv); + + return result; } -/*------------------------------------------------------------------------- - | - | smc_init( void ) - | Input parameters: - | dev->base_addr == 0, try to find all possible locations - | dev->base_addr > 0x1ff, this is the address to check - | dev->base_addr == , return failure code - | - | Output: - | 0 --> there is a device - | anything else, error - | - --------------------------------------------------------------------------- -*/ -static struct net_device *global_dev = NULL; /* needs to be fixed */ - -static int __init -smc_init(void) -{ - int ret; - - PRINTK2("%s: %s\n", CARDNAME, __FUNCTION__); - -#ifdef MODULE - if (io == -1) - printk( KERN_WARNING - "%s: You shouldn't use auto-probing with insmod!\n", - CARDNAME ); -#endif +static int __init smc91x_init (void) +{ + int interface; + int device_present = 0; + int result; - if (global_dev) { - printk("%s: already initialized.\n", CARDNAME); - return -EBUSY; - } - - global_dev = init_etherdev(0, sizeof(struct smc_local)); - if (!global_dev) { - printk("%s: could not allocate device.\n", CARDNAME); - return -ENOMEM; - } - SET_MODULE_OWNER(global_dev); - - /* copy the parameters from insmod into the device structure */ - if (io != -1) - global_dev->base_addr = io; - if (irq != -1) - global_dev->irq = irq; - -#ifdef CONFIG_ISA - /* try a specific location */ - if (global_dev->base_addr > 0x1ff) - ret = smc_probe(global_dev, global_dev->base_addr); - else if (global_dev->base_addr != 0) - ret = -ENXIO; - else { - int i; - - /* check every ethernet address */ - for (i = 0; smc_portlist[i]; i++) { - ret = smc_probe(global_dev, smc_portlist[i]); - if (ret == 0) - break; - } - } -#elif defined(CONFIG_ARCH_LUBBOCK) - { - int ioaddr = LUBBOCK_ETH_VIRT + (0x300 << 2); - volatile int *attaddr = (int *)(LUBBOCK_ETH_VIRT + 0x100000); - unsigned long flags; - - /* first reset, then enable the device. Sequence is critical */ - local_irq_save(flags); - attaddr[ECOR] |= ECOR_RESET; - udelay(100); - attaddr[ECOR] &= ~ECOR_RESET; - attaddr[ECOR] |= ECOR_ENABLE; + printk( KERN_INFO DRIVER_NAME " driver for Linux " DRIVER_VERSION "\n"); - /* force 16-bit mode */ - attaddr[ECSR] &= ~ECSR_IOIS8; - mdelay(1); - local_irq_restore(flags); - - global_dev->irq = LUBBOCK_ETH_IRQ; - ret = smc_probe(global_dev, ioaddr); - } -#elif defined(CONFIG_ARCH_PXA_IDP) - { - int ioaddr = IDP_ETH_BASE + 0x300; - global_dev->irq = SMC_IRQ; - ret = smc_probe(global_dev, ioaddr); - } -#else - if (global_dev->base_addr == -1) { - printk(KERN_WARNING"%s: SMC91X_BASE_ADDR not set!\n", CARDNAME); - ret = -ENXIO; - } else { - void *ioaddr = ioremap(global_dev->base_addr, SMC_IO_EXTENT); - ret = smc_probe(global_dev, (unsigned long)ioaddr); - if (ret != 0) - iounmap(ioaddr); - } -#endif + for( interface=0; interfacename, DMA_PRIO_LOW, - smc_pxa_dma_irq, NULL); - if (dma >= 0) { - global_dev->dma = dma; - PRINTK("%s: using DMA channel %d\n", global_dev->name, dma); + if ((result = register_netdev(&smc91x_dev[interface])) != 0) { + /* if we fail, indicate to exit function there's + * nothing to clean up */ + smc91x_dev[interface].irq = -1; } else { - global_dev->dma = -1; + device_present++; } } -#endif - - if (ret != 0) { - printk("%s: not found.\n", CARDNAME); - kfree(global_dev->priv); - unregister_netdev(global_dev); - kfree(global_dev); - } + if( smc_debug) printk("Detected %d smc91x chips...\n", device_present); - return ret; + return device_present ? 0 : -ENODEV; } -static void __exit -smc_cleanup(void) +static void __exit smc91x_cleanup (void) { - unregister_netdev(global_dev); - free_irq(global_dev->irq, global_dev); - release_region(global_dev->base_addr, SMC_IO_EXTENT); - -#ifndef CONFIG_ISA - iounmap((void *)global_dev->base_addr); -#endif + int interface; + for( interface=0; interface - . Daris A Nevil - . Nicolas Pitre - . - ---------------------------------------------------------------------------*/ +/* ---------------------------------------------------------------------- + * smc91x.h + * + * Copyright (C) 2003 Intrinsyc Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 18-Feb-2003 : Initial version [FB] + * 15-Apr-2003 : Moved structs into header [FB] + * + ------------------------------------------------------------------------*/ #ifndef _SMC91X_H_ #define _SMC91X_H_ +#include -/* - * Define your architecture specific configuration parameters here. - */ - -#if defined(CONFIG_SA1100_GRAPHICSCLIENT) || \ - defined(CONFIG_SA1100_PFS168) || \ - defined(CONFIG_SA1100_FLEXANET) || \ - defined(CONFIG_SA1100_GRAPHICSMASTER) || \ - defined(CONFIG_ARCH_LUBBOCK) - -/* We can only do 16-bit reads and writes in the static memory space. */ -#define SMC_CAN_USE_8BIT 0 -#define SMC_CAN_USE_16BIT 1 -#define SMC_CAN_USE_32BIT 0 -#define SMC_NOWAIT 1 - -/* The first two address lines aren't connected... */ -#define SMC_IO_SHIFT 2 - -#define SMC_inw(a, r) readw((a) + (r)) -#define SMC_outw(v, a, r) writew(v, (a) + (r)) -#define SMC_insw(a, r, p, l) insw((a) + (r), p, l) -#define SMC_outsw(a, r, p, l) outsw((a) + (r), p, l) - -#ifdef CONFIG_ARCH_LUBBOCK -#define SMC_IOADDR LUBBOCK_ETH_PHYS -#endif - -#elif defined(CONFIG_ARCH_MAINSTONE) || defined(CONFIG_ARCH_PXA_IDP) - -#ifdef CONFIG_ARCH_MAINSTONE -#include -#define SMC_IOADDR (MST_ETH_PHYS + 0x300) -#define SMC_IRQ MAINSTONE_IRQ(3) - -#elif CONFIG_ARCH_PXA_IDP -#include -#define SMC_IOADDR (IDP_ETH_PHYS + 0x300) -#define SMC_IRQ ETHERNET_IRQ -#endif - -#define SMC_CAN_USE_8BIT 1 -#define SMC_CAN_USE_16BIT 1 -#define SMC_CAN_USE_32BIT 1 -#define SMC_IO_SHIFT 0 -#define SMC_NOWAIT 1 -#define SMC_USE_PXA_DMA 1 - -#define SMC_inb(a, r) readb((a) + (r)) -#define SMC_inw(a, r) readw((a) + (r)) -#define SMC_inl(a, r) readl((a) + (r)) -#define SMC_outb(v, a, r) writeb(v, (a) + (r)) -#define SMC_outl(v, a, r) writel(v, (a) + (r)) -#define SMC_insl(a, r, p, l) insl((a) + (r), p, l) -#define SMC_outsl(a, r, p, l) outsl((a) + (r), p, l) - -/* We actually can't write halfwords properly if not word aligned */ -static inline void -SMC_outw(u16 val, unsigned long ioaddr, int reg) -{ - if (reg & 2) { - unsigned int v = val << 16; - v |= readl(ioaddr + (reg & ~2)) & 0xffff; - writel(v, ioaddr + (reg & ~2)); - } else { - writew(val, ioaddr + reg); - } -} - -#elif defined(CONFIG_ISA) - -#define SMC_CAN_USE_8BIT 1 -#define SMC_CAN_USE_16BIT 1 -#define SMC_CAN_USE_32BIT 0 - -#define SMC_inb(a, r) inb((a) + (r)) -#define SMC_inw(a, r) inw((a) + (r)) -#define SMC_outb(v, a, r) outb(v, (a) + (r)) -#define SMC_outw(v, a, r) outw(v, (a) + (r)) -#define SMC_insw(a, r, p, l) insw((a) + (r), p, l) -#define SMC_outsw(a, r, p, l) outsw((a) + (r), p, l) - -#endif - - -#ifdef SMC_USE_PXA_DMA -/* - * Let's use the DMA engine on the XScale PXA2xx for RX packets. This is - * always happening in irq context so no need to worry about races. TX is - * different and probably not worth it for that reason, and not as critical - * as RX which can overrun memory and lose packets. - */ -#include -#include - -#ifdef SMC_insl -#undef SMC_insl -#define SMC_insl(a, r, p, l) smc_pxa_dma_insl(a, r, dev->dma, p, l) -static inline void -smc_pxa_dma_insl(u_long ioaddr, int reg, int dma, u_char *buf, int len) -{ - dma_addr_t dmabuf; - - /* fallback if no DMA available */ - if (dma == -1) { - insl(ioaddr + reg, buf, len); - return; - } - - /* 64 bit alignment is required for memory to memory DMA */ - if ((long)buf & 4) { - *((u32 *)buf)++ = SMC_inl(ioaddr, reg); - len--; - } - - len *= 4; - dmabuf = pci_map_single(NULL, buf, len, PCI_DMA_FROMDEVICE); - DCSR(dma) = DCSR_NODESC; - DTADR(dma) = dmabuf; - DSADR(dma) = SMC_IOADDR + reg; - DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 | - DCMD_WIDTH4 | (DCMD_LENGTH & len)); - DCSR(dma) = DCSR_NODESC | DCSR_RUN; - while (!(DCSR(dma) & DCSR_STOPSTATE)); - DCSR(dma) = 0; - pci_unmap_single(NULL, dmabuf,len, PCI_DMA_FROMDEVICE); -} -#endif - -#ifdef SMC_insw -#undef SMC_insw -#define SMC_insw(a, r, p, l) smc_pxa_dma_insw(a, r, dev->dma, p, l) -static inline void -smc_pxa_dma_insw(u_long ioaddr, int reg, int dma, u_char *buf, int len) -{ - dma_addr_t dmabuf; - - /* fallback if no DMA available */ - if (dma == -1) { - insw(ioaddr + reg, buf, len); - return; - } - - /* 64 bit alignment is required for memory to memory DMA */ - while ((long)buf & 6) { - *((u16 *)buf)++ = SMC_inw(ioaddr, reg); - len--; - } - - len *= 2; - dmabuf = pci_map_single(NULL, buf, len, PCI_DMA_FROMDEVICE); - DCSR(dma) = DCSR_NODESC; - DTADR(dma) = dmabuf; - DSADR(dma) = SMC_IOADDR + reg; - DCMD(dma) = (DCMD_INCTRGADDR | DCMD_BURST32 | - DCMD_WIDTH2 | (DCMD_LENGTH & len)); - DCSR(dma) = DCSR_NODESC | DCSR_RUN; - while (!(DCSR(dma) & DCSR_STOPSTATE)); - DCSR(dma) = 0; - pci_unmap_single(NULL, dmabuf,len, PCI_DMA_FROMDEVICE); -} -#endif - -static void -smc_pxa_dma_irq(int dma, void *dummy, struct pt_regs *regs) -{ - DCSR(dma) = 0; -} -#endif /* SMC_USE_PXA_DMA */ - - -/* Because of bank switching, the LAN91xxx uses only 16 I/O ports */ -#ifndef SMC_IO_SHIFT -#define SMC_IO_SHIFT 0 -#endif -#define SMC_IO_EXTENT (16 << SMC_IO_SHIFT) - - -/* - . Bank Select Register: - . - . yyyy yyyy 0000 00xx - . xx = bank number - . yyyy yyyy = 0x33, for identification purposes. -*/ -#define BANK_SELECT (14 << SMC_IO_SHIFT) - - -// Transmit Control Register -/* BANK 0 */ -#define TCR_REG SMC_REG(0x0000, 0) -#define TCR_ENABLE 0x0001 // When 1 we can transmit -#define TCR_LOOP 0x0002 // Controls output pin LBK -#define TCR_FORCOL 0x0004 // When 1 will force a collision -#define TCR_PAD_EN 0x0080 // When 1 will pad tx frames < 64 bytes w/0 -#define TCR_NOCRC 0x0100 // When 1 will not append CRC to tx frames -#define TCR_MON_CSN 0x0400 // When 1 tx monitors carrier -#define TCR_FDUPLX 0x0800 // When 1 enables full duplex operation -#define TCR_STP_SQET 0x1000 // When 1 stops tx if Signal Quality Error -#define TCR_EPH_LOOP 0x2000 // When 1 enables EPH block loopback -#define TCR_SWFDUP 0x8000 // When 1 enables Switched Full Duplex mode - -#define TCR_CLEAR 0 /* do NOTHING */ -/* the default settings for the TCR register : */ -#define TCR_DEFAULT (TCR_ENABLE | TCR_PAD_EN) - - -// EPH Status Register -/* BANK 0 */ -#define EPH_STATUS_REG SMC_REG(0x0002, 0) -#define ES_TX_SUC 0x0001 // Last TX was successful -#define ES_SNGL_COL 0x0002 // Single collision detected for last tx -#define ES_MUL_COL 0x0004 // Multiple collisions detected for last tx -#define ES_LTX_MULT 0x0008 // Last tx was a multicast -#define ES_16COL 0x0010 // 16 Collisions Reached -#define ES_SQET 0x0020 // Signal Quality Error Test -#define ES_LTXBRD 0x0040 // Last tx was a broadcast -#define ES_TXDEFR 0x0080 // Transmit Deferred -#define ES_LATCOL 0x0200 // Late collision detected on last tx -#define ES_LOSTCARR 0x0400 // Lost Carrier Sense -#define ES_EXC_DEF 0x0800 // Excessive Deferral -#define ES_CTR_ROL 0x1000 // Counter Roll Over indication -#define ES_LINK_OK 0x4000 // Driven by inverted value of nLNK pin -#define ES_TXUNRN 0x8000 // Tx Underrun - - -// Receive Control Register -/* BANK 0 */ -#define RCR_REG SMC_REG(0x0004, 0) -#define RCR_RX_ABORT 0x0001 // Set if a rx frame was aborted -#define RCR_PRMS 0x0002 // Enable promiscuous mode -#define RCR_ALMUL 0x0004 // When set accepts all multicast frames -#define RCR_RXEN 0x0100 // IFF this is set, we can receive packets -#define RCR_STRIP_CRC 0x0200 // When set strips CRC from rx packets -#define RCR_ABORT_ENB 0x0200 // When set will abort rx on collision -#define RCR_FILT_CAR 0x0400 // When set filters leading 12 bit s of carrier -#define RCR_SOFTRST 0x8000 // resets the chip - -/* the normal settings for the RCR register : */ -#define RCR_DEFAULT (RCR_STRIP_CRC | RCR_RXEN) -#define RCR_CLEAR 0x0 // set it to a base state - - -// Counter Register -/* BANK 0 */ -#define COUNTER_REG SMC_REG(0x0006, 0) - - -// Memory Information Register -/* BANK 0 */ -#define MIR_REG SMC_REG(0x0008, 0) - - -// Receive/Phy Control Register -/* BANK 0 */ -#define RPC_REG SMC_REG(0x000A, 0) -#define RPC_SPEED 0x2000 // When 1 PHY is in 100Mbps mode. -#define RPC_DPLX 0x1000 // When 1 PHY is in Full-Duplex Mode -#define RPC_ANEG 0x0800 // When 1 PHY is in Auto-Negotiate Mode -#define RPC_LSXA_SHFT 5 // Bits to shift LS2A,LS1A,LS0A to lsb -#define RPC_LSXB_SHFT 2 // Bits to get LS2B,LS1B,LS0B to lsb -#define RPC_LED_100_10 (0x00) // LED = 100Mbps OR's with 10Mbps link detect -#define RPC_LED_RES (0x01) // LED = Reserved -#define RPC_LED_10 (0x02) // LED = 10Mbps link detect -#define RPC_LED_FD (0x03) // LED = Full Duplex Mode -#define RPC_LED_TX_RX (0x04) // LED = TX or RX packet occurred -#define RPC_LED_100 (0x05) // LED = 100Mbps link dectect -#define RPC_LED_TX (0x06) // LED = TX packet occurred -#define