diff -urN -X kernel-patches/dontdiff linux-2.4.18-pre4/CREDITS linux-2.4.18-pre4-elan/CREDITS --- linux-2.4.18-pre4/CREDITS Tue Jan 22 17:11:17 2002 +++ linux-2.4.18-pre4-elan/CREDITS Sun Jan 20 21:35:37 2002 @@ -2659,6 +2659,16 @@ S: Oldenburg S: Germany +N: Robert Schwebel +E: robert@schwebel.de +W: http://www.schwebel.de +D: Embedded hacker and book author, +D: AMD Elan support for Linux +S: Pengutronix +S: Braunschweiger Strasse 79 +S: 31134 Hildesheim +S: Germany + N: Darren Senn E: sinster@darkwater.com D: Whatever I notice needs doing (so far: itimers, /proc) diff -urN -X kernel-patches/dontdiff linux-2.4.18-pre4/Documentation/Configure.help linux-2.4.18-pre4-elan/Documentation/Configure.help --- linux-2.4.18-pre4/Documentation/Configure.help Tue Jan 22 17:11:18 2002 +++ linux-2.4.18-pre4-elan/Documentation/Configure.help Tue Jan 22 17:18:56 2002 @@ -3172,6 +3172,22 @@ The module will be called mwave.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +/proc/elan/osc programmable oscillator support for AMD ELAN +CONFIG_ELAN_OSC + The AMD Elan family has a programmable oscillator for the core frequency. + This driver supports an interface in /proc/elan/osc which you can use + to change the clock rate. + + !!! DON'T OVERCLOCK THE PROCESSOR WITH RATES HIGHER THAN !!! + !!! WHAT IT IS SPECIFIED FOR! YOU FIND THE MAXIMAL CLOCK !!! + !!! RATE ON THE PROCESSOR CHIP. IF YOU IGNORE THIS THE !!! + !!! PROCESSOR CAN BE DAMAGED PERMANENTLY! !!! + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called elan-osc.o. If you want to compile it as + a module, say M here. + /dev/agpgart (AGP Support) CONFIG_AGP AGP (Accelerated Graphics Port) is a bus system mainly used to @@ -3813,6 +3829,7 @@ - "Pentium-4" for the Intel Pentium 4. - "K6" for the AMD K6, K6-II and K6-III (aka K6-3D). - "Athlon" for the AMD K7 family (Athlon/Duron/Thunderbird). + - "Elan" for the AMD Elan family (Elan SC400/SC410). - "Crusoe" for the Transmeta Crusoe series. - "Winchip-C6" for original IDT Winchip. - "Winchip-2" for IDT Winchip 2. diff -urN -X kernel-patches/dontdiff linux-2.4.18-pre4/arch/i386/boot/setup.S linux-2.4.18-pre4-elan/arch/i386/boot/setup.S --- linux-2.4.18-pre4/arch/i386/boot/setup.S Tue Jan 22 17:11:18 2002 +++ linux-2.4.18-pre4-elan/arch/i386/boot/setup.S Mon Jan 21 07:44:20 2002 @@ -42,6 +42,9 @@ * if CX/DX have been changed in the e801 call and if so use AX/BX . * Michael Miller, April 2001 * + * New A20 code ported from SYSLINUX by H. Peter Anvin. AMD Elan bugfixes + * by Robert Schwebel, December 2001 + * */ #include @@ -651,7 +654,18 @@ # # Enable A20. This is at the very best an annoying procedure. # A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin. +# AMD Elan bug fix by Robert Schwebel. # + +#if defined(CONFIG_MELAN) + movb $0x02, %al # alternate A20 gate + outb %al, $0x92 # this works on SC410/SC520 +a20_elan_wait: + call a20_test + jz a20_elan_wait + jmp a20_done +#endif + A20_TEST_LOOPS = 32 # Iterations per wait A20_ENABLE_LOOPS = 255 # Total loops to try diff -urN -X kernel-patches/dontdiff linux-2.4.18-pre4/arch/i386/config.in linux-2.4.18-pre4-elan/arch/i386/config.in --- linux-2.4.18-pre4/arch/i386/config.in Fri Dec 21 18:41:53 2001 +++ linux-2.4.18-pre4-elan/arch/i386/config.in Sun Jan 20 21:35:38 2002 @@ -37,6 +37,7 @@ Pentium-4 CONFIG_MPENTIUM4 \ K6/K6-II/K6-III CONFIG_MK6 \ Athlon/Duron/K7 CONFIG_MK7 \ + Elan CONFIG_MELAN \ Crusoe CONFIG_MCRUSOE \ Winchip-C6 CONFIG_MWINCHIPC6 \ Winchip-2 CONFIG_MWINCHIP2 \ @@ -125,6 +126,11 @@ define_bool CONFIG_X86_USE_3DNOW y define_bool CONFIG_X86_PGE y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y +fi +if [ "$CONFIG_MELAN" = "y" ]; then + define_int CONFIG_X86_L1_CACHE_SHIFT 4 + define_bool CONFIG_X86_USE_STRING_486 y + define_bool CONFIG_X86_ALIGNMENT_16 y fi if [ "$CONFIG_MCYRIXIII" = "y" ]; then define_int CONFIG_X86_L1_CACHE_SHIFT 5 diff -urN -X kernel-patches/dontdiff linux-2.4.18-pre4/arch/i386/kernel/setup.c linux-2.4.18-pre4-elan/arch/i386/kernel/setup.c --- linux-2.4.18-pre4/arch/i386/kernel/setup.c Tue Jan 22 17:11:19 2002 +++ linux-2.4.18-pre4-elan/arch/i386/kernel/setup.c Sun Jan 20 21:35:38 2002 @@ -329,6 +329,10 @@ { "dma2", 0xc0, 0xdf, IORESOURCE_BUSY }, { "fpu", 0xf0, 0xff, IORESOURCE_BUSY } }; +#ifdef CONFIG_ELAN +standard_io_resources[1] = { "pic1", 0x20, 0x21, IORESOURCE_BUSY }; +standard_io_resources[5] = { "pic2", 0xa0, 0xa1, IORESOURCE_BUSY }; +#endif #define STANDARD_IO_RESOURCES (sizeof(standard_io_resources)/sizeof(struct resource)) diff -urN -X kernel-patches/dontdiff linux-2.4.18-pre4/drivers/char/Config.in linux-2.4.18-pre4-elan/drivers/char/Config.in --- linux-2.4.18-pre4/drivers/char/Config.in Tue Jan 22 17:11:22 2002 +++ linux-2.4.18-pre4-elan/drivers/char/Config.in Tue Jan 22 22:51:43 2002 @@ -240,4 +240,8 @@ tristate 'ACP Modem (Mwave) support' CONFIG_MWAVE fi +if [ "$CONFIG_MELAN" = "y" -a "$CONFIG_PROC_FS" = "y" ]; then + tristate '/proc/elan-osc: AMD Elan programmable oscillator' CONFIG_ELAN_OSC +fi + endmenu diff -urN -X kernel-patches/dontdiff linux-2.4.18-pre4/drivers/char/Makefile linux-2.4.18-pre4-elan/drivers/char/Makefile --- linux-2.4.18-pre4/drivers/char/Makefile Tue Jan 22 17:11:22 2002 +++ linux-2.4.18-pre4-elan/drivers/char/Makefile Tue Jan 22 17:24:37 2002 @@ -164,6 +164,7 @@ obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_SERIAL_TX3912) += generic_serial.o serial_tx3912.o +obj-$(CONFIG_ELAN_OSC) += elan-osc.o subdir-$(CONFIG_RIO) += rio subdir-$(CONFIG_INPUT) += joystick diff -urN -X kernel-patches/dontdiff linux-2.4.18-pre4/drivers/char/elan-osc.c linux-2.4.18-pre4-elan/drivers/char/elan-osc.c --- linux-2.4.18-pre4/drivers/char/elan-osc.c Thu Jan 1 01:00:00 1970 +++ linux-2.4.18-pre4-elan/drivers/char/elan-osc.c Tue Jan 22 22:48:36 2002 @@ -0,0 +1,219 @@ +/* + * elan-osc: /proc Interface for the programmable oscillator + * on AMD's Elan family of embedded processors + * + * (c) Copyright 2002 Sven Geggus , + * Robert Schwebel . + * + * All Rights Reserved. + * + * 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. + * + * 2002-01-22: - ported to 2.4.18-pre4 by Robert Schwebel + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define LPS_PREC 8 +#define SAFE_FREQUENCY 33; + +#define REG_CSCIR 0x22 +#define REG_CSCDR 0x23 + +static int ElanCPUclock = SAFE_FREQUENCY; +static int proc_permissions = 0600; +MODULE_LICENSE("GPL"); +MODULE_AUTHOR( + "Sven Geggus , Robert Schwebel " +); +MODULE_DESCRIPTION( + "/proc/driver/elan/osc: programmable oscillator for AMD Elan processors" +); + +MODULE_PARM(proc_permissions,"i"); +MODULE_PARM_DESC(proc_permissions, + "Permissions for /proc/driver/elan-osc (default=0600)" +); + +EXPORT_NO_SYMBOLS; + + +struct s_elan_multiplier { + int clock; + int val40h; /* PMU Force Mode register */ + int val80h; /* CPU Clock Speed Register */ +}; + +struct s_elan_multiplier elan_multiplier[] = { + {1, 0x02, 0x18}, + {2, 0x02, 0x10}, + {4, 0x02, 0x08}, + {8, 0x00, 0x00}, + {16, 0x00, 0x02}, + {33, 0x00, 0x04}, + {66, 0x01, 0x04}, + {99, 0x01, 0x05} +}; + + +/* This is stolen from main.c; however it's __init there so we can't use it */ +static void elan_osc_calibrate_delay(int MHz) +{ + unsigned long ticks, loopbit; + int lps_precision = LPS_PREC; + + loops_per_jiffy = (1<<12); + + printk(KERN_INFO "Recalibrating delay loop... "); + while (loops_per_jiffy <<= 1) { + /* wait for "start of" clock tick */ + ticks = jiffies; + while (ticks == jiffies); /* nothing */ + ticks = jiffies; + __delay(loops_per_jiffy); + ticks = jiffies - ticks; + if (ticks) break; + } + + /* Do a binary approximation to get loops_per_jiffy set */ + /* to equal one clock (up to lps_precision bits) */ + loops_per_jiffy >>= 1; + loopbit = loops_per_jiffy; + while ( lps_precision-- && (loopbit >>= 1) ) { + loops_per_jiffy |= loopbit; + ticks = jiffies; + while (ticks == jiffies); + ticks = jiffies; + __delay(loops_per_jiffy); + if (jiffies != ticks) /* longer than 1 tick */ + loops_per_jiffy &= ~loopbit; + } + + /* Round the value and print it */ + printk(KERN_INFO "%lu.%02lu BogoMIPS for %d Mhz\n", + loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ)) % 100, MHz); +} + +int elan_osc_set_mhz(int MHz) { + + int i; + + /* search table entry for given Mhz value */ + for (i=0; i<8; i++) { + if (elan_multiplier[i].clock==MHz) break; + } + if (i==8) { + printk(KERN_INFO "Could not set CPU Clock Rate to %d MHz\n",MHz); + return(1); + } + + /* + * Access to the Elan's internal registers is indexed via + * 0x22: Chip Setup & Control Register Index Register (CSCI) + * 0x23: Chip Setup & Control Register Data Register (CSCD) + * + */ + + /* first, we have to set the PMU Force Mode Register */ + cli(); + outb_p(0x40,REG_CSCIR); + outb_p(0x00,REG_CSCDR); + sti(); + udelay(1000); + + cli(); + + /* now, set the CPU clock speed register (0x80) */ + outb_p(0x80,REG_CSCIR); + outb_p(elan_multiplier[i].val80h,REG_CSCDR); + + /* now, the PMU Force Mode Register (0x40) */ + outb_p(0x40,REG_CSCIR); + outb_p(elan_multiplier[i].val40h,REG_CSCDR); + udelay(10000); + sti(); + + elan_osc_calibrate_delay(MHz); + ElanCPUclock=MHz; + + return(0); +}; + +struct proc_dir_entry *proc_entry; + +/* + * The read and write functions for our /proc entry + */ + +ssize_t elan_osc_readproc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int len; + + sprintf(page,"%i\n",ElanCPUclock); + len = strlen(page); + if (len <= off+count) *eof =1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + + return len; +} + +int elan_osc_writeproc(struct file *file, const char *buf, + unsigned long count, void *data) +{ + int val; + + val = simple_strtoul(buf,NULL,10); + + if (val != ElanCPUclock) { + if ((val>0)&&(val<100)) { + elan_osc_set_mhz(val); + } + } + + return count; +} + + +int elan_osc_init(void) { + + printk(KERN_INFO "Resetting CPU frequency to %i MHz\n", ElanCPUclock); + elan_osc_set_mhz(ElanCPUclock); + + printk(KERN_INFO "creating /proc/driver/elan-osc entry..."); + proc_entry = create_proc_entry("driver/elan-osc",proc_permissions,NULL); + proc_entry->read_proc = elan_osc_readproc; + proc_entry->write_proc = elan_osc_writeproc; + printk(KERN_INFO "done.\n"); + + return 0; + +} + +void elan_osc_exit(void) { + + printk(KERN_INFO "removing /proc/driver/elan-osc entry\n"); + remove_proc_entry("osc",NULL); + +} + +module_init(elan_osc_init); +module_exit(elan_osc_exit); diff -urN -X kernel-patches/dontdiff linux-2.4.18-pre4/drivers/char/serial.c linux-2.4.18-pre4-elan/drivers/char/serial.c --- linux-2.4.18-pre4/drivers/char/serial.c Tue Jan 22 17:11:23 2002 +++ linux-2.4.18-pre4-elan/drivers/char/serial.c Tue Jan 22 14:54:30 2002 @@ -57,6 +57,10 @@ * 10/00: add in optional software flow control for serial console. * Kanoj Sarcar (Modified by Theodore Ts'o) * + * 12/01: Fix for AMD Elan bug in transmit irq routine, by + * Christer Weinigel , + * Robert Schwebel + * Juergen Beisert */ static char *serial_version = "5.05c"; @@ -802,6 +806,7 @@ static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs) { int status; + int iir; struct async_struct * info; int pass_counter = 0; struct async_struct *end_mark = 0; @@ -825,12 +830,24 @@ #endif do { - if (!info->tty || - (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) { - if (!end_mark) - end_mark = info; + + /* + * It seems to be important in a SC520 systems to read + * the UART_IIR register only once per loop. But I think + * this small correction does not disturb the normal + * implementation. + */ + + /* Something to test? */ + /* Or is this UART the source of the interrupt? */ + + if (!info->tty || + ((iir=serial_inp(info, UART_IIR)) & 0x01)) { + if (!end_mark) /* last reached? */ + end_mark = info; /* ..yes, leave loop */ goto next; - } + } + #ifdef SERIAL_DEBUG_INTR printk("IIR = %x...", serial_in(info, UART_IIR)); #endif @@ -839,6 +856,24 @@ info->last_active = jiffies; status = serial_inp(info, UART_LSR); + +#ifdef CONFIG_MELAN + + /* + * There is a bug (misfeature?) in the UART on the AMD Elan + * SC4x0 and SC520 embedded processor series; the THRE bit of + * the line status register seems to be delayed one bit + * clock after the interrupt is generated, so kludge this + * if the IIR indicates a Transmit Holding Register Interrupt + */ + if ((iir & UART_IIR_ID) == UART_IIR_THRI) { + status |= UART_LSR_THRE; +#ifdef SERIAL_DEBUG_INTR + printk("|%x", status); +#endif + } +#endif /* CONFIG_MELAN */ + #ifdef SERIAL_DEBUG_INTR printk("status = %x...", status); #endif @@ -853,7 +888,7 @@ if (!info) { info = IRQ_ports[irq]; if (pass_counter++ > RS_ISR_PASS_LIMIT) { -#if 0 +#ifdef SERIAL_DEBUG_INTR printk("rs loop break\n"); #endif break; /* Prevent infinite loops */ @@ -886,6 +921,9 @@ int first_multi = 0; struct rs_multiport_struct *multi; #endif +#ifdef CONFIG_MELAN + int iir; +#endif #ifdef SERIAL_DEBUG_INTR printk("rs_interrupt_single(%d)...", irq); @@ -900,7 +938,9 @@ if (multi->port_monitor) first_multi = inb(multi->port_monitor); #endif - +#ifdef CONFIG_MELAN + iir = serial_in(info, UART_IIR); +#endif do { status = serial_inp(info, UART_LSR); #ifdef SERIAL_DEBUG_INTR @@ -909,10 +949,25 @@ if (status & UART_LSR_DR) receive_chars(info, &status, regs); check_modem_status(info); + +#ifdef CONFIG_MELAN + + /* + * There is a bug (misfeature?) in the UART on the AMD Elan + * SC4x0 and SC520 embedded processor series; the THRE bit of + * the line status register seems to be delayed one bit + * clock after the interrupt is generated, so kludge this + * if the IIR indicates a Transmit Holding Register Interrupt + */ + + if ((iir & UART_IIR_ID) == UART_IIR_THRI) + status |= UART_LSR_THRE; +#endif if (status & UART_LSR_THRE) transmit_chars(info, 0); + if (pass_counter++ > RS_ISR_PASS_LIMIT) { -#if 0 +#ifdef SERIAL_DEBUG_INTR printk("rs_single loop break.\n"); #endif break; @@ -920,7 +975,7 @@ #ifdef SERIAL_DEBUG_INTR printk("IIR = %x...", serial_in(info, UART_IIR)); #endif - } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); + } while (!((iir = serial_in(info, UART_IIR)) & UART_IIR_NO_INT)); info->last_active = jiffies; #ifdef CONFIG_SERIAL_MULTIPORT if (multi->port_monitor) diff -urN -X kernel-patches/dontdiff linux-2.4.18-pre4/include/asm-i386/timex.h linux-2.4.18-pre4-elan/include/asm-i386/timex.h --- linux-2.4.18-pre4/include/asm-i386/timex.h Thu Nov 22 20:46:18 2001 +++ linux-2.4.18-pre4-elan/include/asm-i386/timex.h Sun Jan 20 21:41:35 2002 @@ -9,7 +9,12 @@ #include #include -#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */ +#ifdef CONFIG_MELAN +# define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */ +#else +# define CLOCK_TICK_RATE 1193180 /* Underlying HZ */ +#endif + #define CLOCK_TICK_FACTOR 20 /* Factor of both 1000000 and CLOCK_TICK_RATE */ #define FINETUNE ((((((long)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \ (1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \