diff -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/Documentation/Configure.help linux-2.4.18-rmk7-ptx3/Documentation/Configure.help --- linux-2.4.18-rmk7/Documentation/Configure.help Sun Jun 30 23:14:59 2002 +++ linux-2.4.18-rmk7-ptx3/Documentation/Configure.help Sun Jul 14 13:32:13 2002 @@ -22301,6 +22301,18 @@ and business machine applications. Say Y here if you require support for this target. +SSV Software Systems GmbH DIL/Net-PC DNP/1110 +CONFIG_SA1100_DNP1110 + The SSV Software Systems' DIL/Net-PC DNP/1110 is an IntelŪ StrongArmŪ + SA-1110 based embedded PC which has the same size as the standard + 64 pin JEDEC DIL case. It is pin compatible to it's x86 (AMD Elan) + based brother DNP/1486. + + http://www.dilnetpc.com (The actual hardware) + http://www.pengutronix.de/software/dnp_en.html (Tips and Tricks) + + Say Y here if you require support fot this target. + # Choice: cerf_ram Cerf on-board RAM size CONFIG_SA1100_CERF_8MB diff -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/Makefile linux-2.4.18-rmk7-ptx3/Makefile --- linux-2.4.18-rmk7/Makefile Mon Jul 1 00:45:15 2002 +++ linux-2.4.18-rmk7-ptx3/Makefile Wed Aug 7 22:34:24 2002 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 4 SUBLEVEL = 18 -EXTRAVERSION = -rmk7 +EXTRAVERSION = -rmk7-ptx3 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/arch/arm/config.in linux-2.4.18-rmk7-ptx3/arch/arm/config.in --- linux-2.4.18-rmk7/arch/arm/config.in Sun Jun 30 23:15:00 2002 +++ linux-2.4.18-rmk7-ptx3/arch/arm/config.in Sun Jul 14 13:34:03 2002 @@ -96,6 +96,7 @@ define_bool CONFIG_SA1100_H3XXX n fi #dep_bool ' Consus' CONFIG_SA1100_CONSUS $CONFIG_ARCH_SA1100 +dep_bool ' DNP/1110' CONFIG_SA1100_DNP1110 $CONFIG_ARCH_SA1100 #dep_bool ' Empeg' CONFIG_SA1100_EMPEG $CONFIG_ARCH_SA1100 dep_bool ' Extenex HandHeld Theater (Squashtail)' CONFIG_SA1100_EXTENEX1 $CONFIG_ARCH_SA1100 if [ "$CONFIG_SA1100_EXTENEX1" = "y" ]; then diff -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/arch/arm/kernel/head-armv.S linux-2.4.18-rmk7-ptx3/arch/arm/kernel/head-armv.S --- linux-2.4.18-rmk7/arch/arm/kernel/head-armv.S Sun Jun 30 23:15:00 2002 +++ linux-2.4.18-rmk7-ptx3/arch/arm/kernel/head-armv.S Sat Aug 3 16:41:23 2002 @@ -125,7 +125,14 @@ */ mov r1, #MACH_TYPE_L7200 #endif - +#if defined(CONFIG_SA1100_DNP1110) +/* + * FIXME - The SSV bootcode has arcitecture number 255 although they + * didn't apply for an official number :-( So we set r1 manually to be + * compatible with the original bootcode. + */ + mov r1, #MACH_TYPE_DNP1110 +#endif mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode msr cpsr_c, r0 @ and all irqs disabled bl __lookup_processor_type diff -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/arch/arm/mach-sa1100/Makefile linux-2.4.18-rmk7-ptx3/arch/arm/mach-sa1100/Makefile --- linux-2.4.18-rmk7/arch/arm/mach-sa1100/Makefile Sun Jun 30 23:15:00 2002 +++ linux-2.4.18-rmk7-ptx3/arch/arm/mach-sa1100/Makefile Sun Jul 14 13:35:31 2002 @@ -49,6 +49,7 @@ obj-$(CONFIG_SA1100_CEP) += cep.o obj-$(CONFIG_SA1100_CERF) += cerf.o obj-$(CONFIG_SA1100_CONSUS) += consus.o +obj-$(CONFIG_SA1100_DNP1110) += dnp1110.o obj-$(CONFIG_SA1100_EMPEG) += empeg.o obj-$(CONFIG_SA1100_FLEXANET) += flexanet.o obj-$(CONFIG_SA1100_FREEBIRD) += freebird.o diff -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/arch/arm/mach-sa1100/dnp1110.c linux-2.4.18-rmk7-ptx3/arch/arm/mach-sa1100/dnp1110.c --- linux-2.4.18-rmk7/arch/arm/mach-sa1100/dnp1110.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.18-rmk7-ptx3/arch/arm/mach-sa1100/dnp1110.c Wed Aug 7 22:33:15 2002 @@ -0,0 +1,149 @@ +/* + * linux/arch/arm/mach-sa1100/dnp1110.c + * + * Author: Marco Hasewinkel + * + * This file contains all DNP/1110 specific tweaks + * for "DNP/1110 - mha@ssv " + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + + +#include "generic.h" + +static int __init dnp1110_init(void) +{ + GPDR = 0x08000000; /* GPIO0-GPIO26 to Input */ + /* GPIO27 to Output */ + + GAFR = 0x08000000; /* alternate function on GPIO27 */ + + TUCR = 0x20000000; /* set 3.6864 MHz out to GPIO27 */ + + PPDR = 0x0000008A; /* COM1 CTS+DSR+DCD+RI to Input */ + /* COM1 RTS+DTR to Output */ + /* Vpp is Output and RCM Input */ + /* Port C to Input */ + + PPSR &= ~0x80; /* Vpp disable */ + + Ser1SDCR0 |= SDCR0_UART;/* Select UART function in serial port 1 */ + + MECR = 0x21082108; /* PCMCIA Socket 0 & 1 Timing - FIXME !!! */ + /* cycle time 621ns (=(7*8+8)*9.7ns) */ + return 0; +} + +__initcall(dnp1110_init); + + +static void __init +fixup_dnp1110(struct machine_desc *desc, struct param_struct *params, + char **cmdline, struct meminfo *mi) +{ + SET_BANK(0, 0xc0000000, 32*1024*1024); /* DNP/1110 32MB */ +// SET_BANK(0, 0xc0000000, 16*1024*1024); /* DNP/1110 16MB */ + mi->nr_banks = 1; + + ROOT_DEV = MKDEV(RAMDISK_MAJOR,0); + setup_ramdisk(1, 0, 0, 8192); + setup_initrd(0xc0400000, 3*1024*1024); /* max 3MB INITRAMDISK */ +} + +static struct map_desc dnp1110_io_desc[] __initdata = { + /* virtual physical length domain r w c b */ + { 0xe8000000, 0x00000000, 0x01000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 0 */ + LAST_DESC +}; + +static void dnp1110_set_mctrl(struct uart_port *port, u_int mctrl) +{ + u_int mdm_ctl0 = PPSR; // get Pin State Register + + if (port->mapbase == _Ser1UTCR0) { + if (mctrl & TIOCM_RTS) + mdm_ctl0 &= ~(1<<1); // clear Pin LDD1 = RTS + else + mdm_ctl0 |= (1<<1); // set Pin LDD1 = RTS + + if (mctrl & TIOCM_DTR) + mdm_ctl0 &= ~(1<<3); // clear Pin LDD3 = DTR + else + mdm_ctl0 |= (1<<3); // set Pin LDD3 = DTR + } + else + return; + + PPSR = mdm_ctl0; // set Pin State Register +} + +static u_int dnp1110_get_mctrl(struct uart_port *port) +{ + u_int ret = TIOCM_CTS | TIOCM_DSR | TIOCM_CD; + + u_int mdm_ctl1 = PPSR; // get Pin State Register + + if (port->mapbase == _Ser1UTCR0) { + if (mdm_ctl1 & (1<<0)) // Pin LDD0 = CTS + ret &= ~TIOCM_CTS; + if (mdm_ctl1 & (1<<2)) // Pin LDD2 = DSR + ret &= ~TIOCM_DSR; + if (mdm_ctl1 & (1<<4)) // Pin LDD4 = DCD + ret &= ~TIOCM_CD; + } + + return ret; +} + +static struct sa1100_port_fns dnp1110_port_fns __initdata = { + set_mctrl: dnp1110_set_mctrl, + get_mctrl: dnp1110_get_mctrl, +}; + +static void __init dnp1110_map_io(void) +{ + sa1100_map_io(); + iotable_init(dnp1110_io_desc); + + sa1100_register_uart_fns(&dnp1110_port_fns); + sa1100_register_uart(0, 1); /* COM1 = ttySA0 (SA UART 1) */ + sa1100_register_uart(1, 3); /* COM2 = ttySA1 (SA UART 3) */ +} + +static void __init dnp1110_init_irq(void) +{ + +/* first default SA11X0 IRQ mapping */ + sa1100_init_irq(); + +/* then dnp/1110 special IRQ mapping */ + set_GPIO_IRQ_edge(GPIO_GPIO(0), GPIO_RISING_EDGE); /* USER INT 1 */ + set_GPIO_IRQ_edge(GPIO_GPIO(1), GPIO_RISING_EDGE); /* USER INT 2 */ + set_GPIO_IRQ_edge(GPIO_GPIO(2), GPIO_RISING_EDGE); /* USER INT 3 */ + set_GPIO_IRQ_edge(GPIO_GPIO(3), GPIO_RISING_EDGE); /* USER INT 4 */ + set_GPIO_IRQ_edge(GPIO_GPIO(4), GPIO_RISING_EDGE); /* USER INT 5 */ + + set_GPIO_IRQ_edge(GPIO_GPIO(5), GPIO_RISING_EDGE); /* LAN */ + set_GPIO_IRQ_edge(GPIO_GPIO(6), GPIO_RISING_EDGE); /* SSV only */ +} + +MACHINE_START(DNP1110, "DNP/1110 - SSV Embedded Systems") + MAINTAINER("Marco Hasewinkel ; Robert Schwebel ") + BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000) + BOOT_PARAMS(0xc0000100) + FIXUP(fixup_dnp1110) + MAPIO(dnp1110_map_io) + INITIRQ(dnp1110_init_irq) +MACHINE_END diff -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/arch/arm/tools/Makefile linux-2.4.18-rmk7-ptx3/arch/arm/tools/Makefile --- linux-2.4.18-rmk7/arch/arm/tools/Makefile Sun Jun 30 23:15:01 2002 +++ linux-2.4.18-rmk7-ptx3/arch/arm/tools/Makefile Sat Aug 3 15:10:40 2002 @@ -4,6 +4,10 @@ # Copyright (C) 2001 Russell King # +ifndef $(TOPDIR) +TOPDIR = ../../.. +endif + all: $(TOPDIR)/include/asm-arm/mach-types.h \ $(TOPDIR)/include/asm-arm/constants.h diff -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/arch/arm/tools/mach-types linux-2.4.18-rmk7-ptx3/arch/arm/tools/mach-types --- linux-2.4.18-rmk7/arch/arm/tools/mach-types Sun Jun 30 23:15:01 2002 +++ linux-2.4.18-rmk7-ptx3/arch/arm/tools/mach-types Sat Aug 3 15:30:55 2002 @@ -202,3 +202,5 @@ fester SA1100_FESTER FESTER 191 gpi ARCH_GPI GPI 192 smdk2410 ARCH_SMDK2410 SMDK2410 193 +dnp1110 SA1100_DNP1110 DNP1110 214 + diff -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/drivers/net/Config.in linux-2.4.18-rmk7-ptx3/drivers/net/Config.in --- linux-2.4.18-rmk7/drivers/net/Config.in Sun Jun 30 23:15:02 2002 +++ linux-2.4.18-rmk7-ptx3/drivers/net/Config.in Sat Aug 3 17:55:30 2002 @@ -107,6 +107,7 @@ dep_tristate ' SMC Ultra support' CONFIG_ULTRA $CONFIG_ISA dep_tristate ' SMC Ultra32 EISA support' CONFIG_ULTRA32 $CONFIG_EISA dep_tristate ' SMC 9194 support' CONFIG_SMC9194 $CONFIG_ISA + dep_tristate ' SMC 91111 support (EXPERIMENTAL)' CONFIG_SMC91111 $CONFIG_EXPERIMENTAL fi bool ' Racal-Interlan (Micom) NI cards' CONFIG_NET_VENDOR_RACAL if [ "$CONFIG_NET_VENDOR_RACAL" = "y" ]; then diff -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/drivers/net/Makefile linux-2.4.18-rmk7-ptx3/drivers/net/Makefile --- linux-2.4.18-rmk7/drivers/net/Makefile Sun Jun 30 23:15:02 2002 +++ linux-2.4.18-rmk7-ptx3/drivers/net/Makefile Sat Aug 3 17:38:16 2002 @@ -114,6 +114,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_ARM_AM79C961A) += am79c961a.o obj-$(CONFIG_ARM_ETHERH) += 8390.o obj-$(CONFIG_WD80x3) += wd.o 8390.o diff -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/drivers/net/Space.c linux-2.4.18-rmk7-ptx3/drivers/net/Space.c --- linux-2.4.18-rmk7/drivers/net/Space.c Fri Sep 14 00:21:32 2001 +++ linux-2.4.18-rmk7-ptx3/drivers/net/Space.c Sat Aug 3 17:38:16 2002 @@ -249,6 +249,9 @@ #ifdef CONFIG_SMC9194 {smc_init, 0}, #endif +#ifdef CONFIG_SMC91111 + {smc_init, 0}, +#endif #ifdef CONFIG_SEEQ8005 {seeq8005_probe, 0}, #endif diff -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/drivers/net/smc91111.c linux-2.4.18-rmk7-ptx3/drivers/net/smc91111.c --- linux-2.4.18-rmk7/drivers/net/smc91111.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.18-rmk7-ptx3/drivers/net/smc91111.c Wed Aug 28 11:11:40 2002 @@ -0,0 +1,3916 @@ +/*------------------------------------------------------------------------ + . 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 ) + . Marco Hasewinkel ( mha@ist1.de ) ( m.hasewinkel@web.de ) + . + . 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: + . 08/06/02 Marco Hasewinkel, ARM Linux port for SSV PNP/1110-3V with LCD + . 05/02/02 Marco Hasewinkel, ARM Linux port for SSV PNP/1110-3V + . 02/05/02 Marco Hasewinkel, ARM Linux port for SSV DNP/1110-3V + . 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.0) (Linux Kernel 2.4 + Support for Odd Byte)\n" + "09/24/01 by Pramod Bhardwaj (pramod.bhardwaj@smsc.com)\n" + "ARM-Linux port for SSV DNP/1110-3V Version 20020806 (mha@ist1.de)\n"; + +#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 + +#include "smc91111.h" +/*------------------------------------------------------------------------ + . + . Configuration options, for the experienced user to change. + . + -------------------------------------------------------------------------*/ + +#ifdef CONFIG_ISA +/* + .the LAN91C111 can be at any of the following port addresses. To change, + .for a slightly different card, you can add it to the array. Keep in + .mind that the array must end in zero. +*/ +static unsigned int smc_portlist[] __initdata = + { 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0, + 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0, 0}; +#endif + +/* + . Wait time for memory to be free. This probably shouldn't be + . tuned that much, as waiting for this means nothing else happens + . in the system +*/ +#define MEMORY_WAIT_TIME 16 + +/* + . 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 +*/ +//#define SMC_DEBUG 3 // Must be defined in makefile + +#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 +#define LAN91C111_MEMORY_MULTIPLIER (1024*2) + +/* 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; + + // Set to true during the auto-negotiation sequence + int autoneg_active; + + // Address of our PHY port + word phyaddr; + + // Type of PHY + word phytype; + + // Last contents of PHY Register 18 + word lastPhy18; + + // Contains the current active transmission mode + word tcr_cur_mode; + + // Contains the current active receive mode + word rcr_cur_mode; + + // Contains the current active receive/phy mode + word rpc_cur_mode; + + /* => Pramod, Odd Byte issue */ + // Contains the Current ChipID + unsigned short ChipID; + + //Contains the Current ChipRevision + unsigned short ChipRev; + /* <= Pramod, Odd Byte issue */ + + +#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 the actual 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 only called when an error + . relating to a packet is sent. +*/ +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, int ioaddr); + +/* + . A rather simple routine to print out a packet for debugging purposes. +*/ +#if SMC_DEBUG > 2 +static void print_packet( byte *, 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( 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( 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( 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 word smc_read_phy_register(int ioaddr, byte phyaddr, byte phyreg); +static void smc_write_phy_register(int ioaddr, byte phyaddr, byte phyreg, word 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 */ + +/* + . 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; + 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 (dev->dma) + SMC_outw( SMC_inw( ioaddr + CONFIG_REG ) | CONFIG_NO_WAIT, + ioaddr + CONFIG_REG ); + +#ifdef 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 ); + SMC_outw( SMC_inw( ioaddr + CTL_REG ) | CTL_AUTO_RELEASE , ioaddr + CTL_REG ); + + /* 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 */ + + /* Disable all interrupts */ +#ifndef ONLY_16_BIT + SMC_outb( 0, ioaddr + IM_REG ); +#else + SMC_outw( 0, ioaddr + INT_REG ); +#endif +} + +/* + . 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 ) +{ + 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 ); +#ifndef ONLY_16_BIT + SMC_outb( SMC_INTERRUPT_MASK, ioaddr + IM_REG ); +#else + SMC_outw( (SMC_INTERRUPT_MASK << 8), ioaddr + INT_REG ); +#endif +} + +/* + . 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( int ioaddr ) +{ + PRINTK2("CARDNAME:smc_shutdown\n"); + + /* no more interrupts for me */ + SMC_SELECT_BANK( 2 ); +#ifndef ONLY_16_BIT + 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 ); +#else + SMC_outw( 0, ioaddr + INT_REG ); + /* and tell the card to stay away from that nasty outside world */ + SMC_SELECT_BANK( 0 ); + SMC_outw( RCR_CLEAR, ioaddr + RCR_REG ); + SMC_outw( TCR_CLEAR, ioaddr + TCR_REG ); +#endif + +#ifdef 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( 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("CARDNAME:smc_setmulticast\n"); + + /* 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<>3)&7]); + + } + /* now, the table can be loaded into the chipset */ + SMC_SELECT_BANK( 3 ); +#ifndef ONLY_16_BIT + for ( i = 0; i < 8 ; i++ ) { + SMC_outb( multicast_table[i], ioaddr + MCAST_REG1 + i ); + } +#else + for ( i = 0; i < 8 ; i+=2 ) { + SMC_outw( (multicast_table[i] | (multicast_table[i+1] << 8)), ioaddr + MCAST_REG1 + i ); + } +#endif +} + +/* + 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; + int ioaddr = dev->base_addr; + word length; + unsigned short numPages; + word time_out; + word status; + + PRINTK3("%s:smc_wait_to_send_packet\n", dev->name); + + netif_stop_queue(dev); + + if ( lp->saved_skb) { + /* THIS SHOULD NEVER HAPPEN. */ + lp->stats.tx_aborted_errors++; + printk("%s: Bad Craziness - sent packet while busy.\n", + dev->name); + return 1; + } + lp->saved_skb = skb; + + length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + + + /* + ** 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; + } + /* either way, a packet is waiting now */ + lp->packets_waiting++; + + /* now, try to allocate the memory */ + SMC_SELECT_BANK( 2 ); + 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 ) { + /* 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); + netif_wake_queue(dev); + 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; + byte packet_no; + struct sk_buff * skb = lp->saved_skb; + word length; + int ioaddr; + byte * 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_DEBUG "%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 */ +#ifndef ONLY_16_BIT + SMC_outb( packet_no, ioaddr + PN_REG ); +#else + SMC_outw( packet_no, ioaddr + PN_REG ); +#endif + /* 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 ) */ +#ifdef USE_32_BIT + 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_outw( (length+6), ioaddr + DATA_REG ); +#endif + + /* 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? + */ +#ifdef USE_32_BIT + SMC_outsl(ioaddr + DATA_REG, buf, length >> 2 ); + if ( length & 0x2 ) + SMC_outw(*((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_REG); +#else + SMC_outsw(ioaddr + DATA_REG , buf, (length ) >> 1); +#endif // USE_32_BIT + + /* Send the last byte, if there is one. */ + if ( (length & 1) == 0 ) { + SMC_outw( 0, ioaddr + DATA_REG ); + } else { + // Set odd bit in CONTROL BYTE + SMC_outw( buf[length-1] | 0x2000, ioaddr + DATA_REG); + } + + /* enable the interrupts */ + SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) ); + + /* 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->saved_skb = NULL; + dev_kfree_skb_any (skb); + + dev->trans_start = jiffies; + + /* we can send another packet */ + netif_wake_queue(dev); + + + 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_ISA + int i; + int base_addr = dev ? dev->base_addr : 0; + + PRINTK2("CARDNAME:smc_init\n"); + + SET_MODULE_OWNER (dev); + + /* try a specific location */ + if (base_addr > 0x1ff) + return smc_probe(dev, base_addr); + else if ( 0 != base_addr ) + return -ENXIO; + + /* check every ethernet address */ + for (i = 0; smc_portlist[i]; i++) + if ( smc_probe(dev,smc_portlist[i]) ==0) + return 0; + + /* couldn't find anything */ + return -ENODEV; +#endif +#ifdef CONFIG_SA1100_DNP1110 + /* LAN DNP1110 @ PCMCIA Socket 0 - I/O space */ + int base_addr = 0xF6000300; + int ret; + + PRINTK2(CARDNAME ":smc_init\n"); + + SET_MODULE_OWNER(dev); // for MOD_INC_USE_COUNT + + MECR &= ~0x0000FFFF; /* PCMCIA Socket 0 Timing */ + MECR |= 0x00000421; /* cycle time 146ns =(7*1+8)*9.7ns */ + + ret = smc_probe( dev, base_addr); + if (ret < 0) { + printk("smsc 91C1111 is not found\n"); + } + + return ret; +#endif +#ifdef CONFIG_SA1100_PNP1110 + /* LAN PNP1110 @ PCMCIA Socket 0 - memory space */ + int base_addr = (int)ioremap_nocache(0x2C000000,0x1000); + int ret; + + if (base_addr == 0) + return -ENODEV; + + base_addr += 0x300; + + PRINTK2(CARDNAME ":smc_init\n"); + + SET_MODULE_OWNER(dev); // for MOD_INC_USE_COUNT + + MECR &= ~0x0000FFFF; /* PCMCIA Socket 0 Timing */ + MECR |= 0x00000000; /* cycle time 78ns =(7*0+8)*9.7ns */ + + ret = smc_probe( dev, base_addr); + if (ret < 0) { + printk("smsc 91C1111 is not found\n"); + } + + return ret; +#endif +} + + +/*------------------------------------------------------------------------- + | + | smc_destructor( struct device * dev ) + | Input parameters: + | dev, pointer to the device structure + | + | Output: + | None. + | + --------------------------------------------------------------------------- +*/ +void smc_destructor(struct net_device *dev) +{ + PRINTK2("CARDNAME:smc_destructor\n"); +} + + +#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( int ioaddr ) +{ + int timeout = 20; + unsigned long cookie; + + PRINTK2("CARDNAME:smc_findirq\n"); + + /* 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 */ +#ifndef ONLY_16_BIT + SMC_outb( IM_ALLOC_INT, ioaddr + IM_REG ); +#else + SMC_outw( (IM_ALLOC_INT << 8), ioaddr + INT_REG ); +#endif + /* + . 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 ) { + byte 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 */ +#ifndef ONLY_16_BIT + SMC_outb( 0, ioaddr + IM_REG ); +#else + SMC_outw( 0, ioaddr + INT_REG ); +#endif + /* 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, int ioaddr ) +{ + int i, memory, retval; + static unsigned version_printed = 0; + unsigned int bank; + + const char *version_string; + + /*registers */ + word revision_register; +#ifdef CONFIG_ISA + word base_address_register; +#endif + word memory_info_register; + /*=> Pramod */ + struct smc_local *lp; + /*<= Pramod */ +#if defined(CONFIG_SA1100_DNP1110) || defined(CONFIG_SA1100_PNP1110) + /* MAC ADDRESS AT FLASHBLOCK 1 / OFFSET 0x10 */ + unsigned char *dnp1110_mac = (unsigned char *) (0xE8000000 + 0x20010); +#endif + + PRINTK2("CARDNAME:smc_probe\n"); + + /* Grab the region so that no one else tries to probe our ioports. */ + if (!request_region(ioaddr, SMC_IO_EXTENT, dev->name)) return -EBUSY; + + /* First, see if the high byte is 0x33 */ + bank = SMC_inw( ioaddr + BANK_SELECT ); + 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; + } + +#ifdef CONFIG_ISA + /* 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 ); + if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) ) + { + printk("CARDNAME: IOADDR %x doesn't match configuration (%x)." + "Probably not a SMC chip\n", + ioaddr, 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; + } +#endif + + /* 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("CARDNAME: IO %x: Unrecognized revision register:" + " %x, Contact author. \n", + ioaddr, revision_register ); + retval = -ENODEV; + goto err_out; + } + + /* 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. */ + + 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 ) + { + word address; + + address = SMC_inw( ioaddr + ADDR0_REG + i ); + dev->dev_addr[ i + 1] = address >> 8; + dev->dev_addr[ i ] = address & 0xFF; + } + +#if defined(CONFIG_SA1100_DNP1110) || defined(CONFIG_SA1100_PNP1110) + for (i=0; i<6; i++) + if ((dev->dev_addr[i] != 0) && (dev->dev_addr[i] != 0xff)) + break; + if (i==6) { + dev->dev_addr[0] = *(dnp1110_mac+0); + dev->dev_addr[1] = *(dnp1110_mac+1); + dev->dev_addr[2] = *(dnp1110_mac+2); + dev->dev_addr[3] = *(dnp1110_mac+3); + dev->dev_addr[4] = *(dnp1110_mac+4); + dev->dev_addr[5] = *(dnp1110_mac+5); + } + + for (i=0; i<6; i++) + if ((dev->dev_addr[i] != 0) && (dev->dev_addr[i] != 0xff)) + break; + if (i==6) { + printk(CARDNAME": uninitialized MAC address detected -- using a dummy one\n"); + for (i=0;i<6;i++) + dev->dev_addr[i] = i; + } +#endif + /* get the memory information */ + + SMC_SELECT_BANK( 0 ); + memory_info_register = SMC_inw( ioaddr + MIR_REG ); + memory = memory_info_register & (word)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 ); + + /* + . 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!!!! + . + */ +#ifndef NO_AUTOPROBE + 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; + } +#else +#ifdef CONFIG_SA1100_DNP1110 + dev->irq = 5; +#endif +#ifdef CONFIG_SA1100_PNP1110 + dev->irq = 0; +#endif +#endif + + 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; + } + + /* now, print out the card info, in a short format.. */ + + printk("%s: %s(rev:%d) at %#3x IRQ:%d MEMSIZE:%d NOWAIT:%d\n%s: ", + dev->name, + version_string, revision_register & 0xF, ioaddr, dev->irq, + memory, dev->dma, dev->name); + /* + . 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] ); + + + /* 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)); + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + + /* 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); + kfree (dev->priv); + dev->priv = NULL; + goto err_out; + } + + 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 = (struct smc_local *)dev->priv; + lp->ChipID = (revision_register >> 4) & 0xF; + lp->ChipRev = revision_register & 0xF; + + return 0; + +err_out: + release_region (ioaddr, SMC_IO_EXTENT); + return retval; +} + +#if SMC_DEBUG > 2 +static void print_packet( byte * buf, int length ) +{ +#if 1 + 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 ++ ) { + byte a, b; + + a = *(buf ++ ); + b = *(buf ++ ); + printk("%02x%02x ", a, b ); + } + printk("\n"); + } + for ( i = 0; i < remainder/2 ; i++ ) { + byte a, b; + + a = *(buf ++ ); + b = *(buf ++ ); + printk("%02x%02x ", a, b ); + } + printk("\n"); +#endif +#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; + int ioaddr = dev->base_addr; + int i; /* used to set hw ethernet address */ + + PRINTK2("%s:smc_open\n", dev->name); + + /* clear out all the junk that was put here before... */ + memset(dev->priv, 0, sizeof(struct smc_local)); + + netif_start_queue(dev); + + // 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 = 1; + lp->ctl_aspeed = 100; + lp->ctl_lnkfail = 1; + lp->ctl_forcol = 0; + lp->ctl_filtcar = 0; + + /* reset the hardware */ + + smc_reset( dev ); + smc_enable( dev ); + + /* Configure the PHY */ + smc_phy_configure(dev); + + /* + 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 ) { + word address; + + address = dev->dev_addr[ i + 1 ] << 8 ; + address |= dev->dev_addr[ i ]; + SMC_outw( address, ioaddr + ADDR0_REG + i ); + } + +#ifdef CONFIG_SYSCTL + smc_sysctl_register(dev); +#endif /* CONFIG_SYSCTL */ + + 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 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_WARNING "%s: transmit timed out, %s?\n",dev->name, tx_done(dev) ? "IRQ conflict" :"network cable problem"); + /* "kick" the adaptor */ + smc_reset( dev ); + smc_enable( dev ); + + /* Reconfigure the PHY */ + smc_phy_configure(dev); + + dev->trans_start = jiffies; + /* clear anything saved */ + ((struct smc_local *)dev->priv)->saved_skb = NULL; + + netif_wake_queue(dev); +} + +/*-------------------------------------------------------------------- + . + . 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; + int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *)dev->priv; + + byte status; + word card_stats; + byte mask; + int timeout; + /* state registers */ + word saved_bank; + word 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; + } + +/* will Linux let this happen ?? If not, this costs some speed + if ( dev->interrupt ) { + printk(KERN_WARNING "%s: interrupt inside interrupt.\n", + dev->name); + return; + } + + dev->interrupt = 1; */ + + 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 */ +#ifndef ONLY_16_BIT + SMC_outb( 0, ioaddr + IM_REG ); +#else + SMC_outw( 0, ioaddr + INT_REG ); +#endif + + /* set a timeout value, so I don't stay here forever */ + timeout = 4; + + 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). */ + PRINTK2(KERN_WARNING + "%s: Receive Interrupt\n", dev->name); + smc_rcv(dev); + } else if (status & IM_TX_INT ) { + PRINTK2(KERN_WARNING "%s: TX ERROR handled\n", + dev->name); + smc_tx(dev); + // Acknowledge the interrupt + SMC_outb(IM_TX_INT, ioaddr + INT_REG ); + } else if (status & IM_TX_EMPTY_INT ) { + /* 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 + SMC_SELECT_BANK( 2 ); + PRINTK2(KERN_WARNING "%s: TX_BUFFER_EMPTY handled\n", + dev->name); + // Acknowledge the interrupt + SMC_outb( IM_TX_EMPTY_INT, ioaddr + INT_REG ); + mask &= ~IM_TX_EMPTY_INT; + lp->stats.tx_packets += lp->packets_waiting; + lp->packets_waiting = 0; + + } 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; + + smc_hardware_send_packet( dev ); + + /* enable xmit interrupts based on this */ + mask |= ( IM_TX_EMPTY_INT | IM_TX_INT ); + + /* and let the card send more packets to me */ + 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 ); + } else if (status & IM_EPH_INT ) { + PRINTK("%s: UNSUPPORTED: EPH INTERRUPT \n", + dev->name); + } else if (status & IM_MDINT ) { + smc_phy_interrupt(dev); + // Acknowledge the interrupt + SMC_outb(IM_MDINT, 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 ); + +#ifndef ONLY_16_BIT + SMC_outb( mask, ioaddr + IM_REG ); +#else + SMC_outw( (mask<<8), ioaddr + INT_REG ); +#endif + + PRINTK3( KERN_WARNING "%s: MASK is now %x \n", dev->name, mask); + SMC_outw( saved_pointer, ioaddr + PTR_REG ); + + SMC_SELECT_BANK( saved_bank ); + + //dev->interrupt = 0; + PRINTK3("%s: Interrupt done\n", dev->name); + return; +} + +/*------------------------------------------------------------- + . + . 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; + int ioaddr = dev->base_addr; + int packet_number; + word status; + word packet_length; + + 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; + } + + /* 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 */ + status = SMC_inw( ioaddr + DATA_REG ); + packet_length = SMC_inw( ioaddr + DATA_REG ); + + packet_length &= 0x07ff; /* mask off top bits */ + + PRINTK2("RCV: STATUS %4x LENGTH %4x\n", status, packet_length ); + + if ( !(status & RS_ERRORS ) ){ + /* do stuff to make a new packet */ + struct sk_buff * skb; + byte * data; + + /* set multicast stats */ + if ( status & RS_MULTICAST ) + lp->stats.multicast++; + + // Allocate enough memory for entire receive frame, to be safe + skb = dev_alloc_skb( packet_length ); + + /* Adjust for having already read the first two words */ + packet_length -= 4; + + if ( skb == NULL ) { + printk(KERN_NOTICE "%s: Low memory, packet dropped.\n", + dev->name); + lp->stats.rx_dropped++; + goto done; + } + + /* + ! This should work without alignment, but it could be + ! in the worse case + */ + /* TODO: Should I use 32bit alignment here ? */ + skb_reserve( skb, 2 ); /* 16 bit alignment */ + + skb->dev = dev; + + /* => + 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) + data = skb_put( skb, packet_length + 1 ); + else + data = skb_put( skb, packet_length); + + } + else + { + // set odd length for bug in LAN91C111, REV A + // which never sets RS_ODDFRAME + data = skb_put( skb, packet_length + 1 ); + } + +#ifdef USE_32_BIT + PRINTK3(" Reading %d dwords (and %d bytes) \n", + packet_length >> 2, packet_length & 3 ); + /* QUESTION: Like in the TX routine, do I want + to send the DWORDs or the bytes first, or some + mixture. A mixture might improve already slow PIO + performance */ + SMC_insl(ioaddr + DATA_REG , data, packet_length >> 2 ); + /* read the left over bytes */ + SMC_insb( ioaddr + DATA_REG, data + (packet_length & 0xFFFFFC), + packet_length & 0x3 ); +#else + PRINTK3(" Reading %d words and %d byte(s) \n", + (packet_length >> 1 ), packet_length & 1 ); + SMC_insw(ioaddr + DATA_REG , data, packet_length >> 1); + +#endif // USE_32_BIT + +#if SMC_DEBUG > 2 + printk("Receiving Packet\n"); + print_packet( data, packet_length ); +#endif + + skb->protocol = eth_type_trans(skb, dev ); + netif_rx(skb); + lp->stats.rx_packets++; + } else { + /* error ... */ + 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++; + } + + while ( SMC_inw( ioaddr + MMU_CMD_REG ) & MC_BUSY ) + udelay(1); // Wait until not busy +done: + /* error or good, tell the card to get rid of this packet */ + SMC_outw( MC_RELEASE, ioaddr + MMU_CMD_REG ); + + + return; +} + + +/************************************************************************* + . 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 ) +{ + int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *)dev->priv; + byte saved_packet; + byte packet_no; + word 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 */ +#ifndef ONLY_16_BIT + SMC_outb( packet_no, ioaddr + PN_REG ); +#else + SMC_outw( packet_no, ioaddr + PN_REG ); +#endif + + /* read the first word (status word) from this packet */ + SMC_outw( PTR_AUTOINC | PTR_READ, ioaddr + PTR_REG ); + + tx_status = SMC_inw( ioaddr + DATA_REG ); + PRINTK3("%s: TX DONE STATUS: %4x \n", dev->name, tx_status); + + 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 + + if ( tx_status & TS_SUCCESS ) { + printk("%s: Successful packet caused interrupt \n", dev->name); + } + /* re-enable transmit */ + SMC_SELECT_BANK( 0 ); + SMC_outw( SMC_inw( ioaddr + TCR_REG ) | TCR_ENABLE, ioaddr + TCR_REG ); + + /* kill the packet */ + SMC_SELECT_BANK( 2 ); + SMC_outw( MC_FREEPKT, ioaddr + MMU_CMD_REG ); + + /* one less packet waiting for me */ + lp->packets_waiting--; + + /* 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 ); + +#ifndef ONLY_16_BIT + SMC_outb( saved_packet, ioaddr + PN_REG ); +#else + SMC_outw( saved_packet, ioaddr + PN_REG ); +#endif + 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 ); + + 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) +{ + 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; +int io = 0; +int irq = 0; +int nowait = 0; + +MODULE_PARM(io, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(nowait, "i"); + +/*------------------------------------------------------------ + . Module initialization function + .-------------------------------------------------------------*/ +static int __init init_smc91111(void) +{ + int result; + + PRINTK2("CARDNAME:init_module\n"); +#ifdef MODULE + if (io == 0) + printk(KERN_WARNING + CARDNAME": You shouldn't use auto-probing with insmod!\n" ); +#endif + /* copy the parameters from insmod into the device structure */ + devSMC91111.base_addr = io; + devSMC91111.irq = irq; + devSMC91111.dma = nowait; // Use DMA field for nowait + devSMC91111.init = smc_init;/* Kernel 2.4 Changes - Pramod */ + if ((result = register_netdev(&devSMC91111)) != 0) + return result; + + return 0; +} + +/*------------------------------------------------------------ + . Cleanup when module is removed with rmmod + .-------------------------------------------------------------*/ +static void __exit cleanup_smc91111(void) +{ + /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ + unregister_netdev(&devSMC91111); + + free_irq(devSMC91111.irq, &devSMC91111); + release_region(devSMC91111.base_addr, SMC_IO_EXTENT); + + if (devSMC91111.priv) + kfree(devSMC91111.priv); /* Kernel 2.4 Changes - Pramod */ +} + +module_init(init_smc91111); +module_exit(cleanup_smc91111); + + +#ifdef CONFIG_SYSCTL + + +/*------------------------------------------------------------ + . Modify a bit in the LAN91C111 register set + .-------------------------------------------------------------*/ +static word smc_modify_regbit(int bank, int ioaddr, int reg, + unsigned int bit, int val) +{ + word regval; + + SMC_SELECT_BANK( bank ); + + regval = SMC_inw( ioaddr+reg ); + if (val) + regval |= bit; + else + regval &= ~bit; + + SMC_outw( regval, ioaddr ); + return(regval); +} + + +/*------------------------------------------------------------ + . Retrieve a bit in the LAN91C111 register set + .-------------------------------------------------------------*/ +static int smc_get_regbit(int bank, 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 (word access only) + .-------------------------------------------------------------*/ +static void smc_modify_reg(int bank, int ioaddr, int reg, word val) +{ + SMC_SELECT_BANK( bank ); + SMC_outw( val, ioaddr+reg ); +} + + +/*------------------------------------------------------------ + . Retrieve a LAN91C111 register (word access only) + .-------------------------------------------------------------*/ +static int smc_get_reg(int bank, 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; + 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 = ( (word)smc_get_reg(0, ioaddr, MIR_REG) >> 8 ) + * LAN91C111_MEMORY_MULTIPLIER; + break; + + + case CTL_SMC_TOTMEM: + *valp = ( smc_get_reg(0, ioaddr, MIR_REG) & (word)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) & (word)0x0007; + break; + + case CTL_SMC_LEDB: + *valp = (lp->rpc_cur_mode >> RPC_LSXB_SHFT) & (word)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 &= ~(word)(0x07<rpc_cur_mode |= (word)(val<rpc_cur_mode); + break; + + case CTL_SMC_LEDB: + val &= 0x07; // Restrict to 3 ls bits + lp->rpc_cur_mode &= ~(word)(0x07<rpc_cur_mode |= (word)(val<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(byte* 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 word smc_read_phy_register(int ioaddr, byte phyaddr, byte phyreg) +{ + int oldBank; + int i; + byte mask; + word mii_reg; + byte bits[64]; + int clk_idx = 0; + int input_idx; + word 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 = (byte)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 = (byte)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(int ioaddr, + byte phyaddr, byte phyreg, word phydata) +{ + int oldBank; + int i; + word mask; + word mii_reg; + byte 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 = (byte)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 = (byte)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; + int ioaddr = dev->base_addr; + word phy_id1 = 0; + word phy_id2 = 0; + 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) +{ +/* FIX ME - we can`t schedule in interrupt - second doesen't work */ +#if 1 + mdelay(ms); +#else + if (!in_interrupt()) + { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(1 + ms * HZ / 1000); + } + else + { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1 + ms * HZ / 1000); + current->state = TASK_RUNNING; + } +#endif +} + +/*------------------------------------------------------------ + . Sets the PHY to a configuration as determined by the user + .-------------------------------------------------------------*/ +static int smc_phy_fixed(struct net_device* dev) +{ + int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *)dev->priv; + byte phyaddr = lp->phyaddr; + word my_fixed_caps; + word 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) +{ + int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *)dev->priv; + int timeout; + byte phyaddr; + word my_phy_caps; // My PHY capabilities + word my_ad_caps; // My Advertised capabilities + word 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) +{ + int ioaddr = dev->base_addr; + struct smc_local *lp = (struct smc_local *)dev->priv; + byte phyaddr = lp->phyaddr; + word 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 -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/drivers/net/smc91111.h linux-2.4.18-rmk7-ptx3/drivers/net/smc91111.h --- linux-2.4.18-rmk7/drivers/net/smc91111.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.18-rmk7-ptx3/drivers/net/smc91111.h Wed Aug 28 11:17:55 2002 @@ -0,0 +1,722 @@ +/*------------------------------------------------------------------------ + . 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 ) + . Marco Hasewinkel ( mha@ist1.de ) ( m.hasewinkel@web.de ) + . + . History + . 05/02/02 Marco Hasewinkel, ARM Linux port for SSV PNP/1110-3V + . 02/05/02 Marco Hasewinkel, ARM Linux port for SSV DNP/1110-3V + . 03/16/01 Daris A Nevil Modified for use with LAN91C111 device + . + ---------------------------------------------------------------------------*/ +#ifndef _SMC91111_H_ +#define _SMC91111_H_ + +/* I want some simple types */ + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long int dword; + +#ifdef CONFIG_SA1100_DNP1110 +/* + * SSV DNP/1110 have only 16 Bit PCMCIA access on Socket 0 + */ +#undef CONFIG_ISA +#define SMC_IO_EXTENT 16 +#define ONLY_16_BIT +#undef USE_32_BIT +#define NO_AUTOPROBE +#define POWER_DOWN + + +#define SMC_inw(r) (*((volatile word *)((r)))) +#define SMC_inb(r) (((r)&1) ? SMC_inw((r)&~1)>>8 : SMC_inw(r)&0xFF) + +#define SMC_outw(d,r) (*((volatile word *)((r))) = d) +#define SMC_outb(d,r) ({ word __d = (byte)(d);\ + word __w;\ + unsigned int __flags;\ + save_flags_cli(__flags);\ + __w = SMC_inw((r)&~1);\ + __w &= ((r)&1) ? 0x00FF : 0xFF00;\ + __w |= ((r)&1) ? __d<<8 : __d;\ + SMC_outw(__w,(r)&~1);\ + restore_flags(__flags);\ + }) + +#define SMC_outsw(r,b,l) outsw((r), (b), (l)) +#define SMC_insw(r,b,l) insw((r), (b), (l)) + +#endif + +#ifdef CONFIG_SA1100_PNP1110 +/* + * SSV PNP/1110 have only 16 Bit PCMCIA access on Socket 0 + */ +#undef CONFIG_ISA +#define SMC_IO_EXTENT 16 +#define ONLY_16_BIT +#undef USE_32_BIT +#define NO_AUTOPROBE +#define POWER_DOWN + + +#define SMC_inw(r) (*((volatile word *)((r)))) +#define SMC_inb(r) (((r)&1) ? SMC_inw((r)&~1)>>8 : SMC_inw(r)&0xFF) + +#define SMC_outw(d,r) (*((volatile word *)((r))) = d) +#define SMC_outb(d,r) ({ word __d = (byte)(d);\ + word __w;\ + unsigned int __flags;\ + save_flags_cli(__flags);\ + __w = SMC_inw((r)&~1);\ + __w &= ((r)&1) ? 0x00FF : 0xFF00;\ + __w |= ((r)&1) ? __d<<8 : __d;\ + SMC_outw(__w,(r)&~1);\ + restore_flags(__flags);\ + }) + +#if 0 +#define SMC_outsw(r,b,l) outsw((r), (b), (l)) +#else + /* need PCMCIA unlock cycle for anti LCD flicker */ +#define SMC_outsw(r,b,l) ({ int __i ; \ + dword __dummy; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + SMC_outw(*(__b2 + __i), r); \ + __dummy = Ser0UDCAR; \ + }; \ + }) +#endif + +#if 0 +#define SMC_insw(r,b,l) insw((r), (b), (l)) +#else + /* need PCMCIA unlock cycle for anti LCD flicker */ +#define SMC_insw(r,b,l) ({ int __i ; \ + dword __dummy; \ + word *__b2; \ + __b2 = (word *) b; \ + for (__i = 0; __i < l; __i++) { \ + *(__b2 + __i) = SMC_inw(r); \ + __dummy = Ser0UDCAR; \ + }; \ + }) +#endif + +#endif + +#ifdef CONFIG_ISA +/* + * ISA access + */ +#define SMC_IO_EXTENT 16 +#undef ONLY_16_BIT +#define USE_32_BIT +#undef NO_AUTOPROBE +#define POWER_DOWN + + +#define SMC_inb(r) inb((r)) +#define SMC_inw(r) inw((r)) +#define SMC_inl(r) inl((r)) +#define SMC_insb(r,b,l) insb((r),(b),(l)) +#define SMC_insw(r,b,l) insw((r),(b),(l)) +#define SMC_insl(r,b,l) insl((r),(b),(l)) + +#define SMC_outb(d,r) outb((d),(r)) +#define SMC_outw(d,r) outw((d),(r)) +#define SMC_outl(d,r) outl((d),(r)) +#define SMC_outsw(r,b,l) outsw((r),(b),(l)) +#define SMC_outsl(r,b,l) outsl((r),(b),(l)) +#endif + +/*--------------------------------------------------------------- + . + . 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 << RPC_LSXA_SHFT) | (RPC_LED_FD << 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 + + +// 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 + + + +/*------------------------------------------------------------------------- + . 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 ); } + +/* this enables an interrupt in the interrupt mask register */ +#ifndef ONLY_16_BIT + +#define SMC_ENABLE_INT(x) {\ + unsigned char mask;\ + unsigned int flags;\ + save_flags(flags);\ + cli();\ + SMC_SELECT_BANK(2);\ + mask = SMC_inb( ioaddr + IM_REG );\ + mask |= (x);\ + SMC_outb( mask, ioaddr + IM_REG );\ + restore_flags(flags);\ +} + +#else + +#define SMC_ENABLE_INT(x) {\ + unsigned short mask = 0;\ + unsigned int flags;\ + save_flags(flags);\ + cli();\ + SMC_SELECT_BANK(2);\ + mask = SMC_inw( ioaddr + INT_REG );\ + mask |= ((x)<<8);\ + mask &= 0xff00;\ + SMC_outw( mask, ioaddr + INT_REG );\ + restore_flags(flags);\ +} + +#endif +/* this disables an interrupt from the interrupt mask register */ +#ifndef ONLY_16_BIT + +#define SMC_DISABLE_INT(x) {\ + unsigned char mask;\ + unsigned int flags;\ + save_flags(flags);\ + cli();\ + SMC_SELECT_BANK(2);\ + mask = SMC_inb( ioaddr + IM_REG );\ + mask &= ~(x);\ + SMC_outb( mask, ioaddr + IM_REG );\ + restore_flags(flags);\ +} + +#else + +#define SMC_DISABLE_INT(x) {\ + unsigned short mask;\ + unsigned int flags;\ + save_flags(flags);\ + cli();\ + SMC_SELECT_BANK(2);\ + mask = SMC_inw( ioaddr + INT_REG );\ + mask &= (~(x)<<8);\ + mask &= 0xff00;\ + SMC_outw( mask, ioaddr + INT_REG );\ + restore_flags(flags);\ +} + +#endif +/*---------------------------------------------------------------------- + . 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 -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/include/asm-arm/arch-sa1100/dnp1110.h linux-2.4.18-rmk7-ptx3/include/asm-arm/arch-sa1100/dnp1110.h --- linux-2.4.18-rmk7/include/asm-arm/arch-sa1100/dnp1110.h Thu Jan 1 01:00:00 1970 +++ linux-2.4.18-rmk7-ptx3/include/asm-arm/arch-sa1100/dnp1110.h Sun Jul 14 13:46:28 2002 @@ -0,0 +1,13 @@ +/* + * linux/include/asm-arm/arch-sa1100/dnp1110.h + * + * Created 2001/08/18 by mha@ssv + * + * This file contains the hardware specific definitions for DNP/1110 + * + */ + +#ifndef __ASM_ARCH_HARDWARE_H +#error "include instead" +#endif + diff -X kernel-patches/dontdiff -urN linux-2.4.18-rmk7/include/asm-arm/arch-sa1100/hardware.h linux-2.4.18-rmk7-ptx3/include/asm-arm/arch-sa1100/hardware.h --- linux-2.4.18-rmk7/include/asm-arm/arch-sa1100/hardware.h Sun Jun 30 23:15:05 2002 +++ linux-2.4.18-rmk7-ptx3/include/asm-arm/arch-sa1100/hardware.h Wed Aug 28 11:17:20 2002 @@ -120,6 +120,10 @@ #include "badge4.h" +#ifdef CONFIG_SA1100_DNP1110 +#include "dnp1110.h" +#endif + #include "frodo.h" #include "h3600.h"