diff -ruN linux-2.5.59-rmk1/Makefile linux-2.5.59-pxacerf1/Makefile --- linux-2.5.59-rmk1/Makefile Mon Feb 17 13:47:00 2003 +++ linux-2.5.59-pxacerf1/Makefile Mon Feb 17 13:32:44 2003 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 5 SUBLEVEL = 59 -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.59-rmk1/arch/arm/def-configs/cerfcube_pxa linux-2.5.59-pxacerf1/arch/arm/def-configs/cerfcube_pxa --- linux-2.5.59-rmk1/arch/arm/def-configs/cerfcube_pxa Wed Dec 31 16:00:00 1969 +++ linux-2.5.59-pxacerf1/arch/arm/def-configs/cerfcube_pxa Mon Feb 17 13:31:05 2003 @@ -0,0 +1,652 @@ +# +# 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=14 + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODULE_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=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +# 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_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 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# 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_SUNRPC_GSS is not set +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.59-rmk1/arch/arm/mach-pxa/Kconfig linux-2.5.59-pxacerf1/arch/arm/mach-pxa/Kconfig --- linux-2.5.59-rmk1/arch/arm/mach-pxa/Kconfig Mon Feb 17 13:34:18 2003 +++ linux-2.5.59-pxacerf1/arch/arm/mach-pxa/Kconfig Mon Feb 17 12:55:43 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.59-rmk1/arch/arm/mach-pxa/Makefile linux-2.5.59-pxacerf1/arch/arm/mach-pxa/Makefile --- linux-2.5.59-rmk1/arch/arm/mach-pxa/Makefile Mon Feb 17 13:34:18 2003 +++ linux-2.5.59-pxacerf1/arch/arm/mach-pxa/Makefile Mon Feb 17 12:55:43 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.59-rmk1/arch/arm/mach-pxa/cerf.c linux-2.5.59-pxacerf1/arch/arm/mach-pxa/cerf.c --- linux-2.5.59-rmk1/arch/arm/mach-pxa/cerf.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.59-pxacerf1/arch/arm/mach-pxa/cerf.c Mon Feb 17 12:55:43 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.59-rmk1/arch/arm/mach-pxa/irq.c linux-2.5.59-pxacerf1/arch/arm/mach-pxa/irq.c --- linux-2.5.59-rmk1/arch/arm/mach-pxa/irq.c Mon Feb 17 13:34:18 2003 +++ linux-2.5.59-pxacerf1/arch/arm/mach-pxa/irq.c Mon Feb 17 12:55:43 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.59-rmk1/drivers/mtd/maps/Kconfig linux-2.5.59-pxacerf1/drivers/mtd/maps/Kconfig --- linux-2.5.59-rmk1/drivers/mtd/maps/Kconfig Mon Feb 17 13:47:03 2003 +++ linux-2.5.59-pxacerf1/drivers/mtd/maps/Kconfig Mon Feb 17 12:55:43 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.59-rmk1/drivers/mtd/maps/Makefile linux-2.5.59-pxacerf1/drivers/mtd/maps/Makefile --- linux-2.5.59-rmk1/drivers/mtd/maps/Makefile Mon Feb 17 13:47:03 2003 +++ linux-2.5.59-pxacerf1/drivers/mtd/maps/Makefile Mon Feb 17 12:55:43 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.59-rmk1/drivers/mtd/maps/cerf_pxa.c linux-2.5.59-pxacerf1/drivers/mtd/maps/cerf_pxa.c --- linux-2.5.59-rmk1/drivers/mtd/maps/cerf_pxa.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.59-pxacerf1/drivers/mtd/maps/cerf_pxa.c Mon Feb 17 12:55:43 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.59-rmk1/drivers/net/Kconfig linux-2.5.59-pxacerf1/drivers/net/Kconfig --- linux-2.5.59-rmk1/drivers/net/Kconfig Mon Feb 17 13:47:03 2003 +++ linux-2.5.59-pxacerf1/drivers/net/Kconfig Mon Feb 17 12:55:43 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.59-rmk1/drivers/net/Makefile linux-2.5.59-pxacerf1/drivers/net/Makefile --- linux-2.5.59-rmk1/drivers/net/Makefile Mon Feb 17 13:47:03 2003 +++ linux-2.5.59-pxacerf1/drivers/net/Makefile Mon Feb 17 12:55:43 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.59-rmk1/drivers/net/smc91111.c linux-2.5.59-pxacerf1/drivers/net/smc91111.c --- linux-2.5.59-rmk1/drivers/net/smc91111.c Wed Dec 31 16:00:00 1969 +++ linux-2.5.59-pxacerf1/drivers/net/smc91111.c Mon Feb 17 12:55:43 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 turnaround (2 bit times) + bits[clk_idx++] = 0; + //bits[clk_idx++] = 0; + + // Input starts at this bit time + input_idx = clk_idx; + + // Will input 16 bits + for (i = 0; i < 16; ++i) + bits[clk_idx++] = 0; + + // Final clock bit + bits[clk_idx++] = 0; + + // Save the current bank + oldBank = SMC_inw(ioaddr, BANK_SELECT); + + // Select bank 3 + SMC_SELECT_BANK(3); + + // Get the current MII register value + mii_reg = SMC_inw(ioaddr, MII_REG); + + // Turn off all MII Interface bits + mii_reg &= ~(MII_MDOE | MII_MCLK | MII_MDI | MII_MDO); + + // Clock all 64 cycles + for (i = 0; i < sizeof bits; ++i) { + // Clock Low - output data + SMC_outw((mii_reg | bits[i]), ioaddr, MII_REG); + udelay(50); + + // Clock Hi - input data + SMC_outw((mii_reg | bits[i] | MII_MCLK), ioaddr, MII_REG); + udelay(50); + bits[i] |= (SMC_inw(ioaddr, MII_REG) & MII_MDI); + } + + // Return to idle state + // Set clock to low, data to low, and output tristated + SMC_outw(mii_reg, ioaddr, MII_REG); + udelay(50); + + // Restore original bank select + SMC_SELECT_BANK(oldBank); + + // Recover input data + phydata = 0; + for (i = 0; i < 16; ++i) { + phydata <<= 1; + + if (bits[input_idx++] & MII_MDI) + phydata |= 0x0001; + } + +#if (SMC_DEBUG > 2 ) + printk("smc_read_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n", + phyaddr, phyreg, phydata); + smc_dump_mii_stream(bits, sizeof bits); +#endif + + return (phydata); +} + +/*------------------------------------------------------------ + . Writes a register to the MII Management serial interface + .-------------------------------------------------------------*/ +static void +smc_write_phy_register(unsigned int ioaddr, + u8 phyaddr, u8 phyreg, u16 phydata) +{ + int oldBank; + int i; + u16 mask; + u16 mii_reg; + u8 bits[65]; + int clk_idx = 0; + + // 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; + + // Write command <01> + bits[clk_idx++] = MII_MDOE; + bits[clk_idx++] = MII_MDOE | MII_MDO; + + // 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 turnaround (2 bit times) + bits[clk_idx++] = 0; + bits[clk_idx++] = 0; + + // Write out 16 bits of data, msb first + mask = 0x8000; + for (i = 0; i < 16; ++i) { + if (phydata & mask) + bits[clk_idx++] = MII_MDOE | MII_MDO; + else + bits[clk_idx++] = MII_MDOE; + + // Shift to next lowest bit + mask >>= 1; + } + + // Final clock bit (tristate) + bits[clk_idx++] = 0; + + // Save the current bank + oldBank = SMC_inw(ioaddr, BANK_SELECT); + + // Select bank 3 + SMC_SELECT_BANK(3); + + // Get the current MII register value + mii_reg = SMC_inw(ioaddr, MII_REG); + + // Turn off all MII Interface bits + mii_reg &= ~(MII_MDOE | MII_MCLK | MII_MDI | MII_MDO); + + // Clock all cycles + for (i = 0; i < sizeof bits; ++i) { + // Clock Low - output data + SMC_outw((mii_reg | bits[i]), ioaddr, MII_REG); + udelay(50); + + // Clock Hi - input data + SMC_outw((mii_reg | bits[i] | MII_MCLK), ioaddr, MII_REG); + udelay(50); + bits[i] |= SMC_inw(ioaddr, MII_REG) & MII_MDI; + } + + // Return to idle state + // Set clock to low, data to low, and output tristated + SMC_outw(mii_reg, ioaddr, MII_REG); + udelay(50); + + // Restore original bank select + SMC_SELECT_BANK(oldBank); + +#if (SMC_DEBUG > 2 ) + printk("smc_write_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n", + phyaddr, phyreg, phydata); + smc_dump_mii_stream(bits, sizeof bits); +#endif +} + +/*------------------------------------------------------------ + . Finds and reports the PHY address + .-------------------------------------------------------------*/ +static int +smc_detect_phy(struct net_device *dev) +{ + struct smc_local *lp = (struct smc_local *) dev->priv; + unsigned int ioaddr = dev->base_addr; + u16 phy_id1; + u16 phy_id2; + int phyaddr; + int found = 0; + + PRINTK3("%s:smc_detect_phy()\n", dev->name); + + // Scan all 32 PHY addresses if necessary + for (phyaddr = 0; phyaddr < 32; ++phyaddr) { + // Read the PHY identifiers + phy_id1 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID1_REG); + phy_id2 = smc_read_phy_register(ioaddr, phyaddr, PHY_ID2_REG); + + PRINTK3("%s: phy_id1=%x, phy_id2=%x\n", + dev->name, phy_id1, phy_id2); + + // Make sure it is a valid identifier + if ((phy_id2 > 0x0000) && (phy_id2 < 0xffff) && + (phy_id1 > 0x0000) && (phy_id1 < 0xffff)) { + if ((phy_id1 != 0x8000) && (phy_id2 != 0x8000)) { + // Save the PHY's address + lp->phyaddr = phyaddr; + found = 1; + break; + } + } + } + + if (!found) { + PRINTK("%s: No PHY found\n", dev->name); + return (0); + } + // Set the PHY type + if ((phy_id1 == 0x0016) && ((phy_id2 & 0xFFF0) == 0xF840)) { + lp->phytype = PHY_LAN83C183; + PRINTK("%s: PHY=LAN83C183 (LAN91C111 Internal)\n", dev->name); + } + + if ((phy_id1 == 0x0282) && ((phy_id2 & 0xFFF0) == 0x1C50)) { + lp->phytype = PHY_LAN83C180; + PRINTK("%s: PHY=LAN83C180\n", dev->name); + } + + return (1); +} + +/*------------------------------------------------------------ + . Waits the specified number of milliseconds - kernel friendly + .-------------------------------------------------------------*/ +static void +smc_wait_ms(unsigned int ms) +{ + + if (!in_interrupt()) { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(1 + ms * HZ / 1000); + } else { +#if 0 + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1 + ms * HZ / 1000); + current->state = TASK_RUNNING; +#else + mdelay(ms); +#endif + } +} + +/*------------------------------------------------------------ + . Sets the PHY to a configuration as determined by the user + .-------------------------------------------------------------*/ +static int +smc_phy_fixed(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *) dev->priv; + u8 phyaddr = lp->phyaddr; + u16 my_fixed_caps; + u16 cfg1; + + PRINTK3("%s:smc_phy_fixed()\n", dev->name); + + // Enter Link Disable state + cfg1 = smc_read_phy_register(ioaddr, phyaddr, PHY_CFG1_REG); + cfg1 |= PHY_CFG1_LNKDIS; + smc_write_phy_register(ioaddr, phyaddr, PHY_CFG1_REG, cfg1); + + // Set our fixed capabilities + // Disable auto-negotiation + my_fixed_caps = 0; + + if (lp->ctl_rfduplx) + my_fixed_caps |= PHY_CNTL_DPLX; + + if (lp->ctl_rspeed == 100) + my_fixed_caps |= PHY_CNTL_SPEED; + + // Write our capabilities to the phy control register + smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, my_fixed_caps); + + // Re-Configure the Receive/Phy Control register + SMC_outw(lp->rpc_cur_mode, ioaddr, RPC_REG); + + // Success + return (1); +} + +/*------------------------------------------------------------ + . Configures the specified PHY using Autonegotiation. Calls + . smc_phy_fixed() if the user has requested a certain config. + .-------------------------------------------------------------*/ +static void +smc_phy_configure(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *) dev->priv; + int timeout; + u8 phyaddr; + u16 my_phy_caps; // My PHY capabilities + u16 my_ad_caps; // My Advertised capabilities + u16 status = 0; + int failed = 0; + + PRINTK3("%s:smc_program_phy()\n", dev->name); + + // Set the blocking flag + lp->autoneg_active = 1; + + // Find the address and type of our phy + if (!smc_detect_phy(dev)) { + goto smc_phy_configure_exit; + } + // Get the detected phy address + phyaddr = lp->phyaddr; + + // Reset the PHY, setting all other bits to zero + smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, PHY_CNTL_RST); + + // Wait for the reset to complete, or time out + timeout = 6; // Wait up to 3 seconds + while (timeout--) { + if (!(smc_read_phy_register(ioaddr, phyaddr, PHY_CNTL_REG) + & PHY_CNTL_RST)) { + // reset complete + break; + } + + smc_wait_ms(500); // wait 500 millisecs + if (signal_pending(current)) // Exit anyway if signaled + { + PRINTK2("%s:PHY reset interrupted by signal\n", + dev->name); + timeout = 0; + break; + } + } + + if (timeout < 1) { + PRINTK2("%s:PHY reset timed out\n", dev->name); + goto smc_phy_configure_exit; + } + // Read PHY Register 18, Status Output + lp->lastPhy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG); + + // Enable PHY Interrupts (for register 18) + // Interrupts listed here are disabled + smc_write_phy_register(ioaddr, phyaddr, PHY_MASK_REG, + PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD | + PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB | + PHY_INT_SPDDET | PHY_INT_DPLXDET); + + /* Configure the Receive/Phy Control register */ + SMC_SELECT_BANK(0); + SMC_outw(lp->rpc_cur_mode, ioaddr, RPC_REG); + + // Copy our capabilities from PHY_STAT_REG to PHY_AD_REG + my_phy_caps = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG); + my_ad_caps = PHY_AD_CSMA; // I am CSMA capable + + if (my_phy_caps & PHY_STAT_CAP_T4) + my_ad_caps |= PHY_AD_T4; + + if (my_phy_caps & PHY_STAT_CAP_TXF) + my_ad_caps |= PHY_AD_TX_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TXH) + my_ad_caps |= PHY_AD_TX_HDX; + + if (my_phy_caps & PHY_STAT_CAP_TF) + my_ad_caps |= PHY_AD_10_FDX; + + if (my_phy_caps & PHY_STAT_CAP_TH) + my_ad_caps |= PHY_AD_10_HDX; + + // Disable capabilities not selected by our user + if (lp->ctl_rspeed != 100) { + my_ad_caps &= ~(PHY_AD_T4 | PHY_AD_TX_FDX | PHY_AD_TX_HDX); + } + + if (!lp->ctl_rfduplx) { + my_ad_caps &= ~(PHY_AD_TX_FDX | PHY_AD_10_FDX); + } + // Update our Auto-Neg Advertisement Register + smc_write_phy_register(ioaddr, phyaddr, PHY_AD_REG, my_ad_caps); + + PRINTK2("%s:phy caps=%x\n", dev->name, my_phy_caps); + PRINTK2("%s:phy advertised caps=%x\n", dev->name, my_ad_caps); + + // If the user requested no auto neg, then go set his request + if (!(lp->ctl_autoneg)) { + smc_phy_fixed(dev); + goto smc_phy_configure_exit; + } + // Restart auto-negotiation process in order to advertise my caps + smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST); + + // Wait for the auto-negotiation to complete. This may take from + // 2 to 3 seconds. + // Wait for the reset to complete, or time out + timeout = 20; // Wait up to 10 seconds + while (timeout--) { + status = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG); + if (status & PHY_STAT_ANEG_ACK) { + // auto-negotiate complete + break; + } + + smc_wait_ms(500); // wait 500 millisecs + if (signal_pending(current)) // Exit anyway if signaled + { + printk(KERN_DEBUG + "%s:PHY auto-negotiate interrupted by signal\n", + dev->name); + timeout = 0; + break; + } + // Restart auto-negotiation if remote fault + if (status & PHY_STAT_REM_FLT) { + PRINTK2("%s:PHY remote fault detected\n", dev->name); + + // Restart auto-negotiation + PRINTK2("%s:PHY restarting auto-negotiation\n", + dev->name); + smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, + PHY_CNTL_ANEG_EN | + PHY_CNTL_ANEG_RST | + PHY_CNTL_SPEED | PHY_CNTL_DPLX); + } + } + + if (timeout < 1) { + printk(KERN_DEBUG "%s:PHY auto-negotiate timed out\n", + dev->name); + PRINTK2("%s:PHY auto-negotiate timed out\n", dev->name); + failed = 1; + } + // Fail if we detected an auto-negotiate remote fault + if (status & PHY_STAT_REM_FLT) { + printk(KERN_DEBUG "%s:PHY remote fault detected\n", dev->name); + PRINTK2("%s:PHY remote fault detected\n", dev->name); + failed = 1; + } + // The smc_phy_interrupt() routine will be called to update lastPhy18 + + // Set our sysctl parameters to match auto-negotiation results + if (lp->lastPhy18 & PHY_INT_SPDDET) { + PRINTK2("%s:PHY 100BaseT\n", dev->name); + lp->rpc_cur_mode |= RPC_SPEED; + } else { + PRINTK2("%s:PHY 10BaseT\n", dev->name); + lp->rpc_cur_mode &= ~RPC_SPEED; + } + + if (lp->lastPhy18 & PHY_INT_DPLXDET) { + PRINTK2("%s:PHY Full Duplex\n", dev->name); + lp->rpc_cur_mode |= RPC_DPLX; + } else { + PRINTK2("%s:PHY Half Duplex\n", dev->name); + lp->rpc_cur_mode &= ~RPC_DPLX; + } + + // Re-Configure the Receive/Phy Control register + SMC_outw(lp->rpc_cur_mode, ioaddr, RPC_REG); + + smc_phy_configure_exit: + + // Exit auto-negotiation + lp->autoneg_active = 0; +} + +/************************************************************************* + . smc_phy_interrupt + . + . Purpose: Handle interrupts relating to PHY register 18. This is + . called from the "hard" interrupt handler. + . + ************************************************************************/ +static void +smc_phy_interrupt(struct net_device *dev) +{ + unsigned int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *) dev->priv; + u8 phyaddr = lp->phyaddr; + u16 phy18; + + PRINTK2("%s: smc_phy_interrupt\n", dev->name); + + while (1) { + // Read PHY Register 18, Status Output + phy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG); + + // Exit if not more changes + if (phy18 == lp->lastPhy18) + break; + +#if (SMC_DEBUG > 1 ) + + PRINTK2("%s: phy18=0x%x\n", dev->name, phy18); + PRINTK2("%s: lastPhy18=0x%x\n", dev->name, lp->lastPhy18); + + // Handle events + if ((phy18 & PHY_INT_LNKFAIL) != + (lp->lastPhy18 & PHY_INT_LNKFAIL)) { + PRINTK2("%s: PHY Link Fail=%x\n", dev->name, + phy18 & PHY_INT_LNKFAIL); + } + + if ((phy18 & PHY_INT_LOSSSYNC) != + (lp->lastPhy18 & PHY_INT_LOSSSYNC)) { + PRINTK2("%s: PHY LOSS SYNC=%x\n", dev->name, + phy18 & PHY_INT_LOSSSYNC); + } + + if ((phy18 & PHY_INT_CWRD) != (lp->lastPhy18 & PHY_INT_CWRD)) { + PRINTK2("%s: PHY INVALID 4B5B code=%x\n", dev->name, + phy18 & PHY_INT_CWRD); + } + + if ((phy18 & PHY_INT_SSD) != (lp->lastPhy18 & PHY_INT_SSD)) { + PRINTK2("%s: PHY No Start Of Stream=%x\n", dev->name, + phy18 & PHY_INT_SSD); + } + + if ((phy18 & PHY_INT_ESD) != (lp->lastPhy18 & PHY_INT_ESD)) { + PRINTK2("%s: PHY No End Of Stream=%x\n", dev->name, + phy18 & PHY_INT_ESD); + } + + if ((phy18 & PHY_INT_RPOL) != (lp->lastPhy18 & PHY_INT_RPOL)) { + PRINTK2("%s: PHY Reverse Polarity Detected=%x\n", + dev->name, phy18 & PHY_INT_RPOL); + } + + if ((phy18 & PHY_INT_JAB) != (lp->lastPhy18 & PHY_INT_JAB)) { + PRINTK2("%s: PHY Jabber Detected=%x\n", dev->name, + phy18 & PHY_INT_JAB); + } + + if ((phy18 & PHY_INT_SPDDET) != + (lp->lastPhy18 & PHY_INT_SPDDET)) { + PRINTK2("%s: PHY Speed Detect=%x\n", dev->name, + phy18 & PHY_INT_SPDDET); + } + + if ((phy18 & PHY_INT_DPLXDET) != + (lp->lastPhy18 & PHY_INT_DPLXDET)) { + PRINTK2("%s: PHY Duplex Detect=%x\n", dev->name, + phy18 & PHY_INT_DPLXDET); + } +#endif + + // Update the last phy 18 variable + lp->lastPhy18 = phy18; + + } // end while +} diff -ruN linux-2.5.59-rmk1/drivers/net/smc91111.h linux-2.5.59-pxacerf1/drivers/net/smc91111.h --- linux-2.5.59-rmk1/drivers/net/smc91111.h Wed Dec 31 16:00:00 1969 +++ linux-2.5.59-pxacerf1/drivers/net/smc91111.h Mon Feb 17 12:55:43 2003 @@ -0,0 +1,526 @@ +/*------------------------------------------------------------------------ + . smc91111.h - macros for the LAN91C111 Ethernet Driver + . + . 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 + . + . This file contains register information and access macros for + . the LAN91C111 single chip ethernet controller. It is a modified + . version of the smc9194.h file. + . + . 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. + . + . Authors + . Erik Stahlman ( erik@vt.edu ) + . Daris A Nevil ( dnevil@snmc.com ) + . + . History + . 03/16/01 Daris A Nevil Modified for use with LAN91C111 device + . 12/20/01 Jeff Sutherland Added support for + . architectures other than i386; ported to ARM specifically + . + ---------------------------------------------------------------------------*/ +#ifndef _SMC91111_H_ +#define _SMC91111_H_ + +#include + +/*--------------------------------------------------------------- + . + . A description of the SMSC registers is probably in order here, + . although for details, the SMC datasheet is invaluable. + . + . Basically, the chip has 4 banks of registers ( 0 to 3 ), which + . are accessed by writing a number into the BANK_SELECT register + . ( I also use a SMC_SELECT_BANK macro for this ). + . + . The banks are configured so that for most purposes, bank 2 is all + . that is needed for simple run time tasks. + -----------------------------------------------------------------------*/ + +/* + . Bank Select Register: + . + . yyyy yyyy 0000 00xx + . xx = bank number + . yyyy yyyy = 0x33, for identification purposes. +*/ +#define BANK_SELECT 14 + +// Transmit Control Register +/* BANK 0 */ +#define TCR_REG 0x0000 // transmit control register +#define TCR_ENABLE 0x0001 // When 1 we can transmit +#define TCR_LOOP 0x0002 // Controls output pin LBK +#define TCR_FORCOL 0x0004 // When 1 will force a collision +#define TCR_PAD_EN 0x0080 // When 1 will pad tx frames < 64 bytes w/0 +#define TCR_NOCRC 0x0100 // When 1 will not append CRC to tx frames +#define TCR_MON_CSN 0x0400 // When 1 tx monitors carrier +#define TCR_FDUPLX 0x0800 // When 1 enables full duplex operation +#define TCR_STP_SQET 0x1000 // When 1 stops tx if Signal Quality Error +#define TCR_EPH_LOOP 0x2000 // When 1 enables EPH block loopback +#define TCR_SWFDUP 0x8000 // When 1 enables Switched Full Duplex mode + +#define TCR_CLEAR 0 /* do NOTHING */ +/* the default settings for the TCR register : */ +/* QUESTION: do I want to enable padding of short packets ? */ +#define TCR_DEFAULT TCR_ENABLE + + +// EPH Status Register +/* BANK 0 */ +#define EPH_STATUS_REG 0x0002 +#define ES_TX_SUC 0x0001 // Last TX was successful +#define ES_SNGL_COL 0x0002 // Single collision detected for last tx +#define ES_MUL_COL 0x0004 // Multiple collisions detected for last tx +#define ES_LTX_MULT 0x0008 // Last tx was a multicast +#define ES_16COL 0x0010 // 16 Collisions Reached +#define ES_SQET 0x0020 // Signal Quality Error Test +#define ES_LTXBRD 0x0040 // Last tx was a broadcast +#define ES_TXDEFR 0x0080 // Transmit Deferred +#define ES_LATCOL 0x0200 // Late collision detected on last tx +#define ES_LOSTCARR 0x0400 // Lost Carrier Sense +#define ES_EXC_DEF 0x0800 // Excessive Deferral +#define ES_CTR_ROL 0x1000 // Counter Roll Over indication +#define ES_LINK_OK 0x4000 // Driven by inverted value of nLNK pin +#define ES_TXUNRN 0x8000 // Tx Underrun + + +// Receive Control Register +/* BANK 0 */ +#define RCR_REG 0x0004 +#define RCR_RX_ABORT 0x0001 // Set if a rx frame was aborted +#define RCR_PRMS 0x0002 // Enable promiscuous mode +#define RCR_ALMUL 0x0004 // When set accepts all multicast frames +#define RCR_RXEN 0x0100 // IFF this is set, we can receive packets +#define RCR_STRIP_CRC 0x0200 // When set strips CRC from rx packets +#define RCR_ABORT_ENB 0x0200 // When set will abort rx on collision +#define RCR_FILT_CAR 0x0400 // When set filters leading 12 bit s of carrier +#define RCR_SOFTRST 0x8000 // resets the chip + +/* the normal settings for the RCR register : */ +#define RCR_DEFAULT (RCR_STRIP_CRC | RCR_RXEN) +#define RCR_CLEAR 0x0 // set it to a base state + +// Counter Register +/* BANK 0 */ +#define COUNTER_REG 0x0006 + +// Memory Information Register +/* BANK 0 */ +#define MIR_REG 0x0008 + +// Receive/Phy Control Register +/* BANK 0 */ +#define RPC_REG 0x000A +#define RPC_SPEED 0x2000 // When 1 PHY is in 100Mbps mode. +#define RPC_DPLX 0x1000 // When 1 PHY is in Full-Duplex Mode +#define RPC_ANEG 0x0800 // When 1 PHY is in Auto-Negotiate Mode +#define RPC_LSXA_SHFT 5 // Bits to shift LS2A,LS1A,LS0A to lsb +#define RPC_LSXB_SHFT 2 // Bits to get LS2B,LS1B,LS0B to lsb +#define RPC_LED_100_10 (0x00) // LED = 100Mbps OR's with 10Mbps link detect +#define RPC_LED_RES (0x01) // LED = Reserved +#define RPC_LED_10 (0x02) // LED = 10Mbps link detect +#define RPC_LED_FD (0x03) // LED = Full Duplex Mode +#define RPC_LED_TX_RX (0x04) // LED = TX or RX packet occurred +#define RPC_LED_100 (0x05) // LED = 100Mbps link dectect +#define RPC_LED_TX (0x06) // LED = TX packet occurred +#define RPC_LED_RX (0x07) // LED = RX packet occurred +#define RPC_DEFAULT (RPC_ANEG | (RPC_LED_100_10 << RPC_LSXA_SHFT) | (RPC_LED_TX_RX << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX) + +/* Bank 0 0x000C is reserved */ + +// Bank Select Register +/* All Banks */ +#define BSR_REG 0x000E + + +// Configuration Reg +/* BANK 1 */ +#define CONFIG_REG 0x0000 +#define CONFIG_EXT_PHY 0x0200 // 1=external MII, 0=internal Phy +#define CONFIG_GPCNTRL 0x0400 // Inverse value drives pin nCNTRL +#define CONFIG_NO_WAIT 0x1000 // When 1 no extra wait states on ISA bus +#define CONFIG_EPH_POWER_EN 0x8000 // When 0 EPH is placed into low power mode. + +// Default is powered-up, Internal Phy, Wait States, and pin nCNTRL=low +#define CONFIG_DEFAULT (CONFIG_EPH_POWER_EN) + + +// Base Address Register +/* BANK 1 */ +#define BASE_REG 0x0002 + + +// Individual Address Registers +/* BANK 1 */ +#define ADDR0_REG 0x0004 +#define ADDR1_REG 0x0006 +#define ADDR2_REG 0x0008 + + +// General Purpose Register +/* BANK 1 */ +#define GP_REG 0x000A + + +// Control Register +/* BANK 1 */ +#define CTL_REG 0x000C +#define CTL_RCV_BAD 0x4000 // When 1 bad CRC packets are received +#define CTL_AUTO_RELEASE 0x0800 // When 1 tx pages are released automatically +#define CTL_LE_ENABLE 0x0080 // When 1 enables Link Error interrupt +#define CTL_CR_ENABLE 0x0040 // When 1 enables Counter Rollover interrupt +#define CTL_TE_ENABLE 0x0020 // When 1 enables Transmit Error interrupt +#define CTL_EEPROM_SELECT 0x0004 // Controls EEPROM reload & store +#define CTL_RELOAD 0x0002 // When set reads EEPROM into registers +#define CTL_STORE 0x0001 // When set stores registers into EEPROM + + +// MMU Command Register +/* BANK 2 */ +#define MMU_CMD_REG 0x0000 +#define MC_BUSY 1 // When 1 the last release has not completed +#define MC_NOP (0<<5) // No Op +#define MC_ALLOC (1<<5) // OR with number of 256 byte packets +#define MC_RESET (2<<5) // Reset MMU to initial state +#define MC_REMOVE (3<<5) // Remove the current rx packet +#define MC_RELEASE (4<<5) // Remove and release the current rx packet +#define MC_FREEPKT (5<<5) // Release packet in PNR register +#define MC_ENQUEUE (6<<5) // Enqueue the packet for transmit +#define MC_RSTTXFIFO (7<<5) // Reset the TX FIFOs + + +// Packet Number Register +/* BANK 2 */ +#define PN_REG 0x0002 + + +// Allocation Result Register +/* BANK 2 */ +#define AR_REG 0x0003 +#define AR_FAILED 0x80 // Alocation Failed + + +// RX FIFO Ports Register +/* BANK 2 */ +#define RXFIFO_REG 0x0004 // Must be read as a word +#define RXFIFO_REMPTY 0x8000 // RX FIFO Empty + + +// TX FIFO Ports Register +/* BANK 2 */ +#define TXFIFO_REG RXFIFO_REG // Must be read as a word +#define TXFIFO_TEMPTY 0x80 // TX FIFO Empty + + +// Pointer Register +/* BANK 2 */ +#define PTR_REG 0x0006 +#define PTR_RCV 0x8000 // 1=Receive area, 0=Transmit area +#define PTR_AUTOINC 0x4000 // Auto increment the pointer on each access +#define PTR_READ 0x2000 // When 1 the operation is a read +#define PTR_NOTEMPTY 0x0800 // set when data register fifo is not empty + +// Data Register +/* BANK 2 */ +#define DATA_REG 0x0008 + + +// Interrupt Status/Acknowledge Register +/* BANK 2 */ +#define INT_REG 0x000C + + +// Interrupt Mask Register +/* BANK 2 */ +#define IM_REG 0x000D +#define IM_MDINT 0x80 // PHY MI Register 18 Interrupt +#define IM_ERCV_INT 0x40 // Early Receive Interrupt +#define IM_EPH_INT 0x20 // Set by Etheret Protocol Handler section +#define IM_RX_OVRN_INT 0x10 // Set by Receiver Overruns +#define IM_ALLOC_INT 0x08 // Set when allocation request is completed +#define IM_TX_EMPTY_INT 0x04 // Set if the TX FIFO goes empty +#define IM_TX_INT 0x02 // Transmit Interrrupt +#define IM_RCV_INT 0x01 // Receive Interrupt + + +// Multicast Table Registers +/* BANK 3 */ +#define MCAST_REG1 0x0000 +#define MCAST_REG2 0x0002 +#define MCAST_REG3 0x0004 +#define MCAST_REG4 0x0006 + + +// Management Interface Register (MII) +/* BANK 3 */ +#define MII_REG 0x0008 +#define MII_MSK_CRS100 0x4000 // Disables CRS100 detection during tx half dup +#define MII_MDOE 0x0008 // MII Output Enable +#define MII_MCLK 0x0004 // MII Clock, pin MDCLK +#define MII_MDI 0x0002 // MII Input, pin MDI +#define MII_MDO 0x0001 // MII Output, pin MDO + + +// Revision Register +/* BANK 3 */ +#define REV_REG 0x000A /* ( hi: chip id low: rev # ) */ + + +// Early RCV Register +/* BANK 3 */ +/* this is NOT on SMC9192 */ +#define ERCV_REG 0x000C +#define ERCV_RCV_DISCRD 0x0080 // When 1 discards a packet being received +#define ERCV_THRESHOLD 0x001F // ERCV Threshold Mask + +// External Register +/* BANK 7 */ +#define EXT_REG 0x0000 + + +#define CHIP_9192 3 +#define CHIP_9194 4 +#define CHIP_9195 5 +#define CHIP_9196 6 +#define CHIP_91100 7 +#define CHIP_91100FD 8 +#define CHIP_91111FD 9 + +static const char * chip_ids[ 15 ] = { + NULL, NULL, NULL, + /* 3 */ "SMC91C90/91C92", + /* 4 */ "SMC91C94", + /* 5 */ "SMC91C95", + /* 6 */ "SMC91C96", + /* 7 */ "SMC91C100", + /* 8 */ "SMC91C100FD", + /* 9 */ "SMC91C11xFD", + NULL, NULL, + NULL, NULL, NULL}; + +/* + . Transmit status bits +*/ +#define TS_SUCCESS 0x0001 +#define TS_LOSTCAR 0x0400 +#define TS_LATCOL 0x0200 +#define TS_16COL 0x0010 + +/* + . Receive status bits +*/ +#define RS_ALGNERR 0x8000 +#define RS_BRODCAST 0x4000 +#define RS_BADCRC 0x2000 +#define RS_ODDFRAME 0x1000 // bug: the LAN91C111 never sets this on receive +#define RS_TOOLONG 0x0800 +#define RS_TOOSHORT 0x0400 +#define RS_MULTICAST 0x0001 +#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) + + +// PHY Types +enum { + PHY_LAN83C183 = 1, // LAN91C111 Internal PHY + PHY_LAN83C180 +}; + + +// PHY Register Addresses (LAN91C111 Internal PHY) + +// PHY Control Register +#define PHY_CNTL_REG 0x00 +#define PHY_CNTL_RST 0x8000 // 1=PHY Reset +#define PHY_CNTL_LPBK 0x4000 // 1=PHY Loopback +#define PHY_CNTL_SPEED 0x2000 // 1=100Mbps, 0=10Mpbs +#define PHY_CNTL_ANEG_EN 0x1000 // 1=Enable Auto negotiation +#define PHY_CNTL_PDN 0x0800 // 1=PHY Power Down mode +#define PHY_CNTL_MII_DIS 0x0400 // 1=MII 4 bit interface disabled +#define PHY_CNTL_ANEG_RST 0x0200 // 1=Reset Auto negotiate +#define PHY_CNTL_DPLX 0x0100 // 1=Full Duplex, 0=Half Duplex +#define PHY_CNTL_COLTST 0x0080 // 1= MII Colision Test + +// PHY Status Register +#define PHY_STAT_REG 0x01 +#define PHY_STAT_CAP_T4 0x8000 // 1=100Base-T4 capable +#define PHY_STAT_CAP_TXF 0x4000 // 1=100Base-X full duplex capable +#define PHY_STAT_CAP_TXH 0x2000 // 1=100Base-X half duplex capable +#define PHY_STAT_CAP_TF 0x1000 // 1=10Mbps full duplex capable +#define PHY_STAT_CAP_TH 0x0800 // 1=10Mbps half duplex capable +#define PHY_STAT_CAP_SUPR 0x0040 // 1=recv mgmt frames with not preamble +#define PHY_STAT_ANEG_ACK 0x0020 // 1=ANEG has completed +#define PHY_STAT_REM_FLT 0x0010 // 1=Remote Fault detected +#define PHY_STAT_CAP_ANEG 0x0008 // 1=Auto negotiate capable +#define PHY_STAT_LINK 0x0004 // 1=valid link +#define PHY_STAT_JAB 0x0002 // 1=10Mbps jabber condition +#define PHY_STAT_EXREG 0x0001 // 1=extended registers implemented + +// PHY Identifier Registers +#define PHY_ID1_REG 0x02 // PHY Identifier 1 +#define PHY_ID2_REG 0x03 // PHY Identifier 2 + +// PHY Auto-Negotiation Advertisement Register +#define PHY_AD_REG 0x04 +#define PHY_AD_NP 0x8000 // 1=PHY requests exchange of Next Page +#define PHY_AD_ACK 0x4000 // 1=got link code word from remote +#define PHY_AD_RF 0x2000 // 1=advertise remote fault +#define PHY_AD_T4 0x0200 // 1=PHY is capable of 100Base-T4 +#define PHY_AD_TX_FDX 0x0100 // 1=PHY is capable of 100Base-TX FDPLX +#define PHY_AD_TX_HDX 0x0080 // 1=PHY is capable of 100Base-TX HDPLX +#define PHY_AD_10_FDX 0x0040 // 1=PHY is capable of 10Base-T FDPLX +#define PHY_AD_10_HDX 0x0020 // 1=PHY is capable of 10Base-T HDPLX +#define PHY_AD_CSMA 0x0001 // 1=PHY is capable of 802.3 CMSA + +// PHY Auto-negotiation Remote End Capability Register +#define PHY_RMT_REG 0x05 +// Uses same bit definitions as PHY_AD_REG + +// PHY Configuration Register 1 +#define PHY_CFG1_REG 0x10 +#define PHY_CFG1_LNKDIS 0x8000 // 1=Rx Link Detect Function disabled +#define PHY_CFG1_XMTDIS 0x4000 // 1=TP Transmitter Disabled +#define PHY_CFG1_XMTPDN 0x2000 // 1=TP Transmitter Powered Down +#define PHY_CFG1_BYPSCR 0x0400 // 1=Bypass scrambler/descrambler +#define PHY_CFG1_UNSCDS 0x0200 // 1=Unscramble Idle Reception Disable +#define PHY_CFG1_EQLZR 0x0100 // 1=Rx Equalizer Disabled +#define PHY_CFG1_CABLE 0x0080 // 1=STP(150ohm), 0=UTP(100ohm) +#define PHY_CFG1_RLVL0 0x0040 // 1=Rx Squelch level reduced by 4.5db +#define PHY_CFG1_TLVL_SHIFT 2 // Transmit Output Level Adjust +#define PHY_CFG1_TLVL_MASK 0x003C +#define PHY_CFG1_TRF_MASK 0x0003 // Transmitter Rise/Fall time + + +// PHY Configuration Register 2 +#define PHY_CFG2_REG 0x11 +#define PHY_CFG2_APOLDIS 0x0020 // 1=Auto Polarity Correction disabled +#define PHY_CFG2_JABDIS 0x0010 // 1=Jabber disabled +#define PHY_CFG2_MREG 0x0008 // 1=Multiple register access (MII mgt) +#define PHY_CFG2_INTMDIO 0x0004 // 1=Interrupt signaled with MDIO pulseo + +// PHY Status Output (and Interrupt status) Register +#define PHY_INT_REG 0x12 // Status Output (Interrupt Status) +#define PHY_INT_INT 0x8000 // 1=bits have changed since last read +#define PHY_INT_LNKFAIL 0x4000 // 1=Link Not detected +#define PHY_INT_LOSSSYNC 0x2000 // 1=Descrambler has lost sync +#define PHY_INT_CWRD 0x1000 // 1=Invalid 4B5B code detected on rx +#define PHY_INT_SSD 0x0800 // 1=No Start Of Stream detected on rx +#define PHY_INT_ESD 0x0400 // 1=No End Of Stream detected on rx +#define PHY_INT_RPOL 0x0200 // 1=Reverse Polarity detected +#define PHY_INT_JAB 0x0100 // 1=Jabber detected +#define PHY_INT_SPDDET 0x0080 // 1=100Base-TX mode, 0=10Base-T mode +#define PHY_INT_DPLXDET 0x0040 // 1=Device in Full Duplex + +// PHY Interrupt/Status Mask Register +#define PHY_MASK_REG 0x13 // Interrupt Mask +// Uses the same bit definitions as PHY_INT_REG + +/*---------------------------------------------------------------------- + . Define the interrupts that I want to receive from the card + . + . I want: + . IM_EPH_INT, for nasty errors + . IM_RCV_INT, for happy received packets + . IM_RX_OVRN_INT, because I have to kick the receiver + . IM_MDINT, for PHY Register 18 Status Changes + --------------------------------------------------------------------------*/ +#define SMC_INTERRUPT_MASK (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT | \ + IM_MDINT) + +#ifdef CONFIG_SYSCTL + +/* + * Declarations for the sysctl interface, which allows users the ability to + * control the finer aspects of the LAN91C111 chip. Since the smc + * module currently registers its sysctl table dynamically, the sysctl path + * for module FOO is /proc/sys/dev/ethX/FOO + */ +#define CTL_SMC (CTL_BUS+1389) // arbitrary and hopefully unused + +enum { + CTL_SMC_INFO = 1, // Sysctl files information + CTL_SMC_SWVER, // Driver Software Version Info + CTL_SMC_SWFDUP, // Switched Full Duplex Mode + CTL_SMC_EPHLOOP, // EPH Block Internal Loopback + CTL_SMC_MIIOP, // MII Operation + CTL_SMC_AUTONEG, // Auto-negotiate Mode + CTL_SMC_RFDUPLX, // Request Full Duplex Mode + CTL_SMC_RSPEED, // Request Speed Selection + CTL_SMC_AFDUPLX, // Actual Full Duplex Mode + CTL_SMC_ASPEED, // Actual Speed Selection + CTL_SMC_LNKFAIL, // Link Failed + CTL_SMC_FORCOL, // Force a Collision + CTL_SMC_FILTCAR, // Filter Carrier + CTL_SMC_FREEMEM, // Free Buffer Memory + CTL_SMC_TOTMEM, // Total Buffer Memory + CTL_SMC_LEDA, // Output of LED-A + CTL_SMC_LEDB, // Output of LED-B + CTL_SMC_CHIPREV, // LAN91C111 Chip Revision ID +#ifdef SMC_DEBUG + // Register access for debugging + CTL_SMC_REG_BSR, // Bank Select + CTL_SMC_REG_TCR, // Transmit Control + CTL_SMC_REG_ESR, // EPH Status + CTL_SMC_REG_RCR, // Receive Control + CTL_SMC_REG_CTRR, // Counter + CTL_SMC_REG_MIR, // Memory Information + CTL_SMC_REG_RPCR, // Receive/Phy Control + CTL_SMC_REG_CFGR, // Configuration + CTL_SMC_REG_BAR, // Base Address + CTL_SMC_REG_IAR0, // Individual Address 0 + CTL_SMC_REG_IAR1, // Individual Address 1 + CTL_SMC_REG_IAR2, // Individual Address 2 + CTL_SMC_REG_GPR, // General Purpose + CTL_SMC_REG_CTLR, // Control + CTL_SMC_REG_MCR, // MMU Command + CTL_SMC_REG_PNR, // Packet Number + CTL_SMC_REG_FPR, // FIFO Ports + CTL_SMC_REG_PTR, // Pointer + CTL_SMC_REG_DR, // Data + CTL_SMC_REG_ISR, // Interrupt Status + CTL_SMC_REG_MTR1, // Multicast Table Entry 1 + CTL_SMC_REG_MTR2, // Multicast Table Entry 2 + CTL_SMC_REG_MTR3, // Multicast Table Entry 3 + CTL_SMC_REG_MTR4, // Multicast Table Entry 4 + CTL_SMC_REG_MIIR, // Management Interface + CTL_SMC_REG_REVR, // Revision + CTL_SMC_REG_ERCVR, // Early RCV + CTL_SMC_REG_EXTR, // External + CTL_SMC_PHY_CTRL, // PHY Control + CTL_SMC_PHY_STAT, // PHY Status + CTL_SMC_PHY_ID1, // PHY ID1 + CTL_SMC_PHY_ID2, // PHY ID2 + CTL_SMC_PHY_ADC, // PHY Advertise Capability + CTL_SMC_PHY_REMC, // PHY Advertise Capability + CTL_SMC_PHY_CFG1, // PHY Configuration 1 + CTL_SMC_PHY_CFG2, // PHY Configuration 2 + CTL_SMC_PHY_INT, // PHY Interrupt/Status Output + CTL_SMC_PHY_MASK, // PHY Interrupt/Status Mask +#endif + // --------------------------------------------------- + CTL_SMC_LAST_ENTRY // Add new entries above the line +}; + +#endif // CONFIG_SYSCTL + +#endif /* _SMC_91111_H_ */ + + diff -ruN linux-2.5.59-rmk1/drivers/net/smc91111_cfg.h linux-2.5.59-pxacerf1/drivers/net/smc91111_cfg.h --- linux-2.5.59-rmk1/drivers/net/smc91111_cfg.h Wed Dec 31 16:00:00 1969 +++ linux-2.5.59-pxacerf1/drivers/net/smc91111_cfg.h Mon Feb 17 12:55:43 2003 @@ -0,0 +1,197 @@ +/*------------------------------------------------------------------------ + . smc91111_cfg.h - local configurations for the LAN91C111 Ethernet Driver + . + . Copyright (C) 2002 Accelent Systems, Inc. + . + . 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 + . + . Author: Jeff Sutherland + . + . Purpose: This file contains machine/architecture specific configuration + . twiddles, hacks, and kludges for the smc91111 driver so that all + . that crud can be removed from the driver in an effort to make it + . as generic (and clean) as possible. + . + . + ---------------------------------------------------------------------------*/ + +#ifndef _SMC91111_CFG_H_ +#define _SMC91111_CFG_H_ + +/* Support for the Accelent PXA IDP and friends: + * Architecture: ARM + * Processor: Intel PXA + * Machine: Accelent PXA IDP + */ + +#ifdef CONFIG_ARCH_PXA + +/* Local i/o macros for the PXA IDP (32 bit bus i/f) +* Hack alert: 8 bit reads are broken on the PXA. Don't use u8 types for +* reading any fifo registers. u8 reads will succeed on registers that can +* tolerate multiple reads without changing their contents. +*/ + +/* Platform specific defines: */ + +#ifdef CONFIG_ARCH_PXA_IDP +#define NETWORK_INTERFACE0_PHYS_DATA_ADDR (IDP_ETH_PHYS + 0x300 + DATA_REG) +#define NETWORK_INTERFACE0_INTERRUPT (IRQ_GPIO(4)) +#define NETWORK_INTERFACE0_USE_32_BIT 1 +#define NETWORK_INTERFACE0_REGISTER_BASE_ADDRESS (IDP_ETH_BASE+ 0x300) + +#define NETWORK_INTERFACE1_PHYS_DATA_ADDR (IDP_ETH_PHYS + 0x200 + DATA_REG) +#define NETWORK_INTERFACE1_INTERRUPT 0 +#define NETWORK_INTERFACE1_USE_32_BIT 0 +#define NETWORK_INTERFACE1_REGISTER_BASE_ADDRESS 0 + +inline void smc_init_mac_addr( struct net_device *dev, unsigned int ioaddr, int interface) +{ +} + +#elif defined CONFIG_ARCH_PXA_CERF +#define NETWORK_INTERFACE0_PHYS_DATA_ADDR (CERF_ETH_PHYS + 0x300 + DATA_REG) +#define NETWORK_INTERFACE0_INTERRUPT CERF_ETH_IRQ +#define NETWORK_INTERFACE0_USE_32_BIT 1 +#define NETWORK_INTERFACE0_REGISTER_BASE_ADDRESS (CERF_ETH_BASE + 0x300) + +#define NETWORK_INTERFACE1_PHYS_DATA_ADDR (CERF_ETH2_PHYS + 0x300 + DATA_REG) +#define NETWORK_INTERFACE1_INTERRUPT CERF_ETH2_IRQ +#define NETWORK_INTERFACE1_USE_32_BIT 0 +#define NETWORK_INTERFACE1_REGISTER_BASE_ADDRESS (CERF_ETH2_BASE + 0x300) + +//#include + +inline void smc_init_mac_addr( struct net_device *dev, unsigned int ioaddr, int interface) +{ +#if 0 + char tagname[20]; + u8 data[10]; + u16 datalen=10; + + sprintf( tagname, "MACAD%c", '0'+interface); + if( config_eeprom_get_data( tagname, strlen(tagname), data, &datalen)) + { + if( datalen == 6) + { + int i; + for( i=0; i<6; i++) + { + dev->dev_addr[i] = data[i]; + } + } + else + { + printk("%s: Bad config eeprom tag data\n", dev->name); + } + } +#else + dev->dev_addr[0] = 0x00; + dev->dev_addr[1] = 0xd0; + dev->dev_addr[2] = 0xca; + dev->dev_addr[3] = 0xf1; + dev->dev_addr[4] = 0x1e; + dev->dev_addr[5] = 0xc3; +#endif + +#if 0 + else + { + printk("%s: config eeprom tag [%s] not found\n", dev->name, tagname); + } +#endif +} + +#endif + +#define NO_AUTOPROBE 1 + +/* Because of bank switching, the LAN91xxx uses only 16 I/O ports (on an + * isa based system, that is.) */ + +#ifdef SMC_IO_EXTENT +#undef SMC_IO_EXTENT +#endif + +#define SMC_IO_EXTENT (16 << 2) + +// Use power-down feature of the chip +#define SMC_POWER_DOWN 1 + +#define MAX_NETWORK_INTERFACE_COUNT 8 +static struct +{ + unsigned long port; + unsigned long dma_addr; + int use_32bit; + int irq; +} smc_config[MAX_NETWORK_INTERFACE_COUNT] = { + { + port: NETWORK_INTERFACE0_REGISTER_BASE_ADDRESS, + dma_addr: NETWORK_INTERFACE0_PHYS_DATA_ADDR, + use_32bit: NETWORK_INTERFACE0_USE_32_BIT, + irq: NETWORK_INTERFACE0_INTERRUPT, + }, +#if 0 + { + port: NETWORK_INTERFACE1_REGISTER_BASE_ADDRESS, + dma_addr: NETWORK_INTERFACE1_PHYS_DATA_ADDR, + use_32bit: NETWORK_INTERFACE1_USE_32_BIT, + irq: NETWORK_INTERFACE1_INTERRUPT, + } +#endif +}; + +/* no support for pcmcia card drivers just yet...*/ +/* +#if NETWORK_DRIVER_PCMCIA_MODE == TRUE +static unsigned long smc_attmem_list[] = { + NETWORK_INTERFACE0_ATTRIBUTE_BASE_ADDRESS, + +#if NETWORK_INTERFACE_COUNT > 1 + NETWORK_INTERFACE1_ATTRIBUTE_BASE_ADDRESS, +#endif + 0 +}; +#endif +*/ + +const char fallback_mac[6] = { 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00 }; + +/* + * Wait time for TX packet memory to be free. This probably shouldn't be + * tuned that much, as waiting for this means nothing else happens + * in the system. Originally this was set to 16 but I'm not sure you want to + * sit and spin here waiting to tx packets. Let interrupts do their job. + */ + +#define MEMORY_WAIT_TIME 4 + +#endif /* config_arch_pxa_idp */ + +/* If you want to use autoprobing for your hardware, you may make use of + * this table. The LAN91C111 can be at any of the following port addresses. + * To change for a slightly different card, you can add it to the array. + * Keep in mind that the array must end in zero. + */ + +#if 0 +static unsigned int smc_portlist[] __initdata = + { 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0, + 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0, 0 +}; +#endif + +#endif /* _SMC91111_CFG_H_ */ diff -ruN linux-2.5.59-rmk1/drivers/serial/8250.c linux-2.5.59-pxacerf1/drivers/serial/8250.c --- linux-2.5.59-rmk1/drivers/serial/8250.c Mon Feb 17 13:47:04 2003 +++ linux-2.5.59-pxacerf1/drivers/serial/8250.c Mon Feb 17 12:55:43 2003 @@ -163,6 +163,7 @@ { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH | UART_EFRAFE }, { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH | UART_EFRAFE }, { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "PXA", 32, UART_CLEAR_FIFO | UART_USE_FIFO }, }; static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset) @@ -177,6 +178,9 @@ case SERIAL_IO_MEM: return readb(up->port.membase + offset); + case SERIAL_IO_MEM32: + return readl(up->port.membase + offset); + default: return inb(up->port.iobase + offset); } @@ -197,6 +201,10 @@ writeb(value, up->port.membase + offset); break; + case SERIAL_IO_MEM32: + writel(value, up->port.membase + offset); + break; + default: outb(value, up->port.iobase + offset); } @@ -523,6 +531,20 @@ if (!up->port.iobase && !up->port.mapbase && !up->port.membase) return; +#ifdef CONFIG_ARCH_PXA + /* + * The serial_core code just doesn't mind its own business + * and insist to trash our port type. It would be nice if + * we could register into the generic code with all details + * already set and not be forced into this autoconfiguration + * maze if we don't need to. Sigh. + */ + if (up->port.iotype == SERIAL_IO_MEM32) { + up->port.type = PORT_PXA; + return; + } +#endif + DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%p): ", up->port.line, up->port.iobase, up->port.membase); @@ -1187,6 +1209,16 @@ enable_rsa(up); #endif +#ifdef CONFIG_ARCH_PXA + if (up->port.type == PORT_PXA) { + switch ((long)up->port.membase) { + case (long)&FFUART: CKEN |= CKEN6_FFUART; break; + case (long)&BTUART: CKEN |= CKEN7_BTUART; break; + case (long)&STUART: CKEN |= CKEN5_STUART; break; + } + } +#endif + /* * Clear the FIFO buffers and disable them. * (they will be reeanbled in set_termios()) @@ -1260,6 +1292,8 @@ * anyway, so we don't enable them here. */ up->ier = UART_IER_RLSI | UART_IER_RDI; + if (up->port.type == PORT_PXA) + up->ier |= UART_IER_UUE | UART_IER_RTOIE | UART_IER_THRI; serial_outp(up, UART_IER, up->ier); if (up->port.flags & UPF_FOURPORT) { @@ -1321,6 +1355,18 @@ disable_rsa(up); #endif +#if 0 +#ifdef CONFIG_ARCH_PXA + if (up->port.type == PORT_PXA) { + switch ((long)up->port.membase) { + case (long)&FFUART: CKEN &= ~CKEN6_FFUART; break; + case (long)&BTUART: CKEN &= ~CKEN7_BTUART; break; + case (long)&STUART: CKEN &= ~CKEN5_STUART; break; + } + } +#endif +#endif + /* * Read data port to reset things, and then unlink from * the IRQ chain. @@ -1797,6 +1843,18 @@ return; first = 0; +#ifdef CONFIG_ARCH_PXA + up = serial8250_ports; + up->port.type = PORT_PXA; + up->port.mapbase = (void *)&FFUART; + up->port.membase = (void *)&FFUART; + up->port.regshift = 2; + up->port.iotype = SERIAL_IO_MEM32; + up->port.irq = IRQ_FFUART; + up->port.uartclk = 921600 * 16; + up->port.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST; + up->port.ops = &serial8250_pops; +#else for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port); i++, up++) { up->port.iobase = old_serial_port[i].port; @@ -1816,6 +1874,7 @@ if (share_irqs) up->port.flags |= UPF_SHARE_IRQ; } +#endif } static void __init serial8250_register_ports(struct uart_driver *drv) @@ -1893,6 +1952,9 @@ ier = serial_in(up, UART_IER); serial_out(up, UART_IER, 0); + if (up->port.type == PORT_PXA) + serial_out(up, UART_IER, UART_IER_UUE); + /* * Now, do each character */ diff -ruN linux-2.5.59-rmk1/fs/jffs2/file.c linux-2.5.59-pxacerf1/fs/jffs2/file.c --- linux-2.5.59-rmk1/fs/jffs2/file.c Mon Feb 17 13:36:23 2003 +++ linux-2.5.59-pxacerf1/fs/jffs2/file.c Mon Feb 17 12:55:43 2003 @@ -58,7 +58,7 @@ .read = generic_file_read, .write = generic_file_write, .ioctl = jffs2_ioctl, - .mmap = generic_file_readonly_mmap, + .mmap = generic_file_mmap, .fsync = jffs2_fsync, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,29) .sendfile = generic_file_sendfile diff -ruN linux-2.5.59-rmk1/include/asm-arm/arch-pxa/bitfield.h linux-2.5.59-pxacerf1/include/asm-arm/arch-pxa/bitfield.h --- linux-2.5.59-rmk1/include/asm-arm/arch-pxa/bitfield.h Wed Dec 31 16:00:00 1969 +++ linux-2.5.59-pxacerf1/include/asm-arm/arch-pxa/bitfield.h Mon Feb 17 12:55:43 2003 @@ -0,0 +1,113 @@ +/* + * FILE bitfield.h + * + * Version 1.1 + * Author Copyright (c) Marc A. Viredaz, 1998 + * DEC Western Research Laboratory, Palo Alto, CA + * Date April 1998 (April 1997) + * System Advanced RISC Machine (ARM) + * Language C or ARM Assembly + * Purpose Definition of macros to operate on bit fields. + */ + + + +#ifndef __BITFIELD_H +#define __BITFIELD_H + +#ifndef __ASSEMBLY__ +#define UData(Data) ((unsigned long) (Data)) +#else +#define UData(Data) (Data) +#endif + + +/* + * MACRO: Fld + * + * Purpose + * The macro "Fld" encodes a bit field, given its size and its shift value + * with respect to bit 0. + * + * Note + * A more intuitive way to encode bit fields would have been to use their + * mask. However, extracting size and shift value information from a bit + * field's mask is cumbersome and might break the assembler (255-character + * line-size limit). + * + * Input + * Size Size of the bit field, in number of bits. + * Shft Shift value of the bit field with respect to bit 0. + * + * Output + * Fld Encoded bit field. + */ + +#define Fld(Size, Shft) (((Size) << 16) + (Shft)) + + +/* + * MACROS: FSize, FShft, FMsk, FAlnMsk, F1stBit + * + * Purpose + * The macros "FSize", "FShft", "FMsk", "FAlnMsk", and "F1stBit" return + * the size, shift value, mask, aligned mask, and first bit of a + * bit field. + * + * Input + * Field Encoded bit field (using the macro "Fld"). + * + * Output + * FSize Size of the bit field, in number of bits. + * FShft Shift value of the bit field with respect to bit 0. + * FMsk Mask for the bit field. + * FAlnMsk Mask for the bit field, aligned on bit 0. + * F1stBit First bit of the bit field. + */ + +#define FSize(Field) ((Field) >> 16) +#define FShft(Field) ((Field) & 0x0000FFFF) +#define FMsk(Field) (((UData (1) << FSize (Field)) - 1) << FShft (Field)) +#define FAlnMsk(Field) ((UData (1) << FSize (Field)) - 1) +#define F1stBit(Field) (UData (1) << FShft (Field)) + + +/* + * MACRO: FInsrt + * + * Purpose + * The macro "FInsrt" inserts a value into a bit field by shifting the + * former appropriately. + * + * Input + * Value Bit-field value. + * Field Encoded bit field (using the macro "Fld"). + * + * Output + * FInsrt Bit-field value positioned appropriately. + */ + +#define FInsrt(Value, Field) \ + (UData (Value) << FShft (Field)) + + +/* + * MACRO: FExtr + * + * Purpose + * The macro "FExtr" extracts the value of a bit field by masking and + * shifting it appropriately. + * + * Input + * Data Data containing the bit-field to be extracted. + * Field Encoded bit field (using the macro "Fld"). + * + * Output + * FExtr Bit-field value. + */ + +#define FExtr(Data, Field) \ + ((UData (Data) >> FShft (Field)) & FAlnMsk (Field)) + + +#endif /* __BITFIELD_H */ diff -ruN linux-2.5.59-rmk1/include/asm-arm/arch-pxa/cerf.h linux-2.5.59-pxacerf1/include/asm-arm/arch-pxa/cerf.h --- linux-2.5.59-rmk1/include/asm-arm/arch-pxa/cerf.h Wed Dec 31 16:00:00 1969 +++ linux-2.5.59-pxacerf1/include/asm-arm/arch-pxa/cerf.h Mon Feb 17 12:55:43 2003 @@ -0,0 +1,205 @@ +/* + * linux/include/asm-arm/arch-pxa/cerf.h + * + * 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. + */ + +/* + * Add CerfBoard Specifics here... + */ + +/* + * Memory sizes + */ + +#define CERF_RAM_BASE 0xa0000000 + +#ifdef CONFIG_PXA_CERF_RAM_128MB +#define CERF_RAM_SIZE 128*1024*1024 + +#elif defined (CONFIG_PXA_CERF_RAM_64MB) +#define CERF_RAM_SIZE 64*1024*1024 + +#elif defined (CONFIG_PXA_CERF_RAM_32MB) +#define CERF_RAM_SIZE 32*1024*1024 + +#elif defined (CONFIG_PXA_CERF_RAM_16MB) +#define CERF_RAM_SIZE 16*1024*1024 +#endif + +/* + * CS memory timing via Static Memory Control Register (MSC0-2) + */ + +#define MSC_CS(cs,val) ((val)<<((cs&1)<<4)) + +#define MSC_RBUFF_SHIFT 15 +#define MSC_RBUFF_SLOW (0) +#define MSC_RBUFF_FAST (1) +#define MSC_RBUFF(x) ((x)<>= 8; \ + else __v &= 0xff; \ + __v; }) +#else +/* 32 bit fixup version */ +#define inb(p) ({ \ + unsigned int __p = (unsigned int)(p); \ + unsigned int __v = *(volatile unsigned int *)(__p & ~3); \ + if (__p & 1) __v >>= 8; \ + if (__p & 2) __v >>= 16; \ + __v & 0xff; }) +#endif + +#define outb(v,p) __raw_writeb(v,p) +#define outw(v,p) __raw_writew(v,p) +#define outl(v,p) __raw_writel(v,p) + +#define inw(p) ({ unsigned int __v = __raw_readw(p); __v; }) +#define inl(p) ({ unsigned int __v = __raw_readl(p); __v; }) + +#define outsb(p,d,l) __raw_writesb(p,d,l) +#define outsw(p,d,l) __raw_writesw(p,d,l) +#define outsl(p,d,l) __raw_writesl(p,d,l) + +// commented out for now to be sure it's not used +//#define insb(p,d,l) __raw_readsb(p,d,l) +#define insw(p,d,l) __raw_readsw(p,d,l) +#define insl(p,d,l) __raw_readsl(p,d,l) + +/* + * We don't actually have real ISA nor PCI buses, but there is so many * drivers out there that might just work if we fake them... */ -#define __io(a) (a) #define __mem_pci(a) ((unsigned long)(a)) #define __mem_isa(a) ((unsigned long)(a)) diff -ruN linux-2.5.59-rmk1/include/asm-arm/arch-pxa/pxa-regs.h linux-2.5.59-pxacerf1/include/asm-arm/arch-pxa/pxa-regs.h --- linux-2.5.59-rmk1/include/asm-arm/arch-pxa/pxa-regs.h Mon Feb 17 13:35:40 2003 +++ linux-2.5.59-pxacerf1/include/asm-arm/arch-pxa/pxa-regs.h Mon Feb 17 12:55:43 2003 @@ -9,6 +9,10 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#ifndef _PXA_REGS_H_ +#define _PXA_REGS_H_ + +#include "bitfield.h" // FIXME hack so that SA-1111.h will work [cb] @@ -440,7 +444,7 @@ #define ISR_RWM 0x1 /* read/write mode */ #define ISR_ACKNAK 0x2 /* ack/nak status */ #define ISR_UB 0x4 /* unit busy */ -#define ISR_IBB 0x8 /* bus busy */ +#define ISR_IBB 0x8 /* bus busy */ #define ISR_SSD 0x10 /* slave stop detected */ #define ISR_ALD 0x20 /* arbitration loss detected */ #define ISR_ITE 0x40 /* tx buffer empty */ @@ -1171,19 +1175,105 @@ #define LCCR0_BM (1 << 20) /* Branch mask */ #define LCCR0_OUM (1 << 21) /* Output FIFO underrun mask */ +#define LCCR1_PPL Fld (10, 0) /* Pixels Per Line - 1 */ +#define LCCR1_DisWdth(Pixel) /* Display Width [1..800 pix.] */ \ + (((Pixel) - 1) << FShft (LCCR1_PPL)) + +#define LCCR1_HSW Fld (6, 10) /* Horizontal Synchronization */ +#define LCCR1_HorSnchWdth(Tpix) /* Horizontal Synchronization */ \ + /* pulse Width [1..64 Tpix] */ \ + (((Tpix) - 1) << FShft (LCCR1_HSW)) + +#define LCCR1_ELW Fld (8, 16) /* End-of-Line pixel clock Wait */ + /* count - 1 [Tpix] */ +#define LCCR1_EndLnDel(Tpix) /* End-of-Line Delay */ \ + /* [1..256 Tpix] */ \ + (((Tpix) - 1) << FShft (LCCR1_ELW)) + +#define LCCR1_BLW Fld (8, 24) /* Beginning-of-Line pixel clock */ + /* Wait count - 1 [Tpix] */ +#define LCCR1_BegLnDel(Tpix) /* Beginning-of-Line Delay */ \ + /* [1..256 Tpix] */ \ + (((Tpix) - 1) << FShft (LCCR1_BLW)) + + +#define LCCR2_LPP Fld (10, 0) /* Line Per Panel - 1 */ +#define LCCR2_DisHght(Line) /* Display Height [1..1024 lines] */ \ + (((Line) - 1) << FShft (LCCR2_LPP)) + +#define LCCR2_VSW Fld (6, 10) /* Vertical Synchronization pulse */ + /* Width - 1 [Tln] (L_FCLK) */ +#define LCCR2_VrtSnchWdth(Tln) /* Vertical Synchronization pulse */ \ + /* Width [1..64 Tln] */ \ + (((Tln) - 1) << FShft (LCCR2_VSW)) + +#define LCCR2_EFW Fld (8, 16) /* End-of-Frame line clock Wait */ + /* count [Tln] */ +#define LCCR2_EndFrmDel(Tln) /* End-of-Frame Delay */ \ + /* [0..255 Tln] */ \ + ((Tln) << FShft (LCCR2_EFW)) + +#define LCCR2_BFW Fld (8, 24) /* Beginning-of-Frame line clock */ + /* Wait count [Tln] */ +#define LCCR2_BegFrmDel(Tln) /* Beginning-of-Frame Delay */ \ + /* [0..255 Tln] */ \ + ((Tln) << FShft (LCCR2_BFW)) + +#if 0 #define LCCR3_PCD (0xff) /* Pixel clock divisor */ #define LCCR3_ACB (0xff << 8) /* AC Bias pin frequency */ #define LCCR3_ACB_S 8 +#endif + #define LCCR3_API (0xf << 16) /* AC Bias pin trasitions per interrupt */ #define LCCR3_API_S 16 #define LCCR3_VSP (1 << 20) /* vertical sync polarity */ #define LCCR3_HSP (1 << 21) /* horizontal sync polarity */ #define LCCR3_PCP (1 << 22) /* pixel clock polarity */ #define LCCR3_OEP (1 << 23) /* output enable polarity */ +#if 0 #define LCCR3_BPP (7 << 24) /* bits per pixel */ #define LCCR3_BPP_S 24 +#endif #define LCCR3_DPC (1 << 27) /* double pixel clock mode */ + +#define LCCR3_PCD Fld (8, 0) /* Pixel Clock Divisor */ +#define LCCR3_PixClkDiv(Div) /* Pixel Clock Divisor */ \ + (((Div) << FShft (LCCR3_PCD))) + + +#define LCCR3_BPP Fld (3, 24) /* Bit Per Pixel */ +#define LCCR3_Bpp(Bpp) /* Bit Per Pixel */ \ + (((Bpp) << FShft (LCCR3_BPP))) + +#define LCCR3_ACB Fld (8, 8) /* AC Bias */ +#define LCCR3_Acb(Acb) /* BAC Bias */ \ + (((Acb) << FShft (LCCR3_ACB))) + +#define LCCR3_HorSnchH (LCCR3_HSP*0) /* Horizontal Synchronization */ + /* pulse active High */ +#define LCCR3_HorSnchL (LCCR3_HSP*1) /* Horizontal Synchronization */ + +#define LCCR3_VrtSnchH (LCCR3_VSP*0) /* Vertical Synchronization pulse */ + /* active High */ +#define LCCR3_VrtSnchL (LCCR3_VSP*1) /* Vertical Synchronization pulse */ + /* active Low */ + +#define LCSR_LDD (1 << 0) /* LCD Disable Done */ +#define LCSR_SOF (1 << 1) /* Start of frame */ +#define LCSR_BER (1 << 2) /* Bus error */ +#define LCSR_ABC (1 << 3) /* AC Bias count */ +#define LCSR_IUL (1 << 4) /* input FIFO underrun Lower panel */ +#define LCSR_IUU (1 << 5) /* input FIFO underrun Upper panel */ +#define LCSR_OU (1 << 6) /* output FIFO underrun */ +#define LCSR_QD (1 << 7) /* quick disable */ +#define LCSR_EOF (1 << 8) /* end of frame */ +#define LCSR_BS (1 << 9) /* branch status */ +#define LCSR_SINT (1 << 10) /* subsequent interrupt */ + +#define LDCMD_PAL (1 << 26) /* instructs DMA to load palette buffer */ + #define LCSR_LDD (1 << 0) /* LCD Disable Done */ #define LCSR_SOF (1 << 1) /* Start of frame */ #define LCSR_BER (1 << 2) /* Bus error */ @@ -1234,5 +1324,4 @@ #define MDREFR_K0RUN (1 << 13) /* SDCLK0 Run Control/Status */ #define MDREFR_E0PIN (1 << 12) /* SDCKE0 Level Control/Status */ - - +#endif diff -ruN linux-2.5.59-rmk1/include/asm-arm/arch-pxa/serial.h linux-2.5.59-pxacerf1/include/asm-arm/arch-pxa/serial.h --- linux-2.5.59-rmk1/include/asm-arm/arch-pxa/serial.h Mon Feb 17 13:35:40 2003 +++ linux-2.5.59-pxacerf1/include/asm-arm/arch-pxa/serial.h Mon Feb 17 12:55:43 2003 @@ -10,42 +10,11 @@ */ -#define BAUD_BASE 921600 - -/* Standard COM flags */ -#define STD_COM_FLAGS (ASYNC_SKIP_TEST) - -#define RS_TABLE_SIZE 5 - -#define STD_SERIAL_PORT_DEFNS \ - { \ - type: PORT_PXA, \ - xmit_fifo_size: 64, \ - baud_base: BAUD_BASE, \ - iomem_base: &FFUART, \ - iomem_reg_shift: 2, \ - io_type: SERIAL_IO_MEM, \ - irq: IRQ_FFUART, \ - flags: STD_COM_FLAGS, \ - }, { \ - type: PORT_PXA, \ - xmit_fifo_size: 64, \ - baud_base: BAUD_BASE, \ - iomem_base: &STUART, \ - iomem_reg_shift: 2, \ - io_type: SERIAL_IO_MEM, \ - irq: IRQ_STUART, \ - flags: STD_COM_FLAGS, \ - }, { \ - type: PORT_PXA, \ - xmit_fifo_size: 64, \ - baud_base: BAUD_BASE, \ - iomem_base: &BTUART, \ - iomem_reg_shift: 2, \ - io_type: SERIAL_IO_MEM, \ - irq: IRQ_BTUART, \ - flags: STD_COM_FLAGS, \ - } +/* just for serial_8250.c to compile... */ +#define BASE_BAUD ( 1843200 / 16 ) +/* this is only to have UART_NR be large enough in serial_8250.c */ +#define STD_SERIAL_PORT_DEFNS {0,}, {0,}, {0,}, {0,}, {0,}, #define EXTRA_SERIAL_PORT_DEFNS + diff -ruN linux-2.5.59-rmk1/include/asm-arm/arch-pxa/time.h linux-2.5.59-pxacerf1/include/asm-arm/arch-pxa/time.h --- linux-2.5.59-rmk1/include/asm-arm/arch-pxa/time.h Mon Feb 17 13:35:40 2003 +++ linux-2.5.59-pxacerf1/include/asm-arm/arch-pxa/time.h Mon Feb 17 12:55:43 2003 @@ -42,7 +42,7 @@ elapsed = LATCH - ticks_to_match; /* Now convert them to usec */ - usec = (unsigned long)(elapsed*tick)/LATCH; + usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH; return usec; } diff -ruN linux-2.5.59-rmk1/include/asm-arm/mach/irq.h linux-2.5.59-pxacerf1/include/asm-arm/mach/irq.h --- linux-2.5.59-rmk1/include/asm-arm/mach/irq.h Mon Feb 17 13:47:05 2003 +++ linux-2.5.59-pxacerf1/include/asm-arm/mach/irq.h Mon Feb 17 13:16:10 2003 @@ -10,6 +10,8 @@ #ifndef __ASM_ARM_MACH_IRQ_H #define __ASM_ARM_MACH_IRQ_H +#include + struct irqdesc; struct pt_regs; struct seq_file; diff -ruN linux-2.5.59-rmk1/include/linux/serial.h linux-2.5.59-pxacerf1/include/linux/serial.h --- linux-2.5.59-rmk1/include/linux/serial.h Mon Feb 17 13:47:05 2003 +++ linux-2.5.59-pxacerf1/include/linux/serial.h Mon Feb 17 12:55:43 2003 @@ -76,11 +76,13 @@ #define PORT_16654 11 #define PORT_16850 12 #define PORT_RSA 13 /* RSA-DV II/S card */ -#define PORT_MAX 13 +#define PORT_PXA 14 +#define PORT_MAX 14 #define SERIAL_IO_PORT 0 #define SERIAL_IO_HUB6 1 #define SERIAL_IO_MEM 2 +#define SERIAL_IO_MEM32 3 /* * Definitions for async_struct (and serial_struct) flags field diff -ruN linux-2.5.59-rmk1/include/linux/serial_core.h linux-2.5.59-pxacerf1/include/linux/serial_core.h --- linux-2.5.59-rmk1/include/linux/serial_core.h Mon Feb 17 13:47:05 2003 +++ linux-2.5.59-pxacerf1/include/linux/serial_core.h Mon Feb 17 12:55:43 2003 @@ -37,7 +37,8 @@ #define PORT_16654 11 #define PORT_16850 12 #define PORT_RSA 13 -#define PORT_MAX_8250 13 /* max port ID */ +#define PORT_PXA 14 +#define PORT_MAX_8250 14 /* max port ID */ /* * ARM specific type numbers. These are not currently guaranteed diff -ruN linux-2.5.59-rmk1/include/linux/serial_reg.h linux-2.5.59-pxacerf1/include/linux/serial_reg.h --- linux-2.5.59-rmk1/include/linux/serial_reg.h Mon Feb 17 13:47:05 2003 +++ linux-2.5.59-pxacerf1/include/linux/serial_reg.h Mon Feb 17 12:55:43 2003 @@ -119,6 +119,14 @@ #define UART_IERX_SLEEP 0x10 /* Enable sleep mode */ /* + * The Intel PXA250/210 chip defines those bits + */ +#define UART_IER_DMAE 0x80 /* DMA Requests Enable */ +#define UART_IER_UUE 0x40 /* UART Unit Enable */ +#define UART_IER_NRZE 0x20 /* NRZ coding Enable */ +#define UART_IER_RTOIE 0x10 /* Receiver Time Out Interrupt Enable */ + +/* * These are the definitions for the Modem Control Register */ #define UART_MCR_AFE 0x20 /* Enable auto-RTS/CTS (TI16C750) */