diff -ruN linux-2.5.55/Makefile linux-2.5.55-pxacerf1/Makefile --- linux-2.5.55/Makefile Fri Jan 17 12:13:38 2003 +++ linux-2.5.55-pxacerf1/Makefile Wed Jan 22 11:15:18 2003 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 5 SUBLEVEL = 55 -EXTRAVERSION =-rmk1 +EXTRAVERSION =-rmk1-pxacerf1 # *DOCUMENTATION* # To see a list of typical targets execute "make help" @@ -50,7 +50,7 @@ HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer HOSTCXXFLAGS = -O2 -CROSS_COMPILE = +CROSS_COMPILE = arm-linux- # That's our default target when none is given on the command line diff -ruN linux-2.5.55/arch/arm/def-configs/cerfcube_pxa linux-2.5.55-pxacerf1/arch/arm/def-configs/cerfcube_pxa --- linux-2.5.55/arch/arm/def-configs/cerfcube_pxa Wed Dec 31 16:00:00 1969 +++ linux-2.5.55-pxacerf1/arch/arm/def-configs/cerfcube_pxa Mon Feb 17 10:54:45 2003 @@ -0,0 +1,655 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_SWAP=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# General setup +# +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_LOG_BUF_SHIFT_17 is not set +# CONFIG_LOG_BUF_SHIFT_16 is not set +# CONFIG_LOG_BUF_SHIFT_15 is not set +CONFIG_LOG_BUF_SHIFT_14=y +# CONFIG_LOG_BUF_SHIFT_13 is not set +# CONFIG_LOG_BUF_SHIFT_12 is not set +CONFIG_LOG_BUF_SHIFT=14 + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODULE_UNLOAD is not set +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_KMOD is not set + +# +# 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_IOP310 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 +# + +# +# IOP310 Implementation Options +# + +# +# IOP310 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=0 +CONFIG_ZBOOT_ROM_BSS=0 +# CONFIG_PCI_LEGACY_PROC is not set +# CONFIG_HOTPLUG is not set + +# +# At least one math emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_FASTFPE is not set +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 is not set +# 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_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_FILTER 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_XFRM_USER is not set +# CONFIG_IPV6 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_NET_VENDOR_SMC=y +CONFIG_SMC91111=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 is not set + +# +# 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_DMA 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 +# +# CONFIG_I2O is not set + +# +# 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_VT is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_DZ is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +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 +# + +# +# L3 serial bus support +# +# CONFIG_L3 is not set + +# +# Mice +# +CONFIG_BUSMOUSE=y +# CONFIG_QIC02_TAPE 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 + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# 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_EXT3_FS is not set +# CONFIG_JBD is not set +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +# CONFIG_VFAT_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_TMPFS=y +CONFIG_RAMFS=y +# CONFIG_ISO9660_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UFS_FS is not set +# CONFIG_XFS_FS is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +# CONFIG_CIFS is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED 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 + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# 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 is not set +# CONFIG_DEBUG_LL is not set + +# +# 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.55/arch/arm/mach-pxa/Kconfig linux-2.5.55-pxacerf1/arch/arm/mach-pxa/Kconfig --- linux-2.5.55/arch/arm/mach-pxa/Kconfig Fri Jan 17 12:07:59 2003 +++ linux-2.5.55-pxacerf1/arch/arm/mach-pxa/Kconfig Wed Jan 22 12:01:18 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.55/arch/arm/mach-pxa/Makefile linux-2.5.55-pxacerf1/arch/arm/mach-pxa/Makefile --- linux-2.5.55/arch/arm/mach-pxa/Makefile Fri Jan 17 12:09:22 2003 +++ linux-2.5.55-pxacerf1/arch/arm/mach-pxa/Makefile Wed Jan 22 12:01:23 2003 @@ -11,6 +11,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 leds-y := leds.o diff -ruN linux-2.5.55/arch/arm/mach-pxa/cerf.c linux-2.5.55-pxacerf1/arch/arm/mach-pxa/cerf.c --- linux-2.5.55/arch/arm/mach-pxa/cerf.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.55-pxacerf1/arch/arm/mach-pxa/cerf.c Thu Jan 23 12:01:44 2003 @@ -0,0 +1,97 @@ +/* + * 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 */ +}; + +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.55/arch/arm/mach-pxa/irq.c linux-2.5.55-pxacerf1/arch/arm/mach-pxa/irq.c --- linux-2.5.55/arch/arm/mach-pxa/irq.c Fri Jan 17 12:11:29 2003 +++ linux-2.5.55-pxacerf1/arch/arm/mach-pxa/irq.c Wed Jan 22 12:46:34 2003 @@ -58,7 +58,8 @@ { int gpio, idx; - gpio = irq - ((irq >= IRQ_GPIO(2)) ? IRQ_GPIO(2) + 2 : IRQ_GPIO(0)); +// gpio = irq - ((irq >= IRQ_GPIO(2)) ? IRQ_GPIO(2) + 2 : IRQ_GPIO(0)); + gpio = (irq> IRQ_GPIO1) ? IRQ_TO_GPIO_2_80(irq) : IRQ_GPIO(0); printk(KERN_DEBUG "IRQ%d (GPIO%d): ", irq, gpio); diff -ruN linux-2.5.55/drivers/mtd/maps/Kconfig linux-2.5.55-pxacerf1/drivers/mtd/maps/Kconfig --- linux-2.5.55/drivers/mtd/maps/Kconfig Fri Jan 17 12:13:41 2003 +++ linux-2.5.55-pxacerf1/drivers/mtd/maps/Kconfig Wed Jan 22 13:11:35 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.55/drivers/mtd/maps/Makefile linux-2.5.55-pxacerf1/drivers/mtd/maps/Makefile --- linux-2.5.55/drivers/mtd/maps/Makefile Fri Jan 17 12:13:41 2003 +++ linux-2.5.55-pxacerf1/drivers/mtd/maps/Makefile Wed Jan 22 13:11:18 2003 @@ -12,6 +12,7 @@ obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.o obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o obj-$(CONFIG_MTD_IQ80310) += iq80310.o +obj-$(CONFIG_MTD_PXA_CERF) += cerf_pxa.o obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_MBX860) += mbx860.o obj-$(CONFIG_MTD_NORA) += nora.o diff -ruN linux-2.5.55/drivers/mtd/maps/cerf_pxa.c linux-2.5.55-pxacerf1/drivers/mtd/maps/cerf_pxa.c --- linux-2.5.55/drivers/mtd/maps/cerf_pxa.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.55-pxacerf1/drivers/mtd/maps/cerf_pxa.c Wed Jan 22 11:42:11 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.55/drivers/net/Kconfig linux-2.5.55-pxacerf1/drivers/net/Kconfig --- linux-2.5.55/drivers/net/Kconfig Fri Jan 17 12:13:41 2003 +++ linux-2.5.55-pxacerf1/drivers/net/Kconfig Wed Jan 22 11:58:14 2003 @@ -766,7 +766,7 @@ config NET_VENDOR_SMC bool "Western Digital/SMC cards" - depends on NET_ETHERNET && (ISA || MCA || EISA || MAC) + depends on NET_ETHERNET && (ISA || MCA || EISA || MAC || ARCH_PXA) help If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from @@ -857,6 +857,18 @@ module, say M here and read as well as . +config SMC91111 + tristate "SMC 91111 support" + depends on NET_VENDOR_SMC && (ISA || MAC || ARCH_PXA) + ---help--- + This is support for the SMC91111 based Ethernet cards. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called smc91111.o. If you want to compile it as a + module, say M here and read as well + as . + config NET_VENDOR_RACAL bool "Racal-Interlan (Micom) NI cards" depends on NET_ETHERNET && ISA diff -ruN linux-2.5.55/drivers/net/Makefile linux-2.5.55-pxacerf1/drivers/net/Makefile --- linux-2.5.55/drivers/net/Makefile Fri Jan 17 12:13:41 2003 +++ linux-2.5.55-pxacerf1/drivers/net/Makefile Wed Jan 22 11:55:59 2003 @@ -83,6 +83,7 @@ obj-$(CONFIG_SK_G16) += sk_g16.o obj-$(CONFIG_HP100) += hp100.o obj-$(CONFIG_SMC9194) += smc9194.o +obj-$(CONFIG_SMC91111) += smc91111.o obj-$(CONFIG_FEC) += fec.o obj-$(CONFIG_68360_ENET) += 68360enet.o obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o diff -ruN linux-2.5.55/drivers/net/smc91111.c linux-2.5.55-pxacerf1/drivers/net/smc91111.c --- linux-2.5.55/drivers/net/smc91111.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.55-pxacerf1/drivers/net/smc91111.c Thu Jan 23 12:30:17 2003 @@ -0,0 +1,4288 @@ +/*------------------------------------------------------------------------ + . smc91111.c + . This is a driver for SMSC's 91C111 single-chip Ethernet device. + . + . Copyright (C) 2001 Standard Microsystems Corporation (SMSC) + . Developed by Simple Network Magic Corporation (SNMC) + . Copyright (C) 1996 by Erik Stahlman (ES) + . + . 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 + . + . Information contained in this file was obtained from the LAN91C111 + . manual from SMC. To get a copy, if you really want one, you can find + . information under www.smsc.com. + . + . + . "Features" of the SMC chip: + . Integrated PHY/MAC for 10/100BaseT Operation + . Supports internal and external MII + . Integrated 8K packet memory + . EEPROM interface for configuration + . + . Arguments: + . io = for the base address + . irq = for the IRQ + . nowait = 0 for normal wait states, 1 eliminates additional wait states + . + . author: + . Erik Stahlman ( erik@vt.edu ) + . Daris A Nevil ( dnevil@snmc.com ) + . Pramod B Bhardwaj (pramod.bhardwaj@smsc.com) + . + . + . Hardware multicast code from Peter Cammaert ( pc@denkart.be ) + . + . Sources: + . o SMSC LAN91C111 databook (www.smsc.com) + . o smc9194.c by Erik Stahlman + . o skeleton.c by Donald Becker ( becker@cesdis.gsfc.nasa.gov ) + . + . History: + . 12/20/01 Jeff Sutherland , ported to ARM Xscale proc. + . 09/24/01 Pramod B Bhardwaj, Added the changes for Kernel 2.4 + . 08/21/01 Pramod B Bhardwaj Added support for RevB of LAN91C111 + . 04/25/01 Daris A Nevil Initial public release through SMSC + . 03/16/01 Daris A Nevil Modified smc9194.c for use with LAN91C111 + ----------------------------------------------------------------------------*/ + +static const char version[] = + "SMSC LAN91C111 Driver (v2.1), (Linux Kernel 2.4 + Support for Odd Byte) 09/24/01\nby Pramod Bhardwaj (pramod.bhardwaj@smsc.com)\n\n"; + +#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 + +#ifdef CONFIG_SYSCTL +#include +#include +#endif + +#undef SMC_DEBUG // May be defined in makefile +//#define SMC_DEBUG 2 + +#include "smc91111.h" +#include "smc91111_cfg.h" //provides localization for driver + +/* + * Define to use AUTO_RELEASE for transmit packets. This will + * release the allocated memory automatically upon successful + * transmission. At least in theory... + * During heavy transmit loads the MMU seems to get confused + * and it takes down the transmit and receive FIFOs with it. + * The data register will return a single dword (upper 16 + * bits 0) no matter which address was selected. Since the + * upper word is zero, the byte count will be zero resulting + * in a "Received Packet Size TOO Small" error. At that point + * only a reset will restore order :( + * ## WARNING: Use at your own risk ## + */ +//#define USE_AUTO_RELEASE 1 + +/* + . DEBUGGING LEVELS + . + . 0 for normal operation + . 1 for slightly more details + . >2 for various levels of increasingly useless information + . 2 for interrupt tracking, status flags + . 3 for packet info + . 4 for complete packet dumps +*/ + +#if (SMC_DEBUG > 2 ) +#define PRINTK3(args...) printk(args) +#else +#define PRINTK3(args...) +#endif + +#if SMC_DEBUG > 1 +#define PRINTK2(args...) printk(args) +#else +#define PRINTK2(args...) +#endif + +#ifdef SMC_DEBUG +#define PRINTK(args...) printk(args) +#else +#define PRINTK(args...) +#endif + +/*------------------------------------------------------------------------ + . + . 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 "LAN91C111" + +// Memory sizing constant +// This will be dynamic, in hunks of 128, on the 91C112 :-) +#define LAN91C111_MEMORY_MULTIPLIER (1024*2) + +/* a hack to fix a kludge :\ */ +static int wait_mode[MAX_NETWORK_INTERFACE_COUNT]; + +/* store this information for the driver.. */ +struct smc_local { + + // 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; + + // 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; + + // This keeps track of how many packets that I have + // sent out. When an TX_EMPTY interrupt comes, I know + // that all of these have been sent. + int packets_waiting; + + // keep track of tx byte count for packets that have been + // sent to the tx-queue but haven't received an interrupt + // indicating they've left the chip. + unsigned int tx_bytes; + + // Set to true during the auto-negotiation sequence + int autoneg_active; + + // Address of our PHY port + u16 phyaddr; + + // Type of PHY + u16 phytype; + + // Last contents of PHY Register 18 + u16 lastPhy18; + + // Contains the current active transmission mode + u16 tcr_cur_mode; + + // Contains the current active receive mode + u16 rcr_cur_mode; + + // Contains the current active receive/phy mode + u16 rpc_cur_mode; + + spinlock_t mmu_lock; + + /* => Pramod, Odd Byte issue */ + // Contains the Current ChipID + unsigned short ChipID; + + //Contains the Current ChipRevision + unsigned short ChipRev; + /* <= Pramod, Odd Byte issue */ + + // JWS- DMA support: + unsigned long dev_dma_phys; + + int use_32bit; + +#ifdef CONFIG_SYSCTL + + // Root directory /proc/sys/dev + // Second entry must be null to terminate the table + ctl_table root_table[2]; + + // Directory for this device /proc/sys/dev/ethX + // Again the second entry must be zero to terminate + ctl_table eth_table[2]; + + // This is the parameters (file) table + ctl_table param_table[CTL_SMC_LAST_ENTRY]; + + // Saves the sysctl header returned by register_sysctl_table() + // we send this to unregister_sysctl_table() + struct ctl_table_header *sysctl_header; + + // Parameter variables (files) go here + char ctl_info[1024]; + int ctl_swfdup; + int ctl_ephloop; + int ctl_miiop; + int ctl_autoneg; + int ctl_rfduplx; + int ctl_rspeed; + int ctl_afduplx; + int ctl_aspeed; + int ctl_lnkfail; + int ctl_forcol; + int ctl_filtcar; + int ctl_freemem; + int ctl_totmem; + int ctl_leda; + int ctl_ledb; + int ctl_chiprev; +#ifdef SMC_DEBUG + int ctl_reg_bsr; + int ctl_reg_tcr; + int ctl_reg_esr; + int ctl_reg_rcr; + int ctl_reg_ctrr; + int ctl_reg_mir; + int ctl_reg_rpcr; + int ctl_reg_cfgr; + int ctl_reg_bar; + int ctl_reg_iar0; + int ctl_reg_iar1; + int ctl_reg_iar2; + int ctl_reg_gpr; + int ctl_reg_ctlr; + int ctl_reg_mcr; + int ctl_reg_pnr; + int ctl_reg_fpr; + int ctl_reg_ptr; + int ctl_reg_dr; + int ctl_reg_isr; + int ctl_reg_mtr1; + int ctl_reg_mtr2; + int ctl_reg_mtr3; + int ctl_reg_mtr4; + int ctl_reg_miir; + int ctl_reg_revr; + int ctl_reg_ercvr; + int ctl_reg_extr; + int ctl_phy_ctrl; + int ctl_phy_stat; + int ctl_phy_id1; + int ctl_phy_id2; + int ctl_phy_adc; + int ctl_phy_remc; + int ctl_phy_cfg1; + int ctl_phy_cfg2; + int ctl_phy_int; + int ctl_phy_mask; +#endif // SMC_DEBUG + +#endif // CONFIG_SYSCTL + +}; + +/*----------------------------------------------------------------- + . + . The driver can be entered at any of the following entry points. + . + .------------------------------------------------------------------ */ + +/* + . This is called by register_netdev(). It is responsible for + . checking the portlist for the SMC9000 series chipset. If it finds + . one, then it will initialize the device, find the hardware information, + . and sets up the appropriate device parameters. + . NOTE: Interrupts are *OFF* when this procedure is called. + . + . NB:This shouldn't be static since it is referred to externally. +*/ +int smc_init(struct net_device *dev); + +/* + . This is called by unregister_netdev(). It is responsible for + . cleaning up before the driver is finally unregistered and discarded. +*/ +void smc_destructor(struct net_device *dev); + +/* + . The kernel calls this function when someone wants to use the net_device, + . typically 'ifconfig ethX up'. +*/ +static int smc_open(struct net_device *dev); + +/* + . This is called by the kernel to send a packet out into the net. it's + . responsible for doing a best-effort send, but if it's simply not possible + . to send it, the packet gets dropped. +*/ +static void smc_timeout(struct net_device *dev); +/* + . This is called by the kernel in response to 'ifconfig ethX down'. It + . is responsible for cleaning up everything that the open routine + . does, and maybe putting the card into a powerdown state. +*/ +static int smc_close(struct net_device *dev); + +/* + . This routine allows the proc file system to query the driver's + . statistics. +*/ +static struct net_device_stats *smc_query_statistics(struct net_device *dev); + +/* + . Finally, a call to set promiscuous mode ( for TCPDUMP and related + . programs ) and multicast modes. +*/ +static void smc_set_multicast_list(struct net_device *dev); + +/* + . Configures the PHY through the MII Management interface +*/ +static void smc_phy_configure(struct net_device *dev); + +/*--------------------------------------------------------------- + . + . Interrupt level calls.. + . + ----------------------------------------------------------------*/ +/* + * handles dma irq for receive + */ +static void smc_dma_irq(int ch, void *lp, struct pt_regs *regs); + +/* + . Handles chip's interrupt +*/ +static void smc_interrupt(int irq, void *, struct pt_regs *regs); +/* + . This is a separate procedure to handle the receipt of a packet, to + . leave the interrupt code looking slightly cleaner +*/ +inline static void smc_rcv(struct net_device *dev); +/* + . This handles a TX interrupt, which is called when an error + . relating to a packet is sent or for single TX mode + . (non-AUTO_RELEASE). +*/ +inline static void smc_tx(struct net_device *dev); + +/* + . This handles interrupts generated from PHY register 18 +*/ +static void smc_phy_interrupt(struct net_device *dev); + +/* + ------------------------------------------------------------ + . + . Internal routines + . + ------------------------------------------------------------ +*/ + +/* + . Test if a given location contains a chip, trying to cause as + . little damage as possible if it's not a SMC chip. +*/ +static int smc_probe(struct net_device *dev, unsigned int ioaddr); + +/* + . A rather simple routine to print out a packet for debugging purposes. +*/ +#if SMC_DEBUG > 2 +static void print_packet(u8 *, int); +#endif + +#define tx_done(dev) 1 + +/* this is called to actually send the packet to the chip */ +static void smc_hardware_send_packet(struct net_device *dev); + +/* 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 generates an interrupt when the card is ready for the + . packet */ +static int smc_wait_to_send_packet(struct sk_buff *skb, struct net_device *dev); + +/* this does a soft reset on the device */ +static void smc_reset(struct net_device *dev); + +/* Enable Interrupts, Receive, and Transmit */ +static void smc_enable(struct net_device *dev); + +/* this puts the device in an inactive state */ +static void smc_shutdown(unsigned int ioaddr); + +#ifndef NO_AUTOPROBE +/* This routine will find the IRQ of the driver if one is not + . specified in the input to the device. */ +static int smc_findirq(unsigned int ioaddr); +#endif + +/* + this routine will set the hardware multicast table to the specified + values given it by the higher level routines +*/ +static void smc_setmulticast(unsigned int ioaddr, int count, + struct dev_mc_list *); +static int crc32(char *, int); + +/* Routines to Read and Write the PHY Registers across the + MII Management Interface +*/ + +static u16 smc_read_phy_register(unsigned int ioaddr, u8 phyaddr, + u8 phyreg); +static void smc_write_phy_register(unsigned int ioaddr, u8 phyaddr, + u8 phyreg, u16 phydata); + +/* Initilizes our device's sysctl proc filesystem */ + +#ifdef CONFIG_SYSCTL +static void smc_sysctl_register(struct net_device *); +static void smc_sysctl_unregister(struct net_device *); +#endif /* CONFIG_SYSCTL */ + +#ifdef CONFIG_ARCH_PXA + +/* + * These functions allow us to handle IO addressing as we wish - this + * ethernet controller can be connected to a variety of busses. Some + * busses do not support 16 bit or 32 bit transfers. --rmk + * For PXA250 we are assuming 32 bit because that's the only thing + * that works on fifo receive operations. --jws + */ +static inline u8 +SMC_inb(u_int base, u_int reg) +{ + /* byte and word reads on Cotulla a0/a1 are broken + * on fifo reads. See the 4.0 errata (ugh...) */ + int offset; + offset = (base + reg) & 0x3; + return inl((base + reg) & ~3) >> 8 * offset; +} + +static inline u16 +SMC_inw(u_int base, u_int reg) +{ + int offset; + + offset = (base + reg) & 0x3; + if (offset % 2) + printk(KERN_CRIT "%s: read not word aligned!\n", __FUNCTION__); + + return inl((base + reg) & ~3) >> 8 * offset; +} + +static inline u32 +SMC_inl(u_int base, u_int reg) +{ + if ((base + reg) & 3) + printk(KERN_CRIT "%s: read not dword aligned!\n", __FUNCTION__); + + return inl(base + reg); +} + +/* Only one release command at a time, please */ +static inline void smc_wait_mmu_release_complete( unsigned int ioaddr) +{ + int count = 0; + /* assume bank 2 selected */ + while ( SMC_inw( ioaddr, MMU_CMD_REG ) & MC_BUSY ) + { + udelay(1); // Wait until not busy + if( ++count > 200) break; + } +} + +/* Only one allocate command at a time, please */ +static inline void smc_check_mmu_allocate_complete( unsigned int ioaddr) +{ + if ( SMC_inb( ioaddr , AR_REG) & AR_FAILED) + { + printk("Memory alloc result: FAILED. \n"); + } +} + +static inline void +SMC_dma_inl(struct net_device *dev, unsigned char *data, u_int len) +{ +#if defined(CONFIG_ARCH_PXA) + struct smc_local *lp = (struct smc_local *) dev->priv; + dma_addr_t dma_handle; + unsigned int dcmd_width = DCMD_WIDTH2; + + if( lp->use_32bit) dcmd_width = DCMD_WIDTH4; + + //get sk_buff phys addr for dma engine: + dma_handle = + pci_map_single(NULL, data, (size_t) len, PCI_DMA_FROMDEVICE); + + if (dma_handle == 0) { + printk(KERN_CRIT + "%s: Couldn't acquire phys addr of dma target buffer!\n", + __FUNCTION__); + return; + } + + PRINTK("%s: using %0#10x for phys addr for target buffer\n", + __FUNCTION__, dma_handle); + PRINTK("%s: using %0#10lx for phys addr of smc rx buffer\n", + __FUNCTION__, lp->dev_dma_phys); + PRINTK("%s: using dma channel %d\n", __FUNCTION__, dev->dma); + PRINTK("%s: attempting to xfer %d bytes of data...\n", __FUNCTION__, + len); + + DCSR(dev->dma) = DCSR_NODESC; + DTADR(dev->dma) = dma_handle; + DSADR(dev->dma) = lp->dev_dma_phys; + DCMD(dev->dma) = + DCMD_INCTRGADDR | DCMD_BURST32 | dcmd_width | (DCMD_LENGTH & len); + DCSR(dev->dma) = DCSR_NODESC | DCSR_RUN; + + while (!(DCSR(dev->dma) & DCSR_STOPSTATE)); //may as well spin since this only takes 100uSec or less... + + DCSR(dev->dma) = 0; + pci_unmap_single(NULL, dma_handle, (size_t) len, PCI_DMA_FROMDEVICE); +#endif +} + +static inline void +SMC_ins(u_int base, u_int reg, unsigned char *data, u_int len, + struct net_device *dev) +{ + struct smc_local *lp = (struct smc_local *) dev->priv; + + if( lp->dev_dma_phys) + { + SMC_dma_inl(dev, data, len); + } + else + { + u_int port = base + reg; + if( lp->use_32bit) + { + int i; + unsigned char *tail; + unsigned long leftover; + + PRINTK ("SMC_ins: got %x for base, %x for reg, %p for data, %d for len\n", + base, reg, data, len); + PRINTK(" Reading %d dwords (and %d bytes) \n", len >> 2, len & 3); + + insl(port, data, len >> 2); + + /* read the left over bytes (this is where things go bad + * on PXA. Don't even THINK of using insb... */ + + if (len & 3) { + tail = data + (len & ~3); + leftover = SMC_inl(base, reg); + for (i = 0; i < (len & 3); i++) + *tail++ = (char) (leftover >> (8 * i)) & 0xff; + PRINTK("Leftover (last) fifo read: %0#10lx saving to %p\n", + leftover, tail); + } + } + else + { + PRINTK(" Reading %d words and %d byte(s) \n", len >> 1, len & 1); + insw(port, data, len >> 1); + if (len & 1) { + data += len & ~1; + *data = inb(port); + } + } + } +} + +/* byte, word writes function normally on Cotulla */ + +static inline void +SMC_outb(u8 val, u_int base, u_int reg) +{ + outb(val, base + reg); +} + +static inline void +SMC_outw(u16 val, u_int base, u_int reg) +{ + outw(val, base + reg); +} + +static inline void +SMC_outl(u32 val, u_int base, u_int reg) +{ + u_int port = base + reg; + outl(val, port); +} + +static inline void +SMC_outsl(u_int base, u_int reg, unsigned char *data, u_int len) +{ + u_int port = base + reg; + outsl(port, data, len >> 2); +} + +static inline void +SMC_outsw(u_int base, u_int reg, unsigned char *data, u_int len) +{ + u_int port = base + reg; + outsw(port, data, len >> 1); +} + +/*------------------------------------------------------------------------- + . I define some macros to make it easier to do somewhat common + . or slightly complicated, repeated tasks. + --------------------------------------------------------------------------*/ + +/* select a register bank, 0 to 3 */ + +#define SMC_SELECT_BANK(x) \ + { \ + SMC_outw(x, ioaddr, BANK_SELECT); \ + } + +/* define a small delay for the reset */ +#define SMC_DELAY() \ + { \ + SMC_inw(ioaddr, RCR); \ + SMC_inw(ioaddr, RCR); \ + SMC_inw(ioaddr, RCR); \ + } + +/* this enables an interrupt in the interrupt mask register */ +#define SMC_ENABLE_INT(x) \ + { \ + u8 mask; \ + SMC_SELECT_BANK(2) \ + mask = SMC_inb(ioaddr, IM_REG); \ + mask |= (x); \ + SMC_outb(mask, ioaddr, IM_REG); \ + } + +/* this disables an interrupt from the interrupt mask register */ + +#define SMC_DISABLE_INT(x) { \ + u8 mask; \ + SMC_SELECT_BANK(2); \ + mask = SMC_inb( ioaddr, IM_REG ); \ + mask &= ~(x); \ + SMC_outb( mask, ioaddr, IM_REG ); \ +} + +#endif +/* CONFIG_ARCH_PXA*/ + +/* + . Function: smc_reset( struct device* dev ) + . Purpose: + . This sets the SMC91111 chip to its normal state, hopefully from whatever + . mess that any other DOS driver has put it in. + . + . Maybe I should reset more registers to defaults in here? SOFTRST should + . do that for me. + . + . Method: + . 1. send a SOFT RESET + . 2. wait for it to finish + . 3. enable autorelease mode + . 4. reset the memory management unit + . 5. clear all interrupts + . +*/ +static void +smc_reset(struct net_device *dev) +{ + //struct smc_local *lp = (struct smc_local *)dev->priv; + unsigned int ioaddr = dev->base_addr; + + PRINTK2("%s:smc_reset\n", dev->name); + + /* This resets the registers mostly to defaults, but doesn't + affect EEPROM. That seems unnecessary */ + SMC_SELECT_BANK(0); + SMC_outw(RCR_SOFTRST, ioaddr, RCR_REG); + + /* Setup the Configuration Register */ + /* This is necessary because the CONFIG_REG is not affected */ + /* by a soft reset */ + + SMC_SELECT_BANK(1); + SMC_outw(CONFIG_DEFAULT, ioaddr, CONFIG_REG); + + /* 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 (wait_mode[(int)(dev->name[3] - 48)]) //an ascii digit, don't forget + SMC_outw(((SMC_inw(ioaddr, CONFIG_REG)) | CONFIG_NO_WAIT), + ioaddr, CONFIG_REG); + +#ifdef SMC_POWER_DOWN + /* Release from possible power-down state */ + /* Configuration register is not affected by Soft Reset */ + SMC_SELECT_BANK(1); + SMC_outw((SMC_inw(ioaddr, CONFIG_REG) | CONFIG_EPH_POWER_EN), + ioaddr, CONFIG_REG); +#endif + + SMC_SELECT_BANK(0); + + /* this should pause enough for the chip to be happy */ + mdelay(10); + + /* Disable transmit and receive functionality */ + SMC_outw(RCR_CLEAR, ioaddr, RCR_REG); + SMC_outw(TCR_CLEAR, ioaddr, TCR_REG); + + /* set the control register to automatically + release successfully transmitted packets, to make the best + use out of our limited memory */ + SMC_SELECT_BANK(1); +#ifdef USE_AUTO_RELEASE + SMC_outw((SMC_inw(ioaddr, CTL_REG) | CTL_AUTO_RELEASE), ioaddr, + CTL_REG); +#else + SMC_outw((SMC_inw(ioaddr, CTL_REG) & ~CTL_AUTO_RELEASE), ioaddr, + CTL_REG); +#endif + + + /* Reset the MMU */ + SMC_SELECT_BANK(2); + SMC_outw(MC_RESET, ioaddr, MMU_CMD_REG); + + /* Note: It doesn't seem that waiting for the MMU busy is needed here, + but this is a place where future chipsets _COULD_ break. Be wary + of issuing another MMU command right after this */ + + /* Well, then, how about doing this? [jws] */ + while (SMC_inw(ioaddr, MMU_CMD_REG) & MC_BUSY) + udelay(1); // Wait until not busy + + /* Disable all interrupts */ + SMC_outb(0, ioaddr, IM_REG); +} + +/* + . Function: smc_enable + . Purpose: let the chip talk to the outside work + . Method: + . 1. Enable the transmitter + . 2. Enable the receiver + . 3. Enable interrupts +*/ +static void +smc_enable(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *) dev->priv; + + PRINTK2("%s:smc_enable\n", dev->name); + + SMC_SELECT_BANK(0); + /* see the header file for options in TCR/RCR DEFAULT */ + SMC_outw(lp->tcr_cur_mode, ioaddr, TCR_REG); + SMC_outw(lp->rcr_cur_mode, ioaddr, RCR_REG); + + /* now, enable interrupts */ + SMC_SELECT_BANK(2); + SMC_outb(SMC_INTERRUPT_MASK, ioaddr, IM_REG); +} + +/* + . Function: smc_shutdown + . Purpose: closes down the SMC91xxx chip. + . Method: + . 1. zero the interrupt mask + . 2. clear the enable receive flag + . 3. clear the enable xmit flags + . + . TODO: + . (1) maybe utilize power down mode. + . Why not yet? Because while the chip will go into power down mode, + . the manual says that it will wake up in response to any I/O requests + . in the register space. Empirical results do not show this working. +*/ +static void +smc_shutdown(unsigned int ioaddr) +{ + PRINTK2("%s:smc_shutdown\n", CARDNAME); + + /* no more interrupts for me */ + SMC_SELECT_BANK(2); + SMC_outb(0, ioaddr, IM_REG); + + /* and tell the card to stay away from that nasty outside world */ + SMC_SELECT_BANK(0); + SMC_outb(RCR_CLEAR, ioaddr, RCR_REG); + SMC_outb(TCR_CLEAR, ioaddr, TCR_REG); + +#ifdef SMC_POWER_DOWN + /* finally, shut the chip down */ + SMC_SELECT_BANK(1); + SMC_outw(SMC_inw(ioaddr, CONFIG_REG) & ~CONFIG_EPH_POWER_EN, + ioaddr, CONFIG_REG); +#endif +} + +/* + . Function: smc_setmulticast( int ioaddr, int count, dev_mc_list * adds ) + . Purpose: + . 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 int ioaddr, int count, struct dev_mc_list *addrs) +{ + int i; + unsigned char multicast_table[8]; + struct dev_mc_list *cur_addr; + /* table for flipping the order of 3 bits */ + unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + + PRINTK2("%s:smc_setmulticast\n", CARDNAME); + + /* start with a table of all zeros: reject all */ + memset(multicast_table, 0, sizeof (multicast_table)); + + cur_addr = addrs; + for (i = 0; i < count; i++, cur_addr = cur_addr->next) { + int position; + + /* do we have a pointer here? */ + if (!cur_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)) + continue; + + /* only use the low order bits */ + position = crc32(cur_addr->dmi_addr, 6) & 0x3f; + + /* do some messy swapping to put the bit in the right spot */ + multicast_table[invert3[position & 7]] |= + (1 << invert3[(position >> 3) & 7]); + + } + /* now, the table can be loaded into the chipset */ + SMC_SELECT_BANK(3); + + for (i = 0; i < 8; i++) { + SMC_outb(multicast_table[i], ioaddr, MCAST_REG1 + i); + } +} + +/* + Finds the CRC32 of a set of bytes. + Again, from Peter Cammaert's code. +*/ +static int +crc32(char *s, int length) +{ + /* indices */ + int perByte; + int perBit; + /* crc polynomial for Ethernet */ + const unsigned long poly = 0xedb88320; + /* crc value - preinitialized to all 1's */ + unsigned long crc_value = 0xffffffff; + + 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); + c >>= 1; + } + } + return crc_value; +} + +/* + . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct device * ) + . Purpose: + . Attempt to allocate memory for a packet, if chip-memory is not + . available, then tell the card to generate an interrupt when it + . is available. + . + . Algorithm: + . + . o if the saved_skb is not currently null, then drop this packet + . on the floor. This should never happen, because of TBUSY. + . o if the saved_skb is null, then replace it with the current packet, + . o See if I can sending it now. + . o (NO): Enable interrupts and let the interrupt handler deal with it. + . o (YES):Send it now. +*/ +static int +smc_wait_to_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct smc_local *lp = (struct smc_local *) dev->priv; + unsigned int ioaddr = dev->base_addr; + u16 length; + unsigned short numPages; +#ifdef USE_AUTO_RELEASE + u16 time_out; + u16 status; +#endif + + PRINTK3("%s:smc_wait_to_send_packet\n", dev->name); + + if (lp->saved_skb) { + /* THIS SHOULD NEVER HAPPEN. */ + lp->stats.tx_aborted_errors++; + printk("%s: Unexpected send packet request. Not finished with previous one.\n", + dev->name); + return 1; + } + lp->saved_skb = skb; + + length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + +#if 0 + /* + ** 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 this header. + */ + numPages = ((length & 0xfffe) + 6); + numPages >>= 8; // Divide by 256 + + if (numPages > 7) { + printk("%s: Far too big packet error. \n", dev->name); + /* freeing the packet is a good thing here... but should + . any packets of this size get down here? */ + dev_kfree_skb(skb); + lp->saved_skb = NULL; + /* this IS an error, but, i don't want the skb saved */ + netif_wake_queue(dev); + return 0; + } +#else + numPages = 0; +#endif + /* either way, a packet is waiting now */ + lp->packets_waiting++; + + /* now, try to allocate the memory */ + SMC_SELECT_BANK(2); + +#ifdef USE_AUTO_RELEASE + smc_wait_mmu_release_complete( ioaddr); + SMC_outw((MC_ALLOC | numPages), ioaddr, MMU_CMD_REG); + /* + . Performance Hack + . + . wait a short amount of time.. if I can send a packet now, I send + . it now. Otherwise, I enable an interrupt and wait for one to be + . available. + . + . I could have handled this a slightly different way, by checking to + . see if any memory was available in the FREE MEMORY register. However, + . either way, I need to generate an allocation, and the allocation works + . no matter what, so I saw no point in checking free memory. + */ + time_out = MEMORY_WAIT_TIME; + do { + status = SMC_inb(ioaddr, INT_REG); + if (status & IM_ALLOC_INT) { + /* acknowledge the interrupt */ + SMC_outb(IM_ALLOC_INT, ioaddr, INT_REG); + break; + } + } while (--time_out); + + if (!time_out) { + netif_stop_queue (dev); + /* oh well, wait until the chip finds memory later */ + SMC_ENABLE_INT(IM_ALLOC_INT); + /* Check the status bit one more time just in case */ + /* it snuk in between the time we last checked it */ + /* and when we set the interrupt bit */ + status = SMC_inb(ioaddr, INT_REG); + if (!(status & IM_ALLOC_INT)) { + PRINTK2("%s: memory allocation deferred. \n", + dev->name); + /* it's deferred, but I'll handle it later */ + return 0; + } + + /* Looks like it did sneak in, so disable */ + /* the interrupt */ + SMC_DISABLE_INT(IM_ALLOC_INT); + } + /* or YES! I can send the packet now.. */ + smc_hardware_send_packet(dev); +#else + /* dont use performance hack */ + /* stop the queue and wait for alloc interrupt */ + netif_stop_queue (dev); + + spin_lock_irq(&lp->mmu_lock); + SMC_ENABLE_INT( IM_ALLOC_INT ); + smc_wait_mmu_release_complete( ioaddr); + SMC_outw((MC_ALLOC | numPages), ioaddr, MMU_CMD_REG); + spin_unlock_irq(&lp->mmu_lock); +#endif + return 0; +} + +/* + . Function: smc_hardware_send_packet(struct device * ) + . Purpose: + . This sends the actual packet to the SMC9xxx chip. + . + . Algorithm: + . First, see if a saved_skb is available. + . ( this should NOT be called if there is no 'saved_skb' + . Now, find the packet number that the chip allocated + . Point the data pointers at it in memory + . Set the length word in the chip's memory + . Dump the packet to chip memory + . Check if a last byte is needed ( odd length packet ) + . if so, set the control flag right + . Tell the card to send it + . Enable the transmit interrupt, so I know if it failed + . Free the kernel data if I actually sent it. +*/ +static void +smc_hardware_send_packet(struct net_device *dev) +{ + struct smc_local *lp = (struct smc_local *) dev->priv; + u8 packet_no; + struct sk_buff *skb = lp->saved_skb; + u16 length; + unsigned int ioaddr; + unsigned char *buf; + + PRINTK3("%s:smc_hardware_send_packet\n", dev->name); + + ioaddr = dev->base_addr; + + if (!skb) { + PRINTK("%s: In XMIT with no packet to send \n", dev->name); + return; + } + length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + buf = skb->data; + + /* If I get here, I _know_ there is a packet slot waiting for me */ + packet_no = SMC_inb(ioaddr, AR_REG); + if (packet_no & AR_FAILED) { + /* or isn't there? BAD CHIP! */ + printk(KERN_INFO "%s: Memory allocation failed. \n", + dev->name); + dev_kfree_skb_any(skb); + lp->saved_skb = NULL; + netif_wake_queue(dev); + return; + } + + /* we have a packet address, so tell the card to use it */ + SMC_outb(packet_no, ioaddr, PN_REG); + + /* point to the beginning of the packet */ + SMC_outw(PTR_AUTOINC, ioaddr, PTR_REG); + + PRINTK3("%s: Trying to xmit packet of length %x\n", dev->name, length); + +#if SMC_DEBUG > 2 + printk("Transmitting Packet\n"); + print_packet(buf, length); +#endif + + /* send the packet length ( +6 for status, length and ctl byte ) + and the status word ( set to zeros ) */ + if( lp->use_32bit) + SMC_outl((length + 6) << 16, ioaddr, DATA_REG); + else + { + SMC_outw(0, ioaddr, DATA_REG); + /* send the packet length ( +6 for status words, length, and ctl */ + SMC_outb((length + 6) & 0xFF, ioaddr, DATA_REG); + SMC_outb((length + 6) >> 8, ioaddr, DATA_REG); + } + + /* send the actual data + . I _think_ it's faster to send the longs first, and then + . mop up by sending the last word. It depends heavily + . on alignment, at least on the 486. Maybe it would be + . a good idea to check which is optimal? But that could take + . almost as much time as is saved? + */ + if( lp->use_32bit) + { + SMC_outsl(ioaddr, DATA_REG, buf, length); + if (length & 0x2) + SMC_outw(*((u16 *) (buf + (length & ~3))), ioaddr, DATA_REG); + } + else + SMC_outsw(ioaddr, DATA_REG, buf, length); + + /* Send the last byte, if there is one. */ + if ((length & 1) == 0) { + SMC_outw(0, ioaddr, DATA_REG); + } else { + SMC_outb(buf[length - 1], ioaddr, DATA_REG); + SMC_outb(0x20, ioaddr, DATA_REG); // Set odd bit in CONTROL BYTE + } + + /* enable the interrupts */ +#ifdef USE_AUTO_RELEASE + SMC_ENABLE_INT((IM_TX_INT | IM_TX_EMPTY_INT)); +#else + SMC_ENABLE_INT( (IM_TX_INT) ); + SMC_SELECT_BANK( 0 ); + SMC_outw( SMC_inw( ioaddr , TCR_REG ) | TCR_ENABLE, ioaddr , TCR_REG ); + SMC_SELECT_BANK( 2 ); +#endif + + /* and let the chipset deal with it */ + SMC_outw(MC_ENQUEUE, ioaddr, MMU_CMD_REG); + + PRINTK2("%s: Sent packet of length %d \n", dev->name, length); + lp->tx_bytes += length; + + lp->saved_skb = NULL; + dev_kfree_skb_any(skb); + + dev->trans_start = jiffies; + + return; +} + +/*------------------------------------------------------------------------- + | + | smc_init( struct device * dev ) + | Input parameters: + | dev->base_addr == 0, try to find all possible locations + | dev->base_addr == 1, return failure code + | dev->base_addr == 2, always allocate space, and return success + | dev->base_addr == this is the address to check + | + | Output: + | 0 --> there is a device + | anything else, error + | + --------------------------------------------------------------------------- +*/ +int __init +smc_init(struct net_device *dev) +{ +#ifdef CONFIG_ARCH_PXA + unsigned long base_addr = dev->base_addr; + + SET_MODULE_OWNER(dev); + PRINTK2("%s: smc_init\n", dev->name); + + /* try a specific location... */ + if (base_addr) + return smc_probe(dev, base_addr); +#endif + + /* couldn't find anything */ + return -ENODEV; +} + +/*------------------------------------------------------------------------- + | + | smc_destructor( struct device * dev ) + | Input parameters: + | dev, pointer to the device structure + | + | Output: + | None. + | + --------------------------------------------------------------------------- +*/ +void +smc_destructor(struct net_device *dev) +{ + PRINTK2("%s: smc_destructor\n", dev->name); +} + +#ifndef NO_AUTOPROBE +/*---------------------------------------------------------------------- + . 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 int ioaddr) +{ + int timeout = 20; + unsigned long cookie; + + PRINTK2("%s:smc_findirq\n", CARDNAME); + + /* I have to do a STI() here, because this is called from + a routine that does an CLI during this process, making it + rather difficult to get interrupts for auto detection */ + sti(); + + 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. + */ + + SMC_SELECT_BANK(2); + /* enable ALLOCation interrupts ONLY */ + SMC_outb(IM_ALLOC_INT, ioaddr, IM_REG); + + /* + . Allocate 512 bytes of memory. Note that the chip was just + . reset so all the memory is available + */ + SMC_outw(MC_ALLOC | 1, ioaddr, MMU_CMD_REG); + + /* + . Wait until positive that the interrupt has been generated + */ + while (timeout) { + u8 int_status; + + int_status = SMC_inb(ioaddr, INT_REG); + + if (int_status & IM_ALLOC_INT) + break; /* got the interrupt */ + 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. */ + + /* DELAY HERE! + On a fast machine, the status might change before the interrupt + is given to the processor. This means that the interrupt was + never detected, and autoirq_report fails to report anything. + This should fix autoirq_* problems. + */ + mdelay(10); + + /* and disable all interrupts again */ + SMC_outb(0, ioaddr, IM_REG); + + /* clear hardware interrupts again, because that's how it + was when I was called... */ + cli(); + + /* and return what I found */ + return probe_irq_off(cookie); +} +#endif + +/*---------------------------------------------------------------------- + . Function: smc_probe( int ioaddr ) + . + . Purpose: + . Tests to see if a given ioaddr points to an SMC91111 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 int ioaddr) +{ + int i, memory, retval; + static unsigned version_printed = 0; + unsigned int bank; + const char *version_string; + int interface; + + /*registers */ + u16 revision_register; + u16 base_address_register; + u16 memory_info_register; + /*=> Pramod */ + struct smc_local *lp; + /*<= Pramod */ + + /* find the interface numbed */ + for (interface = 0; interface < MAX_NETWORK_INTERFACE_COUNT; interface++) { + if ( smc_config[interface].port == dev->base_addr) + { + break; + } + } + + PRINTK2("%s: smc_probe\n", dev->name); + PRINTK("Trying to request region for base addr %x\n", ioaddr); + /* Grab the region so that no one else tries to probe our ioports. */ + if (!request_region(ioaddr, SMC_IO_EXTENT, dev->name)) + return -EBUSY; + PRINTK("request_region successful, try to read data from chip:\n"); + /* First, see if the high byte is 0x33 */ + bank = SMC_inw(ioaddr, BANK_SELECT); + PRINTK("bank got %x from SMC_inw\n", bank); + if ((bank & 0xFF00) != 0x3300) + return -ENODEV; + + /* The above MIGHT indicate a device, but I need to write to further test this. */ + SMC_outw(0x0, ioaddr, BANK_SELECT); + bank = SMC_inw(ioaddr, BANK_SELECT); + if ((bank & 0xFF00) != 0x3300) { + retval = -ENODEV; + goto err_out; + } + + /* 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); + base_address_register = SMC_inw(ioaddr, BASE_REG); + PRINTK("base_address_register = %x\n",base_address_register); + + /* go check those pesky isa io addresses >:( */ + if ((ioaddr&0x0fff) != (base_address_register >> 3 & 0x3E0)) { + printk("%s: ioaddr %x doesn't match configuration (%x)." + "Probably not a SMC chip\n", CARDNAME, + (ioaddr&0x0fff), base_address_register >> 3 & 0x3E0); + /* well, the base address register didn't match. Must not have + been a SMC chip after all. */ + retval = -ENODEV; + goto err_out; + } + + /* check if the revision register is something that I recognize. + These might need to be added to later, as future revisions + could be added. */ + SMC_SELECT_BANK(3); + revision_register = SMC_inw(ioaddr, REV_REG); + if (!chip_ids[(revision_register >> 4) & 0xF]) { + /* I don't recognize this chip, so... */ + printk + ("%s: IO %x: Unrecognized revision register: %x, Contact author. \n", + dev->name, ioaddr, revision_register); + retval = -ENODEV; + goto err_out; + } + PRINTK("revision_register = %x\n",revision_register); + + /* at this point I'll assume that the chip is an SMC9xxx. + It might be prudent to check a listing of MAC addresses + against the hardware address, or do some other tests. */ + /* Note also that ChipID's for smc91c110 and 91c111 are + * both '9'. ????? [jws] */ + + if (version_printed++ == 0) + PRINTK("%s", version); + + /* fill in some of the fields */ + dev->base_addr = ioaddr; + + /* + . Get the MAC address ( bank 1, regs 4 - 9 ) + */ + SMC_SELECT_BANK(1); + for (i = 0; i < 6; i += 2) { + u16 address; + + address = SMC_inw(ioaddr, ADDR0_REG + i); + dev->dev_addr[i + 1] = address >> 8; + dev->dev_addr[i] = address & 0xFF; + } + smc_init_mac_addr( dev, ioaddr, interface); + +#if defined(CONFIG_ARCH_PXA) +/* +* Don't assume that the eeprom had a valid mac address, y'all- Some +* switches *really* have a cow if they see all 0's or F's on the wire... +*/ + + for (i = 0; i < 6; i++) + if (dev->dev_addr[i] != 0 && dev->dev_addr[i] != 0xff) + break; + if (i == 6) { + /* ruh roh, bogus mac addr detected, add a dummy one: */ + printk + ("%s: Unitialized MAC address detected -- using a dummy one\n", + dev->name); + for (i = 0; i < 6; i++) + dev->dev_addr[i] = fallback_mac[i]; + dev->dev_addr[5] = (ioaddr >> 8) & 0xff; /* support for multiple chips */ + } +#endif + /* get the memory information */ + + SMC_SELECT_BANK(0); + memory_info_register = SMC_inw(ioaddr, MIR_REG); + memory = memory_info_register & (u16) 0x00ff; + memory *= LAN91C111_MEMORY_MULTIPLIER; + + /* + Now, I want to find out more about the chip. This is sort of + redundant, but it's cleaner to have it in both, rather than having + one VERY long probe procedure. + */ + SMC_SELECT_BANK(3); + revision_register = SMC_inw(ioaddr, REV_REG); + version_string = chip_ids[(revision_register >> 4) & 0xF]; + if (!version_string) { + /* I shouldn't get here because this call was done before.... */ + retval = -ENODEV; + goto err_out; + } + + /* now, reset the chip, and put it into a known state */ + smc_reset(dev); +#ifndef CONFIG_ARCH_PXA + /* + . 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 < 2) { + 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; + goto err_out; + } + /* can't assume this for non-PC architectures, folks */ + if (dev->irq == 2) { + /* Fixup for users that don't know that IRQ 2 is really IRQ 9, + * or don't know which one to set. + */ + dev->irq = 9; + } +#endif + + /* Initialize the private structure. */ + if (dev->priv == NULL) { + dev->priv = kmalloc(sizeof (struct smc_local), GFP_KERNEL); + if (dev->priv == NULL) { + retval = -ENOMEM; + goto err_out; + } + } + /* set the private data to zero by default */ + memset(dev->priv, 0, sizeof (struct smc_local)); + lp = (struct smc_local *) dev->priv; + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + + /* get extra config data for interface */ + lp->dev_dma_phys = smc_config[interface].dma_addr; + lp->use_32bit = smc_config[interface].use_32bit; + +#if defined(CONFIG_ARCH_PXA) + GPDR(IRQ_TO_GPIO_2_80(dev->irq)) &= + ~GPIO_bit(IRQ_TO_GPIO_2_80(dev->irq)); +// set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(dev->irq), GPIO_RISING_EDGE); + + /* treat a dma addr of zero as no dma */ + if( lp->dev_dma_phys) + { + retval = pxa_request_dma(dev->name, DMA_PRIO_LOW, smc_dma_irq, (void *) lp); + if (retval < 0) { + printk("%s: unable to acquire a DMA channel.\n", dev->name); + kfree(dev->priv); + dev->priv = NULL; + goto err_out; + } + + dev->dma = retval; + PRINTK("%s: got dma channel number %d for smc rx\n", __FUNCTION__, + dev->dma); + PRINTK("%s: using %0#10lx for phys addr for smc device number %d\n", + __FUNCTION__, lp->dev_dma_phys, i); + } +#endif + + /* Grab the IRQ */ + retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev); + if (retval) { + printk("%s: unable to get IRQ %d (irqval=%d).\n", + dev->name, dev->irq, retval); +#if defined(CONFIG_ARCH_PXA) + if( lp->dev_dma_phys) + pxa_free_dma(dev->dma); +#endif + kfree(dev->priv); + dev->priv = NULL; + goto err_out; + } +#if defined(CONFIG_ARM) + set_irq_type(dev->irq, IRQT_RISING); +#endif + + dev->open = smc_open; + dev->stop = smc_close; + dev->hard_start_xmit = smc_wait_to_send_packet; + dev->tx_timeout = smc_timeout; + dev->get_stats = smc_query_statistics; +#ifdef HAVE_MULTICAST + dev->set_multicast_list = &smc_set_multicast_list; +#endif + + /* => Store the ChipRevision and ChipID, to be used in resolving the Odd-Byte issue in RevB of LAN91C111; Pramod */ + SMC_SELECT_BANK(3); + revision_register = SMC_inw(ioaddr, REV_REG); + lp->ChipID = (revision_register >> 4) & 0xF; + lp->ChipRev = revision_register & 0xF; + + /* now, print out the card info, in a short format.. */ + + printk("%s: %s(rev:%d) at %#3x IRQ:%d DMA:%d (%s) MEM:%db NOWAIT:%d ", + dev->name, + version_string, revision_register & 0xF, ioaddr, dev->irq, dev->dma, + lp->use_32bit?"32-bit":"16-bit", + memory, wait_mode[(int)(dev->name[3]) - 48]); + /* + . Print the Ethernet address + */ + printk("ADDR: "); + for (i = 0; i < 5; i++) + printk("%2.2x:", dev->dev_addr[i]); + printk("%2.2x \n", dev->dev_addr[5]); + + PRINTK("ChipID: %x, ChipRev: %x, stored in dev->priv at %p\n", + lp->ChipID, lp->ChipRev, lp); + return 0; + + err_out: + release_region(ioaddr, SMC_IO_EXTENT); + return retval; +} + +#if SMC_DEBUG > 2 +static void +print_packet(unsigned char *buf, int length) +{ + int i; + int remainder; + int lines; + + printk("Packet of length %d \n", length); + +#if SMC_DEBUG > 3 + lines = length / 16; + remainder = length % 16; + + for (i = 0; i < lines; i++) { + int cur; + + for (cur = 0; cur < 8; cur++) { + u8 a, b; + + a = *(buf++); + b = *(buf++); + printk("%02x%02x ", a, b); + } + printk("\n"); + } + for (i = 0; i < remainder / 2; i++) { + u8 a, b; + + a = *(buf++); + b = *(buf++); + printk("%02x%02x ", a, b); + } + printk("\n"); +#endif +} +#endif + +/* + * 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 int ioaddr = dev->base_addr; + int i; /* used to set hw ethernet address */ +// u16 phy_status; +// u8 phyaddr = lp->phyaddr; + + PRINTK2("%s:smc_open\n", dev->name); + +// 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 (files) + lp->ctl_swfdup = 0; + lp->ctl_ephloop = 0; + lp->ctl_miiop = 0; + lp->ctl_autoneg = 1; + lp->ctl_rfduplx = 1; + lp->ctl_rspeed = 100; + lp->ctl_afduplx = 0; + lp->ctl_aspeed = 100; + lp->ctl_lnkfail = 1; + lp->ctl_forcol = 0; + lp->ctl_filtcar = 0; + + spin_lock_init(&lp->mmu_lock); + + /* reset the hardware */ + + smc_reset(dev); + smc_enable(dev); + + /* Configure the PHY */ + smc_phy_configure(dev); + +#if 0 + /* jws: read status of phy */ + phy_status = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG); + + if ((phy_status & PHY_STAT_REM_FLT) || !(phy_status & PHY_STAT_LINK)) { + printk("%s: ERROR: remote fault or cable not connected.\n", + __FUNCTION__); + return -ENOLINK; + } +#endif + + /* + According to Becker, I have to set the hardware address + at this point, because the (l)user can set it with an + ioctl. Easily done... + */ + SMC_SELECT_BANK(1); + for (i = 0; i < 6; i += 2) { + u16 address; + + address = dev->dev_addr[i + 1] << 8; + address |= dev->dev_addr[i]; + SMC_outw(address, ioaddr, ADDR0_REG + i); + } + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + +#ifdef CONFIG_SYSCTL + smc_sysctl_register(dev); +#endif + netif_start_queue(dev); + return 0; +} + +/*-------------------------------------------------------- + . Called by the kernel to send a packet out into the void + . of the net. This routine is largely based on + . skeleton.c, from Becker. + .-------------------------------------------------------- +*/ +static int smc_get_reg(int bank, unsigned int ioaddr, int reg); +static inline void smc_dump_registers( unsigned int ioaddr) +{ + int oldbank = inw( ioaddr + BANK_SELECT); + + printk("-----smc_dump-----\n"); + printk("-----BANK0-----\n"); + printk("TCR = %04x\n", smc_get_reg(0, ioaddr, TCR_REG)); + printk("EPH = %04x\n", smc_get_reg(0, ioaddr, EPH_STATUS_REG)); + printk("RCR = %04x\n", smc_get_reg(0, ioaddr, RCR_REG)); + printk("CNT = %04x\n", smc_get_reg(0, ioaddr, COUNTER_REG)); + printk("MIR = %04x\n", smc_get_reg(0, ioaddr, MIR_REG)); + printk("RPC = %04x\n", smc_get_reg(0, ioaddr, RPC_REG)); + + printk("-----BANK1-----\n"); + printk("CFG = %04x\n", smc_get_reg(1, ioaddr, CONFIG_REG)); + printk("BAS = %04x\n", smc_get_reg(1, ioaddr, BASE_REG)); + printk("GPR = %04x\n", smc_get_reg(1, ioaddr, GP_REG)); + printk("CTL = %04x\n", smc_get_reg(1, ioaddr, CTL_REG)); + + printk("-----BANK2-----\n"); + printk("ARPN= %04x\n", smc_get_reg(2, ioaddr, PN_REG)); + printk("RXTX= %04x\n", smc_get_reg(2, ioaddr, RXFIFO_REG)); + printk("PTR = %04x\n", smc_get_reg(2, ioaddr, PTR_REG)); + printk("ISR = %04x\n", smc_get_reg(2, ioaddr, INT_REG)); + + printk("-----BANK3-----\n"); + printk("REV = %04x\n", smc_get_reg(3, ioaddr, REV_REG)); + printk("ERX = %04x\n", smc_get_reg(3, ioaddr, ERCV_REG)); + + outw( oldbank, ioaddr + BANK_SELECT ); +} + +static void +smc_timeout(struct net_device *dev) +{ + PRINTK3("%s:smc_send_packet\n", dev->name); + + /* If we get here, some higher level has decided we are broken. + There should really be a "kick me" function call instead. */ + printk(KERN_INFO "%s: transmit timed out, %s?\n", dev->name, + tx_done(dev) ? "IRQ conflict" : "network cable problem"); + + smc_dump_registers( dev->base_addr); + +#if 0 + /* "kick" the adaptor */ + smc_reset(dev); + smc_enable(dev); + + /* Reconfigure the PHY */ + smc_phy_configure(dev); +#endif + + /* clear anything saved */ + ((struct smc_local *) dev->priv)->saved_skb = NULL; + netif_wake_queue(dev); + dev->trans_start = jiffies; +} + +/*-------------------------------------------------------------------- + . + . This is the main routine of the driver, to handle the net_device when + . it needs some attention. + . + . So: + . first, save state of the chipset + . branch off into routines to handle each case, and acknowledge + . each to the interrupt register + . and finally restore state. + . + ---------------------------------------------------------------------*/ +static void +smc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + unsigned int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *) dev->priv; + + u8 status; + u8 mask; + int timeout; + /* state registers */ + u16 saved_bank; + u16 saved_pointer; + + PRINTK3("%s: SMC interrupt started \n", dev->name); + + if (dev == NULL) { + printk(KERN_WARNING "%s: irq %d for unknown device.\n", + dev->name, irq); + return; + } + + saved_bank = SMC_inw(ioaddr, BANK_SELECT); + + SMC_SELECT_BANK(2); + saved_pointer = SMC_inw(ioaddr, PTR_REG); + + /* read the interrupt status register */ + mask = SMC_inb(ioaddr, IM_REG); + + /* disable all interrupts */ + SMC_outb(0, ioaddr, IM_REG); + + /* set a timeout value, so I don't stay here forever */ + timeout = 16; + + PRINTK2(KERN_WARNING "%s: MASK IS %x \n", dev->name, mask); + do { + /* read the status flag, and mask it */ + status = SMC_inb(ioaddr, INT_REG) & mask; + if (!status) + break; + + PRINTK3(KERN_WARNING "%s: Handling interrupt status %x \n", + dev->name, status); + + if (status & IM_RCV_INT) { + /* Got a packet(s). */ + //printk("rxint\n"); + PRINTK2(KERN_WARNING + "%s: Receive Interrupt\n", dev->name); + smc_rcv(dev); + + } else if (status & IM_TX_INT) { + unsigned short ephstatus; + + SMC_SELECT_BANK (0); + ephstatus = SMC_inw (ioaddr , EPH_STATUS_REG); + + if( ephstatus & ES_TX_SUC) + { +#ifdef USE_AUTO_RELEASE +#else + SMC_SELECT_BANK(2); + smc_tx(dev); + mask &= ~(IM_TX_INT); +#endif + SMC_SELECT_BANK (2); + SMC_outb(IM_TX_INT, ioaddr , INT_REG ); + } + else + { + PRINTK("%s: ##### TX ERROR handled\n", dev->name); + SMC_SELECT_BANK(2); + smc_tx(dev); + // Acknowledge the interrupt + SMC_outb(IM_TX_INT, ioaddr , INT_REG ); + + if( netif_queue_stopped (dev)) + netif_wake_queue(dev); + } + + } else if (status & IM_TX_EMPTY_INT) { +#ifdef USE_AUTO_RELEASE + byte tmpstatus; + + SMC_SELECT_BANK( 2 ); + // Acknowledge the interrupt + SMC_outb( IM_TX_EMPTY_INT, ioaddr , INT_REG ); + tmpstatus = SMC_inb( ioaddr , INT_REG ); + if( tmpstatus & IM_TX_INT) + { + printk("transmit error!\n"); + } + else + { + smc_update_stats( ioaddr, lp); + mask &= ~(IM_TX_EMPTY_INT | IM_TX_INT); + + if( netif_queue_stopped (dev)) + netif_wake_queue(dev); + } +#endif + } else if (status & IM_ALLOC_INT) { + PRINTK2(KERN_DEBUG "%s: Allocation interrupt \n", + dev->name); + /* clear this interrupt so it doesn't happen again */ + mask &= ~IM_ALLOC_INT; + + /* enable xmit interrupts based on this */ +#ifdef USE_AUTO_RELEASE + mask |= (IM_TX_EMPTY_INT | IM_TX_INT); +#else + mask |= ( IM_TX_INT ); +#endif + /* send packet to chip */ + smc_hardware_send_packet( dev ); + + SMC_outb( IM_ALLOC_INT, ioaddr, INT_REG ); + smc_check_mmu_allocate_complete( ioaddr); + + /* and let the card send more packets to me */ + /* FB: only wakeup for 100mpbs, something goes poopy if I + * try it for 10mbps. Many more rcv overruns. + */ + if( lp->rpc_cur_mode & RPC_SPEED) + netif_wake_queue(dev); + + PRINTK2("%s: Handoff done successfully.\n", dev->name); + } else if (status & IM_RX_OVRN_INT) { + lp->stats.rx_errors++; + lp->stats.rx_fifo_errors++; + // Acknowledge the interrupt + SMC_outb(IM_RX_OVRN_INT, ioaddr, INT_REG); + PRINTK("%s: rx overrun or fifo error!\n", dev->name); + //printk("fifo err!\n"); + } else if (status & IM_MDINT) { + smc_phy_interrupt(dev); + // Acknowledge the interrupt + SMC_outb(IM_MDINT, ioaddr, INT_REG); + } else if (status & IM_EPH_INT) { + PRINTK("%s: UNSUPPORTED: EPH INTERRUPT \n", dev->name); + SMC_outb(IM_EPH_INT, ioaddr, INT_REG); + } else if (status & IM_ERCV_INT) { + PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT \n", dev->name); + // Acknowledge the interrupt + SMC_outb(IM_ERCV_INT, ioaddr, INT_REG); + } + } while (timeout--); + + /* restore register states */ + + SMC_SELECT_BANK(2); + SMC_outb(mask, ioaddr, IM_REG); + + PRINTK3(KERN_WARNING "%s: MASK is now %x \n", dev->name, mask); + while (SMC_inw(ioaddr, PTR_REG) & PTR_NOTEMPTY) + udelay(2); + + SMC_outw(saved_pointer, ioaddr, PTR_REG); + + SMC_SELECT_BANK(saved_bank); + + PRINTK3("%s: Interrupt done\n", dev->name); + return; +} + +/* since we don't use descriptor mode, and activity tends to be quite +* bursty, we don't bother with an irq, but if by chance the irqen +* bit in the dma channel gets set, this will handle it. +*/ + +static void +smc_dma_irq(int ch, void *lp, struct pt_regs *regs) +{ +#ifdef CONFIG_ARCH_PXA + DCSR(ch) = 0; // Stop channel and disable ints +#endif +} + +/*------------------------------------------------------------- + . + . smc_rcv - receive a packet from the card + . + . There is ( at least ) a packet waiting to be read from + . chip-memory. + . + . o Read the status + . o If an error, record it + . o otherwise, read in the packet + -------------------------------------------------------------- +*/ +static void +smc_rcv(struct net_device *dev) +{ + struct smc_local *lp = (struct smc_local *) dev->priv; + unsigned int ioaddr = dev->base_addr; + int packet_number; + u16 status; + u16 packet_length; + u32 inval; + + PRINTK3("%s:smc_rcv\n", dev->name); + + /* assume bank 2 */ + + packet_number = SMC_inw(ioaddr, RXFIFO_REG); + + if (packet_number & RXFIFO_REMPTY) { + + /* we got called , but nothing was on the FIFO */ + PRINTK("%s: WARNING: smc_rcv with nothing on FIFO. \n", + dev->name); + /* don't need to restore anything */ + return; + } + //printk("pn: %x\n",packet_number); + PRINTK("Packet number: %x\n", packet_number); + /* start reading from the start of the packet */ + SMC_outw((PTR_READ | PTR_RCV | PTR_AUTOINC), ioaddr, PTR_REG); + + /* First two words are status and packet_length */ +#ifdef CONFIG_ARCH_PXA + inval = SMC_inl(ioaddr, DATA_REG); + status = (u16) inval & 0xffff; + packet_length = (u16) (inval >> 16) & 0x7ff; /* mask off top bits */ +#else + status = smc_inw(ioaddr, DATA_REG); + packet_length = smc_inw(ioaddr, DATA_REG) & 0x7ff; +#endif + PRINTK("status: %x, raw packet_length: %x\n", status, packet_length); + PRINTK2("RCV: STATUS %4x LENGTH %4x\n", status, packet_length); + + if (packet_length <= 6) + { + printk(KERN_ERR"Received Packet Size TOO Small (%d) - forcing reset!\n", + packet_length); +#if 0 + /* This indicates a bad state, reset the mmu. */ + SMC_outw( MC_RESET, ioaddr, MMU_CMD_REG); + return; +#endif + } + + /* The packet length contains 3 extra words: + * status, length, and an extra word with an odd byte (maybe) + */ + packet_length -= 6; + + if (!(status & RS_ERRORS)) { + /* do stuff to make a new packet */ + struct sk_buff *skb; + unsigned char *data; + int pad = 0; + + /* set multicast stats */ + if (status & RS_MULTICAST) + lp->stats.multicast++; + + /* => + ODD-BYTE ISSUE : The odd byte problem has been fixed in the LAN91C111 Rev B. + So we check if the Chip Revision, stored in smsc_local->ChipRev, is = 1. + If so then we increment the packet length only if RS_ODDFRAME is set. + If the Chip's revision is equal to 0, then we blindly increment the packet length + by 1, thus always assuming that the packet is odd length, leaving the higher layer + to decide the actual length. + -- Pramod + <= */ + if ((9 == lp->ChipID) && (1 == lp->ChipRev)) { + if (status & RS_ODDFRAME) + packet_length++; + + } else { + // set odd length for bug in LAN91C111, REV A + // which never sets RS_ODDFRAME + packet_length++; + } + + // Allocate enough memory for entire receive frame, + // plus alignment needs, to be safe + skb = dev_alloc_skb(packet_length + 12); //was 5 + + if (skb == NULL) { + printk(KERN_NOTICE "%s: Low memory, packet dropped.\n", + dev->name); + lp->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); + +#if defined(CONFIG_ARCH_PXA) + /* hack alert: we shift by 2 bytes to make sure the data after + * the ip header is 4-byte aligned. Note that we do not change + * the "data" pointer. It has to be 8-byte aligned to + * make the dma engine happy. Thus we have to step 2 bytes + * back in the ptr register (i.e. we will re-read the packet + * length, but ignore it). + */ + pad = 2; + skb_reserve(skb, pad); /* offset frame past pad bytes */ + SMC_outw((PTR_READ | PTR_RCV | PTR_AUTOINC | pad), dev->base_addr, PTR_REG); +#endif + SMC_ins(ioaddr, DATA_REG, data, packet_length + pad, dev); + +// This operation doesn't seem to be necessary for the smc91c111... +// while ( SMC_inw( ioaddr, MMU_CMD_REG ) & MC_BUSY ) +// udelay(2); // Wait until not busy + +// smc_wait_mmu_release_complete( ioaddr); +// SMC_outw(MC_RELEASE, ioaddr, MMU_CMD_REG); + +#if SMC_DEBUG > 2 + printk("Receiving Packet\n"); + // print_packet( data, packet_length ); + print_packet(skb->data, skb->len); +#endif + + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + dev->last_rx = jiffies; + lp->stats.rx_packets++; + lp->stats.rx_bytes += packet_length; + } else { + /* error ... */ + PRINTK("Got error on receive.\n"); + lp->stats.rx_errors++; + + 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++; + } + +done: +// while ( SMC_inw( ioaddr, MMU_CMD_REG ) & MC_BUSY ) +// udelay(2); // Wait until not busy + smc_wait_mmu_release_complete( ioaddr); + SMC_outw(MC_RELEASE, ioaddr, MMU_CMD_REG); +// smc_wait_mmu_release_complete( ioaddr); + + return; +} + +static void smc_update_stats( unsigned int ioaddr, struct smc_local *lp) +{ + u16 card_stats; + + /* update stats */ + SMC_SELECT_BANK( 0 ); + card_stats = SMC_inw( ioaddr , COUNTER_REG ); + /* single collisions */ + lp->stats.collisions += card_stats & 0xF; + card_stats >>= 4; + /* multiple collisions */ + lp->stats.collisions += card_stats & 0xF; + + /* these are for when linux supports these statistics */ +#if 0 + card_stats >>= 4; + /* deferred */ + card_stats >>= 4; + /* excess deferred */ +#endif + + lp->stats.tx_packets += lp->packets_waiting; + lp->stats.tx_bytes += lp->tx_bytes; + lp->tx_bytes = 0; + lp->packets_waiting = 0; +} + + +/************************************************************************* + . smc_tx + . + . Purpose: Handle a transmit error message. This will only be called + . when an error, because of the AUTO_RELEASE mode. + . + . Algorithm: + . Save pointer and packet no + . Get the packet no from the top of the queue + . check if it's valid ( if not, is this an error??? ) + . read the status word + . record the error + . ( resend? Not really, since we don't want old packets around ) + . Restore saved values + ************************************************************************/ +static void +smc_tx(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *) dev->priv; + u8 saved_packet; + u8 packet_no; + u16 tx_status; + + PRINTK3("%s:smc_tx\n", dev->name); + + /* assume bank 2 */ + + saved_packet = SMC_inb(ioaddr, PN_REG); + packet_no = SMC_inw(ioaddr, RXFIFO_REG); + packet_no &= 0x7F; + + /* If the TX FIFO is empty then nothing to do */ + if (packet_no & TXFIFO_TEMPTY) + return; + + /* select this as the packet to read from */ + SMC_outb(packet_no, ioaddr, PN_REG); + + /* read the first word (status word) from this packet */ + SMC_outw((PTR_AUTOINC | PTR_READ), ioaddr, PTR_REG); +// #ifdef CONFIG_ARCH_PXA +// tx_status = (u16) SMC_inl(ioaddr, DATA_REG) & 0xffff; +//#else + tx_status = SMC_inw(ioaddr, DATA_REG); +//#endif + PRINTK3("%s: TX DONE STATUS: %4x \n", dev->name, tx_status); + + if ( tx_status & TS_SUCCESS ) { +#ifdef USE_AUTO_RELEASE +#else + smc_update_stats( ioaddr, lp); + + if( netif_queue_stopped (dev)) + netif_wake_queue(dev); +#endif + } + else + { + 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++; + lp->ctl_forcol = 0; // Reset forced collsion + } + } +#if 0 + if (tx_status & TS_16COL) { + ...} +#endif + +#ifdef USE_AUTO_RELEASE + /* re-enable transmit */ + SMC_SELECT_BANK(0); + SMC_outw((SMC_inw(ioaddr, TCR_REG) | TCR_ENABLE), ioaddr, TCR_REG); + + /* one less packet waiting for me */ + lp->packets_waiting--; +#endif + + /* kill the packet */ + SMC_SELECT_BANK(2); + spin_lock_irq(&lp->mmu_lock); + smc_wait_mmu_release_complete( ioaddr); + SMC_outw(MC_FREEPKT, ioaddr, MMU_CMD_REG); + spin_unlock_irq(&lp->mmu_lock); + + /* Don't change Packet Number Reg until busy bit is cleared */ + /* Per LAN91C111 Spec, Page 50 */ +// while (SMC_inw(ioaddr, MMU_CMD_REG) & MC_BUSY) ; +// smc_wait_mmu_release_complete( ioaddr); + + SMC_outb(saved_packet, ioaddr, PN_REG); + return; +} + +/*---------------------------------------------------- + . 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) +{ + netif_stop_queue(dev); + //dev->start = 0; + + PRINTK2("%s:smc_close\n", dev->name); + +#ifdef CONFIG_SYSCTL + smc_sysctl_unregister(dev); +#endif /* CONFIG_SYSCTL */ + + /* clear everything */ + smc_shutdown(dev->base_addr); + + /* Update the statistics here. */ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + + 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; + + PRINTK2("%s:smc_query_statistics\n", dev->name); + + return &lp->stats; +} + +/*----------------------------------------------------------- + . smc_set_multicast_list + . + . 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) +{ + unsigned int ioaddr = dev->base_addr; + + PRINTK2("%s:smc_set_multicast_list\n", dev->name); + + SMC_SELECT_BANK(0); + if (dev->flags & IFF_PROMISC) { + PRINTK2("%s:smc_set_multicast_list:RCR_PRMS\n", dev->name); + SMC_outw((SMC_inw(ioaddr, RCR_REG) | RCR_PRMS), ioaddr, + RCR_REG); + } + +/* 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) { + SMC_outw((SMC_inw(ioaddr, RCR_REG) | RCR_ALMUL), ioaddr, + RCR_REG); + PRINTK2("%s:smc_set_multicast_list: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 */ + SMC_outw(SMC_inw(ioaddr, RCR_REG) & ~(RCR_PRMS | RCR_ALMUL), + ioaddr, RCR_REG); + /* 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:smc_set_multicast_list:~(RCR_PRMS|RCR_ALMUL)\n", + dev->name); + SMC_outw(SMC_inw(ioaddr, RCR_REG) & ~(RCR_PRMS | RCR_ALMUL), + ioaddr, RCR_REG); + + /* + since I'm disabling all multicast entirely, I need to + clear the multicast list + */ + SMC_SELECT_BANK(3); + SMC_outw(0, ioaddr, MCAST_REG1); + SMC_outw(0, ioaddr, MCAST_REG2); + SMC_outw(0, ioaddr, MCAST_REG3); + SMC_outw(0, ioaddr, MCAST_REG4); + } +} + +static struct net_device devSMC91111[MAX_NETWORK_INTERFACE_COUNT]; +static int io = 0; +static int irq = 0; +static int nowait = 1; + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Erik Stahlman "); +MODULE_DESCRIPTION + ("Ethernet driver for SMC 91C111 w. integral phy 10/100MB/s chip"); + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(nowait, "i"); + +MODULE_PARM_DESC(io, "SMC 91C111 I/O base address"); +MODULE_PARM_DESC(irq, "SMC 91C111 IRQ number"); +MODULE_PARM_DESC(nowait, "SMC 91C111 wait state usage"); + +/*------------------------------------------------------------ + . Module initialization function + .-------------------------------------------------------------*/ +static int __init +smc91111_init(void) +{ + int result, interface; + int device_present = 0; + + PRINTK("Initializing smc91111 device...\n"); + for (interface = 0; interface < MAX_NETWORK_INTERFACE_COUNT; interface++) { + PRINTK("Configuring interface number %d\n", interface); + + strcpy(devSMC91111[interface].name, "eth%d"); + devSMC91111[interface].init = smc_init; + + if( io) + { + wait_mode[interface] = nowait; + devSMC91111[interface].base_addr = io; + devSMC91111[interface].irq = irq; + } + else + { + wait_mode[interface] = nowait; //FIXME: get via smc_config? + devSMC91111[interface].base_addr = smc_config[interface].port; + devSMC91111[interface].irq = smc_config[interface].irq; + } + + PRINTK("Trying to register if%d irq=%d dma=%lx addr=%lx 32bit=%d\n", + interface, devSMC91111[interface].irq, + smc_config[interface].dma_addr, devSMC91111[interface].base_addr, + smc_config[interface].use_32bit ); + + if ((result = register_netdev(&devSMC91111[interface])) != 0) + { + /* if we fail, indicate to exit function there's + * nothing to clean up */ + devSMC91111[interface].irq = -1; + } + else + device_present++; + + if( io) break; + } + PRINTK("Detected %d smc chips...\n", device_present); + + return device_present ? 0 : -ENODEV; +} + +/*------------------------------------------------------------ + . Cleanup when module is removed with rmmod + .-------------------------------------------------------------*/ +void __exit +smc91111_exit(void) +{ + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + int interface; + + for (interface = 0; interface < MAX_NETWORK_INTERFACE_COUNT; interface++) { + + if( devSMC91111[interface].irq == -1) continue; + + unregister_netdev(&devSMC91111[interface]); + + free_irq(devSMC91111[interface].irq, &devSMC91111[interface]); + release_region(devSMC91111[interface].base_addr, SMC_IO_EXTENT); + + if( smc_config[interface].dma_addr) + pxa_free_dma(devSMC91111[interface].dma); + + if (devSMC91111[interface].priv) + kfree(devSMC91111[interface].priv); /* Kernel 2.4 Changes - Pramod */ + } +} + +module_init(smc91111_init); +module_exit(smc91111_exit); + +#ifdef CONFIG_SYSCTL + +/*------------------------------------------------------------ + . Modify a bit in the LAN91C111 register set + .-------------------------------------------------------------*/ +static u16 +smc_modify_regbit(int bank, unsigned int ioaddr, int reg, + unsigned int bit, int val) +{ + u16 regval; + + SMC_SELECT_BANK(bank); + + regval = SMC_inw(ioaddr, reg); + if (val) + regval |= bit; + else + regval &= ~bit; + + SMC_outw(regval, ioaddr, reg); + return (regval); +} + +/*------------------------------------------------------------ + . Retrieve a bit in the LAN91C111 register set + .-------------------------------------------------------------*/ +static int +smc_get_regbit(int bank, unsigned int ioaddr, int reg, unsigned int bit) +{ + SMC_SELECT_BANK(bank); + if (SMC_inw(ioaddr, reg) & bit) + return (1); + else + return (0); +} + +/*------------------------------------------------------------ + . Modify a LAN91C111 register (u16 access only) + .-------------------------------------------------------------*/ +static void +smc_modify_reg(int bank, unsigned int ioaddr, int reg, u16 val) +{ + SMC_SELECT_BANK(bank); + SMC_outw(val, ioaddr, reg); +} + +/*------------------------------------------------------------ + . Retrieve a LAN91C111 register (u16 access only) + .-------------------------------------------------------------*/ +static int +smc_get_reg(int bank, unsigned int ioaddr, int reg) +{ + SMC_SELECT_BANK(bank); + return (SMC_inw(ioaddr, reg)); +} + +static const char smc_info_string[] = + "\n" + "info Provides this information blurb\n" + "swver Prints the software version information of this driver\n" + "autoneg Auto-negotiate Mode = 1\n" + "rspeed Requested Speed, 100=100Mbps, 10=10Mpbs\n" + "rfduplx Requested Full Duplex Operation\n" + "aspeed Actual Speed, 100=100Mbps, 10=10Mpbs\n" + "afduplx Actual Full Duplex Operation\n" + "lnkfail PHY Link Failure when 1\n" + "miiop External MII when 1, Internal PHY when 0\n" + "swfdup Switched Full Duplex Mode (allowed only in MII operation)\n" + "ephloop EPH Block Loopback\n" + "forcol Force a collision\n" + "filtcar Filter leading edge of carrier sense for 12 bit times\n" + "freemem Free buffer memory in bytes\n" + "totmem Total buffer memory in bytes\n" + "leda Output of LED-A (green)\n" + "ledb Output of LED-B (yellow)\n" + "chiprev Revision ID of the LAN91C111 chip\n" ""; + +/*------------------------------------------------------------ + . Sysctl handler for all integer parameters + .-------------------------------------------------------------*/ +static int +smc_sysctl_handler(ctl_table * ctl, int write, struct file *filp, + void *buffer, size_t * lenp) +{ + struct net_device *dev = (struct net_device *) ctl->extra1; + struct smc_local *lp = (struct smc_local *) ctl->extra2; + unsigned int ioaddr = dev->base_addr; + int *valp = ctl->data; + int val; + int ret; + + // Update parameters from the real registers + switch (ctl->ctl_name) { + case CTL_SMC_FORCOL: + *valp = smc_get_regbit(0, ioaddr, TCR_REG, TCR_FORCOL); + break; + + case CTL_SMC_FREEMEM: + *valp = ((u16) smc_get_reg(0, ioaddr, MIR_REG) >> 8) + * LAN91C111_MEMORY_MULTIPLIER; + break; + + case CTL_SMC_TOTMEM: + *valp = (smc_get_reg(0, ioaddr, MIR_REG) & (u16) 0x00ff) + * LAN91C111_MEMORY_MULTIPLIER; + break; + + case CTL_SMC_CHIPREV: + *valp = smc_get_reg(3, ioaddr, REV_REG); + break; + + case CTL_SMC_AFDUPLX: + *valp = (lp->lastPhy18 & PHY_INT_DPLXDET) ? 1 : 0; + break; + + case CTL_SMC_ASPEED: + *valp = (lp->lastPhy18 & PHY_INT_SPDDET) ? 100 : 10; + break; + + case CTL_SMC_LNKFAIL: + *valp = (lp->lastPhy18 & PHY_INT_LNKFAIL) ? 1 : 0; + break; + + case CTL_SMC_LEDA: + *valp = (lp->rpc_cur_mode >> RPC_LSXA_SHFT) & (u16) 0x0007; + break; + + case CTL_SMC_LEDB: + *valp = (lp->rpc_cur_mode >> RPC_LSXB_SHFT) & (u16) 0x0007; + break; + + case CTL_SMC_MIIOP: + *valp = smc_get_regbit(1, ioaddr, CONFIG_REG, CONFIG_EXT_PHY); + break; + +#ifdef SMC_DEBUG + case CTL_SMC_REG_BSR: // Bank Select + *valp = smc_get_reg(0, ioaddr, BSR_REG); + break; + + case CTL_SMC_REG_TCR: // Transmit Control + *valp = smc_get_reg(0, ioaddr, TCR_REG); + break; + + case CTL_SMC_REG_ESR: // EPH Status + *valp = smc_get_reg(0, ioaddr, EPH_STATUS_REG); + break; + + case CTL_SMC_REG_RCR: // Receive Control + *valp = smc_get_reg(0, ioaddr, RCR_REG); + break; + + case CTL_SMC_REG_CTRR: // Counter + *valp = smc_get_reg(0, ioaddr, COUNTER_REG); + break; + + case CTL_SMC_REG_MIR: // Memory Information + *valp = smc_get_reg(0, ioaddr, MIR_REG); + break; + + case CTL_SMC_REG_RPCR: // Receive/Phy Control + *valp = smc_get_reg(0, ioaddr, RPC_REG); + break; + + case CTL_SMC_REG_CFGR: // Configuration + *valp = smc_get_reg(1, ioaddr, CONFIG_REG); + break; + + case CTL_SMC_REG_BAR: // Base Address + *valp = smc_get_reg(1, ioaddr, BASE_REG); + break; + + case CTL_SMC_REG_IAR0: // Individual Address + *valp = smc_get_reg(1, ioaddr, ADDR0_REG); + break; + + case CTL_SMC_REG_IAR1: // Individual Address + *valp = smc_get_reg(1, ioaddr, ADDR1_REG); + break; + + case CTL_SMC_REG_IAR2: // Individual Address + *valp = smc_get_reg(1, ioaddr, ADDR2_REG); + break; + + case CTL_SMC_REG_GPR: // General Purpose + *valp = smc_get_reg(1, ioaddr, GP_REG); + break; + + case CTL_SMC_REG_CTLR: // Control + *valp = smc_get_reg(1, ioaddr, CTL_REG); + break; + + case CTL_SMC_REG_MCR: // MMU Command + *valp = smc_get_reg(2, ioaddr, MMU_CMD_REG); + break; + + case CTL_SMC_REG_PNR: // Packet Number + *valp = smc_get_reg(2, ioaddr, PN_REG); + break; + + case CTL_SMC_REG_FPR: // Allocation Result/FIFO Ports + *valp = smc_get_reg(2, ioaddr, RXFIFO_REG); + break; + + case CTL_SMC_REG_PTR: // Pointer + *valp = smc_get_reg(2, ioaddr, PTR_REG); + break; + + case CTL_SMC_REG_DR: // Data + *valp = smc_get_reg(2, ioaddr, DATA_REG); + break; + + case CTL_SMC_REG_ISR: // Interrupt Status/Mask + *valp = smc_get_reg(2, ioaddr, INT_REG); + break; + + case CTL_SMC_REG_MTR1: // Multicast Table Entry 1 + *valp = smc_get_reg(3, ioaddr, MCAST_REG1); + break; + + case CTL_SMC_REG_MTR2: // Multicast Table Entry 2 + *valp = smc_get_reg(3, ioaddr, MCAST_REG2); + break; + + case CTL_SMC_REG_MTR3: // Multicast Table Entry 3 + *valp = smc_get_reg(3, ioaddr, MCAST_REG3); + break; + + case CTL_SMC_REG_MTR4: // Multicast Table Entry 4 + *valp = smc_get_reg(3, ioaddr, MCAST_REG4); + break; + + case CTL_SMC_REG_MIIR: // Management Interface + *valp = smc_get_reg(3, ioaddr, MII_REG); + break; + + case CTL_SMC_REG_REVR: // Revision + *valp = smc_get_reg(3, ioaddr, REV_REG); + break; + + case CTL_SMC_REG_ERCVR: // Early RCV + *valp = smc_get_reg(3, ioaddr, ERCV_REG); + break; + + case CTL_SMC_REG_EXTR: // External + *valp = smc_get_reg(7, ioaddr, EXT_REG); + break; + + case CTL_SMC_PHY_CTRL: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_CNTL_REG); + break; + + case CTL_SMC_PHY_STAT: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_STAT_REG); + break; + + case CTL_SMC_PHY_ID1: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, PHY_ID1_REG); + break; + + case CTL_SMC_PHY_ID2: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, PHY_ID2_REG); + break; + + case CTL_SMC_PHY_ADC: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, PHY_AD_REG); + break; + + case CTL_SMC_PHY_REMC: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, PHY_RMT_REG); + break; + + case CTL_SMC_PHY_CFG1: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_CFG1_REG); + break; + + case CTL_SMC_PHY_CFG2: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_CFG2_REG); + break; + + case CTL_SMC_PHY_INT: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, PHY_INT_REG); + break; + + case CTL_SMC_PHY_MASK: + *valp = smc_read_phy_register(ioaddr, lp->phyaddr, + PHY_MASK_REG); + break; + +#endif // SMC_DEBUG + + default: + // Just ignore unsupported parameters + break; + } + + // Save old state + val = *valp; + + // Perform the generic integer operation + if ((ret = proc_dointvec(ctl, write, filp, buffer, lenp)) != 0) + return (ret); + + // Write changes out to the registers + if (write && *valp != val) { + + val = *valp; + switch (ctl->ctl_name) { + + case CTL_SMC_SWFDUP: + if (val) + lp->tcr_cur_mode |= TCR_SWFDUP; + else + lp->tcr_cur_mode &= ~TCR_SWFDUP; + + smc_modify_regbit(0, ioaddr, TCR_REG, TCR_SWFDUP, val); + break; + + case CTL_SMC_EPHLOOP: + if (val) + lp->tcr_cur_mode |= TCR_EPH_LOOP; + else + lp->tcr_cur_mode &= ~TCR_EPH_LOOP; + + smc_modify_regbit(0, ioaddr, TCR_REG, TCR_EPH_LOOP, + val); + break; + + case CTL_SMC_FORCOL: + if (val) + lp->tcr_cur_mode |= TCR_FORCOL; + else + lp->tcr_cur_mode &= ~TCR_FORCOL; + + // Update the EPH block + smc_modify_regbit(0, ioaddr, TCR_REG, TCR_FORCOL, val); + break; + + case CTL_SMC_FILTCAR: + if (val) + lp->rcr_cur_mode |= RCR_FILT_CAR; + else + lp->rcr_cur_mode &= ~RCR_FILT_CAR; + + // Update the EPH block + smc_modify_regbit(0, ioaddr, RCR_REG, RCR_FILT_CAR, + val); + break; + + case CTL_SMC_RFDUPLX: + // Disallow changes if in auto-negotiation mode + if (lp->ctl_autoneg) + break; + + if (val) { + lp->rpc_cur_mode |= RPC_DPLX; + } else { + lp->rpc_cur_mode &= ~RPC_DPLX; + } + + // Reconfigure the PHY + smc_phy_configure(dev); + + break; + + case CTL_SMC_RSPEED: + // Disallow changes if in auto-negotiation mode + if (lp->ctl_autoneg) + break; + + if (val > 10) + lp->rpc_cur_mode |= RPC_SPEED; + else + lp->rpc_cur_mode &= ~RPC_SPEED; + + // Reconfigure the PHY + smc_phy_configure(dev); + + break; + + case CTL_SMC_AUTONEG: + if (val) + lp->rpc_cur_mode |= RPC_ANEG; + else + lp->rpc_cur_mode &= ~RPC_ANEG; + + // Reconfigure the PHY + smc_phy_configure(dev); + + break; + + case CTL_SMC_LEDA: + val &= 0x07; // Restrict to 3 ls bits + lp->rpc_cur_mode &= ~(u16) (0x07 << RPC_LSXA_SHFT); + lp->rpc_cur_mode |= (u16) (val << RPC_LSXA_SHFT); + + // Update the Internal PHY block + smc_modify_reg(0, ioaddr, RPC_REG, lp->rpc_cur_mode); + break; + + case CTL_SMC_LEDB: + val &= 0x07; // Restrict to 3 ls bits + lp->rpc_cur_mode &= ~(u16) (0x07 << RPC_LSXB_SHFT); + lp->rpc_cur_mode |= (u16) (val << RPC_LSXB_SHFT); + + // Update the Internal PHY block + smc_modify_reg(0, ioaddr, RPC_REG, lp->rpc_cur_mode); + break; + + case CTL_SMC_MIIOP: + // Update the Internal PHY block + smc_modify_regbit(1, ioaddr, CONFIG_REG, + CONFIG_EXT_PHY, val); + break; + +#ifdef SMC_DEBUG + case CTL_SMC_REG_BSR: // Bank Select + smc_modify_reg(0, ioaddr, BSR_REG, val); + break; + + case CTL_SMC_REG_TCR: // Transmit Control + smc_modify_reg(0, ioaddr, TCR_REG, val); + break; + + case CTL_SMC_REG_ESR: // EPH Status + smc_modify_reg(0, ioaddr, EPH_STATUS_REG, val); + break; + + case CTL_SMC_REG_RCR: // Receive Control + smc_modify_reg(0, ioaddr, RCR_REG, val); + break; + + case CTL_SMC_REG_CTRR: // Counter + smc_modify_reg(0, ioaddr, COUNTER_REG, val); + break; + + case CTL_SMC_REG_MIR: // Memory Information + smc_modify_reg(0, ioaddr, MIR_REG, val); + break; + + case CTL_SMC_REG_RPCR: // Receive/Phy Control + smc_modify_reg(0, ioaddr, RPC_REG, val); + break; + + case CTL_SMC_REG_CFGR: // Configuration + smc_modify_reg(1, ioaddr, CONFIG_REG, val); + break; + + case CTL_SMC_REG_BAR: // Base Address + smc_modify_reg(1, ioaddr, BASE_REG, val); + break; + + case CTL_SMC_REG_IAR0: // Individual Address + smc_modify_reg(1, ioaddr, ADDR0_REG, val); + break; + + case CTL_SMC_REG_IAR1: // Individual Address + smc_modify_reg(1, ioaddr, ADDR1_REG, val); + break; + + case CTL_SMC_REG_IAR2: // Individual Address + smc_modify_reg(1, ioaddr, ADDR2_REG, val); + break; + + case CTL_SMC_REG_GPR: // General Purpose + smc_modify_reg(1, ioaddr, GP_REG, val); + break; + + case CTL_SMC_REG_CTLR: // Control + smc_modify_reg(1, ioaddr, CTL_REG, val); + break; + + case CTL_SMC_REG_MCR: // MMU Command + smc_modify_reg(2, ioaddr, MMU_CMD_REG, val); + break; + + case CTL_SMC_REG_PNR: // Packet Number + smc_modify_reg(2, ioaddr, PN_REG, val); + break; + + case CTL_SMC_REG_FPR: // Allocation Result/FIFO Ports + smc_modify_reg(2, ioaddr, RXFIFO_REG, val); + break; + + case CTL_SMC_REG_PTR: // Pointer + smc_modify_reg(2, ioaddr, PTR_REG, val); + break; + + case CTL_SMC_REG_DR: // Data + smc_modify_reg(2, ioaddr, DATA_REG, val); + break; + + case CTL_SMC_REG_ISR: // Interrupt Status/Mask + smc_modify_reg(2, ioaddr, INT_REG, val); + break; + + case CTL_SMC_REG_MTR1: // Multicast Table Entry 1 + smc_modify_reg(3, ioaddr, MCAST_REG1, val); + break; + + case CTL_SMC_REG_MTR2: // Multicast Table Entry 2 + smc_modify_reg(3, ioaddr, MCAST_REG2, val); + break; + + case CTL_SMC_REG_MTR3: // Multicast Table Entry 3 + smc_modify_reg(3, ioaddr, MCAST_REG3, val); + break; + + case CTL_SMC_REG_MTR4: // Multicast Table Entry 4 + smc_modify_reg(3, ioaddr, MCAST_REG4, val); + break; + + case CTL_SMC_REG_MIIR: // Management Interface + smc_modify_reg(3, ioaddr, MII_REG, val); + break; + + case CTL_SMC_REG_REVR: // Revision + smc_modify_reg(3, ioaddr, REV_REG, val); + break; + + case CTL_SMC_REG_ERCVR: // Early RCV + smc_modify_reg(3, ioaddr, ERCV_REG, val); + break; + + case CTL_SMC_REG_EXTR: // External + smc_modify_reg(7, ioaddr, EXT_REG, val); + break; + + case CTL_SMC_PHY_CTRL: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_CNTL_REG, val); + break; + + case CTL_SMC_PHY_STAT: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_STAT_REG, val); + break; + + case CTL_SMC_PHY_ID1: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_ID1_REG, val); + break; + + case CTL_SMC_PHY_ID2: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_ID2_REG, val); + break; + + case CTL_SMC_PHY_ADC: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_AD_REG, val); + break; + + case CTL_SMC_PHY_REMC: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_RMT_REG, val); + break; + + case CTL_SMC_PHY_CFG1: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_CFG1_REG, val); + break; + + case CTL_SMC_PHY_CFG2: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_CFG2_REG, val); + break; + + case CTL_SMC_PHY_INT: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_INT_REG, val); + break; + + case CTL_SMC_PHY_MASK: + smc_write_phy_register(ioaddr, lp->phyaddr, + PHY_MASK_REG, val); + break; + +#endif // SMC_DEBUG + + default: + // Just ignore unsupported parameters + break; + } // end switch + + } // end if + + return ret; +} + +/*------------------------------------------------------------ + . Sysctl registration function for all parameters (files) + .-------------------------------------------------------------*/ +static void +smc_sysctl_register(struct net_device *dev) +{ + struct smc_local *lp = (struct smc_local *) dev->priv; + static int ctl_name = CTL_SMC; + ctl_table *ct; + int i; + + // Make sure the ctl_tables start out as all zeros + memset(lp->root_table, 0, sizeof lp->root_table); + memset(lp->eth_table, 0, sizeof lp->eth_table); + memset(lp->param_table, 0, sizeof lp->param_table); + + // Initialize the root table + ct = lp->root_table; + ct->ctl_name = CTL_DEV; + ct->procname = "dev"; + ct->maxlen = 0; + ct->mode = 0555; + ct->child = lp->eth_table; + // remaining fields are zero + + // Initialize the ethX table (this device's table) + ct = lp->eth_table; + ct->ctl_name = ctl_name++; // Must be unique + ct->procname = dev->name; + ct->maxlen = 0; + ct->mode = 0555; + ct->child = lp->param_table; + // remaining fields are zero + + // Initialize the parameter (files) table + // Make sure the last entry remains null + ct = lp->param_table; + for (i = 0; i < (CTL_SMC_LAST_ENTRY - 1); ++i) { + // Initialize fields common to all table entries + ct[i].proc_handler = smc_sysctl_handler; + ct[i].extra1 = (void *) dev; // Save our device pointer + ct[i].extra2 = (void *) lp; // Save our smc_local data pointer + } + + // INFO - this is our only string parameter + i = 0; + ct[i].proc_handler = proc_dostring; // use default handler + ct[i].ctl_name = CTL_SMC_INFO; + ct[i].procname = "info"; + ct[i].data = (void *) smc_info_string; + ct[i].maxlen = sizeof smc_info_string; + ct[i].mode = 0444; // Read only + + // SWVER + ++i; + ct[i].proc_handler = proc_dostring; // use default handler + ct[i].ctl_name = CTL_SMC_SWVER; + ct[i].procname = "swver"; + ct[i].data = (void *) version; + ct[i].maxlen = sizeof version; + ct[i].mode = 0444; // Read only + + // SWFDUP + ++i; + ct[i].ctl_name = CTL_SMC_SWFDUP; + ct[i].procname = "swfdup"; + ct[i].data = (void *) &(lp->ctl_swfdup); + ct[i].maxlen = sizeof lp->ctl_swfdup; + ct[i].mode = 0644; // Read by all, write by root + + // EPHLOOP + ++i; + ct[i].ctl_name = CTL_SMC_EPHLOOP; + ct[i].procname = "ephloop"; + ct[i].data = (void *) &(lp->ctl_ephloop); + ct[i].maxlen = sizeof lp->ctl_ephloop; + ct[i].mode = 0644; // Read by all, write by root + + // MIIOP + ++i; + ct[i].ctl_name = CTL_SMC_MIIOP; + ct[i].procname = "miiop"; + ct[i].data = (void *) &(lp->ctl_miiop); + ct[i].maxlen = sizeof lp->ctl_miiop; + ct[i].mode = 0644; // Read by all, write by root + + // AUTONEG + ++i; + ct[i].ctl_name = CTL_SMC_AUTONEG; + ct[i].procname = "autoneg"; + ct[i].data = (void *) &(lp->ctl_autoneg); + ct[i].maxlen = sizeof lp->ctl_autoneg; + ct[i].mode = 0644; // Read by all, write by root + + // RFDUPLX + ++i; + ct[i].ctl_name = CTL_SMC_RFDUPLX; + ct[i].procname = "rfduplx"; + ct[i].data = (void *) &(lp->ctl_rfduplx); + ct[i].maxlen = sizeof lp->ctl_rfduplx; + ct[i].mode = 0644; // Read by all, write by root + + // RSPEED + ++i; + ct[i].ctl_name = CTL_SMC_RSPEED; + ct[i].procname = "rspeed"; + ct[i].data = (void *) &(lp->ctl_rspeed); + ct[i].maxlen = sizeof lp->ctl_rspeed; + ct[i].mode = 0644; // Read by all, write by root + + // AFDUPLX + ++i; + ct[i].ctl_name = CTL_SMC_AFDUPLX; + ct[i].procname = "afduplx"; + ct[i].data = (void *) &(lp->ctl_afduplx); + ct[i].maxlen = sizeof lp->ctl_afduplx; + ct[i].mode = 0444; // Read only + + // ASPEED + ++i; + ct[i].ctl_name = CTL_SMC_ASPEED; + ct[i].procname = "aspeed"; + ct[i].data = (void *) &(lp->ctl_aspeed); + ct[i].maxlen = sizeof lp->ctl_aspeed; + ct[i].mode = 0444; // Read only + + // LNKFAIL + ++i; + ct[i].ctl_name = CTL_SMC_LNKFAIL; + ct[i].procname = "lnkfail"; + ct[i].data = (void *) &(lp->ctl_lnkfail); + ct[i].maxlen = sizeof lp->ctl_lnkfail; + ct[i].mode = 0444; // Read only + + // FORCOL + ++i; + ct[i].ctl_name = CTL_SMC_FORCOL; + ct[i].procname = "forcol"; + ct[i].data = (void *) &(lp->ctl_forcol); + ct[i].maxlen = sizeof lp->ctl_forcol; + ct[i].mode = 0644; // Read by all, write by root + + // FILTCAR + ++i; + ct[i].ctl_name = CTL_SMC_FILTCAR; + ct[i].procname = "filtcar"; + ct[i].data = (void *) &(lp->ctl_filtcar); + ct[i].maxlen = sizeof lp->ctl_filtcar; + ct[i].mode = 0644; // Read by all, write by root + + // FREEMEM + ++i; + ct[i].ctl_name = CTL_SMC_FREEMEM; + ct[i].procname = "freemem"; + ct[i].data = (void *) &(lp->ctl_freemem); + ct[i].maxlen = sizeof lp->ctl_freemem; + ct[i].mode = 0444; // Read only + + // TOTMEM + ++i; + ct[i].ctl_name = CTL_SMC_TOTMEM; + ct[i].procname = "totmem"; + ct[i].data = (void *) &(lp->ctl_totmem); + ct[i].maxlen = sizeof lp->ctl_totmem; + ct[i].mode = 0444; // Read only + + // LEDA + ++i; + ct[i].ctl_name = CTL_SMC_LEDA; + ct[i].procname = "leda"; + ct[i].data = (void *) &(lp->ctl_leda); + ct[i].maxlen = sizeof lp->ctl_leda; + ct[i].mode = 0644; // Read by all, write by root + + // LEDB + ++i; + ct[i].ctl_name = CTL_SMC_LEDB; + ct[i].procname = "ledb"; + ct[i].data = (void *) &(lp->ctl_ledb); + ct[i].maxlen = sizeof lp->ctl_ledb; + ct[i].mode = 0644; // Read by all, write by root + + // CHIPREV + ++i; + ct[i].ctl_name = CTL_SMC_CHIPREV; + ct[i].procname = "chiprev"; + ct[i].data = (void *) &(lp->ctl_chiprev); + ct[i].maxlen = sizeof lp->ctl_chiprev; + ct[i].mode = 0444; // Read only + +#ifdef SMC_DEBUG + // REG_BSR + ++i; + ct[i].ctl_name = CTL_SMC_REG_BSR; + ct[i].procname = "reg_bsr"; + ct[i].data = (void *) &(lp->ctl_reg_bsr); + ct[i].maxlen = sizeof lp->ctl_reg_bsr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_TCR + ++i; + ct[i].ctl_name = CTL_SMC_REG_TCR; + ct[i].procname = "reg_tcr"; + ct[i].data = (void *) &(lp->ctl_reg_tcr); + ct[i].maxlen = sizeof lp->ctl_reg_tcr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_ESR + ++i; + ct[i].ctl_name = CTL_SMC_REG_ESR; + ct[i].procname = "reg_esr"; + ct[i].data = (void *) &(lp->ctl_reg_esr); + ct[i].maxlen = sizeof lp->ctl_reg_esr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_RCR + ++i; + ct[i].ctl_name = CTL_SMC_REG_RCR; + ct[i].procname = "reg_rcr"; + ct[i].data = (void *) &(lp->ctl_reg_rcr); + ct[i].maxlen = sizeof lp->ctl_reg_rcr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_CTRR + ++i; + ct[i].ctl_name = CTL_SMC_REG_CTRR; + ct[i].procname = "reg_ctrr"; + ct[i].data = (void *) &(lp->ctl_reg_ctrr); + ct[i].maxlen = sizeof lp->ctl_reg_ctrr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MIR + ++i; + ct[i].ctl_name = CTL_SMC_REG_MIR; + ct[i].procname = "reg_mir"; + ct[i].data = (void *) &(lp->ctl_reg_mir); + ct[i].maxlen = sizeof lp->ctl_reg_mir; + ct[i].mode = 0644; // Read by all, write by root + + // REG_RPCR + ++i; + ct[i].ctl_name = CTL_SMC_REG_RPCR; + ct[i].procname = "reg_rpcr"; + ct[i].data = (void *) &(lp->ctl_reg_rpcr); + ct[i].maxlen = sizeof lp->ctl_reg_rpcr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_CFGR + ++i; + ct[i].ctl_name = CTL_SMC_REG_CFGR; + ct[i].procname = "reg_cfgr"; + ct[i].data = (void *) &(lp->ctl_reg_cfgr); + ct[i].maxlen = sizeof lp->ctl_reg_cfgr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_BAR + ++i; + ct[i].ctl_name = CTL_SMC_REG_BAR; + ct[i].procname = "reg_bar"; + ct[i].data = (void *) &(lp->ctl_reg_bar); + ct[i].maxlen = sizeof lp->ctl_reg_bar; + ct[i].mode = 0644; // Read by all, write by root + + // REG_IAR0 + ++i; + ct[i].ctl_name = CTL_SMC_REG_IAR0; + ct[i].procname = "reg_iar0"; + ct[i].data = (void *) &(lp->ctl_reg_iar0); + ct[i].maxlen = sizeof lp->ctl_reg_iar0; + ct[i].mode = 0644; // Read by all, write by root + + // REG_IAR1 + ++i; + ct[i].ctl_name = CTL_SMC_REG_IAR1; + ct[i].procname = "reg_iar1"; + ct[i].data = (void *) &(lp->ctl_reg_iar1); + ct[i].maxlen = sizeof lp->ctl_reg_iar1; + ct[i].mode = 0644; // Read by all, write by root + + // REG_IAR2 + ++i; + ct[i].ctl_name = CTL_SMC_REG_IAR2; + ct[i].procname = "reg_iar2"; + ct[i].data = (void *) &(lp->ctl_reg_iar2); + ct[i].maxlen = sizeof lp->ctl_reg_iar2; + ct[i].mode = 0644; // Read by all, write by root + + // REG_GPR + ++i; + ct[i].ctl_name = CTL_SMC_REG_GPR; + ct[i].procname = "reg_gpr"; + ct[i].data = (void *) &(lp->ctl_reg_gpr); + ct[i].maxlen = sizeof lp->ctl_reg_gpr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_CTLR + ++i; + ct[i].ctl_name = CTL_SMC_REG_CTLR; + ct[i].procname = "reg_ctlr"; + ct[i].data = (void *) &(lp->ctl_reg_ctlr); + ct[i].maxlen = sizeof lp->ctl_reg_ctlr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MCR + ++i; + ct[i].ctl_name = CTL_SMC_REG_MCR; + ct[i].procname = "reg_mcr"; + ct[i].data = (void *) &(lp->ctl_reg_mcr); + ct[i].maxlen = sizeof lp->ctl_reg_mcr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_PNR + ++i; + ct[i].ctl_name = CTL_SMC_REG_PNR; + ct[i].procname = "reg_pnr"; + ct[i].data = (void *) &(lp->ctl_reg_pnr); + ct[i].maxlen = sizeof lp->ctl_reg_pnr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_FPR + ++i; + ct[i].ctl_name = CTL_SMC_REG_FPR; + ct[i].procname = "reg_fpr"; + ct[i].data = (void *) &(lp->ctl_reg_fpr); + ct[i].maxlen = sizeof lp->ctl_reg_fpr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_PTR + ++i; + ct[i].ctl_name = CTL_SMC_REG_PTR; + ct[i].procname = "reg_ptr"; + ct[i].data = (void *) &(lp->ctl_reg_ptr); + ct[i].maxlen = sizeof lp->ctl_reg_ptr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_DR + ++i; + ct[i].ctl_name = CTL_SMC_REG_DR; + ct[i].procname = "reg_dr"; + ct[i].data = (void *) &(lp->ctl_reg_dr); + ct[i].maxlen = sizeof lp->ctl_reg_dr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_ISR + ++i; + ct[i].ctl_name = CTL_SMC_REG_ISR; + ct[i].procname = "reg_isr"; + ct[i].data = (void *) &(lp->ctl_reg_isr); + ct[i].maxlen = sizeof lp->ctl_reg_isr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MTR1 + ++i; + ct[i].ctl_name = CTL_SMC_REG_MTR1; + ct[i].procname = "reg_mtr1"; + ct[i].data = (void *) &(lp->ctl_reg_mtr1); + ct[i].maxlen = sizeof lp->ctl_reg_mtr1; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MTR2 + ++i; + ct[i].ctl_name = CTL_SMC_REG_MTR2; + ct[i].procname = "reg_mtr2"; + ct[i].data = (void *) &(lp->ctl_reg_mtr2); + ct[i].maxlen = sizeof lp->ctl_reg_mtr2; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MTR3 + ++i; + ct[i].ctl_name = CTL_SMC_REG_MTR3; + ct[i].procname = "reg_mtr3"; + ct[i].data = (void *) &(lp->ctl_reg_mtr3); + ct[i].maxlen = sizeof lp->ctl_reg_mtr3; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MTR4 + ++i; + ct[i].ctl_name = CTL_SMC_REG_MTR4; + ct[i].procname = "reg_mtr4"; + ct[i].data = (void *) &(lp->ctl_reg_mtr4); + ct[i].maxlen = sizeof lp->ctl_reg_mtr4; + ct[i].mode = 0644; // Read by all, write by root + + // REG_MIIR + ++i; + ct[i].ctl_name = CTL_SMC_REG_MIIR; + ct[i].procname = "reg_miir"; + ct[i].data = (void *) &(lp->ctl_reg_miir); + ct[i].maxlen = sizeof lp->ctl_reg_miir; + ct[i].mode = 0644; // Read by all, write by root + + // REG_REVR + ++i; + ct[i].ctl_name = CTL_SMC_REG_REVR; + ct[i].procname = "reg_revr"; + ct[i].data = (void *) &(lp->ctl_reg_revr); + ct[i].maxlen = sizeof lp->ctl_reg_revr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_ERCVR + ++i; + ct[i].ctl_name = CTL_SMC_REG_ERCVR; + ct[i].procname = "reg_ercvr"; + ct[i].data = (void *) &(lp->ctl_reg_ercvr); + ct[i].maxlen = sizeof lp->ctl_reg_ercvr; + ct[i].mode = 0644; // Read by all, write by root + + // REG_EXTR + ++i; + ct[i].ctl_name = CTL_SMC_REG_EXTR; + ct[i].procname = "reg_extr"; + ct[i].data = (void *) &(lp->ctl_reg_extr); + ct[i].maxlen = sizeof lp->ctl_reg_extr; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Control + ++i; + ct[i].ctl_name = CTL_SMC_PHY_CTRL; + ct[i].procname = "phy_ctrl"; + ct[i].data = (void *) &(lp->ctl_phy_ctrl); + ct[i].maxlen = sizeof lp->ctl_phy_ctrl; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Status + ++i; + ct[i].ctl_name = CTL_SMC_PHY_STAT; + ct[i].procname = "phy_stat"; + ct[i].data = (void *) &(lp->ctl_phy_stat); + ct[i].maxlen = sizeof lp->ctl_phy_stat; + ct[i].mode = 0644; // Read by all, write by root + + // PHY ID1 + ++i; + ct[i].ctl_name = CTL_SMC_PHY_ID1; + ct[i].procname = "phy_id1"; + ct[i].data = (void *) &(lp->ctl_phy_id1); + ct[i].maxlen = sizeof lp->ctl_phy_id1; + ct[i].mode = 0644; // Read by all, write by root + + // PHY ID2 + ++i; + ct[i].ctl_name = CTL_SMC_PHY_ID2; + ct[i].procname = "phy_id2"; + ct[i].data = (void *) &(lp->ctl_phy_id2); + ct[i].maxlen = sizeof lp->ctl_phy_id2; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Advertise Capabilities + ++i; + ct[i].ctl_name = CTL_SMC_PHY_ADC; + ct[i].procname = "phy_adc"; + ct[i].data = (void *) &(lp->ctl_phy_adc); + ct[i].maxlen = sizeof lp->ctl_phy_adc; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Remote Capabilities + ++i; + ct[i].ctl_name = CTL_SMC_PHY_REMC; + ct[i].procname = "phy_remc"; + ct[i].data = (void *) &(lp->ctl_phy_remc); + ct[i].maxlen = sizeof lp->ctl_phy_remc; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Configuration 1 + ++i; + ct[i].ctl_name = CTL_SMC_PHY_CFG1; + ct[i].procname = "phy_cfg1"; + ct[i].data = (void *) &(lp->ctl_phy_cfg1); + ct[i].maxlen = sizeof lp->ctl_phy_cfg1; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Configuration 2 + ++i; + ct[i].ctl_name = CTL_SMC_PHY_CFG2; + ct[i].procname = "phy_cfg2"; + ct[i].data = (void *) &(lp->ctl_phy_cfg2); + ct[i].maxlen = sizeof lp->ctl_phy_cfg2; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Interrupt/Status Output + ++i; + ct[i].ctl_name = CTL_SMC_PHY_INT; + ct[i].procname = "phy_int"; + ct[i].data = (void *) &(lp->ctl_phy_int); + ct[i].maxlen = sizeof lp->ctl_phy_int; + ct[i].mode = 0644; // Read by all, write by root + + // PHY Interrupt/Status Mask + ++i; + ct[i].ctl_name = CTL_SMC_PHY_MASK; + ct[i].procname = "phy_mask"; + ct[i].data = (void *) &(lp->ctl_phy_mask); + ct[i].maxlen = sizeof lp->ctl_phy_mask; + ct[i].mode = 0644; // Read by all, write by root + +#endif // SMC_DEBUG + + // Register /proc/sys/dev/ethX + lp->sysctl_header = register_sysctl_table(lp->root_table, 1); +} + +/*------------------------------------------------------------ + . Sysctl unregistration when driver is closed + .-------------------------------------------------------------*/ +static void +smc_sysctl_unregister(struct net_device *dev) +{ + struct smc_local *lp = (struct smc_local *) dev->priv; + + unregister_sysctl_table(lp->sysctl_header); +} + +#endif /* endif CONFIG_SYSCTL */ + +//---PHY CONTROL AND CONFIGURATION----------------------------------------- + +#if (SMC_DEBUG > 2 ) + +/*------------------------------------------------------------ + . Debugging function for viewing MII Management serial bitstream + .-------------------------------------------------------------*/ +static void +smc_dump_mii_stream(u8 * bits, int size) +{ + int i; + + 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("\nMDO :"); + for (i = 0; i < size; ++i) { + if (bits[i] & MII_MDO) + printk("1"); + else + printk("0"); + } + + printk("\nMDI :"); + for (i = 0; i < size; ++i) { + if (bits[i] & MII_MDI) + printk("1"); + else + printk("0"); + } + + printk("\n"); +} +#endif + +/*------------------------------------------------------------ + . Reads a register from the MII Management serial interface + .-------------------------------------------------------------*/ +static u16 +smc_read_phy_register(unsigned int ioaddr, u8 phyaddr, u8 phyreg) +{ + int oldBank; + int i; + u8 mask; + u16 mii_reg; + u8 bits[64]; + int clk_idx = 0; + int input_idx; + u16 phydata; + + // 32 consecutive ones on MDO to establish sync + for (i = 0; i < 32; ++i) + bits[clk_idx++] = MII_MDOE | MII_MDO; + + // Start code <01> + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + // Read command <10> + bits[clk_idx++] = MII_MDOE | MII_MDO; + bits[clk_idx++] = MII_MDOE; + + // Output the PHY address, msb first + mask = (u8) 0x10; + for (i = 0; i < 5; ++i) { + if (phyaddr & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + // Shift to next lowest bit + mask >>= 1; + } + + // Output the phy register number, msb first + mask = (u8) 0x10; + for (i = 0; i < 5; ++i) { + if (phyreg & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + // Shift to next lowest bit + mask >>= 1; + } + + // Tristate and turnaroun