Submitted By: Jim Gifford (patches at jg555 dot com) Date: 2006-11-15 Initial Package Version: 2.6.18.2 Origin: Gentoo - Jeff Waters Upstream Status: N/A Description: Adds Support for the x86 Cobalt Series diff -Naur linux-2.6.18.2.orig/Makefile linux-2.6.18.2/Makefile --- linux-2.6.18.2.orig/Makefile 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/Makefile 2006-11-15 21:20:19.730033585 -0800 @@ -197,6 +197,7 @@ KBUILD_MODULES := KBUILD_BUILTIN := 1 +DRIVERS-$(CONFIG_COBALT_RAQ) += drivers/cobalt/cobalt.o # If we have only "make modules", don't compile built-in objects. # When we're building modules with modversions, we need to consider # the built-in objects during the descend as well, in order to @@ -702,6 +703,11 @@ cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) \ $(if $(CONFIG_KALLSYMS_ALL),--all-symbols) > $@ + +cobalt: vmlinux + strip vmlinux + bzip2 vmlinux + .tmp_kallsyms1.o .tmp_kallsyms2.o .tmp_kallsyms3.o: %.o: %.S scripts FORCE $(call if_changed_dep,as_o_S) diff -Naur linux-2.6.18.2.orig/arch/i386/kernel/Makefile linux-2.6.18.2/arch/i386/kernel/Makefile --- linux-2.6.18.2.orig/arch/i386/kernel/Makefile 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/arch/i386/kernel/Makefile 2006-11-15 21:20:19.730033585 -0800 @@ -52,6 +52,7 @@ targets += vsyscall-note.o vsyscall.lds # The DSO images are built using a special linker script. +obj-$(CONFIG_COBALT_RAQ) += cobalt.o quiet_cmd_syscall = SYSCALL $@ cmd_syscall = $(CC) -m elf_i386 -nostdlib $(SYSCFLAGS_$(@F)) \ -Wl,-T,$(filter-out FORCE,$^) -o $@ diff -Naur linux-2.6.18.2.orig/arch/i386/kernel/cobalt.c linux-2.6.18.2/arch/i386/kernel/cobalt.c --- linux-2.6.18.2.orig/arch/i386/kernel/cobalt.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/arch/i386/kernel/cobalt.c 2006-11-15 21:20:19.730033585 -0800 @@ -0,0 +1,281 @@ +/* $Id: cobalt.c,v 1.34 2002/11/04 17:54:14 thockin Exp $ */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_NMI_PS 10 + +static u8 last_err; +static u32 last_address; +static unsigned long nmi_repeats; +static struct timer_list nmi_timer; +static int timer_added; +static unsigned long nmi_count; +static spinlock_t nmi_state_lock = SPIN_LOCK_UNLOCKED; + +static inline void +ledonoff(unsigned long on, unsigned long off) +{ +#ifdef CONFIG_COBALT_LED + unsigned long start; + int haltok = current_cpu_data.hlt_works_ok; + + if (on) { + start = jiffies; + cobalt_led_set(cobalt_led_get() | LED_SHUTDOWN); + while (jiffies < start + on) { + if (haltok) __asm__("hlt"); + } + } + + if (off) { + start = jiffies; + cobalt_led_set(cobalt_led_get() & ~LED_SHUTDOWN); + while (jiffies < start + off) { + if (haltok) __asm__("hlt"); + } + } +#endif +} + +/* clla this holding nmi_state_lock */ +static inline void +do_repeats(void) +{ + if (nmi_repeats) { + printk("NMI: last error repeated %lu times\n", nmi_repeats); + nmi_repeats = 0; + } +} + +static void +nmi_throttle_fn(unsigned long data) +{ + unsigned long flags; + + spin_lock_irqsave(&nmi_state_lock, flags); + + /* clear any repeated NMIs */ + do_repeats(); + + /* have we had a lot of errors this second */ + if (nmi_count > MAX_NMI_PS) { + printk("NMI: %lu messages were throttled\n", + nmi_count - MAX_NMI_PS); + nmi_count = 0; + } + + /* de-activate the timer - will be reactivated by an NMI */ + del_timer(&nmi_timer); + timer_added = 0; + + spin_unlock_irqrestore(&nmi_state_lock, flags); +} + +void +cobalt_nmi(unsigned char reason, struct pt_regs *regs) +{ + if (cobt_is_5k()) { + static struct pci_dev *cnb_dev; + u8 err; + u32 address = 0; + unsigned long flags; + + /* find our memory controller */ + if (!cnb_dev) { + cnb_dev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_LE, NULL); + } + if (!cnb_dev) { + EPRINTK("can't find north bridge for NMI status\n"); + return; + } + + /* read the error number */ + pci_read_config_byte(cnb_dev, 0x47, &err); + + /* if a memory error was detected, where? */ + if (err & 0x06) { + pci_read_config_dword(cnb_dev, 0x94, &address); + } + + spin_lock_irqsave(&nmi_state_lock, flags); + + /* set up the timer, if it isn't set to go already */ + if (!timer_added) { + init_timer(&nmi_timer); + nmi_timer.expires = jiffies + HZ; + nmi_timer.function = nmi_throttle_fn; + add_timer(&nmi_timer); + timer_added = 1; + } + + /* if we already printed this error */ + if (last_err && err == last_err && address == last_address) { + nmi_repeats++; + spin_unlock_irqrestore(&nmi_state_lock, flags); + } else { + unsigned long nmi_now; + + /* different error - show repeats */ + do_repeats(); + + /* we only want to do a few messages per second */ + nmi_now = nmi_count++; + + spin_unlock_irqrestore(&nmi_state_lock, flags); + + /* generate a new message */ + if (nmi_now < MAX_NMI_PS) { + /* only remember NMIs that we can print */ + last_err = err; + last_address = address; + + printk("NMI:"); + if (err & 0x40) + printk(" (PCI tx data error)"); + if (err & 0x20) + printk(" (PCI rx data error)"); + if (err & 0x10) + printk(" (PCI address error)"); + if (err & 0x04) + printk(" (DRAM uncorrectable error)"); + if (err & 0x02) + printk(" (DRAM correctable error)"); + if (err & 0x01) + printk(" (Shutdown cycle detected)"); + + if (err & 0x06) { + u8 row, dimm, ecc; + + row = (address >> 29) & 0x7; + pci_read_config_byte(cnb_dev, + 0x7c + (row >> 1), &dimm); + dimm = ((row & 1) ? + (dimm >> 4) : dimm) & 0xf; + pci_read_config_byte(cnb_dev, 0xe8, + &ecc); + + printk(" [memory row %d, DIMM type %d, " + "col=0x%x, row=0x%x, ECC=0x%x]", + row, dimm, + (address >> 15) & 0x3fff, + address & 0x7fff, ecc); + } + printk("\n"); + } + } + + /* clear errors */ + pci_write_config_byte(cnb_dev, 0x47, err); + } else { + /* TODO: make throttling generic, handle GP NMIs */ + printk("NMI: unknown error\n"); + } +} + +void +cobalt_restart(void) +{ + if (cobt_is_3k()) { + /* kick watchdog */ + cobalt_wdt_trigger_reboot(); + } else if (cobt_is_5k()) { + /* set "Enable Hard Reset" bit to 1 */ + outb(0x02, 0x0cf9); + + /* 0-to-1 transition of bit 2 will cause reset of processor */ + outb(0x06, 0x0cf9); + } + mdelay(3000); + + /* we should not get here unless there is a BAD error */ + EPRINTK("can not restart - halting\n"); + machine_halt(); +} + +void +cobalt_halt(void) +{ + int haltok = current_cpu_data.hlt_works_ok; + + if (cobt_is_5k()) { + /* we have soft power-off */ + machine_power_off(); + } + + /* + * we want to do cpu_idle, but we don't actually want to + * call cpu_idle. bleah. + */ + while (1) { + ledonoff(HZ >> 1, HZ >> 1); + if (haltok) { + __asm__("hlt"); + } + } +} + +void +cobalt_power_off(void) +{ + u16 addr; + + if (cobt_is_monterey()) { + u8 val; + /* use card control reg. 7 to select logical device 2 (APC) */ + addr = superio_ldev_base(PC87317_DEV_RTC); + + /* set up bank 2 */ + outb(PC87317_RTC_CRA, addr); + val = inb(addr + 1) & 0x8f; + outb(val | PC87317_RTC_BANK_2, addr + 1); + + /* power off the machine with APCR1 */ + outb(PC87317_APCR1, addr); + val = inb(addr + 1); + outb(0x20 | val, addr + 1); + } else if (cobt_is_alpine()) { + int i; + /* clear status bits, base addr 3 */ + addr = superio_ldev_base_n(PC87417_DEV_SWC, 3); + for (i = 0; i < 4; i++) { + /* + * if we have an event while running, + * we can't halt unless we clear these + * */ + outb(0xff, addr+i); + } + + /* set sleep state, base addr 2 */ + addr = superio_ldev_base_n(PC87417_DEV_SWC, 2); + /* PM1b_CNT_HIGH @offset 1 - set state to S5 */ + outb(0x34, addr+1); + } + mdelay(3000); + EPRINTK("can not power off\n"); +} + +/* put arch specific stuff to run at init time here */ +static int __init +cobalt_arch_init(void) +{ + return 0; +} +module_init(cobalt_arch_init); diff -Naur linux-2.6.18.2.orig/arch/i386/kernel/process.c linux-2.6.18.2/arch/i386/kernel/process.c --- linux-2.6.18.2.orig/arch/i386/kernel/process.c 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/arch/i386/kernel/process.c 2006-11-15 21:28:11.450334183 -0800 @@ -51,6 +51,11 @@ #include #endif +#ifdef CONFIG_COBALT_RAQ +#include +#include +#endif + #include #include @@ -482,6 +487,12 @@ void dump_thread(struct pt_regs * regs, struct user * dump) { int i; + +#ifdef CONFIG_COBALT_RAQ + cobalt_flush(); + cobalt_restart(); +#endif + /* changed the size calculations - should hopefully work better. lbt */ dump->magic = CMAGIC; diff -Naur linux-2.6.18.2.orig/arch/i386/kernel/reboot.c linux-2.6.18.2/arch/i386/kernel/reboot.c --- linux-2.6.18.2.orig/arch/i386/kernel/reboot.c 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/arch/i386/kernel/reboot.c 2006-11-15 21:26:57.096812163 -0800 @@ -18,6 +18,11 @@ #include "mach_reboot.h" #include +#ifdef CONFIG_COBALT_RAQ +#include +#include +#endif + /* * Power off function, if any */ @@ -283,8 +288,38 @@ EXPORT_SYMBOL(machine_real_restart); #endif +/* kill some time at halt/reboot to allow drives with large cache to sync */ + +#ifdef CONFIG_COBALT_RAQ +void cobalt_flush(void) +{ + int i; + static int flushed; + + if (flushed) + return; + flushed = 1; + + printk("waiting for devices to flush"); + for (i = 0 ; i < 10; i++) { + printk("."); + mdelay(500); +#ifdef CONFIG_COBALT_LCD + if (i == 8) + cobalt_lcd_off(); +#endif + } + printk("done\n"); +} +#endif + void machine_shutdown(void) { +#ifdef CONFIG_COBALT_RAQ + cobalt_flush(); + cobalt_halt(); +#endif + #ifdef CONFIG_SMP int reboot_cpu_id; @@ -321,6 +356,9 @@ void machine_emergency_restart(void) { +#ifdef CONFIG_COBALT_RAQ + cobalt_restart(); +#endif if (!reboot_thru_bios) { if (efi_enabled) { efi.reset_system(EFI_RESET_COLD, EFI_SUCCESS, 0, NULL); @@ -345,6 +383,10 @@ void machine_restart(char * __unused) { +#ifdef CONFIG_COBALT_RAQ + cobalt_flush(); + cobalt_restart(); +#endif machine_shutdown(); machine_emergency_restart(); } diff -Naur linux-2.6.18.2.orig/arch/i386/kernel/traps.c linux-2.6.18.2/arch/i386/kernel/traps.c --- linux-2.6.18.2.orig/arch/i386/kernel/traps.c 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/arch/i386/kernel/traps.c 2006-11-15 21:20:19.734033452 -0800 @@ -56,6 +56,10 @@ #include "mach_traps.h" +#ifdef CONFIG_COBALT_RAQ +#include +#endif + asmlinkage int system_call(void); struct desc_struct default_ldt[] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, @@ -631,11 +635,14 @@ static void mem_parity_error(unsigned char reason, struct pt_regs * regs) { +#ifdef CONFIG_COBALT_RAQ + cobalt_nmi(reason, regs); +#else printk(KERN_EMERG "Uhhuh. NMI received. Dazed and confused, but trying " "to continue\n"); printk(KERN_EMERG "You probably have a hardware problem with your RAM " "chips\n"); - +#endif /* Clear and disable the memory parity error line. */ clear_mem_error(reason); } diff -Naur linux-2.6.18.2.orig/drivers/Kconfig linux-2.6.18.2/drivers/Kconfig --- linux-2.6.18.2.orig/drivers/Kconfig 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/drivers/Kconfig 2006-11-15 21:20:19.734033452 -0800 @@ -74,4 +74,6 @@ source "drivers/dma/Kconfig" +source "drivers/cobalt/Kconfig" + endmenu diff -Naur linux-2.6.18.2.orig/drivers/Makefile linux-2.6.18.2/drivers/Makefile --- linux-2.6.18.2.orig/drivers/Makefile 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/drivers/Makefile 2006-11-15 21:20:19.734033452 -0800 @@ -65,6 +65,8 @@ obj-$(CONFIG_EDAC) += edac/ obj-$(CONFIG_MCA) += mca/ obj-$(CONFIG_EISA) += eisa/ +obj-$(CONFIG_COBALT_RAQ) += cobalt/ + obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_NEW_LEDS) += leds/ diff -Naur linux-2.6.18.2.orig/drivers/char/Kconfig linux-2.6.18.2/drivers/char/Kconfig --- linux-2.6.18.2.orig/drivers/char/Kconfig 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/drivers/char/Kconfig 2006-11-15 21:20:19.734033452 -0800 @@ -809,7 +809,7 @@ Samsung S3C2410. This can provide periodic interrupt rates from 1Hz to 64Hz for user programs, and wakeup from Alarm. -config COBALT_LCD +config COBALT_MIPS_LCD bool "Support for Cobalt LCD" depends on MIPS_COBALT help diff -Naur linux-2.6.18.2.orig/drivers/char/Makefile linux-2.6.18.2/drivers/char/Makefile --- linux-2.6.18.2.orig/drivers/char/Makefile 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/drivers/char/Makefile 2006-11-15 21:20:19.734033452 -0800 @@ -77,7 +77,7 @@ obj-$(CONFIG_DS1620) += ds1620.o obj-$(CONFIG_HW_RANDOM) += hw_random/ obj-$(CONFIG_FTAPE) += ftape/ -obj-$(CONFIG_COBALT_LCD) += lcd.o +obj-$(CONFIG_COBALT_MIPS_LCD) += lcd.o obj-$(CONFIG_PPDEV) += ppdev.o obj-$(CONFIG_NWBUTTON) += nwbutton.o obj-$(CONFIG_NWFLASH) += nwflash.o diff -Naur linux-2.6.18.2.orig/drivers/char/misc.c linux-2.6.18.2/drivers/char/misc.c --- linux-2.6.18.2.orig/drivers/char/misc.c 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/drivers/char/misc.c 2006-11-15 21:20:19.734033452 -0800 @@ -49,6 +49,17 @@ #include #include +#ifdef CONFIG_COBALT_RAQ +#include +#include +#include +#include +#include +#include +#include +#endif + + /* * Head entry for the doubly linked miscdevice list */ @@ -63,6 +74,13 @@ extern int pmu_device_init(void); +#ifdef CONFIG_COBALT_RAQ +extern int cobalt_init(void); +#endif +#ifdef CONFIG_COBALT_MIPS_LCD +module_init(lcd_init); +#endif + #ifdef CONFIG_PROC_FS static void *misc_seq_start(struct seq_file *seq, loff_t *pos) { diff -Naur linux-2.6.18.2.orig/drivers/char/nvram.c linux-2.6.18.2/drivers/char/nvram.c --- linux-2.6.18.2.orig/drivers/char/nvram.c 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/drivers/char/nvram.c 2006-11-15 21:20:19.734033452 -0800 @@ -49,8 +49,7 @@ #if defined(CONFIG_ATARI) # define MACH ATARI #elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) /* and others?? */ -#define MACH PC -# if defined(CONFIG_COBALT) +# if defined(CONFIG_COBALT_RAQ) # include # define MACH COBALT # else diff -Naur linux-2.6.18.2.orig/drivers/cobalt/Kconfig linux-2.6.18.2/drivers/cobalt/Kconfig --- linux-2.6.18.2.orig/drivers/cobalt/Kconfig 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/Kconfig 2006-11-15 21:20:19.734033452 -0800 @@ -0,0 +1,133 @@ +# +# Cobalt Drivers +# + +menu "Cobalt RaQ/Qube Hardware" + +config COBALT_RAQ + bool "Cobalt RaQ/Qube Hardware Support" + select INPUT + default n + ---help--- + NOTE: This support is for x86 Cobalts, not MIPS versions + + If you have a Gen III or Gen V Cobalt RaQ/Qube machine, it's probably + a good idea to say Y here and choose from the options below. + +config COBALT_GEN_III + bool "Gen III (3000 series) system support" + depends on COBALT_RAQ + default y + ---help--- + If you have one of the following Gen III Cobalt systems, say Y here. + Otherwise, it's best to say N. + + - RaQ 3 + - RaQ 4 + - Qube3 + +config COBALT_GEN_V + bool "Gen V (5000 series) system support" + depends on COBALT_RAQ + default n + ---help--- + If you have one of the following Gen V Cobalt systems, say Y here. + Otherwise, it's best to say N. + + - RaQ XTR + - RaQ550 + +config COBALT_OLDPROC + bool "Create legacy /proc files" + depends on COBALT_RAQ + default y + ---help--- + Creates entries in /proc/cobalt which provide useful information about + your RaQ/Qube. Best to say Y here. + +menu "Cobalt Hardware Options" + depends on COBALT_RAQ + + config COBALT_LCD + bool "Front panel LCD support" + default y + ---help--- + Handles support for the front panel LCD screen and buttons. + + config COBALT_LCD_TWIDDLE + bool "Twiddle LCD on boot" + depends on COBALT_LCD + default y + ---help--- + Gives you a nice little twiddle on the LCD while booting. + + config COBALT_LED + bool "Software controlled LED support" + default y + ---help--- + Allows software to play with the LEDs on the front of the + system. + + config COBALT_SERNUM + tristate "Serial number support" + depends on COBALT_OLDPROC + default y + ---help--- + Allows you to retrieve the system's serial number via a /proc + entry. + + config COBALT_WDT + bool "Watchdog timer support" + depends on WATCHDOG + default y + ---help--- + w00f? + + config COBALT_SENSORS + bool "System sensors support" + depends on COBALT_OLDPROC + default y + ---help--- + Allows you to retrieve system temperatures via /proc entries. + + config COBALT_FANS + tristate "Fan tachometer support" + depends on COBALT_OLDPROC + depends on COBALT_GEN_V + default y + ---help--- + Allows you to retrieve fan speeds via /proc entries. + + config COBALT_RAMINFO + tristate "Memory information support" + depends on COBALT_OLDPROC + default y + ---help--- + Got DIMMs? This will tell you how much and in which slot via a + /proc entry. + + config COBALT_RULER + bool "Disk drive ruler support" + depends on COBALT_OLDPROC + depends on COBALT_GEN_V + default y + ---help--- + Not sure what this does... A purple tape measure maybe? + + config COBALT_ACPI + bool "Cobalt ACPI support" + depends on COBALT_GEN_V + default y + ---help--- + ACPI support for the Generation V Cobalts. + + config COBALT_EMU_ACPI + bool "/proc/acpi emulation" + depends on COBALT_ACPI + default y + ---help--- + Emulates the /proc/acpi interface. + +endmenu + +endmenu diff -Naur linux-2.6.18.2.orig/drivers/cobalt/Makefile linux-2.6.18.2/drivers/cobalt/Makefile --- linux-2.6.18.2.orig/drivers/cobalt/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/Makefile 2006-11-15 21:20:19.734033452 -0800 @@ -0,0 +1,18 @@ +# +# Makefile for the Sun/Cobalt device drivers +# + +#O_TARGET := cobalt.o + +#export-objs := init.o systype.o wdt.o i2c.o + +obj-$(CONFIG_COBALT_RAQ) += init.o systype.o i2c.o wdt.o +obj-$(CONFIG_COBALT_ACPI) += acpi.o +obj-$(CONFIG_COBALT_SERNUM) += serialnum.o +obj-$(CONFIG_COBALT_LCD) += lcd.o +obj-$(CONFIG_COBALT_LED) += net.o led.o +obj-$(CONFIG_COBALT_SENSORS) += sensors.o +obj-$(CONFIG_COBALT_FANS) += fans.o +obj-$(CONFIG_COBALT_RAMINFO) += raminfo.o +obj-$(CONFIG_COBALT_RULER) += ruler.o + diff -Naur linux-2.6.18.2.orig/drivers/cobalt/README linux-2.6.18.2/drivers/cobalt/README --- linux-2.6.18.2.orig/drivers/cobalt/README 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/README 2006-11-15 21:20:19.734033452 -0800 @@ -0,0 +1,19 @@ +Notes on Cobalt's drivers: + +You will notice in several places constructs such as this: + + if (cobt_is_3k()) { + foo(); + } else if (cobt_is_5k()) { + bar(); + } + +The goal here is to only compile in code that is needed, but to allow one to +define support for both 3k and 5k (and more?) style systems. The systype +check macros are very simple and clean. They check whether config-time +support for the generation has been enabled, and (if so) whether the current +systype matches the spcified generation. This leaves the code free from +#ifdef cruft, but lets the compiler throw out unsupported generation-specific +code with if (0) detection. + +-- diff -Naur linux-2.6.18.2.orig/drivers/cobalt/acpi.c linux-2.6.18.2/drivers/cobalt/acpi.c --- linux-2.6.18.2.orig/drivers/cobalt/acpi.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/acpi.c 2006-11-15 21:20:19.734033452 -0800 @@ -0,0 +1,1994 @@ + /* + * cobalt acpi driver + * Copyright (c) 2000, Cobalt Networks, Inc. + * Copyright (c) 2001, Sun Microsystems, Inc. + * $Id: acpi.c,v 1.32 2002/06/26 19:08:54 duncan Exp $ + * + * author: asun@cobalt.com, thockin@sun.com + * modified by: jeff@404ster.com + * + * this driver just sets stuff up for ACPI interrupts + * + * if acpi support really existed in the kernel, we would read + * data from the ACPI tables. however, it doesn't. as a result, + * we use some hardcoded values. + * + * This should be SMP safe. The only data that needs protection is the acpi + * handler list. It gets scanned at timer-interrupts, must use + * irqsave/restore locks. Read/write locks would be useful if there were any + * other times that the list was read but never written. --TPH + * + * /proc/acpi emulation emulates the /proc/acpi/events interface supplied by + * the INTEL acpi drivers. A lot of the code to handle it has been adapted + * from there. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define ACPI_DRIVER "Cobalt Networks ACPI driver" +#define ACPI_DRIVER_VMAJ 1 +#define ACPI_DRIVER_VMIN 0 + +#define POWER_BUTTON_SHUTDOWN 0 + +#define ACPI_IRQ 10 /* XXX: hardcoded interrupt */ +#define ACPI_NAME "sci" +#define ACPI_MAGIC 0xc0b7ac21 + +#define SUPERIO_EVENT 0xff +#define OSB4_EVENT 0x40 +#define OSB4_INDEX_PORT SERVERWORKS_ACPI_INDEX_PORT +#define OSB4_DATA_PORT SERVERWORKS_ACPI_DATA_PORT + +#define GEN_ACPI_TMR_STS (0x1 << 0) +#define GEN_ACPI_BM_STS (0x1 << 4) +#define GEN_ACPI_GBL_STS (0x1 << 5) +#define GEN_ACPI_PWRBTN_STS (0x1 << 8) +#define GEN_ACPI_SLPBTN_STS (0x1 << 9) +#define GEN_ACPI_RTC_STS (0x1 << 10) +#define GEN_ACPI_WAK_STS (0x1 << 15) + +#ifdef CONFIG_COBALT_EMU_ACPI +static int cobalt_acpi_setup_proc(void); +static int cobalt_acpi_open_event(struct inode *inode, struct file *file); +static int cobalt_acpi_close_event(struct inode *inode, struct file *file); +static ssize_t cobalt_acpi_read_event(struct file *file, char *buf, + size_t count, loff_t *ppos); +static unsigned int cobalt_acpi_poll_event(struct file *file, poll_table *wait); +#endif + + + +typedef struct +{ + u16 hw_type; + cobalt_acpi_hw_handler hw_handler; + cobalt_acpi_enable_handler en_handler; + void *data; + struct list_head link; +} hw_handler_datum; + +typedef struct +{ + u16 hw_type; + u16 table_len; + u16 *table; + struct list_head link; +} trans_table_datum; + +typedef struct +{ + cobalt_acpi_evt_handler handler; + u16 ev_type; + void *data; + struct list_head link; +} evt_handler_datum; + +typedef struct +{ + cobalt_acpi_evt evt; + struct list_head link; +} evt_list_datum; + +static LIST_HEAD( hw_handler_list ); +static spinlock_t hw_handler_list_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD( trans_table_list ); +static spinlock_t trans_table_list_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD( evt_handler_list ); +static spinlock_t evt_handler_list_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD( dispatch_queue ); +static spinlock_t dispatch_queue_lock = SPIN_LOCK_UNLOCKED; + +typedef struct +{ + u16 hw_type; + + /* block lengths */ + u16 pm1_evt_len; + u16 pm1_cnt_len; + u16 pm2_cnt_len; + u16 pm_tmr_len; + u16 gpe0_len; + u16 gpe1_len; + + /* block I/O locations */ + u16 pm1a_evt_blk; + u16 pm1b_evt_blk; + u16 pm1a_cnt_blk; + u16 pm1b_cnt_blk; + u16 pm2_cnt_blk; + u16 pm_tmr_blk; + u16 p_blk; + u16 gpe0_blk; + u16 gpe1_blk; + + /* ponters to strings for the io names */ + char *pm1a_evt_nam; + char *pm1b_evt_nam; + char *pm1a_cnt_nam; + char *pm1b_cnt_nam; + char *pm2_cnt_nam; + char *pm_tmr_nam; + char *p_nam; + char *gpe0_nam; + char *gpe1_nam; + + /* reference counts for events */ + atomic_t tmr_ref_cnt; + atomic_t bm_ref_cnt; + atomic_t gbl_ref_cnt; + atomic_t pwrbtn_ref_cnt; + atomic_t slpbtn_ref_cnt; + atomic_t rtc_ref_cnt; + atomic_t wak_ref_cnt; + atomic_t *gpe_ref_cnt; + + +} generic_acpi_regions; + + +static void cobalt_acpi_enable_event( u16 ev_type, int en ); +static void cobalt_acpi_run_enable_handler( u16 hw_type, u16 ev_type, + u16 ev_data, int en); +static int cobalt_acpi_apply_evt_handlers( evt_list_datum *d ); +static int cobalt_acpi_run_dispatch_queue( void ); +static irqreturn_t acpi_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void cobalt_acpi_cleanup( void ); + +static int register_acpi_regions( generic_acpi_regions *regions, char * subsys_name ); +static int unregister_acpi_regions( generic_acpi_regions *regions ); +static void cobalt_acpi_handle_pm1_blk( u16 io_addr, u16 len, + generic_acpi_regions * regions ); +static void cobalt_acpi_handle_gpe_blk( u16 io_addr, u16 len, + generic_acpi_regions * regions ); +static int cobalt_acpi_generic_hw_handler( int irq, void *dev_id, + struct pt_regs *regs, void * data ); + +static int cobalt_acpi_osb4_init( void ); +static int cobalt_acpi_osb4_cleanup( void ); +static int get_osb4_regions( generic_acpi_regions *regions); + +static int cobalt_acpi_csb5_init( void ); +static int cobalt_acpi_csb5_cleanup( void ); +static int get_csb5_regions( generic_acpi_regions *regions); + +static int cobalt_acpi_pc8731x_init( void ); +static int cobalt_acpi_pc8731x_cleanup( void ); +static int get_pc8731x_regions( generic_acpi_regions *regions ); + +static int cobalt_acpi_pc8741x_init( void ); +static int cobalt_acpi_pc8741x_cleanup( void ); +static int get_pc8741x_regions( generic_acpi_regions *regions ); + +static int cobalt_acpi_monterey_init( void ); +static int cobalt_acpi_monterey_cleanup( void ); + +static int cobalt_acpi_alpine_init( void ); +static int cobalt_acpi_alpine_cleanup( void ); + +static __inline__ struct list_head *list_pop( struct list_head *head ) +{ + struct list_head *e; + + if( list_empty( head ) ) + return NULL; + + e = head->next; + list_del( e ); + return e; +} + +static __inline__ u16 get_reg(u16 index, u16 data, u8 port) +{ + u16 reg; + + outb(port, index); + reg = inb(data); + outb(port + 1, index); + reg |= inb(data) << 8; + return reg; +} + +/* + * + * Main ACPI Subsystem Code + * + */ + +extern int cobalt_acpi_register_hw_handler( u16 hw_type, + cobalt_acpi_hw_handler hw_handler, + cobalt_acpi_enable_handler en_handler, + void *data ) +{ + hw_handler_datum *d; + unsigned long flags; + + if( ! (d = (hw_handler_datum *) kmalloc( sizeof( hw_handler_datum ), GFP_ATOMIC )) ) + return -ENOMEM; + + d->hw_type = hw_type; + d->hw_handler = hw_handler; + d->en_handler = en_handler; + d->data = data; + + spin_lock_irqsave( &hw_handler_list_lock, flags ); + list_add( &(d->link), &hw_handler_list ); + spin_unlock_irqrestore( &hw_handler_list_lock, flags ); + + return 0; +} + +extern int cobalt_acpi_unregister_hw_handler( cobalt_acpi_hw_handler handler ) +{ + struct list_head *pos; + unsigned long flags; + + spin_lock_irqsave( &hw_handler_list_lock, flags ); + list_for_each( pos, &hw_handler_list ) + { + if( list_entry( pos, hw_handler_datum, link )->hw_handler == handler ) + { + list_del( pos ); + spin_unlock_irqrestore( &hw_handler_list_lock, flags ); + + kfree( list_entry( pos, hw_handler_datum, link ) ); + return 0; + } + + }; + + spin_unlock_irqrestore( &hw_handler_list_lock, flags ); + return -1; +} + +extern int cobalt_acpi_register_trans_table( u16 hw_type, u16 table_len, u16 *table ) +{ + trans_table_datum *d; + unsigned long flags; + + if( ! (d = (trans_table_datum *) kmalloc( sizeof( trans_table_datum ), GFP_ATOMIC )) ) + return -ENOMEM; + + d->hw_type = hw_type; + d->table_len = table_len; + d->table = table; + + spin_lock_irqsave( &trans_table_list_lock, flags ); + list_add( &(d->link), &trans_table_list ); + spin_unlock_irqrestore( &trans_table_list_lock, flags ); + + return 0; +} + +extern int cobalt_acpi_unregister_trans_table( u16 hw_type ) +{ + struct list_head *pos; + unsigned long flags; + + spin_lock_irqsave( &trans_table_list_lock, flags ); + list_for_each( pos, &trans_table_list ) + { + if( list_entry( pos, trans_table_datum, link )->hw_type == hw_type ) + { + list_del( pos ); + spin_unlock_irqrestore( &trans_table_list_lock, flags ); + + kfree( list_entry( pos, trans_table_datum, link ) ); + return 0; + } + + }; + + spin_unlock_irqrestore( &trans_table_list_lock, flags ); + return -1; +} + +extern int cobalt_acpi_register_evt_handler( cobalt_acpi_evt_handler handler, + u16 ev_type, + void *data ) +{ + evt_handler_datum *d; + unsigned long flags; + + if( ! (d = (evt_handler_datum *) kmalloc( sizeof( evt_handler_datum ), GFP_ATOMIC )) ) + return -ENOMEM; + + d->handler = handler; + d->data = data; + d->ev_type = ev_type; + + spin_lock_irqsave( &evt_handler_list_lock, flags ); + list_add( &(d->link), &evt_handler_list ); + spin_unlock_irqrestore( &evt_handler_list_lock, flags ); + + cobalt_acpi_enable_event( ev_type, 1 ); + + return 0; +} + +extern int cobalt_acpi_unregister_evt_handler( cobalt_acpi_evt_handler handler ) +{ + struct list_head *pos; + unsigned long flags; + + + spin_lock_irqsave( &evt_handler_list_lock, flags ); + list_for_each( pos, &evt_handler_list ) + { + if( list_entry( pos, evt_handler_datum, link )->handler == handler ) + { + list_del( pos ); + spin_unlock_irqrestore( &evt_handler_list_lock, flags ); + + cobalt_acpi_enable_event( list_entry( pos, + evt_handler_datum, + link )->ev_type, 0 ); + + kfree( list_entry( pos, evt_handler_datum, link ) ); + return 0; + } + + }; + + spin_unlock_irqrestore( &evt_handler_list_lock, flags ); + return -EINVAL; +} + +static void cobalt_acpi_enable_event( u16 ev_type, int en ) +{ + if( ev_type >= 0x8000 ) + { + struct list_head *pos; + trans_table_datum *d; + int i; + unsigned long flags; + + spin_lock_irqsave( &trans_table_list_lock, flags ); + list_for_each( pos, &trans_table_list ) + { + d = list_entry( pos, trans_table_datum, link ); + for( i=0 ; itable_len ; i++ ) + { + if( d->table[i] == ev_type ) + { + cobalt_acpi_run_enable_handler( d->hw_type, + COBALT_ACPI_EVT_GPE, + i, en ); + } + } + } + spin_unlock_irqrestore( &trans_table_list_lock, flags ); + } + else + cobalt_acpi_run_enable_handler( COBALT_ACPI_HW_ANY, ev_type, 0, en); +} + +static void cobalt_acpi_run_enable_handler( u16 hw_type, u16 ev_type, + u16 ev_data, int en) +{ + struct list_head *pos; + unsigned long flags; + hw_handler_datum *d; + + spin_lock_irqsave(&hw_handler_list_lock, flags); + list_for_each( pos, &hw_handler_list ) + { + d = list_entry( pos, hw_handler_datum, link ); + if( (!hw_type) || (d->hw_type == hw_type) ) + d->en_handler( ev_type, ev_data, en, d->data ); + } + spin_unlock_irqrestore(&hw_handler_list_lock, flags); + +} + +static int cobalt_acpi_translate_event( cobalt_acpi_evt *evt ) +{ + struct list_head *pos; + unsigned long flags; + trans_table_datum *d; + + if( evt->ev_type != COBALT_ACPI_EVT_GPE ) + return 0; + + spin_lock_irqsave( &trans_table_list_lock, flags ); + list_for_each( pos, &trans_table_list ) + { + d = list_entry( pos, trans_table_datum, link ); + if( d->hw_type == evt->hw_type ) + { + if( evt->ev_data >= d->table_len ) + goto err_out; + + if( d->table[ evt->ev_data ] != COBALT_ACPI_EVT_NONE ) + { + evt->ev_type = d->table[ evt->ev_data ]; + evt->ev_data = 0; + } + + spin_unlock_irqrestore( &trans_table_list_lock, flags ); + return 0; + } + } + + err_out: + spin_unlock_irqrestore( &trans_table_list_lock, flags ); + return -1; +} + +extern int cobalt_acpi_post_event( cobalt_acpi_evt evt ) +{ + evt_list_datum *d; + unsigned long flags; + + if( ! (d = (evt_list_datum *) kmalloc( sizeof( evt_handler_datum ), GFP_ATOMIC )) ) + return -ENOMEM; + + + cobalt_acpi_translate_event( &evt ); + + memcpy( &(d->evt), &evt, sizeof(evt) ); + + spin_lock_irqsave( &dispatch_queue_lock, flags ); + list_add_tail( &(d->link), &dispatch_queue ); + spin_unlock_irqrestore( &dispatch_queue_lock, flags ); + + return 0; +} + +static int cobalt_acpi_apply_evt_handlers( evt_list_datum *d ) +{ + struct list_head *pos; + evt_handler_datum *evt_h; + int ret,err = 0; + unsigned long flags; + + spin_lock_irqsave( &evt_handler_list_lock, flags ); + list_for_each( pos, &evt_handler_list ) + { + evt_h = list_entry( pos, evt_handler_datum, link ); + if( (! evt_h->ev_type) || (evt_h->ev_type == d->evt.ev_type) ) + { + if( (ret = evt_h->handler( &d->evt, evt_h->data )) < 0 ) + err = ret; + } + } + spin_unlock_irqrestore( &evt_handler_list_lock, flags ); + + return err; +} + +static int cobalt_acpi_run_dispatch_queue( void ) +{ + struct list_head *pos; + int ret; + int err=0; + evt_list_datum *d; + unsigned long flags; + + spin_lock_irqsave( &dispatch_queue_lock, flags ); + while( (pos = list_pop( &dispatch_queue )) ) + { + d = list_entry( pos, evt_list_datum, link ); + if( (ret = cobalt_acpi_apply_evt_handlers( d )) < 0 ) + err = ret; +#ifdef CONFIG_COBALT_EMU_ACPI + cobalt_acpi_generate_proc_evt( &d->evt ); +#endif + kfree( d ); + } + spin_unlock_irqrestore( &dispatch_queue_lock, flags ); + + return err; +} + +static irqreturn_t acpi_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct list_head *pos; + hw_handler_datum *d; + int ret=0, err=0; + unsigned long flags; + + spin_lock_irqsave(&hw_handler_list_lock, flags); + list_for_each( pos, &hw_handler_list ) + { + d = list_entry( pos, hw_handler_datum, link ); + if( (ret = d->hw_handler( irq, dev_id, regs, d->data )) < 0 ) + err = ret; + + } + spin_unlock_irqrestore(&hw_handler_list_lock, flags); + + if( (err = cobalt_acpi_run_dispatch_queue()) < 0 ) + err = ret; + + if( err ) + EPRINTK( "error at interrupt time of type %d.\n", err ); + + return IRQ_HANDLED; +} + + + + +int __init cobalt_acpi_init(void) +{ + int err; + + printk(KERN_INFO "%s %d.%d (modified by jeff@404ster.com)\n", ACPI_DRIVER,ACPI_DRIVER_VMAJ,ACPI_DRIVER_VMIN); + + if( cobt_is_monterey() ) + cobalt_acpi_monterey_init(); + else if( cobt_is_alpine() ) + cobalt_acpi_alpine_init(); + + if( cobt_is_5k() ) + { + if( pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL ) ) + { + if( (err = cobalt_acpi_osb4_init()) < 0 ) + { + goto cleanup; + } + } + + if( pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL ) ) + { + if( (err = cobalt_acpi_csb5_init()) < 0 ) + { + goto cleanup; + } + } + + switch( superio_type() ) + { + case SIO_TYPE_PC8731X: + if( (err = cobalt_acpi_pc8731x_init()) ) + { + goto cleanup; + } + break; + + case SIO_TYPE_PC8741X: + if( (err = cobalt_acpi_pc8741x_init()) ) + { + goto cleanup; + } + break; + + case SIO_TYPE_UNKNOWN: + EPRINTK("unknown superio type\n"); + break; + } + + /* setup an interrupt handler for an ACPI SCI */ + err = request_irq(ACPI_IRQ, acpi_interrupt, + SA_SHIRQ, ACPI_NAME, (void *)ACPI_MAGIC); + if (err) { + EPRINTK("can't assign ACPI IRQ (%d)\n", ACPI_IRQ); + return err; + } + +#ifdef CONFIG_COBALT_EMU_ACPI + cobalt_acpi_setup_proc(); +#endif + } + + /* enable some events we may want */ + cobalt_acpi_enable_event( COBALT_ACPI_EVT_PWRBTN, 1 ); + + return 0; + + cleanup: + cobalt_acpi_cleanup(); + return err; +} + +static void cobalt_acpi_cleanup( void ) +{ + cobalt_acpi_osb4_cleanup(); + cobalt_acpi_csb5_cleanup(); + cobalt_acpi_pc8731x_cleanup(); + cobalt_acpi_pc8741x_cleanup(); + + if( cobt_is_monterey() ) + cobalt_acpi_monterey_cleanup(); + if( cobt_is_alpine() ) + cobalt_acpi_alpine_cleanup(); +} + +/* + * + * Generic ACPI HW Support + * + */ + +static __inline__ char *region_name( char * subsys_name, char * blk_name ) +{ + char * new_name; + + if( !( new_name = (char *) kmalloc( strlen(subsys_name) + strlen(blk_name) + 14, + GFP_ATOMIC)) ) + return NULL; + + sprintf( new_name, "%s (%s)", subsys_name, blk_name ); + return new_name; +} + +static void free_region_names( generic_acpi_regions *regions ) +{ + if( regions->pm1a_evt_nam ) + kfree( regions->pm1a_evt_nam ); + + if( regions->pm1b_evt_nam ) + kfree( regions->pm1b_evt_nam ); + + if( regions->pm1a_cnt_nam ) + kfree( regions->pm1a_cnt_nam ); + + if( regions->pm1b_cnt_nam ) + kfree( regions->pm1b_cnt_nam ); + + if( regions->pm2_cnt_nam ) + kfree( regions->pm2_cnt_nam ); + + if( regions->pm_tmr_nam ) + kfree( regions->pm_tmr_nam ); + + if( regions->p_nam ) + kfree( regions->p_nam ); + + if( regions->gpe0_nam ) + kfree( regions->gpe0_nam ); + + if( regions->gpe1_nam ) + kfree( regions->gpe1_nam ); +} + +static int register_acpi_regions( generic_acpi_regions *regions, char * subsys_name ) +{ + int i; + + if( regions->pm1a_evt_blk && regions->pm1_evt_len ) + { + if( !(regions->pm1a_evt_nam = region_name( subsys_name, "pm1a_evt_blk" )) ) + goto cleanup0; + + if( !request_region( regions->pm1a_evt_blk, regions->pm1_evt_len, + regions->pm1a_evt_nam ) ) + goto cleanup0; + } + + if( regions->pm1b_evt_blk && regions->pm1_evt_len ) + { + if( !(regions->pm1b_evt_nam = region_name( subsys_name, "pm1b_evt_blk" )) ) + goto cleanup0; + + if( !request_region( regions->pm1b_evt_blk, regions->pm1_evt_len, + regions->pm1b_evt_nam) ) + goto cleanup1; + } + + if( regions->pm1a_cnt_blk && regions->pm1_cnt_len ) + { + if( !(regions->pm1a_cnt_nam = region_name( subsys_name, "pm1a_cnt_blk" )) ) + goto cleanup1; + + if( !request_region( regions->pm1a_cnt_blk, regions->pm1_cnt_len, + regions->pm1a_cnt_nam ) ) + goto cleanup2; + } + + if( regions->pm1b_cnt_blk && regions->pm1_cnt_len ) + { + if( !(regions->pm1b_cnt_nam = region_name( subsys_name, "pm1b_cnt_blk" )) ) + goto cleanup2; + + if( !request_region( regions->pm1b_cnt_blk, regions->pm1_cnt_len, + regions->pm1b_cnt_nam ) ) + goto cleanup3; + } + + if( regions->pm2_cnt_blk && regions->pm2_cnt_len ) + { + if( !(regions->pm2_cnt_nam = region_name( subsys_name, "pm2_cnt_blk" )) ) + goto cleanup3; + + if( !request_region( regions->pm2_cnt_blk, regions->pm2_cnt_len, + regions->pm2_cnt_nam ) ) + goto cleanup4; + } + + if( regions->pm_tmr_blk && regions->pm_tmr_len ) + { + if( !(regions->pm_tmr_nam = region_name( subsys_name, "pm_tmp_blk" )) ) + goto cleanup4; + + if( !request_region( regions->pm_tmr_blk, regions->pm_tmr_len, + regions->pm_tmr_nam ) ) + goto cleanup5; + } + + if( regions->p_blk ) + { + if( !(regions->p_nam = region_name( subsys_name, "p_blk" )) ) + goto cleanup5; + + if( !request_region( regions->p_blk, 6, regions->p_nam ) ) + goto cleanup6; + } + + if( regions->gpe0_blk && regions->gpe0_len ) + { + if( !(regions->gpe0_nam = region_name( subsys_name, "gpe0_blk" )) ) + goto cleanup6; + + if( !request_region( regions->gpe0_blk, regions->gpe0_len, + regions->gpe0_nam ) ) + goto cleanup7; + } + + if( regions->gpe1_blk && regions->gpe1_len ) + { + if( !(regions->gpe1_nam = region_name( subsys_name, "gpe1_blk" )) ) + goto cleanup7; + + if( !request_region( regions->gpe1_blk, regions->gpe1_len, + regions->gpe1_nam ) ) + goto cleanup8; + } + + if( (regions->gpe_ref_cnt = (atomic_t *) kmalloc( sizeof( atomic_t ) * + regions->gpe0_len * 8, + GFP_ATOMIC)) == NULL ) + goto cleanup9; + + memset( regions->gpe_ref_cnt, 0x0, sizeof( atomic_t ) * regions->gpe0_len * 8 ); + + /* disable all events and ack them */ + if( regions->pm1a_evt_blk ) + { + outw( 0x0000, regions->pm1a_evt_blk + regions->pm1_evt_len/2 ); + outw( 0xffff, regions->pm1a_evt_blk ); + } + + if( regions->pm1b_evt_blk ) + { + outw( 0x0000, regions->pm1b_evt_blk + regions->pm1_evt_len/2 ); + outw( 0xffff, regions->pm1b_evt_blk ); + } + + if( regions->gpe0_blk ) + { + for( i=0 ; i<(regions->gpe0_len/2) ; i++ ) + { + outb( 0x00, regions->gpe0_blk + regions->gpe0_len/2 + i ); + outb( 0xff, regions->gpe0_blk + i ); + } + } + + if( regions->gpe1_blk ) + { + for( i=0 ; i<(regions->gpe1_len/2) ; i++ ) + { + outb( 0x00, regions->gpe1_blk + regions->gpe1_len/2 + i ); + outb( 0xff, regions->gpe1_blk + i ); + } + } + + return 0; + + cleanup9: + if( regions->gpe1_blk ) + { + release_region( regions->gpe1_blk, regions->gpe1_len ); + regions->gpe1_blk = 0; + } + + cleanup8: + if( regions->gpe0_blk ) + { + release_region( regions->gpe0_blk, regions->gpe0_len ); + regions->gpe0_blk = 0; + } + + cleanup7: + if( regions->p_blk ) + { + release_region( regions->p_blk, 6 ); + regions->p_blk = 0; + } + + cleanup6: + if( regions->pm_tmr_blk ) + { + release_region( regions->pm_tmr_blk, regions->pm_tmr_len ); + regions->pm_tmr_blk = 0; + } + + cleanup5: + if( regions->pm2_cnt_blk ) + { + release_region( regions->pm2_cnt_blk, regions->pm2_cnt_len ); + regions->pm2_cnt_blk = 0; + } + + cleanup4: + if( regions->pm1b_cnt_blk ) + { + release_region( regions->pm1b_cnt_blk, regions->pm1_cnt_len ); + regions->pm1b_cnt_blk = 0; + } + + cleanup3: + if( regions->pm1a_cnt_blk ) + { + release_region( regions->pm1a_cnt_blk, regions->pm1_cnt_len ); + regions->pm1a_cnt_blk = 0; + } + + cleanup2: + if( regions->pm1b_evt_blk ) + { + release_region( regions->pm1b_evt_blk, regions->pm1_evt_len ); + regions->pm1b_evt_blk = 0; + } + + cleanup1: + if( regions->pm1a_evt_blk ) + { + release_region( regions->pm1a_evt_blk, regions->pm1_evt_len ); + regions->pm1a_evt_blk = 0; + } + + cleanup0: + free_region_names( regions ); + + return -EBUSY; +} + +static int unregister_acpi_regions( generic_acpi_regions *regions ) +{ + if( regions->pm1a_evt_blk && regions->pm1_evt_len ) + { + release_region( regions->pm1a_evt_blk, regions->pm1_evt_len ); + regions->pm1a_evt_blk = 0; + } + + if( regions->pm1b_evt_blk && regions->pm1_evt_len ) + { + release_region( regions->pm1b_evt_blk, regions->pm1_evt_len ); + regions->pm1b_evt_blk = 0; + } + + if( regions->pm1a_cnt_blk && regions->pm1_cnt_len ) + { + release_region( regions->pm1a_cnt_blk, regions->pm1_cnt_len ); + regions->pm1a_cnt_blk = 0; + } + + if( regions->pm1b_cnt_blk && regions->pm1_cnt_len ) + { + release_region( regions->pm1b_cnt_blk, regions->pm1_cnt_len ); + regions->pm1b_cnt_blk = 0; + } + + if( regions->pm2_cnt_blk && regions->pm2_cnt_len ) + { + release_region( regions->pm2_cnt_blk, regions->pm2_cnt_len ); + regions->pm2_cnt_blk = 0; + } + + if( regions->pm_tmr_blk && regions->pm_tmr_len ) + { + release_region( regions->pm_tmr_blk, regions->pm_tmr_len ); + regions->pm_tmr_blk = 0; + } + + if( regions->p_blk ) + { + release_region( regions->p_blk, 6 ); + regions->p_blk = 0; + } + + if( regions->gpe0_blk && regions->gpe0_len ) + { + release_region( regions->gpe0_blk, regions->gpe0_len ); + regions->gpe0_blk = 0; + } + + if( regions->gpe1_blk && regions->gpe1_len ) + { + release_region( regions->gpe1_blk, regions->gpe1_len ); + regions->gpe1_blk = 0; + } + + if( regions->gpe_ref_cnt ) + kfree( regions->gpe_ref_cnt ); + + free_region_names( regions ); + + return 0; +} + +static void cobalt_acpi_handle_pm1_blk( u16 io_addr, u16 len, + generic_acpi_regions * regions ) +{ + cobalt_acpi_evt evt; + u16 sts, en; + + evt.hw_type = regions->hw_type; + + if( (sts = inw( io_addr )) && + (en = inw( io_addr + len/2 )) ) + { + + + /* clear status bits */ + outw( sts, io_addr); + + if( (en & GEN_ACPI_TMR_STS) && + (sts & GEN_ACPI_TMR_STS) ) + { + evt.ev_type = COBALT_ACPI_EVT_TMR; + evt.ev_data = 0x0; + cobalt_acpi_post_event( evt ); + } + if( (en & GEN_ACPI_BM_STS) && + (sts & GEN_ACPI_BM_STS) ) + { + evt.ev_type = COBALT_ACPI_EVT_BM; + evt.ev_data = 0x0; + cobalt_acpi_post_event( evt ); + } + if( (en & GEN_ACPI_GBL_STS) && + (sts & GEN_ACPI_GBL_STS) ) + { + evt.ev_type = COBALT_ACPI_EVT_GBL; + evt.ev_data = 0x0; + cobalt_acpi_post_event( evt ); + } + if( (en & GEN_ACPI_PWRBTN_STS) && + (sts & GEN_ACPI_PWRBTN_STS) ) + { + evt.ev_type = COBALT_ACPI_EVT_PWRBTN; + evt.ev_data = 0x0; + cobalt_acpi_post_event( evt ); + } + if( (en & GEN_ACPI_SLPBTN_STS) && + (sts & GEN_ACPI_SLPBTN_STS) ) + { + evt.ev_type = COBALT_ACPI_EVT_SLPBTN; + evt.ev_data = 0x0; + cobalt_acpi_post_event( evt ); + } + if( (en & GEN_ACPI_RTC_STS) && + (sts & GEN_ACPI_RTC_STS) ) + { + evt.ev_type = COBALT_ACPI_EVT_RTC; + evt.ev_data = 0x0; + cobalt_acpi_post_event( evt ); + } + if( (sts & GEN_ACPI_WAK_STS) ) + { + evt.ev_type = COBALT_ACPI_EVT_WAK; + evt.ev_data = 0x0; + cobalt_acpi_post_event( evt ); + } + } +} + +static void cobalt_acpi_handle_gpe_blk( u16 io_addr, u16 len, + generic_acpi_regions * regions ) +{ + cobalt_acpi_evt evt; + int i,j; + u8 sts, en; + + evt.hw_type = regions->hw_type; + evt.ev_type = COBALT_ACPI_EVT_GPE; + + for( i=0 ; i<(len/2) ; i++ ) + { + sts = inb( io_addr + i ); + en = inb( io_addr + len/2 + i ); + + /* clear status bits */ + outb( sts, io_addr); + + for( j=0 ; j<8 ; j++ ) + { + if( (en & 0x1) && + (sts & 0x1) ) + { + evt.ev_data = i*8 + j; + cobalt_acpi_post_event( evt ); + } + en >>= 1; + sts >>= 1; + } + } +} + +static int cobalt_acpi_generic_hw_handler( int irq, void *dev_id, + struct pt_regs *regs, void * data ) +{ + generic_acpi_regions * regions = (generic_acpi_regions *) data; + cobalt_acpi_evt evt; + + evt.hw_type = regions->hw_type; + + /* various PM events */ + if( regions->pm1a_evt_blk ) + cobalt_acpi_handle_pm1_blk( regions->pm1a_evt_blk, regions->pm1_evt_len, regions ); + + if( regions->pm1b_evt_blk ) + cobalt_acpi_handle_pm1_blk( regions->pm1b_evt_blk, regions->pm1_evt_len, regions ); + + if( regions->gpe0_blk ) + cobalt_acpi_handle_gpe_blk( regions->gpe0_blk, regions->gpe0_len, regions ); + + if( regions->gpe1_blk ) + cobalt_acpi_handle_gpe_blk( regions->gpe1_blk, regions->gpe1_len, regions ); + + + return 0; +} + +static int cobalt_acpi_generic_en_handler( u16 ev_type, u16 ev_data, int en, void *data ) +{ + generic_acpi_regions * regions = (generic_acpi_regions *) data; + int block, offset; + u8 data8; + u16 data16; + + switch( ev_type ) + { + case COBALT_ACPI_EVT_TMR: + data16 = inw( regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + + if( en ) + { + data16 |= GEN_ACPI_TMR_STS; + atomic_inc( ®ions->tmr_ref_cnt ); + } + else + { + if( atomic_dec_and_test( ®ions->tmr_ref_cnt ) ) + data16 &= ~GEN_ACPI_TMR_STS; + } + outw( data16, regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + break; + + case COBALT_ACPI_EVT_BM: + data16 = inw( regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + + if( en ) + { + data16 |= GEN_ACPI_BM_STS; + atomic_inc( ®ions->bm_ref_cnt ); + } + else + { + if( atomic_dec_and_test( ®ions->bm_ref_cnt ) ) + data16 &= ~GEN_ACPI_BM_STS; + } + outw( data16, regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + break; + + case COBALT_ACPI_EVT_GBL: + data16 = inw( regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + + if( en ) + { + data16 |= GEN_ACPI_GBL_STS; + atomic_inc( ®ions->gbl_ref_cnt ); + } + else + { + if( atomic_dec_and_test( ®ions->gbl_ref_cnt ) ) + data16 &= ~GEN_ACPI_GBL_STS; + } + outw( data16, regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + break; + + case COBALT_ACPI_EVT_PWRBTN: + data16 = inw( regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + + if( en ) + { + data16 |= GEN_ACPI_PWRBTN_STS; + atomic_inc( ®ions->pwrbtn_ref_cnt ); + } + else + { + if( atomic_dec_and_test( ®ions->pwrbtn_ref_cnt ) ) + data16 &= ~GEN_ACPI_PWRBTN_STS; + } + outw( data16, regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + break; + + case COBALT_ACPI_EVT_SLPBTN: + data16 = inw( regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + + if( en ) + { + data16 |= GEN_ACPI_SLPBTN_STS; + atomic_inc( ®ions->slpbtn_ref_cnt ); + } + else + { + if( atomic_dec_and_test( ®ions->slpbtn_ref_cnt ) ) + data16 &= ~GEN_ACPI_SLPBTN_STS; + } + outw( data16, regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + break; + + case COBALT_ACPI_EVT_RTC: + data16 = inw( regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + + if( en ) + { + data16 |= GEN_ACPI_RTC_STS; + atomic_inc( ®ions->rtc_ref_cnt ); + } + else + { + if( atomic_dec_and_test( ®ions->rtc_ref_cnt ) ) + data16 &= ~GEN_ACPI_RTC_STS; + } + outw( data16, regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + break; + + case COBALT_ACPI_EVT_WAK: + data16 = inw( regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + + if( en ) + { + data16 |= GEN_ACPI_WAK_STS; + atomic_inc( ®ions->wak_ref_cnt ); + } + else + { + if( atomic_dec_and_test( ®ions->wak_ref_cnt ) ) + data16 &= ~GEN_ACPI_WAK_STS; + } + outw( data16, regions->pm1a_evt_blk + (regions->pm1_evt_len / 2) ); + break; + + case COBALT_ACPI_EVT_GPE: + if( (ev_data/8) >= (regions->gpe0_len / 2) ) + return -EINVAL; + + block = ev_data / 8; + offset = ev_data % 8; + + data8 = inb( regions->gpe0_blk + (regions->gpe0_len / 2) + block ); + + if( en ) + { + data8 |= 0x1 << offset; + atomic_inc( ®ions->gpe_ref_cnt[ev_data] ); + } + else + { + if( atomic_dec_and_test( ®ions->gpe_ref_cnt[ev_data] ) ) + data8 &= ~( 0x1 << offset ); + } + + outb( data8, regions->gpe0_blk + (regions->gpe0_len / 2) + block ); + + break; + + default: + return -EINVAL; + + } + + return 0; +} + +/* + * + * Generic ServerWorks region code + * + */ + +static int get_serverworks_regions( generic_acpi_regions *regions, u16 type ) +{ + int reg; + + memset( regions, 0x0, sizeof( *regions ) ); + + regions->hw_type = type; + + regions->pm1_evt_len = 4; + regions->pm1_cnt_len = 2; + regions->pm_tmr_len = 4; + regions->gpe0_len = 8; + + if( (reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x20)) ) + regions->pm1a_evt_blk = (u16) reg; + + if( (reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x22)) ) + regions->pm1a_cnt_blk = (u16) reg; + + if( (reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x24)) ) + regions->pm_tmr_blk = (u16) reg; + + if( (reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x26)) ) + regions->p_blk = (u16) reg; + + if( (reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x28)) ) + regions->gpe0_blk = (u16) reg; + + if( type == COBALT_ACPI_HW_OSB4 ) + { + regions->pm2_cnt_len = 1; + if( (reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x2E)) ) + regions->pm2_cnt_blk = (u16) reg; + } + + switch( type ) + { + case COBALT_ACPI_HW_OSB4: + return register_acpi_regions( regions, "OSB4" ); + + case COBALT_ACPI_HW_CSB5: + return register_acpi_regions( regions, "CSB5" ); + } + + return -EINVAL; + +} + +/* + * + * ServerWorks OSB4 + * + */ + +static generic_acpi_regions osb4_regions; + +static int cobalt_acpi_osb4_init( void ) +{ + int err; + + if( (err = get_osb4_regions( &osb4_regions )) < 0 ) + return err; + + if( (err = cobalt_acpi_register_hw_handler( COBALT_ACPI_HW_OSB4, + cobalt_acpi_generic_hw_handler, + cobalt_acpi_generic_en_handler, + &osb4_regions )) < 0 ) + return err; + + return 0; +} + +static int cobalt_acpi_osb4_cleanup( void ) +{ + unregister_acpi_regions( &osb4_regions ); + return 0; +} + +static int get_osb4_regions( generic_acpi_regions *regions) +{ + return get_serverworks_regions( regions, COBALT_ACPI_HW_OSB4 ); +} + +/* + * + * ServerWorks CSB5 + * + */ + +/* static generic_acpi_regions csb5_regions; */ + +static generic_acpi_regions csb5_regions; + +static int cobalt_acpi_csb5_init( void ) +{ + int err; + + if( (err = get_csb5_regions( &csb5_regions )) < 0 ) + return err; + + if( (err = cobalt_acpi_register_hw_handler( COBALT_ACPI_HW_CSB5, + cobalt_acpi_generic_hw_handler, + cobalt_acpi_generic_en_handler, + &csb5_regions )) < 0 ) + return err; + + return 0; +} + +static int cobalt_acpi_csb5_cleanup( void ) +{ + unregister_acpi_regions( &csb5_regions ); + return 0; +} + +static int get_csb5_regions( generic_acpi_regions *regions) +{ + return get_serverworks_regions( regions, COBALT_ACPI_HW_CSB5 ); +} + +/* + * + * NatSemi PC8731x + * + */ +static generic_acpi_regions pc8731x_regions; + +static int cobalt_acpi_pc8731x_init( void ) +{ + int err; + + if( (err = get_pc8731x_regions( &pc8731x_regions )) < 0 ) + return err; + + if( (err = cobalt_acpi_register_hw_handler( COBALT_ACPI_HW_PC8731X, + cobalt_acpi_generic_hw_handler, + cobalt_acpi_generic_en_handler, + &pc8731x_regions )) < 0 ) + return err; + + return 0; +} + +static int cobalt_acpi_pc8731x_cleanup( void ) +{ + unregister_acpi_regions( &pc8731x_regions ); + return 0; +} + +static int get_pc8731x_regions( generic_acpi_regions *regions ) +{ + int reg; + u16 addr; + + memset( regions, 0x0, sizeof( *regions ) ); + + regions->hw_type = COBALT_ACPI_HW_PC8731X; + + regions->pm1_evt_len = 4; + regions->pm1_cnt_len = 2; + regions->pm_tmr_len = 4; + regions->gpe0_len = 4; + + /* superi/o -- select pm logical device and get base address */ + addr = superio_ldev_base(PC87317_DEV_PM); + if( addr ) + { + /* get registers */ + if( (reg = get_reg(addr, addr + 1, 0x08)) ) + regions->pm1a_evt_blk = reg; + + if( (reg = get_reg(addr, addr + 1, 0x0a)) ) + regions->pm_tmr_blk = reg; + + if( (reg = get_reg(addr, addr + 1, 0x0c)) ) + regions->pm1a_cnt_blk = reg; + + if( (reg = get_reg(addr, addr + 1, 0x0e)) ) + regions->gpe0_blk = reg; + } + + return register_acpi_regions( regions, "pc8731x" ); +} + +/* + * + * NatSemi PC8741x + * + */ + +static generic_acpi_regions pc8741x_regions; + +static int cobalt_acpi_pc8741x_init( void ) +{ + int err; + + if( (err = get_pc8741x_regions( &pc8741x_regions )) < 0 ) + return err; + + if( (err = cobalt_acpi_register_hw_handler( COBALT_ACPI_HW_PC8741X, + cobalt_acpi_generic_hw_handler, + cobalt_acpi_generic_en_handler, + &pc8741x_regions )) < 0 ) + return err; + + return 0; +} + +static int cobalt_acpi_pc8741x_cleanup( void ) +{ + unregister_acpi_regions( &pc8741x_regions ); + return 0; +} + +static int get_pc8741x_regions( generic_acpi_regions *regions ) +{ + int reg; + + memset( regions, 0x0, sizeof( *regions ) ); + + regions->hw_type = COBALT_ACPI_HW_PC8741X; + + regions->pm1_evt_len = 4; + regions->pm1_cnt_len = 2; + regions->pm_tmr_len = 4; + regions->gpe0_len = 8; + + /* get registers */ + if( (reg = superio_ldev_base_n(PC87417_DEV_SWC, 1)) ) + regions->pm1a_evt_blk = reg; + + if( (reg = superio_ldev_base_n(PC87417_DEV_SWC, 2)) ) + regions->pm1a_cnt_blk = reg; + + if( (reg = superio_ldev_base_n(PC87417_DEV_SWC, 3)) ) + regions->gpe0_blk = reg; + + return register_acpi_regions( regions, "pc8741x" ); +} + +/* + * + * Platform support + * + */ + +/* + * + * Monterey + * + */ + +static u16 cobalt_acpi_monterey_osb4_table[] = { +/* GPE 0 */ COBALT_ACPI_EVT_NONE, +/* GPE 1 */ COBALT_ACPI_EVT_NONE, +/* GPE 2 */ COBALT_ACPI_EVT_NONE, +/* GPE 3 */ COBALT_ACPI_EVT_NONE, +/* GPE 4 */ COBALT_ACPI_EVT_NONE, +/* GPE 5 */ COBALT_ACPI_EVT_NONE, +/* GPE 6 */ COBALT_ACPI_EVT_SLED, +/* GPE 7 */ COBALT_ACPI_EVT_NONE, +/* GPE 8 */ COBALT_ACPI_EVT_NONE, +/* GPE 9 */ COBALT_ACPI_EVT_NONE, +/* GPE 10 */ COBALT_ACPI_EVT_NONE, +/* GPE 11 */ COBALT_ACPI_EVT_NONE, +/* GPE 12 */ COBALT_ACPI_EVT_NONE, +/* GPE 13 */ COBALT_ACPI_EVT_NONE, +/* GPE 14 */ COBALT_ACPI_EVT_NONE, +/* GPE 15 */ COBALT_ACPI_EVT_NONE }; + +static u16 cobalt_acpi_monterey_superio_table[] = { +/* GPE 0 */ COBALT_ACPI_EVT_NONE, +/* GPE 1 */ COBALT_ACPI_EVT_NONE, +/* GPE 2 */ COBALT_ACPI_EVT_NONE, +/* GPE 3 */ COBALT_ACPI_EVT_NONE, +/* GPE 4 */ COBALT_ACPI_EVT_NONE, +/* GPE 5 */ COBALT_ACPI_EVT_NONE, +/* GPE 6 */ COBALT_ACPI_EVT_NONE, +/* GPE 7 */ COBALT_ACPI_EVT_NONE, +/* GPE 8 */ COBALT_ACPI_EVT_NONE, +/* GPE 9 */ COBALT_ACPI_EVT_NONE, +/* GPE 10 */ COBALT_ACPI_EVT_NONE, +/* GPE 11 */ COBALT_ACPI_EVT_NONE, +/* GPE 12 */ COBALT_ACPI_EVT_NONE, +/* GPE 13 */ COBALT_ACPI_EVT_NONE, +/* GPE 14 */ COBALT_ACPI_EVT_NONE, +/* GPE 15 */ COBALT_ACPI_EVT_NONE, +/* GPE 16 */ COBALT_ACPI_EVT_NONE, +/* GPE 17 */ COBALT_ACPI_EVT_NONE, +/* GPE 18 */ COBALT_ACPI_EVT_NONE, +/* GPE 19 */ COBALT_ACPI_EVT_NONE, +/* GPE 20 */ COBALT_ACPI_EVT_NONE, +/* GPE 21 */ COBALT_ACPI_EVT_NONE, +/* GPE 22 */ COBALT_ACPI_EVT_NONE, +/* GPE 23 */ COBALT_ACPI_EVT_NONE, +/* GPE 24 */ COBALT_ACPI_EVT_NONE, +/* GPE 25 */ COBALT_ACPI_EVT_NONE, +/* GPE 26 */ COBALT_ACPI_EVT_NONE, +/* GPE 27 */ COBALT_ACPI_EVT_NONE, +/* GPE 28 */ COBALT_ACPI_EVT_NONE, +/* GPE 29 */ COBALT_ACPI_EVT_NONE, +/* GPE 30 */ COBALT_ACPI_EVT_NONE, +/* GPE 31 */ COBALT_ACPI_EVT_NONE }; + +static int cobalt_acpi_monterey_init( void ) +{ + int err; + + err = cobalt_acpi_register_trans_table( COBALT_ACPI_HW_OSB4, + sizeof( cobalt_acpi_monterey_osb4_table )/sizeof( u16 ), + cobalt_acpi_monterey_osb4_table ); + if( err < 0 ) + return err; + + err = cobalt_acpi_register_trans_table( COBALT_ACPI_HW_PC8731X, + sizeof( cobalt_acpi_monterey_superio_table )/sizeof( u16 ), + cobalt_acpi_monterey_superio_table ); + if( err < 0 ) + return err; + + return 0; +} + +static int cobalt_acpi_monterey_cleanup( void ) +{ + cobalt_acpi_unregister_trans_table( COBALT_ACPI_HW_OSB4 ); + cobalt_acpi_unregister_trans_table( COBALT_ACPI_HW_PC8731X ); + + return 0; +} + +/* + * + * Alpine + * + */ + +static u16 cobalt_acpi_alpine_csb5_table[] = { +/* GPE 0 */ COBALT_ACPI_EVT_NONE, +/* GPE 1 */ COBALT_ACPI_EVT_NONE, +/* GPE 2 */ COBALT_ACPI_EVT_NONE, +/* GPE 3 */ COBALT_ACPI_EVT_FAN, +/* GPE 4 */ COBALT_ACPI_EVT_NONE, +/* GPE 5 */ COBALT_ACPI_EVT_SM_INT, +/* GPE 6 */ COBALT_ACPI_EVT_THERM, +/* GPE 7 */ COBALT_ACPI_EVT_NONE, +/* GPE 8 */ COBALT_ACPI_EVT_NONE, +/* GPE 9 */ COBALT_ACPI_EVT_NONE, +/* GPE 10 */ COBALT_ACPI_EVT_NONE, +/* GPE 11 */ COBALT_ACPI_EVT_NONE, +/* GPE 12 */ COBALT_ACPI_EVT_NONE, +/* GPE 13 */ COBALT_ACPI_EVT_NONE, +/* GPE 14 */ COBALT_ACPI_EVT_NONE, +/* GPE 15 */ COBALT_ACPI_EVT_NONE }; + +static u16 cobalt_acpi_alpine_superio_table[] = { +/* GPE 0 */ COBALT_ACPI_EVT_NONE, +/* GPE 1 */ COBALT_ACPI_EVT_NONE, +/* GPE 2 */ COBALT_ACPI_EVT_NONE, +/* GPE 3 */ COBALT_ACPI_EVT_NONE, +/* GPE 4 */ COBALT_ACPI_EVT_NONE, +/* GPE 5 */ COBALT_ACPI_EVT_NONE, +/* GPE 6 */ COBALT_ACPI_EVT_NONE, +/* GPE 7 */ COBALT_ACPI_EVT_NONE, +/* GPE 8 */ COBALT_ACPI_EVT_NONE, +/* GPE 9 */ COBALT_ACPI_EVT_NONE, +/* GPE 10 */ COBALT_ACPI_EVT_NONE, +/* GPE 11 */ COBALT_ACPI_EVT_NONE, +/* GPE 12 */ COBALT_ACPI_EVT_NONE, +/* GPE 13 */ COBALT_ACPI_EVT_NONE, +/* GPE 14 */ COBALT_ACPI_EVT_NONE, +/* GPE 15 */ COBALT_ACPI_EVT_NONE, +/* GPE 16 */ COBALT_ACPI_EVT_NONE, +/* GPE 17 */ COBALT_ACPI_EVT_NONE, +/* GPE 18 */ COBALT_ACPI_EVT_NONE, +/* GPE 19 */ COBALT_ACPI_EVT_NONE, +/* GPE 20 */ COBALT_ACPI_EVT_NONE, +/* GPE 21 */ COBALT_ACPI_EVT_NONE, +/* GPE 22 */ COBALT_ACPI_EVT_NONE, +/* GPE 23 */ COBALT_ACPI_EVT_NONE, +/* GPE 24 */ COBALT_ACPI_EVT_NONE, +/* GPE 25 */ COBALT_ACPI_EVT_NONE, +/* GPE 26 */ COBALT_ACPI_EVT_NONE, +/* GPE 27 */ COBALT_ACPI_EVT_NONE, +/* GPE 28 */ COBALT_ACPI_EVT_NONE, +/* GPE 29 */ COBALT_ACPI_EVT_NONE, +/* GPE 30 */ COBALT_ACPI_EVT_NONE, +/* GPE 31 */ COBALT_ACPI_EVT_NONE }; + +static int cobalt_acpi_alpine_init( void ) +{ + int err; + + err = cobalt_acpi_register_trans_table( COBALT_ACPI_HW_CSB5, + sizeof( cobalt_acpi_alpine_csb5_table )/sizeof( u16 ), + cobalt_acpi_alpine_csb5_table ); + if( err < 0 ) + return err; + + err = cobalt_acpi_register_trans_table( COBALT_ACPI_HW_PC8741X, + sizeof( cobalt_acpi_alpine_superio_table )/sizeof( u16 ), + cobalt_acpi_alpine_superio_table ); + if( err < 0 ) + return err; + + return 0; +} + +static int cobalt_acpi_alpine_cleanup( void ) +{ + cobalt_acpi_unregister_trans_table( COBALT_ACPI_HW_CSB5 ); + cobalt_acpi_unregister_trans_table( COBALT_ACPI_HW_PC8741X ); + + return 0; +} + +/* + * end platform support + */ +#ifdef CONFIG_COBALT_EMU_ACPI +/* + * This is all necessary because we don't have BIOS support for ACPI yet. + * We can fake it here, and when full support is ready just yank this. + */ +typedef struct { + char *device_type; + char *device_instance; + u32 event_type; + u32 event_data; + struct list_head list; +} cobalt_acpi_event_t; + +#define COBALT_ACPI_MAX_STRING_LENGTH 80 + +static LIST_HEAD(cobalt_acpi_event_list); +static DECLARE_WAIT_QUEUE_HEAD(cobalt_acpi_event_wait_queue); +static int event_is_open = 0; +static spinlock_t cobalt_acpi_event_lock = SPIN_LOCK_UNLOCKED; + +static struct proc_dir_entry *cobalt_acpi_proc_root; +static struct proc_dir_entry *cobalt_acpi_proc_event; + +static struct file_operations proc_event_ops = { + open: cobalt_acpi_open_event, + read: cobalt_acpi_read_event, + release: cobalt_acpi_close_event, + poll: cobalt_acpi_poll_event, +}; + +static int +cobalt_acpi_setup_proc(void) +{ + cobalt_acpi_proc_root = proc_mkdir("acpi", NULL); + if (!cobalt_acpi_proc_root) { + return -ENOMEM; + } + + cobalt_acpi_proc_event = create_proc_entry("event", S_IRUSR, + cobalt_acpi_proc_root); + if (!cobalt_acpi_proc_event) { + return -ENOMEM; + } + + cobalt_acpi_proc_event->proc_fops = &proc_event_ops; + + return 0; +} + + +int +cobalt_acpi_generate_proc_evt( cobalt_acpi_evt * evt ) +{ + cobalt_acpi_event_t *event = NULL, *tmp; + unsigned long flags = 0; + char *dev_type; + char *dev_instance; + u32 event_type; + u32 event_data; + struct list_head *pos; + + /* drop event on the floor if no one's listening */ + if (!event_is_open) + return 0; + + event_type = (evt->ev_type << 0x10) | + evt->hw_type; + event_data = evt->ev_data; + + + /* + * Check to see if an event of this type is already + * pending + */ + spin_lock_irqsave(&cobalt_acpi_event_lock, flags); + list_for_each( pos, &cobalt_acpi_event_list ) + { + tmp = list_entry(pos, cobalt_acpi_event_t, list); + if( (tmp->event_type == event_type) && + (tmp->event_data == event_data) ) + { + spin_unlock_irqrestore(&cobalt_acpi_event_lock, flags); + return 0; + } + + + } + spin_unlock_irqrestore(&cobalt_acpi_event_lock, flags); + + + /* parse the event struct */ + switch( evt->ev_type ) + { + case COBALT_ACPI_EVT_TMR: + dev_type = "generic"; + dev_instance = "timer"; + break; + + case COBALT_ACPI_EVT_BM: + dev_type = "generic"; + dev_instance = "bus-master"; + break; + + case COBALT_ACPI_EVT_GBL: + dev_type = "generic"; + dev_instance = "global"; + break; + + case COBALT_ACPI_EVT_PWRBTN: + dev_type = "button"; + dev_instance = "power"; + break; + + case COBALT_ACPI_EVT_SLPBTN: + dev_type = "button"; + dev_instance = "sleep"; + break; + + case COBALT_ACPI_EVT_RTC: + dev_type = "generic"; + dev_instance = "rtc"; + break; + + case COBALT_ACPI_EVT_WAK: + dev_type = "generic"; + dev_instance = "wake"; + break; + + case COBALT_ACPI_EVT_GPE: + dev_type = "generic"; + dev_instance = "gpe"; + break; + + case COBALT_ACPI_EVT_SLED: + dev_type = "cobalt"; + dev_instance = "sled"; + break; + + case COBALT_ACPI_EVT_THERM: + dev_type = "cobalt"; + dev_instance = "therm_trip"; + break; + + case COBALT_ACPI_EVT_FAN: + dev_type = "cobalt"; + dev_instance = "fan"; + break; + + case COBALT_ACPI_EVT_SM_INT: + dev_type = "cobalt"; + dev_instance = "sm_int"; + break; + + case COBALT_ACPI_EVT_VOLT: + dev_type = "cobalt"; + dev_instance = "volt_trip"; + break; + + default: + dev_type = "unknown"; + dev_instance = "unknown"; + break; + } + + + /* + * Allocate a new event structure. + */ + event = kmalloc(sizeof(*event), GFP_ATOMIC); + if (!event) + goto alloc_error; + + event->device_type=NULL; + event->device_instance=NULL; + + event->device_type = kmalloc(strlen(dev_type) + sizeof(char), + GFP_ATOMIC); + if (!event->device_type) + goto alloc_error; + + event->device_instance = kmalloc(strlen(dev_instance) + sizeof(char), + GFP_ATOMIC ); + if (!event->device_instance) + goto alloc_error; + + /* + * Set event data. + */ + strcpy(event->device_type, dev_type); + strcpy(event->device_instance, dev_instance); + event->event_type = event_type; + event->event_data = event_data; + + /* + * Add to the end of our event list. + */ + spin_lock_irqsave(&cobalt_acpi_event_lock, flags); + list_add_tail(&event->list, &cobalt_acpi_event_list); + spin_unlock_irqrestore(&cobalt_acpi_event_lock, flags); + + /* + * Signal waiting threads (if any). + */ + wake_up_interruptible(&cobalt_acpi_event_wait_queue); + + return 0; + +alloc_error: + if(event) + { + if (event->device_instance) + kfree(event->device_instance); + + if (event->device_type) + kfree(event->device_type); + + kfree(event); + } + + return -ENOMEM; +} + + +static int +cobalt_acpi_open_event(struct inode *inode, struct file *file) +{ + unsigned long flags; + spin_lock_irqsave(&cobalt_acpi_event_lock, flags); + + if (event_is_open) + goto out_busy; + + event_is_open = 1; + + spin_unlock_irqrestore(&cobalt_acpi_event_lock, flags); + return 0; + +out_busy: + spin_unlock_irqrestore(&cobalt_acpi_event_lock, flags); + return -EBUSY; +} + + +static int +cobalt_acpi_close_event(struct inode *inode, struct file *file) +{ + unsigned long flags; + struct list_head *pos; + cobalt_acpi_event_t *tmp; + + spin_lock_irqsave(&cobalt_acpi_event_lock, flags); + + while( (pos = list_pop( &cobalt_acpi_event_list )) ) + { + tmp = list_entry(pos, cobalt_acpi_event_t, list); + if (tmp->device_instance) + kfree(tmp->device_instance); + + if (tmp->device_type) + kfree(tmp->device_type); + + kfree( tmp ); + } + event_is_open = 0; + spin_unlock_irqrestore(&cobalt_acpi_event_lock, flags); + return 0; +} + +#define ACPI_MAX_STRING_LENGTH 80 +static ssize_t +cobalt_acpi_read_event(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + cobalt_acpi_event_t *event = NULL; + unsigned long flags = 0; + static char str[ACPI_MAX_STRING_LENGTH]; + static int strsize; + static char *ptr; + + if (!strsize) { + DECLARE_WAITQUEUE(wait, current); + + if (list_empty(&cobalt_acpi_event_list)) { + if (file->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&cobalt_acpi_event_wait_queue, &wait); + + if (list_empty(&cobalt_acpi_event_list)) { + schedule(); + } + + remove_wait_queue(&cobalt_acpi_event_wait_queue, &wait); + set_current_state(TASK_RUNNING); + + if (signal_pending(current)) { + return -ERESTARTSYS; + } + } + + spin_lock_irqsave(&cobalt_acpi_event_lock, flags); + event = list_entry(cobalt_acpi_event_list.next, + cobalt_acpi_event_t, list); + list_del(&event->list); + spin_unlock_irqrestore(&cobalt_acpi_event_lock, flags); + + strsize = sprintf(str, "%s %s %08x %08x\n", + event->device_type, event->device_instance, + event->event_type, event->event_data); + ptr = str; + + kfree(event->device_type); + kfree(event->device_instance); + kfree(event); + } + if (strsize < count) + count = strsize; + + if (copy_to_user(buf, ptr, count)) + return -EFAULT; + + *ppos += count; + strsize -= count; + ptr += count; + + return count; +} + +static unsigned int +cobalt_acpi_poll_event(struct file *file, poll_table *wait) +{ + poll_wait(file, &cobalt_acpi_event_wait_queue, wait); + if (!list_empty(&cobalt_acpi_event_list)) + return POLLIN | POLLRDNORM; + return 0; +} + +#endif /* CONFIG_COBALT_EMU_ACPI */ diff -Naur linux-2.6.18.2.orig/drivers/cobalt/fans.c linux-2.6.18.2/drivers/cobalt/fans.c --- linux-2.6.18.2.orig/drivers/cobalt/fans.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/fans.c 2006-11-15 21:20:19.738033319 -0800 @@ -0,0 +1,419 @@ +/* $Id: fans.c,v 1.18 2002/03/16 21:33:02 duncan Exp $ + * Copyright (c) 2000-2001 Sun Microsystems, Inc + * + * This should be SMP safe. The critical data (the info list) and the + * critical code (inb()/outb() calls) are protected by fan_lock. It is + * locked at the only external access points - the proc read()/write() + * methods. --TPH + */ +#include +#if defined(CONFIG_COBALT_FANS) || defined(CONFIG_COBALT_FANS_MODULE) + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define FAN_DRIVER "Cobalt Networks Fan driver" +#define FAN_DRIVER_VMAJ 1 +#define FAN_DRIVER_VMIN 0 + +/* GPIO base is assigned by BIOS, perhaps we should probe it */ +#define GPIO_BASE 0x600 +#define FAN_GPIO_MAX 8 +#define FAN_RPM(fn,ms) ((fn).hcyl * (60000000 / (fn).poles) / (ms)) +#define FAN_VALID(f) ((f)->mask && (f)->poles) +#define FAN_CACHE_TIME 2 /* seconds */ +#define FAN_SAMPLE_LEN 50 /* milliseconds */ + +/* + * fans are attached to GPIO pins + * each pin is part of a port, multiple fans are controlled by a port + */ +struct fan_info { + int id; /* fan number */ + uint8_t mask; /* mask within the port */ + int poles; /* # of magnetic poles (divisor) */ + int hcyl; /* # of half cycles */ + unsigned rpm; /* calculated fan speed */ + char *type; /* FAN description */ +}; + +struct fan_gpio { + int port; /* GPIO Port */ + uint16_t base; /* GPDI (data in) base address */ + uint8_t latch; /* latched 'data in' value */ + long tcache; /* latched 'epoch' value */ + struct fan_info fan[FAN_GPIO_MAX]; +}; + +/* the current fanlist */ +static struct fan_gpio *sys_fanlist; +static spinlock_t fan_lock = SPIN_LOCK_UNLOCKED; + +static struct fan_gpio fan_gpio_raqxtr[] = { + { + port: 1, + base: GPIO_BASE, + fan: { + { mask: 0x2, poles: 4, type: "processor" }, + { mask: 0x4, poles: 4, type: "processor" }, + { mask: 0 }, + }, + }, + { + port: 2, + base: GPIO_BASE+4, + fan: { + { mask: 0x10, poles: 4 }, + { mask: 0x20, poles: 4 }, + { mask: 0x40, poles: 4 }, + { mask: 0x80, poles: 4 }, + { mask: 0 }, + }, + }, + { port: -1 } +}; + +static struct fan_gpio fan_gpio_alpine[] = { + { + port: 2, + base: GPIO_BASE+7, + fan: { + { mask: 0x4, poles: 4 }, + { mask: 0x8, poles: 4 }, + { mask: 0x10, poles: 4 }, + { mask: 0x20, poles: 4, type: "power supply" }, + { mask: 0x40, poles: 4, type: "processor" }, + { mask: 0 }, + }, + }, + { port: -1 } +}; + +#ifdef CONFIG_PROC_FS +#ifdef CONFIG_COBALT_OLDPROC +static struct proc_dir_entry *proc_faninfo; +#endif /* CONFIG_COBALT_OLDPROC */ +static struct proc_dir_entry *proc_cfaninfo; +#endif /* CONFIG_PROC_FS */ + +static struct fan_info *fan_info_find(int id); +static int fan_control(struct fan_info *fi, int todo); +static int fan_info_print(char *buffer); +static int fan_read_proc(char *buf, char **start, off_t pos, + int len, int *eof, void *x); +static int fan_write_proc(struct file *file, const char *buf, + unsigned long len, void *x); + +int __init +cobalt_fan_init(void) +{ + if (cobt_is_monterey()) { + sys_fanlist = (struct fan_gpio *)fan_gpio_raqxtr; + } else if (cobt_is_alpine()) { + sys_fanlist = (struct fan_gpio *)fan_gpio_alpine; + } else { + sys_fanlist = NULL; + return -ENOSYS; + } + + printk(KERN_INFO "%s %d.%d (modified by jeff@404ster.com)\n", FAN_DRIVER,FAN_DRIVER_VMAJ,FAN_DRIVER_VMIN); + +#ifdef CONFIG_PROC_FS +#ifdef CONFIG_COBALT_OLDPROC + proc_faninfo = create_proc_entry("faninfo", S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, NULL); + if (!proc_faninfo) { + EPRINTK("can't create /proc/faninfo\n"); + return -ENOENT; + } + proc_faninfo->owner = THIS_MODULE; + proc_faninfo->read_proc = fan_read_proc; + proc_faninfo->write_proc = fan_write_proc; +#endif /* CONFIG_COBALT_OLDPROC */ + proc_cfaninfo = create_proc_entry("faninfo", S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, proc_cobalt); + if (!proc_cfaninfo) { + EPRINTK("can't create /proc/cobalt/faninfo\n"); + return -ENOENT; + } + proc_cfaninfo->owner = THIS_MODULE; + proc_cfaninfo->read_proc = fan_read_proc; + proc_cfaninfo->write_proc = fan_write_proc; +#endif /* CONFIG_PROC_FS */ + + return 0; +} + +static void __exit +cobalt_fan_exit(void) +{ +#ifdef CONFIG_PROC_FS +#ifdef CONFIG_COBALT_OLDPROC + if (proc_faninfo) { + remove_proc_entry("faninfo", NULL); + } +#endif /* CONFIG_COBALT_OLDPROC */ + if (proc_cfaninfo) { + remove_proc_entry("faninfo", proc_cobalt); + } +#endif /* CONFIG_PROC_FS */ + + sys_fanlist = NULL; +} + +/* + * Samples fan tachometer square wave to calculate and report RPM + */ +static int +get_faninfo(char *buffer) +{ + struct fan_gpio *fg; + struct timeval utime; + unsigned long elapsed, start; + int i, val, len; + + if (!sys_fanlist || !cobt_is_5k()) { + /* software is keyed off this string - do not change it ! */ + return sprintf(buffer, "Fan monitoring not supported.\n"); + } + + /* save start timestamp */ + do_gettimeofday(&utime); + start = utime.tv_usec; + + /* initialize 'previous' values. we do edge detection by + * looking for transitions from previous values */ + for (fg = sys_fanlist; fg->port >= 0; fg++) { + if (fg->tcache && utime.tv_sec < fg->tcache+FAN_CACHE_TIME) { + return fan_info_print(buffer); + } + fg->tcache = utime.tv_sec; + fg->latch = inb(fg->base); + for (i = 0; i < FAN_GPIO_MAX; i++) { + fg->fan[i].hcyl = 0; + fg->fan[i].rpm = 0; + } + } + + /* We are counting the number of halfcycles in a square wave + * that pass in a given amount of time to determine frequency */ + do { + for (fg=sys_fanlist; fg->port>=0; fg++) { + val = inb(fg->base); + for (i=0; ifan[i]; + if (FAN_VALID(p)) { + if ((val ^ fg->latch) & p->mask) { + p->hcyl++; + } + } + } + fg->latch = val; + } + + do_gettimeofday(&utime); + if (utime.tv_usec > start) { + elapsed = utime.tv_usec - start; + } else { + elapsed = utime.tv_usec + 1000001 - start; + } + + } while (elapsed < (FAN_SAMPLE_LEN) * 1000); + + /* Fan rpm = 60 / ( t * poles ) + * where t is 1/2 the period and poles are the number of + * magnetic poles for the fan. + * + * For the Sunon KDE1204PKBX fans on Raq XTR, poles = 4 + * So, in terms of cycles, + * + * rpm = 60 s/m halfcycles + * ------ * -------------- * 1,000,000 us/s * 2 + * 4 2 * elapsed us + * + * = (60,000,000 / 4 poles) * halfcycles / elapsed + * = 15,000,000 * halfcycles / elapsed + * + * Note, by this method and sampling for 50ms, our accuracy + * is +/- 300 rpm. The fans are spec'ed for +/- 1000 rpm + */ + for (val=len=0, fg=sys_fanlist; fg->port>=0; fg++) { + for (i=0; ifan[i]; + if (FAN_VALID(p)) { + p->id = val++; + p->rpm = FAN_RPM(fg->fan[i], elapsed); + len += sprintf(buffer+len, "fan %d : %u\n", + p->id, p->rpm); + } + } + } + + return len; +} + +static int +fan_info_print(char *buffer) +{ + struct fan_gpio *fg; + int i, len=0; + + if (!sys_fanlist) { + return -1; + } + + for (fg=sys_fanlist; fg->port>=0; fg++) { + for (i=0; ifan[i]; + if (FAN_VALID(p)) { + len += sprintf(buffer+len, "fan %d : %u\n", + p->id, p->rpm); + } + } + } + + return len; +} + +/* FIXME: generify */ +static int +fan_control(struct fan_info *fi, int todo) +{ + if (fi && cobt_is_alpine()) { + switch (fi->id) { + case 4: { + /* CPU FAN */ + uint8_t gpdo = inb(GPIO_BASE+6); + + if (todo) { + gpdo &= ~fi->mask; /* 0 = on */ + } else { + gpdo |= fi->mask; /* 1 = off */ + } + outb(gpdo, GPIO_BASE+6); + return 0; + } + default: + return -ENODEV; + } + } + + return -ENOSYS; +} + +static struct fan_info * +fan_info_find(int id) +{ + struct fan_gpio *fg; + int i; + + if (!sys_fanlist) { + return NULL; + } + + for (fg=sys_fanlist; fg->port>=0; fg++) { + for (i=0; ifan[i])) { + if (fg->fan[i].id == id) { + return &fg->fan[i]; + } + } + } + } + + return NULL; +} + +#ifdef CONFIG_PROC_FS +static int +fan_read_proc(char *buf, char **start, off_t pos, int len, int *eof, void *x) +{ + int plen; + + //MOD_INC_USE_COUNT; + + spin_lock(&fan_lock); + plen = get_faninfo(buf); + spin_unlock(&fan_lock); + + //MOD_DEC_USE_COUNT; + + return cobalt_gen_proc_read(buf, plen, start, pos, len, eof); +} + +static int +fan_write_proc(struct file *file, const char *buf, unsigned long len, void *x) +{ + char *page; + int retval = -EINVAL; + + //MOD_INC_USE_COUNT; + + if (len > PAGE_SIZE) { + //MOD_DEC_USE_COUNT; + return -EOVERFLOW; + } + + page = (char *)__get_free_page(GFP_KERNEL); + if (!page) { + //MOD_DEC_USE_COUNT; + return -ENOMEM; + } + + if (copy_from_user(page, buf, len)) { + free_page((unsigned long)page); + //MOD_DEC_USE_COUNT; + return -EFAULT; + } + page[len] = '\0'; + + /* format: `fan ID COMMAND' */ + if (len>5 && !strncmp("fan ", page, 4)) { + if (*(page+4) != '\0') { + struct fan_info *finf; + char *nextpg = NULL; + + spin_lock(&fan_lock); + finf = fan_info_find(simple_strtoul(page+4,&nextpg,0)); + if (!finf) { + retval = -ENOENT; + } else if (nextpg != '\0') { + if (!strncmp("on", nextpg+1, 2)) { + retval = fan_control(finf, 1); + } + else if (!strncmp("off", nextpg+1, 3)) { + retval = fan_control(finf, 0); + } + } + spin_unlock(&fan_lock); + } + } + + free_page((unsigned long)page); + //MOD_DEC_USE_COUNT; + + return (retval < 0) ? retval : len; +} +#endif /* CONFIG_PROC_FS */ + +#if defined(CONFIG_COBALT_FANS_MODULE) +module_init(cobalt_fan_init); +module_exit(cobalt_fan_exit); + +MODULE_AUTHOR("Sun Cobalt"); +MODULE_DESCRIPTION("Sun Cobalt fan tachometers"); +#endif + +#endif /* CONFIG_COBALT_FANS || CONFIG_COBALT_FANS_MODULE */ diff -Naur linux-2.6.18.2.orig/drivers/cobalt/i2c.c linux-2.6.18.2/drivers/cobalt/i2c.c --- linux-2.6.18.2.orig/drivers/cobalt/i2c.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/i2c.c 2006-11-15 21:20:19.738033319 -0800 @@ -0,0 +1,519 @@ +/* + * $Id: i2c.c,v 1.19 2002/09/17 23:41:29 sparker Exp $ + * i2c.c : Cobalt I2C driver support + * + * Copyright (C) 2000 Cobalt Networks, Inc. + * Copyright (C) 2001 Sun Microsystems, Inc. + * + * Modified By: jeff@404ster.com + * + * This should be SMP safe. All the exported functions lock on enter and + * unlock on exit. These exported functions may be called at interupt time, + * so we have to use the IRQ safe locks. NOTE: no function herein may call + * any exported function herein. --TPH + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define I2C_3K_STATUS 0x00 +#define I2C_3K_CMD 0x01 +#define I2C_3K_START 0x02 +#define I2C_3K_ADDR 0x03 +#define I2C_3K_LOW_DATA 0x04 +#define I2C_3K_HIGH_DATA 0x05 +#define I2C_3K_BLOCK_DATA 0x06 +#define I2C_3K_INDEX 0x07 +#define I2C_3K_STATUS_IDLE 0x04 +#define I2C_3K_CMD_RW_BYTE 0x20 +#define I2C_3K_CMD_RW_WORD 0x30 +#define I2C_3K_CMD_RW_BLOCK 0xC0 +#define I2C_3K_CMD_RESET_PTR 0x80 + +#define I2C_5K_HOST_STATUS 0x00 +#define I2C_5K_SLAVE_STATUS 0x01 +#define I2C_5K_HOST_CONTROL 0x02 +#define I2C_5K_HOST_COMMAND 0x03 +#define I2C_5K_HOST_ADDR 0x04 +#define I2C_5K_DATA_0 0x05 +#define I2C_5K_DATA_1 0x06 +#define I2C_5K_BLOCK_DATA 0x07 +#define I2C_5K_SLAVE_CONTROL 0x08 +#define I2C_5K_SHADOW_COMMAND 0x09 +#define I2C_5K_SLAVE_EVENT 0x0a +#define I2C_5K_SLAVE_DATA 0x0c +#define I2C_5K_HOST_STATUS_BUSY 0x01 +#define I2C_5K_HOST_CMD_START 0x40 +#define I2C_5K_HOST_CMD_QUICK_RW (0 << 2) +#define I2C_5K_HOST_CMD_BYTE_RW (1 << 2) +#define I2C_5K_HOST_CMD_BYTE_DATA_RW (2 << 2) +#define I2C_5K_HOST_CMD_WORD_DATA_RW (3 << 2) +#define I2C_5K_HOST_CMD_BLOCK_DATA_RW (5 << 2) + +#define I2C_WRITE 0 +#define I2C_READ 1 + +/* this delay was determined empirically */ +#define I2C_WRITE_UDELAY 1000 + +struct cobalt_i2c_data { + const unsigned char status; + const unsigned char addr; + const unsigned char index; + const unsigned char data_low; + const unsigned char data_high; + const unsigned char data_block; + const unsigned char rw_byte; + const unsigned char rw_word; + const unsigned char rw_block; + unsigned int io_port; +}; + +struct cobalt_i2c_data cobalt_i2c_3k = { + I2C_3K_STATUS, + I2C_3K_ADDR, + I2C_3K_INDEX, + I2C_3K_LOW_DATA, + I2C_3K_HIGH_DATA, + I2C_3K_BLOCK_DATA, + I2C_3K_CMD_RW_BYTE, + I2C_3K_CMD_RW_WORD, + I2C_3K_CMD_RW_BLOCK, + 0L +}; + +struct cobalt_i2c_data cobalt_i2c_5k = { + I2C_5K_HOST_STATUS, + I2C_5K_HOST_ADDR, + I2C_5K_HOST_COMMAND, + I2C_5K_DATA_0, + I2C_5K_DATA_1, + I2C_5K_BLOCK_DATA, + I2C_5K_HOST_CMD_BYTE_DATA_RW, + I2C_5K_HOST_CMD_WORD_DATA_RW, + I2C_5K_HOST_CMD_BLOCK_DATA_RW, + 0L +}; + +/* a global pointer for our i2c data */ +struct cobalt_i2c_data *i2c_data; + +#define I2C_REG(r) (i2c_data->io_port + i2c_data->r) +#define I2C_CMD(c) (i2c_data->c) + +#define I2C_LOCK (1 << 0) +#define I2C_DEAD (1 << 1) +static unsigned long i2c_state; + +static int initialized; + +static inline int +do_i2c_lock(void) +{ + int i = 0; + + if (test_bit(I2C_DEAD, &i2c_state)) + return -1; + + while (test_and_set_bit(I2C_LOCK, &i2c_state)) { + if (i++ > 5) + return -1; + udelay(10); + } + udelay(1); + return 0; +} + +static inline void +do_i2c_unlock(void) +{ + clear_bit(I2C_LOCK, &i2c_state); +} + +/* do a little squelching */ +#define NOISE_RATE (5*HZ) +static int +i2c_noisy(void) +{ + static unsigned long last_time; + static unsigned int messages; + + if ((long) (jiffies - last_time) > NOISE_RATE) { + last_time = jiffies; + if (messages) { + WPRINTK("skipped %u kernel messages\n", messages); + messages = 0; + } + return 0; + } + messages++; + return 1; +} + +static int +i2c_wait_for_smi(void) +{ + static unsigned int shutup = 0; + int timeout=10; + int status; + + while (timeout--) { + udelay(100); /* wait */ + status = inb_p(I2C_REG(status)); + + if (cobt_is_3k()) { + if (status & I2C_3K_STATUS_IDLE) { + return 0; + } + } else if (cobt_is_5k()) { + if (!(status & I2C_5K_HOST_STATUS_BUSY)) { + return 0; + } + } + outb_p(status, I2C_REG(status)); + } + + /* still busy - complain */ + if (!i2c_noisy()) { + if (++shutup > 2) { + EPRINTK("i2c seems to be dead - sorry\n"); + set_bit(I2C_DEAD, &i2c_state); + } else { + WPRINTK("i2c timeout: status busy (0x%x), resetting\n", + status); + } + } + + /* punch the abort bit */ + if (cobt_is_3k()) { + outb_p(4, i2c_data->io_port + I2C_3K_CMD); + } else if (cobt_is_5k()) { + outb_p(2, i2c_data->io_port + I2C_5K_HOST_CONTROL); + outb_p(1, i2c_data->io_port + I2C_5K_HOST_CONTROL); + } + + return -1; +} + +static inline int +i2c_setup(const int dev, const int index, const int r) +{ + if (i2c_wait_for_smi() < 0) + return -1; + + /* clear status */ + outb_p(0xff, I2C_REG(status)); + + /* device address */ + outb_p((dev|r) & 0xff, I2C_REG(addr)); + + /* I2C index */ + outb_p(index & 0xff, I2C_REG(index)); + + return 0; +} + +static inline int +i2c_cmd(const unsigned char command) +{ + if (cobt_is_3k()) { + outb_p(command, i2c_data->io_port + I2C_3K_CMD); + outb_p(0xff, i2c_data->io_port + I2C_3K_START); + } else if (cobt_is_5k()) { + outb_p(I2C_5K_HOST_CMD_START | command, + i2c_data->io_port + I2C_5K_HOST_CONTROL); + } + + if (i2c_wait_for_smi() < 0) + return -1; + + return 0; +} + +int +cobalt_i2c_init(void) +{ + struct pci_dev *i2cdev = NULL; + + if( ! initialized ) { + if (cobt_is_3k()) { + i2c_data = &cobalt_i2c_3k; + i2cdev = pci_find_device(PCI_VENDOR_ID_AL, + PCI_DEVICE_ID_AL_M7101, NULL); + if (!i2cdev) { + EPRINTK("can't find PMU for i2c access\n"); + return -1; + } + pci_read_config_dword(i2cdev, 0x14, &i2c_data->io_port); + } else if (cobt_is_5k()) { + i2c_data = &cobalt_i2c_5k; + i2cdev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_OSB4, i2cdev); + if (!i2cdev) { + i2cdev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_CSB5, i2cdev); + if (!i2cdev) { + EPRINTK("can't find OSB4 or CSB5 for i2c access\n"); + return -1; + } + } + pci_read_config_dword(i2cdev, 0x90, &i2c_data->io_port); + } + + i2c_data->io_port &= 0xfff0; + if (!i2c_data->io_port) { + EPRINTK("i2c IO port not found\n"); + } + initialized = 1; + } + + return 0; +} + +int +cobalt_i2c_reset(void) +{ + int r; + + if( !initialized ) { + if( cobalt_i2c_init() < 0 ) + return -1; + } + + if (do_i2c_lock() < 0) + return -1; + + if (cobt_is_3k()) { + /* clear status */ + outb_p(0xff, i2c_data->io_port + I2C_3K_STATUS); + /* reset SMB devs */ + outb_p(0x08, i2c_data->io_port + I2C_3K_CMD); + /* start command */ + outb_p(0xff, i2c_data->io_port + I2C_3K_START); + } else if (cobt_is_5k()) { + /* clear status */ + outb_p(0x2, i2c_data->io_port + I2C_5K_HOST_CONTROL); + outb_p(0x1, i2c_data->io_port + I2C_5K_HOST_CONTROL); + outb_p(0xff, i2c_data->io_port + I2C_5K_HOST_STATUS); + outb_p(I2C_5K_HOST_CMD_START | 0x08, + i2c_data->io_port + I2C_5K_HOST_CONTROL); + } + + r = i2c_wait_for_smi(); + + do_i2c_unlock(); + + return r; +} + +int +cobalt_i2c_read_byte(const int dev, const int index) +{ + int val = 0; + + if( !initialized ) { + if( cobalt_i2c_init() < 0 ) + return -1; + } + + if (do_i2c_lock() < 0) + return -1; + + if (i2c_setup(dev, index, I2C_READ) < 0 + || i2c_cmd(I2C_CMD(rw_byte)) < 0) { + val = -1; + } + + if (val == 0) { + val = inb_p(I2C_REG(data_low)); + } + + do_i2c_unlock(); + + return val; +} + +int +cobalt_i2c_read_word(const int dev, const int index) +{ + int val = 0; + + if( !initialized ) { + if( cobalt_i2c_init() < 0 ) + return -1; + } + + if (do_i2c_lock() < 0) + return -1; + + if (i2c_setup(dev, index, I2C_READ) < 0 + || i2c_cmd(I2C_CMD(rw_word)) < 0) { + val = -1; + } + + if (val == 0) { + val = inb_p(I2C_REG(data_low)); + val += inb_p(I2C_REG(data_high)) << 8; + } + + do_i2c_unlock(); + + return val; +} + +int +cobalt_i2c_read_block(const int dev, const int index, + unsigned char *data, int count) +{ + if( !initialized ) { + if( cobalt_i2c_init() < 0 ) + return -1; + } + + if (do_i2c_lock() < 0) + return -1; + + if (i2c_setup(dev, index, I2C_READ) < 0) { + do_i2c_unlock(); + return -1; + } + + outb_p(count & 0xff, I2C_REG(data_low)); + outb_p(count & 0xff, I2C_REG(data_high)); + + if (i2c_cmd(I2C_CMD(rw_block)) < 0) { + do_i2c_unlock(); + return -1; + } + + while (count) { + /* read a byte of block data */ + *data = inb_p(I2C_REG(data_block)); + data++; + count--; + } + + do_i2c_unlock(); + + return 0; +} + +int +cobalt_i2c_write_byte(const int dev, const int index, const u8 val) +{ + int r = 0; + + if( !initialized ) { + if( cobalt_i2c_init() < 0 ) + return -1; + } + + if (do_i2c_lock() < 0) + return -1; + + if (i2c_setup(dev, index, I2C_WRITE) < 0) { + r = -1; + } + + if (r == 0) { + outb_p(val & 0xff, I2C_REG(data_low)); + + if (i2c_cmd(I2C_CMD(rw_byte)) < 0) { + r = -1; + } + } + + udelay(I2C_WRITE_UDELAY); + + do_i2c_unlock(); + + return r; +} + +int +cobalt_i2c_write_word(const int dev, const int index, const u16 val) +{ + int r = 0; + + if( !initialized ) { + if( cobalt_i2c_init() < 0 ) + return -1; + } + + if (do_i2c_lock() < 0) + return -1; + + if (i2c_setup(dev, index, I2C_WRITE) < 0) { + r = -1; + } + + if (r == 0) { + outb_p(val & 0xff, I2C_REG(data_low)); + outb_p((val >> 8) & 0xff, I2C_REG(data_high)); + + if (i2c_cmd(I2C_CMD(rw_word)) < 0) { + r = -1; + } + } + + udelay(I2C_WRITE_UDELAY); + + do_i2c_unlock(); + + return r; +} + +int +cobalt_i2c_write_block(int dev, int index, unsigned char *data, int count) +{ + if( !initialized ) { + if( cobalt_i2c_init() < 0 ) + return -1; + } + + if (do_i2c_lock() < 0) + return -1; + + if (i2c_setup(dev, index, I2C_WRITE) < 0) { + do_i2c_unlock(); + return -1; + } + + outb_p(count & 0xff, I2C_REG(data_low)); + outb_p(count & 0xff, I2C_REG(data_high)); + + if (i2c_cmd(I2C_CMD(rw_block)) < 0) { + do_i2c_unlock(); + return -1; + } + + while (count) { + /* write a byte of block data */ + outb_p(*data, I2C_REG(data_block)); + data++; + count--; + } + + udelay(I2C_WRITE_UDELAY); + + do_i2c_unlock(); + + return 0; +} + +EXPORT_SYMBOL(cobalt_i2c_reset); +EXPORT_SYMBOL(cobalt_i2c_read_byte); +EXPORT_SYMBOL(cobalt_i2c_read_word); +EXPORT_SYMBOL(cobalt_i2c_read_block); +EXPORT_SYMBOL(cobalt_i2c_write_byte); +EXPORT_SYMBOL(cobalt_i2c_write_word); +EXPORT_SYMBOL(cobalt_i2c_write_block); diff -Naur linux-2.6.18.2.orig/drivers/cobalt/init.c linux-2.6.18.2/drivers/cobalt/init.c --- linux-2.6.18.2.orig/drivers/cobalt/init.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/init.c 2006-11-15 21:20:19.738033319 -0800 @@ -0,0 +1,114 @@ +/* $Id: init.c,v 1.22 2002/11/04 17:54:15 thockin Exp $ */ +/* + * Copyright (c) 2001 Sun Microsystems + * Generic initialization, to reduce pollution of other files + */ +#include +#include +#include +#include +#include +#include +#include +#include + +static int cobalt_proc_init(void); +extern int cobalt_i2c_init(void); +extern int cobalt_net_init(void); +extern int cobalt_systype_init(void); +extern void cobalt_boardrev_init(void); +extern int cobalt_led_init(void); +extern int cobalt_lcd_init(void); +extern int cobalt_serialnum_init(void); +extern int cobalt_wdt_init(void); +extern int cobalt_sensors_init(void); +extern int cobalt_fan_init(void); +extern int cobalt_acpi_init(void); +extern int cobalt_ruler_init(void); +extern int cobalt_raminfo_init(void); + +#ifdef CONFIG_PROC_FS +struct proc_dir_entry *proc_cobalt; +EXPORT_SYMBOL(proc_cobalt); +#endif +spinlock_t cobalt_superio_lock = SPIN_LOCK_UNLOCKED; + +/* initialize all the cobalt specific stuff */ +int __init +cobalt_init(void) +{ + cobalt_proc_init(); + cobalt_systype_init(); +#ifdef CONFIG_COBALT_RAQ + /* we might keep the boardrev on an i2c chip */ + cobalt_i2c_init(); +#endif + cobalt_boardrev_init(); +#ifdef CONFIG_COBALT_ACPI + cobalt_acpi_init(); +#endif +#ifdef CONFIG_COBALT_LED + cobalt_net_init(); + cobalt_led_init(); +#endif +#ifdef CONFIG_COBALT_LCD + cobalt_lcd_init(); +#endif +#ifdef CONFIG_COBALT_RULER + cobalt_ruler_init(); +#endif +#ifdef CONFIG_COBALT_SERNUM + cobalt_serialnum_init(); +#endif +#ifdef CONFIG_COBALT_RAQ + /* some systems use WDT it for reboot */ + cobalt_wdt_init(); +#endif +#ifdef CONFIG_COBALT_SENSORS + cobalt_sensors_init(); +#endif +#ifdef CONFIG_COBALT_FANS + cobalt_fan_init(); +#endif +#ifdef CONFIG_COBALT_RAMINFO + cobalt_raminfo_init(); +#endif + return 0; +} + +static int __init +cobalt_proc_init(void) +{ +#ifdef CONFIG_PROC_FS + proc_cobalt = proc_mkdir("cobalt", 0); + if (!proc_cobalt) { + EPRINTK("can't create /proc/cobalt\n"); + return -1; + } +#endif + + return 0; +} + +/* a function that handles the blah stuff in a simple proc read function */ +int +cobalt_gen_proc_read(char *buf, int plen, char **start, off_t pos, + int len, int *eof) +{ + /* trying to read a bad offset? */ + if (pos >= plen) { + *eof = 1; + return 0; + } + + /* did we write everything we wanted to? */ + if (len >= (plen-pos)) { + *eof = 1; + } + + *start = buf + pos; + plen -= pos; + + return (len > plen) ? plen : len; +} +EXPORT_SYMBOL(cobalt_gen_proc_read); diff -Naur linux-2.6.18.2.orig/drivers/cobalt/lcd.c linux-2.6.18.2/drivers/cobalt/lcd.c --- linux-2.6.18.2.orig/drivers/cobalt/lcd.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/lcd.c 2006-11-15 21:20:19.738033319 -0800 @@ -0,0 +1,835 @@ +/* + * $Id: lcd.c,v 1.44 2002/05/10 18:44:45 duncan Exp $ + * lcd.c : driver for Cobalt LCD/Buttons + * + * Copyright 1996-2000 Cobalt Networks, Inc. + * Copyright 2001 Sun Microsystems, Inc. + * + * By: Andrew Bose + * Timothy Stonis + * Tim Hockin + * Adrian Sun + * Duncan Laurie + * + * Modified By: jeff@404ster.com + * + * This should be SMP safe. We're hardly performance critical, + * so we lock around lcd_ioctl() and just where needed by other external + * functions. There is a static global waiters variable that is atomic_t, and + * so should be safe. --TPH + */ + +#include + +#ifdef CONFIG_COBALT_LCD + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define TWIDDLE_HZ (HZ/10) + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#define LCD_DRIVER "Cobalt Networks LCD driver" +#define LCD_DRIVER_VMAJ 4 +#define LCD_DRIVER_VMIN 0 + +/* io registers */ +#define LPT 0x0378 +#define LCD_DATA_ADDRESS LPT+0 +#define LCD_CONTROL_ADDRESS LPT+2 + +/* LCD device info */ +#define LCD_Addr 0x80 +#define DD_R00 0x00 +#define DD_R01 0x27 +#define DD_R10 0x40 +#define DD_R11 0x67 + +/* driver functions */ +static int cobalt_lcd_open(struct inode *, struct file *); +static ssize_t cobalt_lcd_read(struct file *, char *, size_t, loff_t *); +static int cobalt_lcd_read_proc(char *, char **, off_t, int, int *, void *); +static char *cobalt_lcddev_read_line(int, char *); +static int cobalt_lcd_ioctl(struct inode *, struct file *, + unsigned int, unsigned long); +static int cobalt_lcd_panic(struct notifier_block *self, unsigned long, void *); + +/* globals used throughout */ +#ifdef CONFIG_PROC_FS +#ifdef CONFIG_COBALT_OLDPROC +static struct proc_dir_entry *proc_lcd; +#endif +static struct proc_dir_entry *proc_clcd; +#endif +static int lcd_present; +static int has_i2c_lcd; +static spinlock_t lcd_lock = SPIN_LOCK_UNLOCKED; + +/* various file operations we support for this driver */ +static struct file_operations lcd_fops = { + .read = cobalt_lcd_read, + .ioctl = cobalt_lcd_ioctl, + .open = cobalt_lcd_open, +}; + +/* device structure */ +static struct miscdevice lcd_dev = { + MISC_DYNAMIC_MINOR, + "lcd", + &lcd_fops +}; + +static int disable_lcd; +static int __init +lcd_disable_setup(char *str) +{ + disable_lcd = 1; + return 0; +} +__setup("nolcd", lcd_disable_setup); + +/* Read a control instruction from the LCD */ +static inline int +lcddev_read_inst(void) +{ + int a = 0; + + if (cobt_is_5k() && has_i2c_lcd) { + a = cobalt_i2c_read_byte( + COBALT_I2C_DEV_LCD_INST | COBALT_I2C_READ, 0); + } else if (cobt_is_3k() || (cobt_is_5k() && !has_i2c_lcd)) { + outb(0x21, LCD_CONTROL_ADDRESS); /* RS=0, R/W=1, E=0 */ + outb(0x20, LCD_CONTROL_ADDRESS); /* RS=0, R/W=1, E=1 */ + a = inb(LCD_DATA_ADDRESS); + outb(0x21, LCD_CONTROL_ADDRESS); /* RS=0, R/W=1, E=0 */ + outb(0x01, LCD_CONTROL_ADDRESS); /* RS=0, R/W=1, E=0 */ + } + + /* small delay */ + udelay(100); + + return a; +} + +#define LCD_MAX_POLL 10000 +static inline void +lcddev_poll_wait(void) +{ + int i=0; + + while (i++ < LCD_MAX_POLL) { + int r = lcddev_read_inst(); + if (r < 0 || !(r & 0x80)) + break; + } +} + +/* Write a control instruction to the LCD */ +static inline void +lcddev_write_inst(unsigned char data) +{ + lcddev_poll_wait(); + + if (cobt_is_5k() && has_i2c_lcd) { + cobalt_i2c_write_byte( + COBALT_I2C_DEV_LCD_INST | COBALT_I2C_WRITE, 0, data); + } else if (cobt_is_3k() || (cobt_is_5k() && !has_i2c_lcd)) { + outb(0x03, LCD_CONTROL_ADDRESS); /* RS=0, R/W=0, E=0 */ + outb(data, LCD_DATA_ADDRESS); + outb(0x02, LCD_CONTROL_ADDRESS); /* RS=0, R/W=0, E=1 */ + outb(0x03, LCD_CONTROL_ADDRESS); /* RS=0, R/W=0, E=0 */ + outb(0x01, LCD_CONTROL_ADDRESS); /* RS=0, R/W=1, E=0 */ + } + + /* small delay */ + udelay(100); +} + +/* Write one byte of data to the LCD */ +static inline void +lcddev_write_data(unsigned char data) +{ + lcddev_poll_wait(); + + if (cobt_is_5k() && has_i2c_lcd) { + cobalt_i2c_write_byte( + COBALT_I2C_DEV_LCD_DATA | COBALT_I2C_WRITE, 0, data); + } else if (cobt_is_3k() || (cobt_is_5k() && !has_i2c_lcd)) { + outb(0x07, LCD_CONTROL_ADDRESS); /* RS=1, R/W=0, E=0 */ + outb(data, LCD_DATA_ADDRESS); + outb(0x06, LCD_CONTROL_ADDRESS); /* RS=1, R/W=0, E=1 */ + outb(0x07, LCD_CONTROL_ADDRESS); /* RS=1, R/W=0, E=0 */ + outb(0x05, LCD_CONTROL_ADDRESS); /* RS=1, R/W=1, E=0 */ + } + /* small delay */ + udelay(100); +} + +/* Read one byte of data from the LCD */ +static inline unsigned char +lcddev_read_data(void) +{ + unsigned char a = 0; + + lcddev_poll_wait(); + + if (cobt_is_5k() && has_i2c_lcd) { + a = cobalt_i2c_read_byte( + COBALT_I2C_DEV_LCD_DATA | COBALT_I2C_READ, 0); + } else if (cobt_is_3k() || (cobt_is_5k() && !has_i2c_lcd)) { + outb(0x25, LCD_CONTROL_ADDRESS); /* RS=1, R/W=1, E=0 */ + outb(0x24, LCD_CONTROL_ADDRESS); /* RS=1, R/W=1, E=1 */ + a = inb(LCD_DATA_ADDRESS); + outb(0x25, LCD_CONTROL_ADDRESS); /* RS=1, R/W=1, E=0 */ + outb(0x01, LCD_CONTROL_ADDRESS); /* RS=1, R/W=1, E=0 */ + } + + /* small delay */ + udelay(100); + + return a; +} + +static inline void +lcddev_init(void) +{ + lcddev_write_inst(0x38); + lcddev_write_inst(0x38); + lcddev_write_inst(0x38); + lcddev_write_inst(0x06); + lcddev_write_inst(0x0c); +} + +static inline char +read_buttons(void) +{ + char r = 0; + + if (cobt_is_5k() && has_i2c_lcd) { + unsigned char inst; + inst = cobalt_i2c_read_byte(COBALT_I2C_DEV_FP_BUTTONS, 0); + switch (inst) { + case 0x3e: r = BUTTON_Next_B; break; + case 0x3d: r = BUTTON_Enter_B; break; + case 0x1f: r = BUTTON_Left_B; break; + case 0x3b: r = BUTTON_Right_B; break; + case 0x2f: r = BUTTON_Up_B; break; + case 0x37: r = BUTTON_Down_B; break; + case 0x3f: + default: r = BUTTON_NONE_B; + } + } else if (cobt_is_3k() || (cobt_is_5k() && !has_i2c_lcd)) { + outb(0x29, LCD_CONTROL_ADDRESS); /* Sel=0, Bi=1 */ + r = inb(LCD_DATA_ADDRESS) & BUTTON_MASK; + } + + return r; +} + +static inline int +button_pressed(void) +{ + unsigned char b; + unsigned long flags; + + spin_lock_irqsave(&lcd_lock, flags); + b = read_buttons(); + spin_unlock_irqrestore(&lcd_lock, flags); + + switch (b) { + case BUTTON_Next: + case BUTTON_Next_B: + case BUTTON_Reset_B: + return b; + default: + break; + } + + return 0; +} + +/* this could be protected by CAP_RAW_IO here, or by the FS permissions */ +static int +cobalt_lcd_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct lcd_display button_display, display; + unsigned long address, a; + int index; + int dlen = sizeof(struct lcd_display); + int r = 0; + unsigned long flags; + +#ifdef CONFIG_COBALT_LCD_TWIDDLE + cobalt_lcd_stop_twiddle(); +#endif + switch (cmd) { + /* Turn the LCD on */ + case LCD_On: + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(0x0F); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Turn the LCD off */ + case LCD_Off: + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(0x08); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Reset the LCD */ + case LCD_Reset: + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(0x3F); + lcddev_write_inst(0x3F); + lcddev_write_inst(0x3F); + lcddev_write_inst(0x3F); + lcddev_write_inst(0x01); + lcddev_write_inst(0x06); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Clear the LCD */ + case LCD_Clear: + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(0x01); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Move the cursor one position to the left */ + case LCD_Cursor_Left: + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(0x10); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Move the cursor one position to the right */ + case LCD_Cursor_Right: + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(0x14); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Turn the cursor off */ + case LCD_Cursor_Off: + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(0x0C); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Turn the cursor on */ + case LCD_Cursor_On: + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(0x0F); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Turn blinking off? I don't know what this does - TJS */ + case LCD_Blink_Off: + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(0x0E); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Get the current cursor position */ + case LCD_Get_Cursor_Pos: + spin_lock_irqsave(&lcd_lock, flags); + display.cursor_address = (unsigned char)lcddev_read_inst(); + display.cursor_address = display.cursor_address & 0x07F; + spin_unlock_irqrestore(&lcd_lock, flags); + if (copy_to_user((struct lcd_display *)arg, &display, dlen)) { + r = -EFAULT; + } + break; + + /* Set the cursor position */ + case LCD_Set_Cursor_Pos: + if (copy_from_user(&display, (struct lcd_display *)arg, dlen)) { + r = -EFAULT; + break; + } + a = display.cursor_address | LCD_Addr; + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(a); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Get the value at the current cursor position? - TJS */ + case LCD_Get_Cursor: + spin_lock_irqsave(&lcd_lock, flags); + display.character = lcddev_read_data(); + lcddev_write_inst(0x10); + spin_unlock_irqrestore(&lcd_lock, flags); + if (copy_to_user((struct lcd_display *)arg, &display, dlen)) { + r = -EFAULT; + } + break; + + /* Set the character at the cursor position? - TJS */ + case LCD_Set_Cursor: + if (copy_from_user(&display, (struct lcd_display *)arg, dlen)) { + r = -EFAULT; + break; + } + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_data(display.character); + lcddev_write_inst(0x10); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Dunno what this does - TJS */ + case LCD_Disp_Left: + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(0x18); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Dunno what this does - TJS */ + case LCD_Disp_Right: + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(0x1C); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Dunno what this does - TJS */ + case LCD_Home: + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(0x02); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Write a string to the LCD */ + case LCD_Write: + if (copy_from_user(&display, (struct lcd_display *)arg, dlen)) { + r = -EFAULT; + break; + } + + spin_lock_irqsave(&lcd_lock, flags); + + display.size1 = display.size1 > 0 ? + min(display.size1, (int) sizeof(display.line1)) : 0; + display.size2 = display.size2 > 0 ? + min(display.size2, (int) sizeof(display.line2)) : 0; + + /* First line */ + lcddev_write_inst(0x80); + for (index = 0; index < display.size1; index++) + lcddev_write_data(display.line1[index]); + for (index = display.size1; index < sizeof(display.line1); index++) + lcddev_write_data(' '); + + /* Second line */ + lcddev_write_inst(0xC0); + for (index = 0; index < display.size2; index++) + lcddev_write_data(display.line2[index]); + for (index = display.size2; index < sizeof(display.line2); index++) + lcddev_write_data(' '); + + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + /* Read what's on the LCD */ + case LCD_Read: + spin_lock_irqsave(&lcd_lock, flags); + + for (address = DD_R00; address <= DD_R01; address++) { + lcddev_write_inst(address | LCD_Addr); + display.line1[address] = lcddev_read_data(); + } + for (address = DD_R10; address <= DD_R11; address++) { + lcddev_write_inst(address | LCD_Addr); + display.line2[address - DD_R10] = lcddev_read_data(); + } + + spin_unlock_irqrestore(&lcd_lock, flags); + + display.line1[DD_R01] = '\0'; + display.line2[DD_R01] = '\0'; + + if (copy_to_user((struct lcd_display *)arg, &display, dlen)) { + r = -EFAULT; + } + break; + + case LCD_Raw_Inst: + if (copy_from_user(&display, (struct lcd_display *)arg, dlen)) { + r = -EFAULT; + break; + } + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(display.character); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + case LCD_Raw_Data: + if (copy_from_user(&display, (struct lcd_display *)arg, dlen)) { + r = -EFAULT; + break; + } + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_data(display.character); + spin_unlock_irqrestore(&lcd_lock, flags); + break; + + case LCD_Type: + if (cobt_is_5k() && has_i2c_lcd) { + if (put_user(LCD_TYPE_I2C, (int *)arg)) { + r = -EFAULT; + } + } else if (cobt_is_3k() || (cobt_is_5k() && !has_i2c_lcd)) { + if (put_user(LCD_TYPE_PARALLEL_B, (int *)arg)) { + r = -EFAULT; + } + } + break; + + /* Read the buttons */ + case BUTTON_Read: + spin_lock_irqsave(&lcd_lock, flags); + button_display.buttons = read_buttons(); + spin_unlock_irqrestore(&lcd_lock, flags); + if (copy_to_user((struct lcd_display *)arg, + &button_display, dlen)) { + r = -EFAULT; + } + break; + +#ifdef CONFIG_COBALT_LED + /* a slightly different api that allows you to set 32 leds */ + case LED32_Set: + cobalt_led_set_lazy(arg); + break; + + case LED32_Bit_Set: + cobalt_led_set_bits_lazy(arg); + break; + + case LED32_Bit_Clear: + cobalt_led_clear_bits_lazy(arg); + break; + + case LED32_Get: + *(unsigned int *)arg = cobalt_led_get(); + break; + + /* set all the leds */ + case LED_Set: + if (copy_from_user(&display, (struct lcd_display *)arg, dlen)) { + r = -EFAULT; + break; + } + cobalt_led_set_lazy(display.leds); + break; + + /* set a single led */ + case LED_Bit_Set: + if (copy_from_user(&display, (struct lcd_display *)arg, dlen)) { + r = -EFAULT; + break; + } + cobalt_led_set_bits_lazy(display.leds); + break; + + /* clear an led */ + case LED_Bit_Clear: + if (copy_from_user(&display, (struct lcd_display *)arg, dlen)) { + r = -EFAULT; + break; + } + cobalt_led_clear_bits_lazy(display.leds); + break; +#endif + + default: + break; + } + + return r; +} + +static int +cobalt_lcd_open(struct inode *inode, struct file *file) +{ + if (!lcd_present) { + return -ENXIO; + } else { + return 0; + } +} + +/* LCD daemon sits on this, we wake it up once a key is pressed */ +static ssize_t +cobalt_lcd_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + int bnow; + static unsigned long lcd_waiters; + + if (test_and_set_bit(0, &lcd_waiters)) { + return -EINVAL; + } + + while (((bnow = button_pressed()) == 0) && !(signal_pending(current))) { + if (file->f_flags & O_NONBLOCK) { + lcd_waiters = 0; + return -EAGAIN; + } + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(2 * HZ); + } + lcd_waiters = 0; + + if (signal_pending(current)) { + return -ERESTARTSYS; + } + + return bnow; +} + +/* read a single line from the LCD into a string */ +static char * +cobalt_lcddev_read_line(int lineno, char *line) +{ + unsigned long addr, min, max; + unsigned long flags; + + switch (lineno) { + case 0: + min = DD_R00; + max = DD_R01; + break; + case 1: + min = DD_R10; + max = DD_R11; + break; + default: + min = 1; + max = 0; + } + + spin_lock_irqsave(&lcd_lock, flags); + for (addr = min; addr <= max; addr++) { + lcddev_write_inst(addr | LCD_Addr); + udelay(150); + line[addr-min] = lcddev_read_data(); + udelay(150); + } + spin_unlock_irqrestore(&lcd_lock, flags); + line[addr-min] = '\0'; + + return line; +} + +#ifdef CONFIG_PROC_FS +static int +cobalt_lcd_read_proc(char *buf, char **start, off_t pos, + int len, int *eof, void *private) +{ + int plen = 0; + char line[COBALT_LCD_LINELEN+1]; + + /* first line */ + cobalt_lcddev_read_line(0, line); + plen += sprintf(buf+plen, "%s\n", line); + + /* second line */ + cobalt_lcddev_read_line(1, line); + plen += sprintf(buf+plen, "%s\n", line); + + return cobalt_gen_proc_read(buf, plen, start, pos, len, eof); +} +#endif + +static char *lcd_panic_str1 = "Kernel"; +static char *lcd_panic_str2 = "Panic!"; + +static int cobalt_lcd_panic(struct notifier_block *self, unsigned long a, void *b) +{ + int i; + int len; + + if( !lcd_present ) + return 0; + +#ifdef CONFIG_COBALT_LCD_TWIDDLE + cobalt_lcd_stop_twiddle(); +#endif + + lcddev_write_inst( (DD_R00) | LCD_Addr); + len = strlen( lcd_panic_str1 ); + for( i=0 ; i<16 ; i++ ) + lcddev_write_data( (i 11) { + state = -1; + pos = 10; + } + + lcddev_write_inst((DD_R10+4+pos) | LCD_Addr); + lcddev_write_data(0xff); + + spin_unlock_irqrestore(&lcd_lock, flags); + + mod_timer(&twiddle_timer, jiffies + TWIDDLE_HZ); +} + +void +cobalt_lcd_start_twiddle(void) +{ + init_timer(&twiddle_timer); + twiddle_timer.expires = jiffies + TWIDDLE_HZ; + twiddle_timer.data = 0; + twiddle_timer.function = &twiddle_timer_func; + add_timer(&twiddle_timer); + twiddling=1; +} + +void +cobalt_lcd_stop_twiddle(void) +{ + unsigned long flags; + + spin_lock_irqsave(&lcd_lock, flags); + if (twiddling) { + del_timer_sync(&twiddle_timer); + twiddling = 0; + } + spin_unlock_irqrestore(&lcd_lock, flags); +} +#endif /* CONFIG_COBALT_LCD_TWIDDLE */ + +/* stop the lcd */ +void cobalt_lcd_off(void) +{ + unsigned long flags; + + spin_lock_irqsave(&lcd_lock, flags); + lcddev_write_inst(0x01); /* clear */ + lcddev_write_inst(0x08); /* off */ + spin_unlock_irqrestore(&lcd_lock, flags); +} + +static int initialized; +static struct notifier_block lcd_nb; + +int __init +cobalt_lcd_init(void) +{ + int retval; + + if (initialized) + return 0; + + initialized=1; + printk(KERN_INFO "%s %d.%d (modified by jeff@404ster.com)\n", LCD_DRIVER,LCD_DRIVER_VMAJ,LCD_DRIVER_VMIN); + + if (disable_lcd) { + printk(KERN_INFO "%s DISABLED\n", LCD_DRIVER); + return 0; + } + + retval = misc_register(&lcd_dev); + + if (cobt_is_monterey() + && (cobalt_i2c_read_byte(COBALT_I2C_DEV_LCD_INST, 0) != 0xff)) { + printk(KERN_INFO " - LCD is an I2C device\n"); + has_i2c_lcd = 1; + } else { + has_i2c_lcd = 0; + } + + /* flag ourselves as present */ + lcd_present = 1; + + /* initialize the device */ + lcddev_init(); + +#ifdef CONFIG_PROC_FS +#ifdef CONFIG_COBALT_OLDPROC + /* create /proc/lcd */ + proc_lcd = create_proc_read_entry("lcd", S_IRUSR, NULL, + cobalt_lcd_read_proc, NULL); + if (!proc_lcd) { + EPRINTK("can't create /proc/lcd\n"); + } +#endif + proc_clcd = create_proc_read_entry("lcd", S_IRUSR, proc_cobalt, + cobalt_lcd_read_proc, NULL); + if (!proc_clcd) { + EPRINTK("can't create /proc/cobalt/lcd\n"); + } +#endif + +#ifdef CONFIG_COBALT_LCD_TWIDDLE + cobalt_lcd_start_twiddle(); +#endif + + /* register panic notifier */ + lcd_nb.notifier_call = cobalt_lcd_panic; + lcd_nb.next = NULL; + lcd_nb.priority = 0; + + atomic_notifier_chain_register( &panic_notifier_list, &lcd_nb ); + + return 0; +} + +#endif /* CONFIG_COBALT_LCD */ diff -Naur linux-2.6.18.2.orig/drivers/cobalt/led.c linux-2.6.18.2/drivers/cobalt/led.c --- linux-2.6.18.2.orig/drivers/cobalt/led.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/led.c 2006-11-15 21:20:19.738033319 -0800 @@ -0,0 +1,504 @@ + /* + * $Id: led.c,v 1.36 2002/05/10 18:44:45 duncan Exp $ + * led.c : driver for Cobalt LEDs + * + * Copyright 1996-2000 Cobalt Networks, Inc. + * Copyright 2001 Sun Microsystems, Inc. + * + * By: Andrew Bose + * Timothy Stonis + * Tim Hockin + * Adrian Sun + * Duncan Laurie + * + * Modified By: jeff@404ster.com + * + * This should be SMP safe. There is one definite critical region: the + * handler list (led_handler_lock). The led_state is protected by led_lock, + * so should be safe against simultaneous writes. Bit banging of lights is + * currently also a protected region (led_lock, rather than add a new lock). + */ + +#include + +#ifdef CONFIG_COBALT_LED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define LED_DRIVER "Cobalt Networks LED driver" +#define LED_DRIVER_VMAJ 1 +#define LED_DRIVER_VMIN 0 + +/* the rate at which software controlled frontpanel LEDs blink */ +#define FPLED_DEFAULT_HZ (HZ/20) + +/* + * This is the abstracted state of active LEDs - see the defines for LED_* + * LED masks are always 'unsigned int'. You must hold led_lock to muck with + * these. + */ +static unsigned int led_state; +static unsigned int led_blips; + +/* leds are PCI on genIII */ +static struct pci_dev *led_dev; +/* on XTR the front panel LEDs are software controlled */ +struct led_handler { + unsigned int (*function)(void *); + void *data; + struct led_handler *next; + struct led_handler *prev; +}; +struct led_handler *led_handler_list; +static spinlock_t led_handler_lock = SPIN_LOCK_UNLOCKED; +static struct timer_list timer; + +static spinlock_t led_lock = SPIN_LOCK_UNLOCKED; + +/* + * RaQ 3 + * RaQ 4 + * Qube 3 + */ +#define RAQ3_SHUTLOGO_ADDR 0x7e +#define RAQ3_SHUTDOWN_OFF 0x40 /* reverse polarity */ +#define RAQ3_COBALTLOGO_ON 0x80 +#define QUBE3_LIGHTBAR_ON 0xc0 /* direct polarity */ +#define RAQ3_WEBLIGHT_ADDR 0xb8 +#define RAQ3_WEBLIGHT_ON 0x80 + +/* + * RaQ XTR + */ +#define MONTEREY_FPLED00 0x8000 +#define MONTEREY_FPLED01 0x4000 +#define MONTEREY_FPLED02 0x2000 +#define MONTEREY_FPLED03 0x0200 +#define MONTEREY_FPLED04 0x0080 +#define MONTEREY_FPLED05 0x0040 +#define MONTEREY_FPLED10 0x1000 +#define MONTEREY_FPLED11 0x0800 +#define MONTEREY_FPLED12 0x0400 +#define MONTEREY_FPLED13 0x0100 +#define MONTEREY_FPLED14 0x0020 +#define MONTEREY_FPLED15 0x0010 +#define MONTEREY_FPLED_ETH0_TXRX MONTEREY_FPLED00 +#define MONTEREY_FPLED_ETH0_LINK MONTEREY_FPLED10 +#define MONTEREY_FPLED_ETH1_TXRX MONTEREY_FPLED01 +#define MONTEREY_FPLED_ETH1_LINK MONTEREY_FPLED11 +#define MONTEREY_FPLED_DISK0 MONTEREY_FPLED02 +#define MONTEREY_FPLED_DISK1 MONTEREY_FPLED03 +#define MONTEREY_FPLED_DISK2 MONTEREY_FPLED04 +#define MONTEREY_FPLED_DISK3 MONTEREY_FPLED05 +#define MONTEREY_FPLED_WEB MONTEREY_FPLED12 +#define MONTEREY_LOGOLED_BIT 0x40 +#define MONTEREY_SYSFAULTLED_BIT 0x80 +#define MONTEREY_SLED0 (1<<3) +#define MONTEREY_SLED1 (1<<2) +#define MONTEREY_SLED2 (1<<1) +#define MONTEREY_SLED3 (1<<0) + +/* + * Alpine + */ +#define ALPINE_WEBLED_PORT 0x60e +#define ALPINE_WEBLED_BIT 0x20 +#define ALPINE_POWERLED_PORT 0x50b +#define ALPINE_POWERLED_CFG 0x23 +#define ALPINE_LOGOLED_BIT 0x02 +#define ALPINE_SYSFAULTLED_BIT 0x07 + +/* + * actually set the leds (icky details hidden within) + * this must be protected against itself with led_lock + * */ +static void +__set_led_hw(const unsigned int newstate) +{ + if (cobt_is_pacifica() && led_dev) { + unsigned char tmp; + /* RaQ 3, RaQ 4 + * - shutdown light + * - logo light + * - web light + */ + + /* read the current state of shutdown/logo lights */ + pci_read_config_byte(led_dev, RAQ3_SHUTLOGO_ADDR, &tmp); + + /* reverse polarity for shutdown light */ + if (newstate & LED_SHUTDOWN) + tmp &= ~RAQ3_SHUTDOWN_OFF; + else + tmp |= RAQ3_SHUTDOWN_OFF; + + /* logo light is straight forward */ + if (newstate & LED_COBALTLOGO) + tmp |= RAQ3_COBALTLOGO_ON; + else + tmp &= ~RAQ3_COBALTLOGO_ON; + + /* write new shutdown/logo light state */ + pci_write_config_byte(led_dev, RAQ3_SHUTLOGO_ADDR, tmp); + + /* read web light state */ + pci_read_config_byte(led_dev, RAQ3_WEBLIGHT_ADDR, &tmp); + if (newstate & LED_WEBLIGHT) { + tmp |= RAQ3_WEBLIGHT_ON; + } else { + tmp &= ~RAQ3_WEBLIGHT_ON; + } + + /* write new web light state */ + pci_write_config_byte(led_dev, RAQ3_WEBLIGHT_ADDR, tmp); + } else if (cobt_is_carmel() && led_dev) { + unsigned char tmp; + /* Qube 3 + * - no shutdown light + * - lightbar instead of logo + * - no web led (wired to 2nd IDE reset for staggered startup) + */ + + /* read the current state of lightbar */ + pci_read_config_byte(led_dev, RAQ3_SHUTLOGO_ADDR, &tmp); + if (newstate & LED_COBALTLOGO) { + tmp |= QUBE3_LIGHTBAR_ON; + } else { + tmp &= ~QUBE3_LIGHTBAR_ON; + } + + /* write new lightbar state */ + pci_write_config_byte(led_dev, RAQ3_SHUTLOGO_ADDR, tmp); + } else if (cobt_is_monterey()) { + unsigned int tmp = 0; + u8 val; + unsigned long flags; + + if (newstate & LED_WEBLIGHT) { + tmp |= MONTEREY_FPLED_WEB; + } + if (newstate & LED_ETH0_TXRX) { + tmp |= MONTEREY_FPLED_ETH0_TXRX; + } + if (newstate & LED_ETH0_LINK) { + tmp |= MONTEREY_FPLED_ETH0_LINK; + } + if (newstate & LED_ETH1_TXRX) { + tmp |= MONTEREY_FPLED_ETH1_TXRX; + } + if (newstate & LED_ETH1_LINK) { + tmp |= MONTEREY_FPLED_ETH1_LINK; + } + if (newstate & LED_DISK0) { + tmp |= MONTEREY_FPLED_DISK0; + } + if (newstate & LED_DISK1) { + tmp |= MONTEREY_FPLED_DISK1; + } + if (newstate & LED_DISK2) { + tmp |= MONTEREY_FPLED_DISK2; + } + if (newstate & LED_DISK3) { + tmp |= MONTEREY_FPLED_DISK3; + } + /* 3 LED's are unused on Monterey, but we support them */ + if (newstate & LED_MONTEREY_UNUSED0) { + tmp |= MONTEREY_FPLED13; + } + if (newstate & LED_MONTEREY_UNUSED1) { + tmp |= MONTEREY_FPLED14; + } + if (newstate & LED_MONTEREY_UNUSED2) { + tmp |= MONTEREY_FPLED15; + } + /* I2C controlled front-panel lights */ + cobalt_i2c_write_byte(COBALT_I2C_DEV_LED_I, 0, tmp & 0xff); + cobalt_i2c_write_byte(COBALT_I2C_DEV_LED_II, 0, tmp >> 8); + + /* drive sled LEDs are on a different i2c device */ + tmp = 0xf0; /* high nibble means something else */ + if (newstate * LED_SLED0) + tmp |= MONTEREY_SLED0; + if (newstate * LED_SLED1) + tmp |= MONTEREY_SLED1; + if (newstate * LED_SLED2) + tmp |= MONTEREY_SLED2; + if (newstate * LED_SLED3) + tmp |= MONTEREY_SLED3; + cobalt_i2c_write_byte(COBALT_I2C_DEV_RULER, 0, tmp); + + /* sysfault and logo are in APC page of nvram */ + spin_lock_irqsave(&rtc_lock, flags); + superio_set_rtc_bank(PC87317_RTC_BANK_APC); + val = CMOS_READ(PC87317_APCR4); + + /* reverse polarity */ + if (newstate & LED_COBALTLOGO) { + val &= ~MONTEREY_LOGOLED_BIT; /* logo is on */ + } else { + val |= MONTEREY_LOGOLED_BIT; /* logo is off */ + } + + if (newstate & LED_SYSFAULT) { + val |= MONTEREY_SYSFAULTLED_BIT; + } else { + val &= ~MONTEREY_SYSFAULTLED_BIT; + } + + CMOS_WRITE(val, PC87317_APCR4); + superio_set_rtc_bank(PC87317_RTC_BANK_MAIN); + spin_unlock_irqrestore(&rtc_lock, flags); + } else if (cobt_is_alpine()) { + unsigned char val; + + /* web LED is reverse polarity */ + val = inb(ALPINE_WEBLED_PORT); + if (newstate & LED_WEBLIGHT) { + val &= ~ALPINE_WEBLED_BIT; + } else { + val |= ALPINE_WEBLED_BIT; + } + outb(val, ALPINE_WEBLED_PORT); + + /* + * the power led is controled by switching the pin between + * a GPIO pin (on) and a LED pin (off) + */ + + outb( ALPINE_POWERLED_CFG, 0x2e ); + val = inb( 0x2f ); + if (newstate & LED_COBALTLOGO) { + val &= ~ALPINE_LOGOLED_BIT; + } else { + val |= ALPINE_LOGOLED_BIT; + } + outb( val, 0x2f ); + + if (newstate & LED_SYSFAULT) { + val = ALPINE_SYSFAULTLED_BIT; + } else { + val = 0; + } + + outb(val, ALPINE_POWERLED_PORT); + } +} + +/* blip the front panel leds */ +static void +led_timer_func(unsigned long data) +{ + unsigned int leds = 0; + struct led_handler *p; + unsigned long flags; + + /* call all registered callbacks */ + spin_lock_irqsave(&led_handler_lock, flags); + for (p = led_handler_list; p; p = p->next) { + leds |= p->function(p->data); + } + spin_unlock_irqrestore(&led_handler_lock, flags); + + /* set the led hardware */ + spin_lock_irqsave(&led_lock, flags); + __set_led_hw(led_state | leds | led_blips); + led_blips = 0; + spin_unlock_irqrestore(&led_lock, flags); + + /* re-arm ourself */ + mod_timer(&timer, jiffies + FPLED_DEFAULT_HZ); +} + +static void +__cobalt_led_set(const unsigned int leds) +{ + led_state = leds; + __set_led_hw(leds); +} + +void +cobalt_led_set(const unsigned int leds) +{ + unsigned long flags; + spin_lock_irqsave(&led_lock, flags); + __cobalt_led_set(leds); + spin_unlock_irqrestore(&led_lock, flags); +} + +void +cobalt_led_set_bits(const unsigned int leds) +{ + unsigned long flags; + spin_lock_irqsave(&led_lock, flags); + __cobalt_led_set(led_state | leds); + spin_unlock_irqrestore(&led_lock, flags); +} + +void +cobalt_led_clear_bits(const unsigned int leds) +{ + unsigned long flags; + spin_lock_irqsave(&led_lock, flags); + __cobalt_led_set(led_state & ~leds); + spin_unlock_irqrestore(&led_lock, flags); +} + +static void +__cobalt_led_set_lazy(const unsigned int leds) +{ + /* the next led timer run will catch these changes */ + led_state = leds; + /* remember lights that were 'blipped' to force an edge */ + led_blips |= leds; +} + +void +cobalt_led_set_lazy(const unsigned int leds) +{ + unsigned long flags; + spin_lock_irqsave(&led_lock, flags); + __cobalt_led_set_lazy(leds); + spin_unlock_irqrestore(&led_lock, flags); +} + +void +cobalt_led_set_bits_lazy(const unsigned int leds) +{ + unsigned long flags; + spin_lock_irqsave(&led_lock, flags); + __cobalt_led_set_lazy(led_state | leds); + spin_unlock_irqrestore(&led_lock, flags); +} + +void +cobalt_led_clear_bits_lazy(const unsigned int leds) +{ + unsigned long flags; + spin_lock_irqsave(&led_lock, flags); + __cobalt_led_set_lazy(led_state & ~leds); + spin_unlock_irqrestore(&led_lock, flags); +} + +unsigned int +cobalt_led_get(void) +{ + unsigned int r; + unsigned long flags; + + spin_lock_irqsave(&led_lock, flags); + r = led_state; + spin_unlock_irqrestore(&led_lock, flags); + + return r; +} + +int +cobalt_fpled_register(unsigned int (*function)(void *), void *data) +{ + struct led_handler *newh; + unsigned long flags; + + newh = kmalloc(sizeof(*newh), GFP_ATOMIC); + if (!newh) { + EPRINTK("can't allocate memory for handler %p(%p)\n", + function, data); + return -1; + } + + spin_lock_irqsave(&led_handler_lock, flags); + + /* head insert */ + newh->function = function; + newh->data = data; + newh->next = led_handler_list; + newh->prev = NULL; + if (led_handler_list) { + led_handler_list->prev = newh; + } + led_handler_list = newh; + + spin_unlock_irqrestore(&led_handler_lock, flags); + + return 0; +} + +int +cobalt_fpled_unregister(unsigned int (*function)(void *), void *data) +{ + int r = -1; + struct led_handler *p; + unsigned long flags; + + spin_lock_irqsave(&led_handler_lock, flags); + + for (p = led_handler_list; p; p = p->next) { + if (p->function == function && p->data == data) { + if (p->prev) { + p->prev->next = p->next; + } + if (p->next) { + p->next->prev = p->prev; + } + r = 0; + break; + } + } + + spin_unlock_irqrestore(&led_handler_lock, flags); + + return r; +} + +int __init +cobalt_led_init(void) +{ + unsigned int leds = LED_SHUTDOWN | LED_COBALTLOGO; + + printk(KERN_INFO "%s %d.%d (modified by jeff@404ster.com)\n", LED_DRIVER,LED_DRIVER_VMAJ,LED_DRIVER_VMIN); + + if (cobt_is_3k()) { + /* LEDs for RaQ3/4 and Qube3 are on the PMU */ + led_dev = pci_find_device(PCI_VENDOR_ID_AL, + PCI_DEVICE_ID_AL_M7101, NULL); + if (!led_dev) { + EPRINTK("can't find PMU for LED control\n"); + return -1; + } + } + + /* setup up timer for fp leds */ + init_timer(&timer); + timer.expires = jiffies + FPLED_DEFAULT_HZ; + timer.data = 0; + timer.function = &led_timer_func; + add_timer(&timer); + + /* set the initial state */ + leds |= cobalt_cmos_read_flag(COBT_CMOS_SYSFAULT_FLAG) ? + LED_SYSFAULT : 0; + led_state = leds; + __set_led_hw(leds); + + return 0; +} + +#endif /* CONFIG_COBALT_LED */ diff -Naur linux-2.6.18.2.orig/drivers/cobalt/net.c linux-2.6.18.2/drivers/cobalt/net.c --- linux-2.6.18.2.orig/drivers/cobalt/net.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/net.c 2006-11-15 21:20:19.738033319 -0800 @@ -0,0 +1,133 @@ +/* + * cobalt net wrappers + * Copyright (c) 2000, Cobalt Networks, Inc. + * Copyright (c) 2001, Sun Microsystems, Inc. + * $Id: net.c,v 1.11 2001/10/27 00:40:24 thockin Exp $ + * author: thockin@sun.com + * + * This should be SMP safe. The only critical data is the list of devices. + * The LED handler runs at timer-interrupt, so we must use the IRQ safe forms + * of the locks. --TPH + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define MAX_COBT_NETDEVS 2 +static struct net_device *netdevs[MAX_COBT_NETDEVS]; +static int n_netdevs; +static spinlock_t cobaltnet_lock = SPIN_LOCK_UNLOCKED; + +#if defined(CONFIG_COBALT_LED) +static unsigned int +net_led_handler(void *data) +{ + int i; + unsigned int leds = 0; + static int txrxmap[MAX_COBT_NETDEVS] = {LED_ETH0_TXRX, LED_ETH1_TXRX}; + static int linkmap[MAX_COBT_NETDEVS] = {LED_ETH0_LINK, LED_ETH1_LINK}; + unsigned long flags; + static unsigned long net_old[MAX_COBT_NETDEVS]; + + spin_lock_irqsave(&cobaltnet_lock, flags); + + for (i = 0; i < n_netdevs; i++) { + unsigned long txrxstate; + struct net_device *dev = netdevs[i]; + if (!dev) { + continue; + } + /* check for link */ + if (netif_running(dev) && netif_carrier_ok(dev)) { + leds |= linkmap[i]; + } + /* check for tx/rx */ + txrxstate = dev->trans_start ^ dev->last_rx; + if (txrxstate != net_old[i]) { + leds |= txrxmap[i]; + net_old[i] = txrxstate; + } + } + + spin_unlock_irqrestore(&cobaltnet_lock, flags); + + return leds; +} +#endif + +/* + * We try to be VERY explicit here. Fine for now, may eventually break down. + */ +void +cobalt_net_register(struct net_device *ndev) +{ + unsigned long flags; + int i; + + if (!ndev) { + return; + } + + /* we'll track the first MAX_COBT_NETDEVS NICs */ + if (n_netdevs >= MAX_COBT_NETDEVS) { + return; + } + + spin_lock_irqsave(&cobaltnet_lock, flags); + + /* find a free slot */ + for (i = 0; i < MAX_COBT_NETDEVS; i++) { + if (!netdevs[i]) { + netdevs[i] = ndev; + n_netdevs++; + break; + } + } + + spin_unlock_irqrestore(&cobaltnet_lock, flags); +} + +void +cobalt_net_unregister(struct net_device *ndev) +{ + int i; + unsigned long flags; + + if (!ndev) { + return; + } + + spin_lock_irqsave(&cobaltnet_lock, flags); + + /* try to remove it from the list */ + for (i = 0; i < MAX_COBT_NETDEVS; i++) { + if (netdevs[i] == ndev) { + netdevs[i] = NULL; + n_netdevs--; + break; + } + } + + spin_unlock_irqrestore(&cobaltnet_lock, flags); +} + +int __init +cobalt_net_init(void) +{ +#if defined(CONFIG_COBALT_LED) + /* register an LED handler */ + cobalt_fpled_register(net_led_handler, NULL); +#endif + + return 0; +} diff -Naur linux-2.6.18.2.orig/drivers/cobalt/raminfo.c linux-2.6.18.2/drivers/cobalt/raminfo.c --- linux-2.6.18.2.orig/drivers/cobalt/raminfo.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/raminfo.c 2006-11-15 21:20:19.738033319 -0800 @@ -0,0 +1,320 @@ +/* $Id: raminfo.c,v 1.7 2001/10/29 22:21:36 thockin Exp $ + * + * Copyright (c) 2000-2001 Sun Microsystems, Inc. + * All Rights Reserved. + * + * This is SMP safe - the init runs once on load, and the rest is just + * printing information. --TPH + */ +#include + +#if defined(CONFIG_COBALT_RAMINFO) || defined(CONFIG_COBALT_RAMINFO_MODULE) + +#include +#include +#include +#include + +#include +#include + +#define RAM_DRIVER "Cobalt Networks RAM Info driver" +#define RAM_DRIVER_VMAJ 1 +#define RAM_DRIVER_VMIN 0 + +#define MAX_DIMM_SLOTS 4 + +enum dimm_t { + DIMM_TYPE_FPM_DRAM, + DIMM_TYPE_EDO_DRAM, + DIMM_TYPE_REG_SDRAM, + DIMM_TYPE_SDRAM +}; + +static char *dimm_desc[] = { + "Fast-page Mode DRAM", + "EDO DRAM", + "Registered SDRAM", + "SDRAM", +}; + +struct dimm_slot { + int num; + enum dimm_t type; + uint16_t size; + int ecc; +}; + +struct raminfo { + int total; + int (*query)(struct dimm_slot *); + struct pci_dev *dev; + struct dimm_slot *dimm; +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc; +#endif /* CONFIG_PROC_FS */ +}; + +/*########################################################################*/ + +static int serverworks_le_dimm_info(struct dimm_slot *); +static int ali_1541_dimm_info(struct dimm_slot *); +static int raminfo_read_proc(char*, char**, off_t, int, int*, void*); + +/* RaQ-3, RaQ-4, Qube-3 + * - uses ALI M1541 for memory controller + * - has 2 dimm slots */ +static struct raminfo gen3_raminfo = { + total: 2, + query: ali_1541_dimm_info +}; +/* RaQ-XTR (Monterey) + * - uses ServerWorks CNB30LE for Memory Controller + * - has 4 dimm slots */ +static struct raminfo gen5_monterey_raminfo = { + total: 4, + query: serverworks_le_dimm_info +}; +/* RaQ (Alpine) + * - uses ServerWorks CNB30LE for Memory Controller + * - has 2 dimm slots */ +static struct raminfo gen5_alpine_raminfo = { + total: 2, + query: serverworks_le_dimm_info +}; + +static struct raminfo *sys_raminfo; + +/*########################################################################*/ + +#define SERVERWORKS_DRAM_MRPR (0x90) +#define SERVERWORKS_DRAM_MRAR(slot) (0x7c + (slot)) +#define SERVERWORKS_DRAM_ECCR (0xe0) + +static int +serverworks_le_dimm_info(struct dimm_slot *dimm) +{ + int row; + uint8_t rar, active, eccr; + uint16_t ma_map[] = { + 32, 16, 32, 256, 512, 128, 128, 64, 256, 128, 64, 64, 128, + }; + + if (!sys_raminfo || !sys_raminfo->dev || !dimm) + return -ENOSYS; + + pci_read_config_byte(sys_raminfo->dev, + SERVERWORKS_DRAM_MRPR, &active); + pci_read_config_byte(sys_raminfo->dev, + SERVERWORKS_DRAM_MRAR(dimm->num), &rar); + + /* serverworks uses only registered sdram */ + dimm->type = DIMM_TYPE_REG_SDRAM; + dimm->size = 0; + + /* check to see if ECC is enabled (bit 4 of reg 0xE0) */ + pci_read_config_byte(sys_raminfo->dev, + SERVERWORKS_DRAM_ECCR, &eccr); + dimm->ecc = (eccr & (1<<2)) ? 1 : 0; + + /* two rows for each dimm slot */ + for (row=2*dimm->num; row<=(2*dimm->num+1); row++) { + /* each active row will have corresponding bit + * set in the Memory Row Presence Register */ + if (active & (1 << row)) { + /* lookup size ma_map table */ + dimm->size += ma_map[ rar & 0xf ]; + } + /* two rows per RAR register, bits 7-4 and bits 3-0 */ + rar >>= 4; + } + + return 0; +} + +#define ALI_DRAM_CONF_1(row) (0x60 + ((row) * 2)) +#define ALI_DRAM_CONF_2(row) (0x61 + ((row) * 2)) +#define ALI_DIMM_TYPE(d2) (((d2) >> 4) & 0x3) +#define ALI_DIMM_MMAP(d2) (((d2) >> 6) & 0x3) +#define ALI_DIMM_SIZE(d1, d2) (((((d2) & 0xf) << 8) | (d1)) + 1) + +static int +ali_1541_dimm_info(struct dimm_slot *dimm) +{ + int row; + uint8_t dbc1, dbc2; + + if (!sys_raminfo || !sys_raminfo->dev || !dimm) + return -ENOSYS; + + dimm->size = 0; + dimm->ecc = 0; + + /* read two rows per dimm (for double-side) */ + for (row=2*dimm->num; row<=(2*dimm->num + 1); row++) { + pci_read_config_byte(sys_raminfo->dev, + ALI_DRAM_CONF_2(row), &dbc2); + + /* row is empty iff dimm type and ma_map are both 0 */ + if (!ALI_DIMM_TYPE(dbc2) && !ALI_DIMM_MMAP(dbc2)) + continue; + + pci_read_config_byte(sys_raminfo->dev, + ALI_DRAM_CONF_1(row), &dbc1); + + /* type is bits 4-5 of dimm conf reg 2 */ + dimm->type = ALI_DIMM_TYPE(dbc2); + + /* A27-A20 address lines are bits 7-0 of dimm conf reg 1 + * A31-A28 address lines are bits 3-0 of dimm conf reg 2 */ + dimm->size = ALI_DIMM_SIZE(dbc1, dbc2); + } + + /* the M1541 uses "not less than" policy to determine which row a + * memory address resides in. the top address boundary for each + * row is the maximum memory value minus 1. so to determine the + * size of a row you must subtract the size of the previous row. + * (unless this is slot 0 or the first populated slot) */ + if (dimm->num > 0 && dimm->size > 0) { + uint16_t sz; + pci_read_config_byte(sys_raminfo->dev, + ALI_DRAM_CONF_1(2*dimm->num - 1), &dbc1); + pci_read_config_byte(sys_raminfo->dev, + ALI_DRAM_CONF_2(2*dimm->num - 1), &dbc2); + sz = ALI_DIMM_SIZE(dbc1, dbc2); + dimm->size -= (sz > 1) ? sz : 0; + } + + return 0; +} + +int __init +cobalt_raminfo_init(void) +{ + int j; + + /* determine system type and find memory controller pci dev + * so we don't have to do pci lookup for each proc read */ + if (cobt_is_3k()) { + sys_raminfo = &gen3_raminfo; + sys_raminfo->dev = pci_find_device(PCI_VENDOR_ID_AL, + PCI_DEVICE_ID_AL_M1541, NULL); + } else if (cobt_is_5k()) { + if (cobt_is_monterey()) { + sys_raminfo = &gen5_monterey_raminfo; + } else if (cobt_is_alpine()) { + sys_raminfo = &gen5_alpine_raminfo; + } else { + EPRINTK("unable to identify gen5 board\n"); + return -ENOSYS; + } + sys_raminfo->dev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_LE, NULL); + } + + if (!sys_raminfo || !sys_raminfo->dev) { + EPRINTK("unable to identify system type\n"); + return -ENOSYS; + } + + printk(KERN_INFO "%s %d.%d (modified by jeff@404ster.com)\n", RAM_DRIVER,RAM_DRIVER_VMAJ,RAM_DRIVER_VMIN); + +#ifdef CONFIG_PROC_FS + /* add entry to /proc filesytem */ + sys_raminfo->proc = create_proc_entry("raminfo", + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, proc_cobalt); + if (!sys_raminfo->proc) { + EPRINTK("can't create /proc/cobalt/raminfo\n"); + return -ENOENT; + } + sys_raminfo->proc->owner = THIS_MODULE; + sys_raminfo->proc->write_proc = NULL; + sys_raminfo->proc->read_proc = raminfo_read_proc; +#endif /* CONFIG_PROC_FS */ + + /* create arrary of dimm slots to store info */ + sys_raminfo->dimm = kmalloc( + sys_raminfo->total * sizeof(struct dimm_slot), GFP_ATOMIC); + if (!sys_raminfo->dimm) { + EPRINTK("unable to allocate memory\n"); +#ifdef CONFIG_PROC_FS + if (sys_raminfo->proc) { + remove_proc_entry("raminfo", proc_cobalt); + sys_raminfo->proc = NULL; + } +#endif /* CONFIG_PROC_FS */ + return -ENOMEM; + } + + { + struct dimm_slot *ds = sys_raminfo->dimm; + for (j=0; jtotal; j++, ds++) { + if (!ds) continue; + ds->num = j; + if (sys_raminfo->query(ds) < 0) { + EPRINTK("unable to read dimm %d\n", j); + ds->num = -1; + } + } + } + + return 0; +} + +static void __exit +cobalt_raminfo_exit(void) +{ +#ifdef CONFIG_PROC_FS + if (sys_raminfo->proc) { + remove_proc_entry("raminfo", proc_cobalt); + sys_raminfo->proc = NULL; + } +#endif /* CONFIG_PROC_FS */ + + if (sys_raminfo->dimm) { + kfree(sys_raminfo->dimm); + sys_raminfo->dimm = NULL; + } + + sys_raminfo->dev = NULL; + sys_raminfo = NULL; +} + +#ifdef CONFIG_PROC_FS +static int +raminfo_read_proc(char *buf, char **st, off_t off, int len, int *eof, void *x) +{ + int rlen, i; + struct dimm_slot *ds; + + if (!sys_raminfo) + return -ENOSYS; + + //MOD_INC_USE_COUNT; + + ds = sys_raminfo->dimm; + for (rlen=i=0; itotal; i++, ds++) { + if (!ds || ds->num < 0) + continue; + rlen += sprintf(buf+rlen, "%d [%s%s]: %u MB\n", i, + ds->size ? dimm_desc[ds->type] : "Empty", + ds->size ? ds->ecc ? "+ECC" : "" : "", + ds->size); + } + + //MOD_DEC_USE_COUNT; + + return cobalt_gen_proc_read(buf, rlen, st, off, len, eof); +} +#endif /* CONFIG_PROC_FS */ + +#ifdef CONFIG_COBALT_RAMINFO_MODULE +module_init(cobalt_raminfo_init); +module_exit(cobalt_raminfo_exit); +#endif + +MODULE_AUTHOR("Sun Cobalt"); +MODULE_DESCRIPTION("DIMM Information"); +MODULE_LICENSE("GPL"); + +#endif /* CONFIG_COBALT_RAMINFO || CONFIG_COBALT_RAMINFO_MODULE */ diff -Naur linux-2.6.18.2.orig/drivers/cobalt/ruler.c linux-2.6.18.2/drivers/cobalt/ruler.c --- linux-2.6.18.2.orig/drivers/cobalt/ruler.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/ruler.c 2006-11-15 21:20:19.738033319 -0800 @@ -0,0 +1,419 @@ +/* + * cobalt ruler driver + * Copyright (c) 2000, Cobalt Networks, Inc. + * Copyright (c) 2001, Sun Microsystems, Inc. + * $Id: ruler.c,v 1.23 2002/08/29 00:33:01 uzi Exp $ + * + * author: asun@cobalt.com, thockin@sun.com + * + * This should be SMP safe. There is one critical piece of data, and thus + * one lock. The ruler_lock protects the arrays of channels(hwifs) and + * busproc function pointers. These are only ever written in the + * register/unregister functions but read in several other places. A + * read/write lock is appropriate. The global switches and sled_leds are + * atomic_t. --TPH + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define RULER_TIMEOUT (HZ >> 1) /* .5s */ +#define MAX_COBT_DRIVES 4 + +#define RULER_DRIVER "Cobalt Networks Disk Ruler driver" +#define RULER_DRIVER_VMAJ 1 +#define RULER_DRIVER_VMIN 0 + +/* all of this is for gen V */ +static struct timer_list cobalt_ruler_timer; +static rwlock_t ruler_lock = RW_LOCK_UNLOCKED; +static ide_drive_t *channels[MAX_COBT_DRIVES]; + +static int (*busprocs[MAX_COBT_DRIVES])(ide_drive_t *, int); + +//static cob_busprocs_t busprocs[MAX_COBT_DRIVES]; + +/* NOTE: switches is a bitmask of DETACHED sleds */ +static atomic_t switches = ATOMIC_INIT(0); +static atomic_t sled_leds = ATOMIC_INIT(0); +static int sled_led_map[] = {LED_SLED0, LED_SLED1, LED_SLED2, LED_SLED3}; +static int ruler_detect; +static int initialized; + +static void ruler_hwif_added(ide_hwif_t *hwif, int idx); + +static inline u8 +read_switches(void) +{ + u8 state = 0; + if (cobt_is_monterey()) { + int tries = 3; + + /* i2c can be busy, and this can read wrong - try a few times */ + while (tries--) { + state = cobalt_i2c_read_byte(COBALT_I2C_DEV_DRV_SWITCH, + 0); + if ((state & 0xf0) != 0xf0) { + break; + } + } + } + + return state; +} + +static inline unsigned int +get_sled_leds(void) +{ + return atomic_read(&sled_leds); +} + +/* + * deal with sled leds: LED on means OK to remove + * NOTE: all the reset lines are kept high. + * NOTE: the reset lines are in the reverse order of the switches. + */ +static void +set_sled_leds(unsigned int leds) +{ + if (cobt_is_monterey()) { + unsigned int offed = get_sled_leds(); + + offed &= ~leds; + atomic_set(&sled_leds, leds); +#ifdef CONFIG_COBALT_LED + cobalt_led_clear_bits_lazy(offed); + cobalt_led_set_bits_lazy(leds); +#endif + } +} + +/* this must be called with the ruler_lock held for read */ +static int +do_busproc(int idx, ide_drive_t *drive, int arg) +{ + if (cobt_is_monterey()) { + /* sed sled LEDs */ + switch (arg) { + case BUSSTATE_ON: + set_sled_leds(get_sled_leds() & + ~sled_led_map[idx]); + break; + case BUSSTATE_OFF: + case BUSSTATE_TRISTATE: + set_sled_leds(get_sled_leds() | + sled_led_map[idx]); + break; + default: + WPRINTK("unknown busproc argument (%d)\n", arg); + } + } + + /* do the real work */ + return busprocs[idx](drive, arg); +} + +static void +ruler_timer_fn(unsigned long data) +{ + if (cobt_is_monterey()) { + u8 state; + int i; + unsigned int now, expected, bit, swcur; + + state = read_switches(); + if ((state & 0xf0) == 0xf0) { + return; + } + swcur = atomic_read(&switches); + + state &= 0xf; + read_lock(&ruler_lock); + for (i = 0; i < MAX_COBT_DRIVES; i++) { + bit = 1 << i; + now = state & bit; + expected = swcur & bit; + if (now == expected) { + /* no changes to worry about */ + continue; + } + + if (now) { + /* a freshly detached drive */ + atomic_set(&switches, swcur | bit); + if (channels[i]) { + printk("disabling ide ruler " + "channel %d\n", i); + do_busproc(i, channels[i], + BUSSTATE_TRISTATE); + } else { + WPRINTK("drive detach on bad " + "channel (%d)\n", i); + } + set_sled_leds(get_sled_leds() | + sled_led_map[i]); + } else { + /* + * do we want to do anything when a re-attach + * is detected? + */ + } + } + read_unlock(&ruler_lock); + } +} + +#ifdef CONFIG_COBALT_ACPI +static int +ruler_interrupt(cobalt_acpi_evt *evt, void * data) +{ + if (cobt_is_monterey() && ruler_detect) { + u8 state; + + state = read_switches(); + if ((state & 0xf0) != 0xf0) { + /* this is protected inside mod_timer */ + mod_timer(&cobalt_ruler_timer, jiffies + RULER_TIMEOUT); + } + + evt->ev_data = state; + /* empirical: delay enough to debounce */ + udelay(10); + } + return 0; +} +#endif /* CONFIG_COBALT_ACPI */ + +#if defined(CONFIG_COBALT_LED) +/* figure which LEDs to blink */ +static unsigned int +ide_led_handler(void *data) +{ + ide_hwif_t *hwif; + unsigned int leds = 0; + + if (cobt_is_monterey()) { + int i; + static int ledmap[MAX_COBT_DRIVES] = { + LED_DISK0, LED_DISK1, LED_DISK2, LED_DISK3 + }; + static unsigned long old[MAX_COBT_DRIVES]; + + read_lock(&ruler_lock); + + for (i = 0; i < MAX_COBT_DRIVES; i++) { + if (channels[i]) + { + hwif = HWIF(channels[i]); + if (hwif->drives[0].present && + hwif->drives[0].service_start != old[i]) { + leds |= ledmap[i]; + old[i] = hwif->drives[0].service_start; + } + } + } + + read_unlock(&ruler_lock); + } + + return leds; +} +#endif + +/* this is essentially an exported function - it is in the hwif structs */ +static int ruler_busproc_fn(ide_drive_t *drive, int arg) +{ + int r = 0; + if (cobt_is_monterey()) { + int idx; + + read_lock(&ruler_lock); + + for (idx = 0; idx < MAX_COBT_DRIVES; idx++) { + if (channels[idx] == drive) { + break; + } + } + + if (idx >= MAX_COBT_DRIVES) { + /* not a hwif we manage? */ + return 0; + } + + r = do_busproc(idx, drive, arg); + read_unlock(&ruler_lock); + } + + return r; +} + +/* + * We try to be VERY explicit here. Fine for now, may eventually break down. + */ +void +cobalt_ruler_register(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + + if (cobt_is_monterey()) { + struct pci_dev *dev; + int idx; + unsigned long flags; + + if (!hwif) { + return; + } + + /* Cobalt rulers only have HPT370 controllers on bus 1 */ + dev = hwif->pci_dev; + if (!dev) + return; + + if (dev->vendor != PCI_VENDOR_ID_TTI + || dev->device != PCI_DEVICE_ID_TTI_HPT366 + || dev->bus->number != 1) { + /* ignore it */ + return; + } + + /* IDE ruler has controllers at dev 3 and 4, ONLY */ + if (dev->devfn == PCI_DEVFN(3,0)) { + idx = hwif->channel; + } else if (dev->devfn == PCI_DEVFN(4,0)) { + idx = 2 + hwif->channel; + } else { + return; + } + + if (idx >= MAX_COBT_DRIVES) { + return; + } + + write_lock_irqsave(&ruler_lock, flags); + + /* save a pointer to the hwif, and trap it's busproc() */ + channels[idx] = drive; + if (hwif->busproc) { + busprocs[idx] = HWIF(drive)->busproc; + hwif->busproc = &ruler_busproc_fn; + } + + write_unlock_irqrestore(&ruler_lock, flags); + + /* now that we have trapped it, do what we need to initialize + * the drive - if we haven't been initialized, we'll call this + * later. + */ + if (initialized) { + ruler_hwif_added(hwif, idx); + } + } +} + +static void +ruler_hwif_added(ide_hwif_t *hwif, int idx) +{ + /* the associated switch should be closed */ + if (hwif->drives[0].present) { + /* set the sled LED off - not safe to remove */ + set_sled_leds(get_sled_leds() & ~sled_led_map[idx]); + } +} + +void cobalt_ruler_unregister(ide_drive_t *drive) +{ + if (cobt_is_monterey()) { + int i; + unsigned long flags; + + write_lock_irqsave(&ruler_lock, flags); + + for (i = 0; i < MAX_COBT_DRIVES; i++) { + if (channels[i] == drive) { + channels[i] = NULL; + HWIF(drive)->busproc = busprocs[i]; + busprocs[i] = NULL; + } + } + + write_unlock_irqrestore(&ruler_lock, flags); + } +} + +int __init +cobalt_ruler_init(void) +{ + if (cobt_is_monterey()) { + int err; + u8 tmp; + int i; + + /* initialize switches */ + tmp = read_switches(); + ruler_detect = ((tmp & 0xf0) == 0xf0) ? 0 : 1; + tmp &= 0xf; + atomic_set(&switches, tmp); + + /* initialize our timer */ + init_timer(&cobalt_ruler_timer); + cobalt_ruler_timer.function = ruler_timer_fn; + + printk(KERN_INFO "%s %d.%d (modified by jeff@404ster.com)\n", RULER_DRIVER,RULER_DRIVER_VMAJ,RULER_DRIVER_VMIN); + +#ifdef CONFIG_COBALT_ACPI + err = cobalt_acpi_register_evt_handler(ruler_interrupt, + COBALT_ACPI_EVT_SLED, NULL ); + + if (err) { + EPRINTK("can't register interrupt handler %p\n", + ruler_interrupt); + } +#endif + + /* set initial sled LED state */ + set_sled_leds(LED_SLED0 | LED_SLED1 | LED_SLED2 | LED_SLED3); + + /* run through any devices that were registered before */ + for (i = 0; i < MAX_COBT_DRIVES; i++) { + if (channels[i]) { + ruler_hwif_added(HWIF(channels[i]), i); + } + } + +#if defined(CONFIG_COBALT_LED) + /* register for a blinky LEDs callback */ + err = cobalt_fpled_register(ide_led_handler, NULL); + if (err) { + EPRINTK("can't register LED handler %p\n", + ide_led_handler); + } +#endif + } + + initialized = 1; + + return 0; +} diff -Naur linux-2.6.18.2.orig/drivers/cobalt/sensors.c linux-2.6.18.2/drivers/cobalt/sensors.c --- linux-2.6.18.2.orig/drivers/cobalt/sensors.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/sensors.c 2006-11-15 21:20:19.738033319 -0800 @@ -0,0 +1,525 @@ +/* $Id: sensors.c,v 1.31 2002/08/29 00:33:01 uzi Exp $ + * Copyright (c) 2000-2001 Sun Microsystems, Inc + * + * This should be SMP safe. There is just one race - the read in /proc. + * It now guards against itself with a semaphore. Note that we don't use a + * spinlock because any of the methods may (and do!) block. + */ +#include +#ifdef CONFIG_COBALT_SENSORS + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#define SENS_DRIVER "Cobalt Networks Sensor driver" +#define SENS_DRIVER_VMAJ 1 +#define SENS_DRIVER_VMIN 0 + +/* externals */ +unsigned int cobalt_nthermals; +unsigned int cobalt_nvoltages; + +/* data about a sensor for generic handling */ +/* we could add data about a low/high range, if needed */ +struct sensor { + int sensor; /* sensor #, so maps can be logically ordered */ + char *desc; + int last_val; + unsigned long cache; + unsigned long cache_timeout; + /* pre/post hook - 1 for pre, 0 for post */ + void (*setup)(struct sensor *s, int pre); + /* read as an int, to be passed to format() */ + int (*read)(struct sensor *s); + /* hook for scaling values */ + int (*scale)(struct sensor *s, int val); + /* format the value as a string */ + char *(*format)(struct sensor *s, int val, char *buf, int len); +}; + +/* some stuff for generic formatting */ +#define DEC_SCALAR 100 +static char *decimal_format(struct sensor *s, int val, char *buf, int len); + +static DECLARE_MUTEX(sensor_sem); +static struct sensor *therm_map; +static struct sensor *volt_map; + +#define CACHE_DEF 30 + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *proc_csensors; +static struct proc_dir_entry *proc_therm; +static struct proc_dir_entry *proc_volt; +static int therm_read_proc(char *buf, char **start, off_t pos, int len, + int *eof, void *x); +static int therm_write_proc(struct file *file, const char *buf, + unsigned long len, void *x); +static int volt_read_proc(char *buf, char **start, off_t pos, int len, + int *eof, void *x); +static int volt_write_proc(struct file *file, const char *buf, + unsigned long len, void *x); +#endif + +static int lm77_therm_read(struct sensor *s); +static int adm1029_init(void); +static int adm1029_therm_read(struct sensor *s); +static int adm1029_volt_read(struct sensor *s); +static int alpine_vcore_scale(struct sensor *s, int val); +static void alpine_vbat_switch(struct sensor *s, int pre); +static int alpine_vbat_scale(struct sensor *s, int val); + +/* sensor name mappings */ +static struct sensor gen3_therm_map[] = { + {0, "CPU", 0, 0, CACHE_DEF, NULL, lm77_therm_read, NULL, decimal_format}, +}; +static struct sensor monterey_therm_map[] = { + {0, "CPU0", 0, 0, CACHE_DEF, NULL, lm77_therm_read, NULL, decimal_format}, + {1, "CPU1", 0, 0, CACHE_DEF, NULL, lm77_therm_read, NULL, decimal_format}, + {2, "Case0", 0, 0, CACHE_DEF, NULL, lm77_therm_read, NULL, decimal_format}, + {3, "Case1", 0, 0, CACHE_DEF, NULL, lm77_therm_read, NULL, decimal_format}, +}; +static struct sensor alpine_therm_map[] = { + {1, "CPU", 0, 0, CACHE_DEF, NULL, adm1029_therm_read, NULL, decimal_format}, + {0, "Case", 0, 0, CACHE_DEF, NULL, adm1029_therm_read, NULL, decimal_format}, +}; +static struct sensor alpine_volt_map[] = { + {0, "Vcore", 0, 0, CACHE_DEF, NULL, adm1029_volt_read, + alpine_vcore_scale, decimal_format}, + {1, "Vtt", 0, 0, CACHE_DEF, NULL, adm1029_volt_read, NULL, decimal_format}, + {0, "Vbat", 0, 0, CACHE_DEF<<10, alpine_vbat_switch, adm1029_volt_read, + alpine_vbat_scale, decimal_format}, +}; + +int __init +cobalt_sensors_init(void) +{ + if (cobt_is_3k()) { + cobalt_nthermals = 1; + cobalt_nvoltages = 0; + therm_map = gen3_therm_map; + } else if (cobt_is_monterey()) { + cobalt_nthermals = 4; + cobalt_nvoltages = 0; + therm_map = monterey_therm_map; + } else if (cobt_is_alpine()) { + cobalt_nthermals = 2; + cobalt_nvoltages = 3; + therm_map = alpine_therm_map; + volt_map = alpine_volt_map; + adm1029_init(); + } else { + return -1; + } + + printk(KERN_INFO "%s %d.%d (modified by jeff@404ster.com)\n", SENS_DRIVER,SENS_DRIVER_VMAJ,SENS_DRIVER_VMIN); + +#ifdef CONFIG_PROC_FS + /* make files in /proc */ + proc_csensors = proc_mkdir("sensors", proc_cobalt); + if (!proc_csensors) { + EPRINTK("can't create /proc/cobalt/sensors\n"); + return -1; + } + if (cobalt_nthermals) { + proc_therm = create_proc_entry("thermal", + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, + proc_csensors); + if (!proc_therm) { + EPRINTK("can't create /proc/cobalt/sensors/thermal\n"); + } + proc_therm->read_proc = therm_read_proc; + proc_therm->write_proc = therm_write_proc; + } + if (cobalt_nvoltages) { + proc_volt = create_proc_entry("voltage", + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, + proc_csensors); + if (!proc_volt) { + EPRINTK("can't create /proc/cobalt/sensors/voltage\n"); + } + proc_volt->read_proc = volt_read_proc; + proc_volt->write_proc = volt_write_proc; + + } +#endif + + return 0; +} + +static char * +sensor_read(struct sensor *s, char *buf, int len) +{ + int val; + + if (s->cache && time_after(s->cache_timeout*HZ + s->cache, jiffies)) + val = s->last_val; + else { + if (s->setup) s->setup(s, 1); + val = s->read(s); + s->last_val = val; + s->cache = jiffies; + if (s->setup) s->setup(s, 0); + } + + if (s->scale) val = s->scale(s, val); + return s->format(s, val, buf, len); +} + +/* exported - nicer inline functions in header */ +char * +__cobalt_thermal_read(unsigned int idx, char *buf, int len) +{ + if (idx >= cobalt_nthermals || !buf) { + return NULL; + } + + return sensor_read(&therm_map[idx], buf, len); +} + +/* exported - nicer inline functions in header */ +char * +__cobalt_voltage_read(unsigned int idx, char *buf, int len) +{ + if (idx >= cobalt_nvoltages || !buf) { + return NULL; + } + + return sensor_read(&volt_map[idx], buf, len); +} + +/* generic function for formatting decimal scaled data */ +static char * +decimal_format(struct sensor *s, int val, char *buf, int len) +{ + int plen; + + if (!buf || len <= 0) { + return NULL; + } + + plen = snprintf(buf, len, "%d", val/DEC_SCALAR); + len -= plen; + + if (val % DEC_SCALAR && len > 0) { + snprintf(buf+plen, len, ".%02d", val%DEC_SCALAR); + } + + return buf; +} + +#define LM77_TEMP 0x0 +static int +lm77_therm_read(struct sensor *s) +{ + int sensor = s->sensor; + int tmp; + int val = 0; + int tries = 2; + + /* sometimes it reads as zero... try again */ + while (tries--) { + /* LM77 returns the bytes backwards - */ + /* address = base + deviceid + 1 for read */ + val = cobalt_i2c_read_word(COBALT_I2C_DEV_LM77 + + (sensor<<1) + 1, LM77_TEMP); + if (val < 0) { + /* read failed, return the last known value */ + return s->last_val; + } + + tmp = (val<<8 & 0xff00) + (val>>8 & 0x00ff); + if (tmp) { + val = tmp >> 4; + val *= DEC_SCALAR; + if (tmp & 0x8) { + val += DEC_SCALAR/2; + } + break; + } + } + return val; +} + +#define ADM1029_CTL_CFAULT_OVER 0x01 +#define ADM1029_CTL_ALARM_OVER 0x02 +#define ADM1029_CTL_INT_OVER 0x04 +#define ADM1029_CTL_ALARM_LOW 0x08 +#define ADM1029_CTL_CFAULT_UNDER 0x10 +#define ADM1029_CTL_ALARM_UNDER 0x20 +#define ADM1029_CTL_INT_UNDER 0x40 +#define ADM1029_CTL_LATCH 0x80 + +#define ADM1029_FAN_CTL(i) (0x18 + i) +#define ADM1029_TEMP_CTL(i) (0x40 + i) +#define ADM1029_AIN_CTL(i) (0x50 + i) + +#define ADM1029_TEMP_HIGH(i) (0x90 + i) +#define ADM1029_TEMP_LOW(i) (0x98 + i) +#define ADM1029_AIN_HIGH(i) (0xa8 + i) +#define ADM1029_AIN_LOW(i) (0xb0 + i) + +#define ADM1029_TEMP_VALUE(i) (0xa0 + i) +#define ADM1029_AIN_VALUE(i) (0xb8 + i) + +#ifdef CONFIG_COBALT_ACPI +static int +adm1029_handler(cobalt_acpi_evt *evt, void * data) +{ + int j, k; + + switch (evt->ev_type) { + case COBALT_ACPI_EVT_SM_INT: + evt->ev_data = 0; + evt->ev_type = COBALT_ACPI_EVT_VOLT; + for (j=0; jev_data |= (1 << j); + volt_map[j].cache = 0; + } + } + break; + + case COBALT_ACPI_EVT_THERM: + evt->ev_data = 0; + for (j=0; jev_data |= (1 << j); + therm_map[j].cache = 0; + } + } + break; + + default: + return -1; + } + return 0; +} +#endif /* CONFIG_COBALT_ACPI */ + +static int +adm1029_init(void) +{ + +#ifdef CONFIG_COBALT_ACPI + cobalt_acpi_register_evt_handler(adm1029_handler, + COBALT_ACPI_EVT_THERM, NULL); + cobalt_acpi_register_evt_handler(adm1029_handler, + COBALT_ACPI_EVT_SM_INT, NULL); +#endif + + return 0; +} + +static int +adm1029_therm_read(struct sensor *s) +{ + int sensor = s->sensor; + int val; + + val = cobalt_i2c_read_byte(COBALT_I2C_DEV_ADM1029, + ADM1029_TEMP_VALUE(sensor)); + if (val < 0) { + /* read failed, return the last known value */ + return s->last_val; + } + if (val & 0x80) { + val -= 256; + } + val *= DEC_SCALAR; + + return val; +} + +static int +adm1029_volt_read(struct sensor *s) +{ + int sensor = s->sensor; + int val; + + val = cobalt_i2c_read_byte(COBALT_I2C_DEV_ADM1029, + ADM1029_AIN_VALUE(sensor)); + if (val < 0) { + /* read failed, return the last known value */ + return s->last_val; + } + + /* already scaled by 100 */ + val *= DEC_SCALAR/100; + + return val; +} + +static int +alpine_vcore_scale(struct sensor *s, int val) +{ + /* the measured Vbat switch cost is negligable + * due to very low current through the diode */ + return val; +} + +#define VBAT_REG 0x608 +#define VBAT_BIT 0x1 +static void +alpine_vbat_switch(struct sensor *s, int pre) +{ + unsigned char v = inb(VBAT_REG); + unsigned long j = jiffies; + + if (pre) { + v |= VBAT_BIT; + /* + * disable AIN0 INT# assertion before switching to + * Vbat because the input is shared with Vcore and + * their acceptable ranges are very different. + */ + cobalt_i2c_write_byte(COBALT_I2C_DEV_ADM1029, + ADM1029_AIN_CTL(s->sensor), 0x0); + } else { + v &= ~VBAT_BIT; + } + + outb(v, VBAT_REG); + + /* + * wait for the round-robin monitor to complete a cycle + * before _and_ after toggling Vbat switch, otherwise + * stale data in AIN0 will trigger INT# assertion. + */ + while ((jiffies - j) < HZ) { + /* block for ~ 1sec */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + } + + if (!pre) { + /* + * now re-enable INT# assertion capability for AIN0 + * (this also clears the AIN0 fault latch at bit 7) + */ + cobalt_i2c_write_byte(COBALT_I2C_DEV_ADM1029, + ADM1029_AIN_CTL(s->sensor), + ADM1029_CTL_INT_OVER | ADM1029_CTL_INT_UNDER); + } +} + +static int +alpine_vbat_scale(struct sensor *s, int val) +{ + /* + * The spec says 2.5V max - but empirically, 3.3V works :) + * The Vbat switch costs 0.3 volts + */ + if (val) val += (3 * DEC_SCALAR)/10; + + return val; +} + +#ifdef CONFIG_PROC_FS +static int +sensor_write_proc(int nsensors, struct sensor *map, + struct file *file, const char *buf, unsigned long len, void *x) +{ + char *pg; + + if (len > PAGE_SIZE) { + return -EOVERFLOW; + } + + pg = (char *)__get_free_page(GFP_KERNEL); + if (!pg) { + return -ENOMEM; + } + + if (copy_from_user(pg, buf, len)) { + free_page((unsigned long)pg); + return -EFAULT; + } + pg[len] = '\0'; + + /* format: `cache_timeout #' in seconds */ + if (len>15 && !strncmp("cache_timeout ", pg, 14) && isdigit(*(pg+14))) { + unsigned long i, sec = simple_strtoul(pg+14, NULL, 0); + for (i=0; i= plen) { + *eof = 1; + up(&sensor_sem); + return 0; + } + + plen = 0; + for (i = 0; i < nsensors; i++) { + char sbuf[32]; + if (sensor_read(&map[i], sbuf, sizeof(sbuf))) + plen += sprintf(buf+plen, "%d [%s]: %s\n", i, map[i].desc, sbuf); + } + + up(&sensor_sem); + + return cobalt_gen_proc_read(buf, plen, start, pos, len, eof); +} + +static int +therm_read_proc(char *buf, char **start, off_t pos, int len, int *eof, void *x) +{ + return sensor_read_proc(cobalt_nthermals, therm_map, + buf, start, pos, len, eof, x); +} +static int +therm_write_proc(struct file *file, const char *buf, unsigned long len, void *x) +{ + return sensor_write_proc(cobalt_nthermals, therm_map, file, buf, len, x); +} + +static int +volt_read_proc(char *buf, char **start, off_t pos, int len, int *eof, void *x) +{ + return sensor_read_proc(cobalt_nvoltages, volt_map, + buf, start, pos, len, eof, x); +} +static int +volt_write_proc(struct file *file, const char *buf, unsigned long len, void *x) +{ + return sensor_write_proc(cobalt_nvoltages, volt_map, file, buf, len, x); +} +#endif /* CONFIG_PROC_FS */ + +#endif /* CONFIG_COBALT_SENSORS */ diff -Naur linux-2.6.18.2.orig/drivers/cobalt/serialnum.c linux-2.6.18.2/drivers/cobalt/serialnum.c --- linux-2.6.18.2.orig/drivers/cobalt/serialnum.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/serialnum.c 2006-11-15 21:20:19.742033186 -0800 @@ -0,0 +1,453 @@ +/* $Id: serialnum.c,v 1.15 2001/10/23 20:15:27 thockin Exp $ */ +/* + * + * Author: Philip Gladstone, Axent Technologies + * modified for Nat Semi PC[89]7317 by asun@cobalt.com + * ported to 2.4.x by thockin@sun.com + * alpine serial eeprom by erik.glling@sun.com + * Copyright (c) 2000 Axent Technologies, Cobalt Networks + * Copyright (c) 2001 Axent Technologies, Sun Microsystems + * + * This module interrogates the DS2401 Silicon Serial Number chip + * that is attached to all x86 Cobalt systems. + * + * It exports /proc/cobalt/hostid which is four bytes generated from of + * the id. It can be linked to /var/adm/hostid or /etc/hostid for the + * hostid command to use. + * + * It exports /proc/cobalt/serialnumber which is the entire 64 bit value + * read back (in ascii). + * + * For the guts of the 1 wire protocol used herein, please see the DS2401 + * specification. + * + * 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 driver is SMP safe by nature. --TPH + */ +#include +#if defined (CONFIG_COBALT_SERNUM) || defined(CONFIG_COBALT_SERNUM_MODULE) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define SN_DRIVER "Cobalt Networks Serial Number driver" +#define SN_DRIVER_VMAJ 1 +#define SN_DRIVER_VMIN 6 + +/* dependent on systype */ +static unsigned int sn_direction; +static unsigned int sn_output; +static unsigned int sn_input; +static unsigned int sn_mask; + +/* 3k style systems */ +#define III_SN_DIRECTION 0x7d +#define III_SN_OUTPUT 0x7e +#define III_SN_INPUT 0x7f +#define III_SN_MASK 0x08 +static struct pci_dev *id_dev; + +/* 5k style systems */ +#define V_SN_DIRECTION (sn_io_base + 0x01) +#define V_SN_OUTPUT (sn_io_base + 0x00) +#define V_SN_INPUT (sn_io_base + 0x00) +#define V_SN_MASK (sn_io_base + 0x01) +static unsigned int sn_io_base; + +#define SSN_SIZE 8 /* bytes */ +static char ssn_string[SSN_SIZE * 2 + 1]; +static unsigned long hostid; +static int debug; +#ifdef CONFIG_PROC_FS +#ifdef CONFIG_COBALT_OLDPROC +static struct proc_dir_entry *proc_hostid; +static struct proc_dir_entry *proc_serialnum; +#endif +static struct proc_dir_entry *proc_chostid; +static struct proc_dir_entry *proc_cserialnum; +#endif + +static int +hostid_read(char *buf, char **start, off_t pos, int len, int *eof, void *x) +{ + int plen = sizeof(hostid); + memcpy(buf, &hostid, sizeof(hostid)); + return cobalt_gen_proc_read(buf, plen, start, pos, len, eof); +} + +static int +serialnum_read(char *buf, char **start, off_t pos, int len, int *eof, void *x) +{ + int plen = sizeof(ssn_string); + sprintf(buf, "%s\n", ssn_string); + return cobalt_gen_proc_read(buf, plen, start, pos, len, eof); +} + +/* set up the requisite IO bits */ +static int __init +io_init(void) +{ + unsigned char data; + + if (cobt_is_3k()) { + /* The GPIO tied to the ID chip is on the PMU */ + id_dev = pci_find_device(PCI_VENDOR_ID_AL, + PCI_DEVICE_ID_AL_M7101, NULL); + if (!id_dev) { + EPRINTK("can't find PMU for serialnumber access\n"); + return -ENXIO; + } + + /* Set input mode on GPIO3 */ + pci_read_config_byte(id_dev, sn_direction, &data); + if (debug > 1) { + WPRINTK("read of register 0x%x = 0x%x\n", + sn_direction, data); + } + if (data & sn_mask) { + pci_write_config_byte(id_dev, sn_direction, + data & ~sn_mask); + } + + /* Set the output value to be 0 */ + pci_read_config_byte(id_dev, sn_output, &data); + if (debug > 1) { + WPRINTK("read of register 0x%x = 0x%x\n", + sn_output, data); + } + if (data & sn_mask) { + pci_write_config_byte(id_dev, sn_output, + data & ~sn_mask); + } + } else if (cobt_is_5k()) { + u16 addr; + + addr = superio_ldev_base(PC87317_DEV_GPIO); + if (addr) { + u8 val; + + sn_io_base = addr; + + /* set output value to 0 */ + val = inb(sn_direction); + outb(val | sn_mask, sn_direction); + data = inb(sn_output); + if (data & sn_mask) { + outb(data & ~sn_mask, sn_output); + } + /* set to input */ + outb(val & ~sn_mask, sn_direction); + } + } else { + return -ENXIO; + } + + /* pick proper variables */ + if (cobt_is_3k()) { + sn_direction = III_SN_DIRECTION; + sn_output = III_SN_OUTPUT; + sn_input = III_SN_INPUT; + sn_mask = III_SN_MASK; + } else if (cobt_is_5k()) { + sn_direction = V_SN_DIRECTION; + sn_output = V_SN_OUTPUT; + sn_input = V_SN_INPUT; + sn_mask = V_SN_MASK; + } else { + return -1; + } + + /* Let things calm down */ + udelay(500); + return 0; +} + +/* write out a bit */ +static void __init +io_write(int delay) +{ + if (cobt_is_3k()) { + unsigned char data; + /* Set output mode on GPIO3 */ + pci_read_config_byte(id_dev, sn_direction, &data); + pci_write_config_byte(id_dev, sn_direction, data | sn_mask); + udelay(delay); + + /* Set input mode */ + pci_write_config_byte(id_dev, sn_direction, data & ~sn_mask); + } else if (cobt_is_5k()) { + unsigned char direction; + + /* change to output and back */ + direction = inb(sn_direction); + outb(direction | sn_mask, sn_direction); + udelay(delay); + outb(direction & ~sn_mask, sn_direction); + } +} + +/* read in a bit */ +static int __init +io_read(void) +{ + unsigned char data = 0; + + /* Get the input value */ + if (cobt_is_3k()) { + pci_read_config_byte(id_dev, sn_input, &data); + } else if (cobt_is_5k()) { + data = inb(sn_input); + } + + return (data & sn_mask) ? 1 : 0; +} + +static void __init +io_write_byte(unsigned char c) +{ + int i; + unsigned long flags; + + local_save_flags(flags); + + for (i = 0; i < 8; i++, c >>= 1) { + local_irq_disable(); + if (c & 1) { + /* Transmit a 1 */ + io_write(5); + udelay(80); + } else { + /* Transmit a 0 */ + io_write(80); + udelay(10); + } + local_irq_restore(flags); + } +} + +static int __init +io_read_byte(void) +{ + int i; + int c = 0; + unsigned long flags; + + local_save_flags(flags); + + for (i = 0; i < 8; i++) { + local_irq_disable(); + io_write(1); /* Start the read */ + udelay(2); + if (io_read()) { + c |= 1 << i; + } + udelay(60); + local_irq_restore(flags); + } + + return c; +} + +static int __init +get_ssn(unsigned char *buf) +{ + int i; + unsigned long flags; + + /* + * Alpine does not have a dallas chip. Instead + * we read from an eprom. + */ + if (cobt_is_alpine()) { + for (i = 0; i < 8; i++) { + buf[i] = cobalt_i2c_read_byte(COBALT_I2C_DEV_AT24C02, + 12 + i); + } + return 0; + } + + /* + * bit-bang the Dallas 2401 + */ + + local_save_flags(flags); + local_irq_disable(); + + /* Master Reset Pulse */ + for (i = 0; i < 600; i += 30) { + if (io_read()) { + break; + } + } + + if (i >= 600) { + if (debug) { + EPRINTK("the data line seems to be held low\n"); + } + local_irq_restore(flags); + return -ENXIO; + } + + io_write(600); + + for (i = 0; i < 300; i += 15) { + udelay(15); + if (io_read() == 0) { + /* We got a presence pulse */ + udelay(600); /* Wait for things to quiet down */ + break; + } + } + local_irq_restore(flags); + + if (i >= 300) { + if (debug) + EPRINTK("no presence pulse detected\n"); + return -ENXIO; + } + + io_write_byte(0x33); + + for (i = 0; i < 8; i++) { + int rc; + + rc = io_read_byte(); + if (rc < 0) { + return rc; + } + + *buf++ = rc; + } + + return 0; +} + +int __init +cobalt_serialnum_init(void) +{ + unsigned char ssn[SSN_SIZE]; + int rc; + int i; + + printk(KERN_INFO "%s %d.%d (modified by jeff@404ster.com)\n", SN_DRIVER,SN_DRIVER_VMAJ,SN_DRIVER_VMIN); + /* set up for proper IO */ + rc = io_init(); + if (rc) { + return rc; + } + + /* + * NOTE: the below algorithm CAN NOT be changed. We have many systems + * out there registered with the serial number AS DERIVED by this + * algorithm. + */ + + rc = get_ssn(ssn); + if (rc) { + return rc; + } + + /* Convert to ssn_string */ + for (i = 7; i >= 0; i--) { + sprintf(ssn_string + (7 - i) * 2, "%02x", ssn[i]); + } + + /* get four bytes for a pretty unique (not guaranteed) hostid */ + hostid = *(unsigned long *)ssn ^ *(unsigned long *)(ssn+4); + +#ifdef CONFIG_PROC_FS +#ifdef CONFIG_COBALT_OLDPROC + proc_hostid = create_proc_read_entry("hostid", 0, NULL, + hostid_read, NULL); + if (!proc_hostid) { + EPRINTK("can't create /proc/hostid\n"); + } + proc_serialnum = create_proc_read_entry("serialnumber", 0, NULL, + serialnum_read, NULL); + if (!proc_serialnum) { + EPRINTK("can't create /proc/serialnumber\n"); + } +#endif + proc_chostid = create_proc_read_entry("hostid", 0, proc_cobalt, + hostid_read, NULL); + if (!proc_chostid) { + EPRINTK("can't create /proc/cobalt/hostid\n"); + } + proc_cserialnum = create_proc_read_entry("serialnumber", 0, + proc_cobalt, serialnum_read, NULL); + if (!proc_cserialnum) { + EPRINTK("can't create /proc/cobalt/serialnumber\n"); + } +#endif + + return 0; +} + +char * +cobalt_serialnum_get(void) +{ + return ssn_string; +} + +unsigned long +cobalt_hostid_get(void) +{ + return hostid; +} + +#if defined(CONFIG_COBALT_SERNUM_MODULE) +MODULE_PARM(debug, "i"); + +int +init_module(void) +{ + return cobalt_serialnum_init(); +} + +void +cleanup_module(void) +{ +#ifdef CONFIG_PROC_FS +#ifdef CONFIG_COBALT_OLDPROC + remove_proc_entry("hostid", NULL); + remove_proc_entry("serialnumber", NULL); +#endif + remove_proc_entry("hostid", proc_cobalt); + remove_proc_entry("serialnumber", proc_cobalt); +#endif +} + +module_init(init_module); +module_exit(cleanup_module); +#endif /* MODULE */ + +#endif /* CONFIG_COBALT_SERNUM */ diff -Naur linux-2.6.18.2.orig/drivers/cobalt/systype.c linux-2.6.18.2/drivers/cobalt/systype.c --- linux-2.6.18.2.orig/drivers/cobalt/systype.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/systype.c 2006-11-15 21:20:19.742033186 -0800 @@ -0,0 +1,280 @@ +/* + * $Id: systype.c,v 1.33 2002/11/04 17:54:15 thockin Exp $ + * systype.c : routines for figuring out which Cobalt system this is + * + * Copyright 2001-2002 Sun Microsystems, Inc. + * + * By: Tim Hockin + * Adrian Sun + * Duncan Laurie + * + * This driver is SMP safe by nature. --TPH + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +/* for easy first-pass analysis */ +#if defined(CONFIG_COBALT_GEN_III) +int COBALT_GENERATION_III_DEFINED; +#endif +#if defined(CONFIG_COBALT_GEN_V) +int COBALT_GENERATION_V_DEFINED; +#endif + +cobt_sys_t cobt_type = COBT_UNINITIALIZED; +EXPORT_SYMBOL(cobt_type); +unsigned long cobt_rev; +EXPORT_SYMBOL(cobt_rev); + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *proc_systype; +#endif +static int systype_read_proc(char *buf, char **start, off_t pos, int len, + int *eof, void *x); +static char *systype_str(cobt_sys_t type); +static unsigned long boardrev_read(void); + +void __init +cobalt_boardrev_init(void) +{ + cobt_rev = boardrev_read(); +} + +int __init +cobalt_systype_init(void) +{ + cobalt_systype_probe(); + +#ifdef CONFIG_PROC_FS + proc_systype = create_proc_read_entry("systype", 0, + proc_cobalt, systype_read_proc, NULL); + if (!proc_systype) { + EPRINTK("can't create /proc/cobalt/systype\n"); + } +#endif + + if (cobt_type == COBT_UNKNOWN) { + printk(KERN_INFO "Cobalt system type is unknown, trouble will ensue (I can vouch for this)\n"); + return -1; + } else { + printk(KERN_INFO "Cobalt system type is %s\n",systype_str(cobt_type)); + return 0; + } +} + +#if defined(CONFIG_COBALT_GEN_III) +static cobt_sys_t +systype_probe_3k(void) +{ + struct pci_dev *pdev; + cobt_sys_t retval = COBT_UNKNOWN; + + /* board identifier for RaQ3/4 vs Qube3 is on the PMU @ 0x7f */ + pdev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, NULL); + if (pdev) { + /* + * check to see what board we are on + * ( RaQ 3, RaQ 4, Qube 3 ) + */ + unsigned char val; + + /* momentarily set DOGB# to input */ + pci_read_config_byte(pdev, 0x7d, &val); + pci_write_config_byte(pdev, 0x7d, val & ~0x20); + + /* read the GPIO register */ + pci_read_config_byte(pdev, 0x7f, &val); + /* RaQ3/4 boards have DOGB (0x20) high, + * Qube3 has DOGB low */ + if (val & 0x20) { + retval = COBT_PACIFICA; + } else { + retval = COBT_CARMEL; + } + + /* change DOGB back to output */ + pci_read_config_byte(pdev, 0x7d, &val); + pci_write_config_byte(pdev, 0x7d, val | 0x20); + } + + /* assign to this, so the compiler shuts up */ + COBALT_GENERATION_III_DEFINED = 1; + + return retval; +} +#else +#define systype_probe_3k() (COBT_UNKNOWN) +#endif + +#if defined(CONFIG_COBALT_GEN_V) +static cobt_sys_t +systype_probe_5k(void) +{ + struct pci_dev *pdev; + cobt_sys_t retval = COBT_UNKNOWN; + + /* is it a gen V ? */ + pdev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL); + if (pdev) { + retval = COBT_MONTEREY; + goto out; + } + + pdev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL); + if (pdev) { + pdev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_LE, NULL); + if (pdev) { + retval = COBT_ALPINE; + goto out; + } + } + +out: + /* assign to this, so the compiler shuts up */ + COBALT_GENERATION_V_DEFINED = 1; + + return retval; +} +#else +#define systype_probe_5k() (COBT_UNKNOWN) +#endif + +static cobt_sys_t +systype_probe_gp(void) +{ + struct pci_dev *pdev; + cobt_sys_t retval = COBT_UNKNOWN; + + /* is it a GP system? */ + pdev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL); + if (pdev) { + pdev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_HE, NULL); + if (pdev) { + retval = COBT_BIGBEAR; + } + } + + return retval; +} + +cobt_sys_t +cobalt_systype_probe(void) +{ + static int init_done = 0; + + if (init_done) { + return cobt_type; + } + + /* check for 3k family systems */ + + cobt_type = systype_probe_3k(); + if (cobt_type != COBT_UNKNOWN) + goto out; + + /* check for 5k family systems */ + cobt_type = systype_probe_5k(); + if (cobt_type != COBT_UNKNOWN) + goto out; + + /* it's a GP system or unknown */ + cobt_type = systype_probe_gp(); + +out: + if (cobt_type != COBT_UNKNOWN) { + init_done = 1; + } + + return cobt_type; +} +EXPORT_SYMBOL(cobalt_systype_probe); + +#ifdef CONFIG_PROC_FS +static int +systype_read_proc(char *buf, char **start, off_t pos, int len, + int *eof, void *x) +{ + int plen = sprintf(buf, "%s\n", systype_str(cobt_type)); + return cobalt_gen_proc_read(buf, plen, start, pos, len, eof); +} +#endif + +static char * +systype_str(cobt_sys_t type) +{ + switch (type) { + case COBT_PACIFICA: + return "Pacifica"; + break; + case COBT_CARMEL: + return "Carmel"; + break; + case COBT_MONTEREY: + return "Monterey"; + break; + case COBT_ALPINE: + return "Alpine"; + break; + case COBT_BIGBEAR: + return "BigBear"; + break; + case COBT_UNKNOWN: + default: + return "unknown"; + break; + } +} + +static unsigned long +boardrev_read(void) +{ + unsigned long rev; + + switch (cobt_type) { +#ifdef CONFIG_COBALT_RAQ + case COBT_PACIFICA: + case COBT_CARMEL: + /* No usable board rev on these systems */ + return 0; + case COBT_MONTEREY: + /* + * the boardrev on monterey is strapped off of GPM[3:0] + * and is read from port 0xc52 + */ + return inb(0xc52); + case COBT_ALPINE: + /* + * the boardrev on alpine in stored in the i2c eeprom + * location 4 + */ + rev = cobalt_i2c_read_byte(COBALT_I2C_DEV_AT24C02, 0x04); + rev |= cobalt_i2c_read_byte(COBALT_I2C_DEV_AT24C02, 0x05) << 8; + rev |= cobalt_i2c_read_byte(COBALT_I2C_DEV_AT24C02, 0x06) << 16; + rev |= cobalt_i2c_read_byte(COBALT_I2C_DEV_AT24C02, 0x07) << 24; + if (rev == 0xffffffff) + rev = 0; + return rev; +#endif + case COBT_BIGBEAR: + /* No board revs at this time */ + return 0; + case COBT_UNKNOWN: + case COBT_UNINITIALIZED: + return 0; + } + return 0; +} diff -Naur linux-2.6.18.2.orig/drivers/cobalt/wdt.c linux-2.6.18.2/drivers/cobalt/wdt.c --- linux-2.6.18.2.orig/drivers/cobalt/wdt.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/drivers/cobalt/wdt.c 2006-11-15 21:20:19.742033186 -0800 @@ -0,0 +1,424 @@ +/* $Id: wdt.c,v 1.21 2002/07/02 00:38:17 asun Exp $ */ +/* + * Cobalt kernel WDT timer driver + * Tim Hockin + * Adrian Sun + * Chris Johnson + * Copyright (c)1999-2000, Cobalt Networks + * Copyright (c)2001, Sun Microsystems + * + * This should be SMP safe. Every external function (except trigger_reboot) + * grabs the wdt lock. No function in this file may call any exported + * function (excepting trigger_reboot). The disable counter is an atomic, so + * there should be no issues there. --TPH + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DOGB 0x20 +#define ALI_7101_WDT 0x92 +#define ALI_WDT_ARM 0x01 +#define WDT_3K_TIMEOUT (HZ >> 4) /* 1/16 second */ + +#define WDT_SUPERIO_TIMEOUT (0x01) /* 1 minute */ +#define WDT_5K_TIMEOUT (HZ << 3) /* 8 seconds */ + +#define WDT_DRIVER "Cobalt Networks Watchdog Timer driver" +#define WDT_DRIVER_VMAJ 1 +#define WDT_DRIVER_VMIN 0 + +static unsigned long wdt_timeout; +static unsigned long long tsc_per_wdt; +static int initialized; + +#ifdef CONFIG_COBALT_WDT +struct timer_list cobalt_wdt_timer; +static atomic_t cobalt_wdt_disable_count = ATOMIC_INIT(0); +static spinlock_t wdt_lock = SPIN_LOCK_UNLOCKED; +#endif + +/* gen III */ +static struct pci_dev *cobalt_pmu; +static int use_pic; +/* gen V */ +static u16 superio_pm_port; + +#ifdef CONFIG_COBALT_WDT +static void do_refresh(void); +static void do_cleardog(void); +static void do_disable(void); +static void do_reenable(void); +#endif + +static unsigned long __init +chipset_setup(void) +{ + unsigned char tmp; + if (cobt_is_3k()) { + /* + * Set up the PMU for 3k boards. It has a max + * of a 1 second timeout. + */ + struct pci_dev *south; + + /* PMU (1543 ver A1-E) has a built-in WDT. Set it to 1 sec */ + cobalt_pmu = pci_find_device(PCI_VENDOR_ID_AL, + PCI_DEVICE_ID_AL_M7101, NULL); + if (!cobalt_pmu) { + EPRINTK("can't find south bridge for WDT\n"); + return 0; + } + pci_write_config_byte(cobalt_pmu, ALI_7101_WDT, 0x02); + + /* why it is called 1543, but DevId is 1533 I'll never know */ + south = pci_find_device(PCI_VENDOR_ID_AL, + PCI_DEVICE_ID_AL_M1533, NULL); + if (!south) { + EPRINTK("can't find south bridge for WDT\n"); + use_pic = 1; + } else { + /* reversion # is here - must match ???1001?(b) + * else use PIC for WDT */ + pci_read_config_byte(south, 0x5e, &tmp); + use_pic = ((tmp & 0x1e) != 0x12); + } + + if (!use_pic) { + /* set DOGB GPIO pin to OUTPUT - JIC */ + pci_read_config_byte(cobalt_pmu, 0x7d, &tmp); + pci_write_config_byte(cobalt_pmu, 0x7d, tmp | DOGB); + } + return WDT_3K_TIMEOUT; + } else if (cobt_is_monterey()) { + /* + * Set up the Nat. Semi SuperI/O for XTR. It has a + * minimum of a 1 minute timeout. + */ + + /* superi/o -- select pm logical device and get base address */ + superio_pm_port = superio_ldev_base(PC87317_DEV_PM); +#ifdef CONFIG_COBALT_WDT + if (!superio_pm_port) { + return 0; + } + outb(PC87317_PMDEV_WDTO, superio_pm_port); + outb(WDT_SUPERIO_TIMEOUT, superio_pm_port + 1); +#endif + return WDT_5K_TIMEOUT; + } else if (cobt_is_alpine()) { + /* + * Set up the Nat. Semi SuperI/O for Alpine. It has a + * minimum of a 1 minute timeout. + */ + + /* superi/o -- select pm logical device and get base address */ + superio_pm_port = superio_ldev_base(PC87417_DEV_SWC); +#ifdef CONFIG_COBALT_WDT + if (!superio_pm_port) { + return 0; + } + /* select the WDT bank of SWC */ + outb(PC87417_SWCBANK_WDT, superio_pm_port+PC87417_SWC_BANK); + /* clear event config... */ + tmp = inb(superio_pm_port+PC87417_WDT_CONFIG); + outb(0, superio_pm_port+PC87417_WDT_CONFIG); + /* ...before mucking with timeout */ + outb(WDT_SUPERIO_TIMEOUT, + superio_pm_port+PC87417_WDT_TIMEOUT); + /* restore the event config */ + outb(tmp, superio_pm_port+PC87417_WDT_CONFIG); + /* enable the counter */ + outb(1, superio_pm_port+PC87417_WDT_CONTROL); +#endif + return WDT_5K_TIMEOUT; + } + + return 0; +} + +void __init +cobalt_wdt_init(void) +{ + unsigned long long start, stop; + + printk(KERN_INFO "%s %d.%d (modified by jeff@404ster.com)\n", WDT_DRIVER,WDT_DRIVER_VMAJ,WDT_DRIVER_VMIN); + + wdt_timeout = chipset_setup(); + + /* figure out time */ + rdtscll(start); + udelay(100); + rdtscll(stop); + + /* + * (int) (stop - start) * 10 == tsc per msec + * 1000 / HZ == msec per tick + * wdt_timeout == ticks per watchdog rearm + */ + tsc_per_wdt = (int) (stop - start) * 10 * (1000 * wdt_timeout / HZ); + +#ifdef CONFIG_COBALT_WDT + /* set the timer going */ + init_timer(&cobalt_wdt_timer); + cobalt_wdt_timer.function = cobalt_wdt_refresh; + cobalt_wdt_timer.data = 1; + cobalt_wdt_timer.expires = jiffies + wdt_timeout; + add_timer(&cobalt_wdt_timer); + + /* the first timer tick will set it going */ + + if (cobt_is_3k() && use_pic) { + WPRINTK("Cobalt WDT - old board, using PIC controller\n"); + } +#endif /* CONFIG_COBALT_WDT */ + + initialized = 1; +} + +static inline void +hw_disable(void) +{ + if (cobt_is_3k()) { + char tmp; + /* read the value, disable (reset) WDT */ + pci_read_config_byte(cobalt_pmu, ALI_7101_WDT, &tmp); + pci_write_config_byte(cobalt_pmu, ALI_7101_WDT, + (tmp & ~ALI_WDT_ARM)); + } else if (cobt_is_monterey()) { + outb(PC87317_PMDEV_WDTO, superio_pm_port); + outb(0, superio_pm_port + 1); + } else if (cobt_is_alpine()) { + unsigned char tmp; + /* select the WDT bank of SWC */ + outb(PC87417_SWCBANK_WDT, superio_pm_port + PC87417_SWC_BANK); + /* clear event config before mucking with timeout */ + tmp = inb(superio_pm_port + PC87417_WDT_CONFIG); + outb(0, superio_pm_port + PC87417_WDT_CONFIG); + /* + * Disable it by setting a 0 time-out. + * The spec says 00h is reserved, but NSC confirms this is the + * way to disable the device. + */ + outb(0, superio_pm_port + PC87417_WDT_TIMEOUT); + /* restore the event config */ + outb(tmp, superio_pm_port + PC87417_WDT_CONFIG); + } +} + +static inline void +hw_enable(void) +{ + if (cobt_is_3k()) { + unsigned char tmp; + /* read the value, disable (reset) WDT, enable WDT */ + pci_read_config_byte(cobalt_pmu, ALI_7101_WDT, &tmp); + pci_write_config_byte(cobalt_pmu, ALI_7101_WDT, + (tmp | ALI_WDT_ARM)); + if (use_pic) { + /* transition GPIO 5 (DOGB) to arm/clear timer */ + pci_read_config_byte(cobalt_pmu, 0x7e, &tmp); + pci_write_config_byte(cobalt_pmu, 0x7e, tmp ^ DOGB); + } + } else if (cobt_is_monterey()) { + outb(PC87317_PMDEV_WDTO, superio_pm_port); + outb(WDT_SUPERIO_TIMEOUT, superio_pm_port + 1); + } else if (cobt_is_alpine()) { + unsigned char tmp; + /* select the WDT bank of SWC */ + outb(PC87417_SWCBANK_WDT, superio_pm_port + PC87417_SWC_BANK); + /* clear event config before mucking with timeout */ + tmp = inb(superio_pm_port + PC87417_WDT_CONFIG); + outb(0, superio_pm_port + PC87417_WDT_CONFIG); + /* enable and refresh the timer */ + outb(WDT_SUPERIO_TIMEOUT, + superio_pm_port + PC87417_WDT_TIMEOUT); + outb(0x80, superio_pm_port + PC87417_WDT_CONTROL); + /* restore event config */ + outb(tmp, superio_pm_port + PC87417_WDT_CONFIG); + } +} + +#ifdef CONFIG_COBALT_WDT +static void +do_refresh(void) +{ + if (!initialized) { + return; + } + + do_cleardog(); + + /* re-arm the timer - this is locked in mod_timer() */ + mod_timer(&cobalt_wdt_timer, jiffies + wdt_timeout); +} +#endif + +EXPORT_SYMBOL(cobalt_wdt_refresh); +void +cobalt_wdt_refresh(unsigned long refresh_timer) +{ +#ifdef CONFIG_COBALT_WDT + unsigned long flags; + spin_lock_irqsave(&wdt_lock, flags); + do_refresh(); + spin_unlock_irqrestore(&wdt_lock, flags); +#endif +} + +#ifdef CONFIG_COBALT_WDT +static void +do_cleardog(void) +{ + static unsigned long long last_tsc = 0; + unsigned long long tmp; + + if (!initialized || (atomic_read(&cobalt_wdt_disable_count) > 0)) { + return; + } + + /* only bother if we're due */ + rdtscll(tmp); + if ((int)(tmp - last_tsc) < tsc_per_wdt) { + return; + } + + if (cobt_is_3k() || cobt_is_monterey()) { + /* this is how we re-start the clock */ + hw_disable(); + hw_enable(); + } else if (cobt_is_alpine()) { + /* select the WDT bank of SWC */ + outb(PC87417_SWCBANK_WDT, superio_pm_port + PC87417_SWC_BANK); + /* refresh the timer */ + outb(0x80, superio_pm_port + PC87417_WDT_CONTROL); + } + + rdtscll(last_tsc); +} +#endif + +EXPORT_SYMBOL(cobalt_wdt_cleardog); +void +cobalt_wdt_cleardog(void) +{ +#ifdef CONFIG_COBALT_WDT + unsigned long flags; + + spin_lock_irqsave(&wdt_lock, flags); + do_cleardog(); + spin_unlock_irqrestore(&wdt_lock, flags); +#endif +} + +/* + * this is called from machine_restart. it should not be used on + * 5k machines. + */ +EXPORT_SYMBOL(cobalt_wdt_trigger_reboot); +void +cobalt_wdt_trigger_reboot(void) +{ + if (cobt_is_3k()) { + if (!cobalt_pmu) { + WPRINTK("no PMU found!\n"); + WPRINTK("reboot not possible!\n"); + return; + } + +#ifdef CONFIG_COBALT_WDT + /* stop feeding it */ + del_timer_sync(&cobalt_wdt_timer); +#endif + + /* kiss your rear goodbye... */ + initialized = 0; + hw_disable(); + hw_enable(); + } +} + +#ifdef CONFIG_COBALT_WDT +static void +do_disable(void) +{ + if (!initialized) { + return; + } + + if (atomic_read(&cobalt_wdt_disable_count) == 0) { + atomic_inc(&cobalt_wdt_disable_count); + del_timer_sync(&cobalt_wdt_timer); + hw_disable(); + } +} +#endif + +EXPORT_SYMBOL(cobalt_wdt_disable); +void +cobalt_wdt_disable(void) +{ +#ifdef CONFIG_COBALT_WDT + unsigned long flags; + + if (cobt_is_3k() && use_pic) { + WPRINTK("in PIC mode - cannot disable\n"); + return; + } + + spin_lock_irqsave(&wdt_lock, flags); + do_disable(); + spin_unlock_irqrestore(&wdt_lock, flags); +#endif +} + +#ifdef CONFIG_COBALT_WDT +static void +do_reenable(void) +{ + int dcnt; + + if (!initialized) { + return; + } + + atomic_dec(&cobalt_wdt_disable_count); + dcnt = atomic_read(&cobalt_wdt_disable_count); + + if (dcnt == 0) { + do_refresh(); + } else if (dcnt < 0) { + WPRINTK("too many enables\n"); + atomic_set(&cobalt_wdt_disable_count, 0); + } +} +#endif + + +EXPORT_SYMBOL(cobalt_wdt_reenable); +void +cobalt_wdt_reenable(void) +{ +#ifdef CONFIG_COBALT_WDT + unsigned long flags; + + spin_lock_irqsave(&wdt_lock, flags); + do_reenable(); + spin_unlock_irqrestore(&wdt_lock, flags); +#endif +} diff -Naur linux-2.6.18.2.orig/drivers/net/Kconfig linux-2.6.18.2/drivers/net/Kconfig --- linux-2.6.18.2.orig/drivers/net/Kconfig 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/drivers/net/Kconfig 2006-11-15 21:20:19.742033186 -0800 @@ -1486,6 +1486,16 @@ . The module will be called e100. +config E100_IGNORE_CSUM + bool "Ignore bad EEPROM checksum" + depends on E100 && EXPERIMENTAL && !CLEAN_COMPILE + help + This option tells the e100 driver to ignore bad EEPROM checksums. + Usually this is a bad idea, as an incorrect checksum can indicate a + serious issue with the network card. + + If unsure, say N. + config LNE390 tristate "Mylex EISA LNE390A/B support (EXPERIMENTAL)" depends on NET_PCI && EISA && EXPERIMENTAL diff -Naur linux-2.6.18.2.orig/drivers/net/e100.c linux-2.6.18.2/drivers/net/e100.c --- linux-2.6.18.2.orig/drivers/net/e100.c 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/drivers/net/e100.c 2006-11-15 21:20:19.742033186 -0800 @@ -162,6 +162,7 @@ #define DRV_VERSION "3.5.10-k2"DRV_EXT #define DRV_DESCRIPTION "Intel(R) PRO/100 Network Driver" #define DRV_COPYRIGHT "Copyright(c) 1999-2005 Intel Corporation" +#define DRV_MODIFIED "Modified by to ignore bad EEPROM checksums" #define PFX DRV_NAME ": " #define E100_WATCHDOG_PERIOD (2 * HZ) @@ -758,9 +759,15 @@ * the sum of words should be 0xBABA */ checksum = le16_to_cpu(0xBABA - checksum); if(checksum != nic->eeprom[nic->eeprom_wc - 1]) { +#if defined(CONFIG_E100_IGNORE_CSUM) +DPRINTK(PROBE, ERR, "EEPROM corrupted, ignoring and moving on\n"); +DPRINTK(PROBE, ERR, " Caclulated Checksum: %X\n",checksum); +DPRINTK(PROBE, ERR, " EEPROM Checksum: %X\n",nic->eeprom[nic->eeprom_wc - 1]); +#else DPRINTK(PROBE, ERR, "EEPROM corrupted\n"); if (!eeprom_bad_csum_allow) return -EAGAIN; +#endif } return 0; @@ -2876,6 +2883,9 @@ if(((1 << debug) - 1) & NETIF_MSG_DRV) { printk(KERN_INFO PFX "%s, %s\n", DRV_DESCRIPTION, DRV_VERSION); printk(KERN_INFO PFX "%s\n", DRV_COPYRIGHT); +#if defined(CONFIG_E100_IGNORE_CSUM) + printk(KERN_INFO PFX "%s\n", DRV_MODIFIED); +#endif } return pci_module_init(&e100_driver); } diff -Naur linux-2.6.18.2.orig/include/cobalt/acpi.h linux-2.6.18.2/include/cobalt/acpi.h --- linux-2.6.18.2.orig/include/cobalt/acpi.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/acpi.h 2006-11-15 21:20:19.742033186 -0800 @@ -0,0 +1,82 @@ +/* + * $Id: cobalt-acpi.h,v 1.7 2001/10/27 07:03:31 erik Exp $ + * cobalt-acpi.h : support for ACPI interrupts + * + * Copyright 2000 Cobalt Networks, Inc. + * Copyright 2001 Sun Microsystems, Inc. + */ +#ifndef COBALT_ACPI_H +#define COBALT_ACPI_H + +#define SERVERWORKS_ACPI_INDEX_PORT 0x0cd6 +#define SERVERWORKS_ACPI_DATA_PORT 0x0cd7 + + +typedef struct +{ + u16 hw_type; + u16 ev_type; + u32 ev_data; +} cobalt_acpi_evt; + +enum __cobalt_acpi_hw_types +{ + COBALT_ACPI_HW_ANY = 0x0000, + COBALT_ACPI_HW_OSB4 = 0x0001, + COBALT_ACPI_HW_CSB5 = 0x0002, + COBALT_ACPI_HW_PC8731X = 0x0003, + COBALT_ACPI_HW_PC8741X = 0x0004, +}; + +enum __cobalt_acpi_event_types +{ + COBALT_ACPI_EVT_NONE = 0x0000, + COBALT_ACPI_EVT_TMR = 0x0001, /* Timer Event */ + COBALT_ACPI_EVT_BM = 0x00002, /* Bus Master Event */ + COBALT_ACPI_EVT_GBL = 0x0003, /* BIOS Global Lock release */ + COBALT_ACPI_EVT_PWRBTN = 0x0004, /* Power Button press */ + COBALT_ACPI_EVT_SLPBTN = 0x0005, /* Sleep Button press */ + COBALT_ACPI_EVT_RTC = 0x0006, /* RTC Alarm */ + COBALT_ACPI_EVT_WAK = 0x0007, /* Wake event */ + COBALT_ACPI_EVT_GPE = 0x0008, /* General Purpose Event (ev_data = gpe number) */ + + /* events greater than 0x7fff are symbolic events */ + COBALT_ACPI_EVT_SLED = 0x8000, /* Sled removal */ + COBALT_ACPI_EVT_THERM = 0x8001, /* Thermal trip */ + COBALT_ACPI_EVT_FAN = 0x8002, /* Fan Down */ + COBALT_ACPI_EVT_SM_INT = 0x8003, /* System Monitor Interrupt */ + COBALT_ACPI_EVT_VOLT = 0x8004, /* System Monitor Interrupt */ + +}; + +typedef int (* cobalt_acpi_hw_handler)( int irq, void *dev_id, struct pt_regs *regs, void * data ); +typedef int (* cobalt_acpi_enable_handler)( u16 ev_type, u16 ev_data, int en, void *data ); +typedef int (* cobalt_acpi_evt_handler)( cobalt_acpi_evt *evt, void * data ); + + +extern int cobalt_acpi_register_hw_handler( u16 hw_type, + cobalt_acpi_hw_handler hw_handler, + cobalt_acpi_enable_handler en_handler, + void *data ); +extern int cobalt_acpi_unregister_hw_handler( cobalt_acpi_hw_handler handler ); + +extern int cobalt_acpi_register_trans_table( u16 hw_type, u16 table_len, u16 *table ); +extern int cobalt_acpi_unregister_trans_table( u16 hw_type ); + +extern int cobalt_acpi_register_evt_handler( cobalt_acpi_evt_handler handler, + u16 evt_type, + void *data ); +extern int cobalt_acpi_unregister_evt_handler( cobalt_acpi_evt_handler handler ); + +extern int cobalt_acpi_post_event( cobalt_acpi_evt evt ); + +#ifdef CONFIG_COBALT_EMU_ACPI +int cobalt_acpi_generate_proc_evt( cobalt_acpi_evt * evt ); +#else +#define cobalt_acpi_generate_proc_evt( a ) +#endif + + + + +#endif /* COBALT_ACPI_H */ diff -Naur linux-2.6.18.2.orig/include/cobalt/cobalt.h linux-2.6.18.2/include/cobalt/cobalt.h --- linux-2.6.18.2.orig/include/cobalt/cobalt.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/cobalt.h 2006-11-15 21:20:19.742033186 -0800 @@ -0,0 +1,58 @@ +/* $Id: cobalt.h,v 1.16 2002/11/04 17:54:15 thockin Exp $ */ +/* Copyright 2001 Sun Microsystems, Inc. */ +#include +#if !defined(COBALT_H) && defined(CONFIG_COBALT_RAQ) +#define COBALT_H + +/* generational support - for easy checking */ +#ifdef CONFIG_COBALT_GEN_III +# define COBT_SUPPORT_GEN_III 1 +#else +# define COBT_SUPPORT_GEN_III 0 +#endif + +#ifdef CONFIG_COBALT_GEN_V +# define COBT_SUPPORT_GEN_V 1 +#else +# define COBT_SUPPORT_GEN_V 0 +#endif + +/* macros for consistent errors/warnings */ +#define EPRINTK(fmt, args...) \ + printk(KERN_ERR "%s:%s: " fmt , __FILE__ , __FUNCTION__ , ##args) + +#define WPRINTK(fmt, args...) \ + printk(KERN_WARNING "%s:%s: " fmt , __FILE__ , __FUNCTION__ , ##args) + +/* the root of /proc/cobalt */ +extern struct proc_dir_entry *proc_cobalt; +int cobalt_gen_proc_read(char *buf, int plen, char **start, off_t pos, + int len, int *eof); + +//#ifdef CONFIG_COBALT_RAQ +/* keep this test up to date with new generation defines */ +#if !defined(CONFIG_COBALT_GEN_III) && !defined(CONFIG_COBALT_GEN_V) +/* barf if no generation has been selected */ +#error You asked for CONFIG_COBALT_RAQ, but no CONFIG_COBALT_GEN_* ! +#endif + +/* accesses for CMOS */ +#include +#include + +static inline int +cobalt_cmos_read_flag(const unsigned int flag) +{ + unsigned long flags; + u16 cmosfl; + + spin_lock_irqsave(&rtc_lock, flags); + cmosfl = CMOS_READ(COBT_CMOS_FLAG_BYTE_0) << 8; + cmosfl |= CMOS_READ(COBT_CMOS_FLAG_BYTE_1); + spin_unlock_irqrestore(&rtc_lock, flags); + + return (cmosfl & flag) ? 1 : 0; +} +//#endif /* CONFIG_COBALT_RAQ */ + +#endif /* !defined(COBALT_H) && defined(CONFIG_COBALT_RAQ) */ diff -Naur linux-2.6.18.2.orig/include/cobalt/i2c.h linux-2.6.18.2/include/cobalt/i2c.h --- linux-2.6.18.2.orig/include/cobalt/i2c.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/i2c.h 2006-11-15 21:20:19.742033186 -0800 @@ -0,0 +1,40 @@ +/* + * $Id: cobalt-i2c.h,v 1.3 2001/08/22 05:48:04 asun Exp $ + * cobalt-i2c.h : I2C support for LCD/Front Panel + * + * Copyright 2000 Cobalt Networks, Inc. + * Copyright 2001 Sun Microsystems, Inc. + */ +#ifndef COBALT_I2C_H +#define COBALT_I2C_H + +#include +#include + +#define COBALT_I2C_DEV_LED_I 0x40 +#define COBALT_I2C_DEV_LED_II 0x42 +#define COBALT_I2C_DEV_LCD_DATA 0x4a +#define COBALT_I2C_DEV_LCD_INST 0x48 +#define COBALT_I2C_DEV_FP_BUTTONS 0x41 +#define COBALT_I2C_DEV_DRV_SWITCH 0x45 +#define COBALT_I2C_DEV_RULER 0x46 +#define COBALT_I2C_DEV_LM77 0x90 +#define COBALT_I2C_DEV_ADM1029 0x5e +#define COBALT_I2C_DEV_AT24C02 0xae + +#define COBALT_I2C_READ 0x01 +#define COBALT_I2C_WRITE 0x00 + +extern int cobalt_i2c_reset(void); +extern int cobalt_i2c_read_byte(const int dev, const int index); +extern int cobalt_i2c_read_word(const int dev, const int index); +extern int cobalt_i2c_read_block(const int dev, const int index, + unsigned char *data, int count); +extern int cobalt_i2c_write_byte(const int dev, const int index, + const u8 val); +extern int cobalt_i2c_write_word(const int dev, const int index, + const u16 val); +extern int cobalt_i2c_write_block(const int dev, const int index, + unsigned char *data, int count); + +#endif /* COBALT_I2C_H */ diff -Naur linux-2.6.18.2.orig/include/cobalt/lcd.h linux-2.6.18.2/include/cobalt/lcd.h --- linux-2.6.18.2.orig/include/cobalt/lcd.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/lcd.h 2006-11-15 21:20:19.742033186 -0800 @@ -0,0 +1,95 @@ +/* + * $Id: cobalt-lcd.h,v 1.12 2001/11/30 05:38:46 asun Exp $ + * cobalt-lcd.h : some useful defines for the Cobalt LCD driver + * (must be useable from both kernel and user space) + * + * Copyright 1996-2000 Cobalt Networks, Inc. + * Copyright 2001 Sun Microsystems, Inc. + * + * By: Andrew Bose + * Timothy Stonis (x86 version) + * Tim Hockin + * Adrian Sun + * Erik Gilling + * Duncan Laurie + */ +#ifndef COBALT_LCD_H +#define COBALT_LCD_H + +#ifdef __KERNEL__ +#include +#endif +#include + +#define COBALT_LCD_LINELEN 40 +struct lcd_display { + unsigned long buttons; + int size1; + int size2; + unsigned char line1[COBALT_LCD_LINELEN]; + unsigned char line2[COBALT_LCD_LINELEN]; + unsigned char cursor_address; + unsigned char character; + unsigned char leds; + unsigned char *RomImage; +}; + +/* different lcd types */ +#define LCD_TYPE_UNKNOWN 0 +#define LCD_TYPE_PARALLEL 1 +#define LCD_TYPE_PARALLEL_B 2 +#define LCD_TYPE_I2C 3 + +/* Function command codes for ioctl */ +#define LCD_On 1 +#define LCD_Off 2 +#define LCD_Clear 3 +#define LCD_Reset 4 +#define LCD_Cursor_Left 5 +#define LCD_Cursor_Right 6 +#define LCD_Disp_Left 7 +#define LCD_Disp_Right 8 +#define LCD_Get_Cursor 9 +#define LCD_Set_Cursor 10 +#define LCD_Home 11 +#define LCD_Read 12 +#define LCD_Write 13 +#define LCD_Cursor_Off 14 +#define LCD_Cursor_On 15 +#define LCD_Get_Cursor_Pos 16 +#define LCD_Set_Cursor_Pos 17 +#define LCD_Blink_Off 18 +#define LCD_Raw_Inst 19 +#define LCD_Raw_Data 20 +#define LCD_Type 21 + +/* LED controls */ +#define LED_Set 40 +#define LED_Bit_Set 41 +#define LED_Bit_Clear 42 +#define LED32_Set 43 +#define LED32_Bit_Set 44 +#define LED32_Bit_Clear 45 +#define LED32_Get 46 + +/* button ioctls */ +#define BUTTON_Read 50 + +/* Button defs */ +#define BUTTON_Next 0x3D +#define BUTTON_Next_B 0x7E +#define BUTTON_Reset_B 0xFC +#define BUTTON_NONE_B 0xFE +#define BUTTON_Left_B 0xFA +#define BUTTON_Right_B 0xDE +#define BUTTON_Up_B 0xF6 +#define BUTTON_Down_B 0xEE +#define BUTTON_Enter_B 0xBE + +#define BUTTON_MASK 0xFE + +void cobalt_lcd_start_twiddle(void); +void cobalt_lcd_stop_twiddle(void); +void cobalt_lcd_off(void); + +#endif /* COBALT_LCD_H */ diff -Naur linux-2.6.18.2.orig/include/cobalt/led.h linux-2.6.18.2/include/cobalt/led.h --- linux-2.6.18.2.orig/include/cobalt/led.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/led.h 2006-11-15 21:20:19.742033186 -0800 @@ -0,0 +1,59 @@ +/* + * $Id: cobalt-led.h,v 1.7 2001/11/08 01:15:33 thockin Exp $ + * cobalt-led.c + * + * Copyright 1996-2000 Cobalt Networks, Inc. + * Copyright 2001 Sun Microsystems, Inc. + * + * By: Andrew Bose + * Timothy Stonis (x86 version) + * Tim Hockin + * Adrian Sun + * Erik Gilling + * Duncan Laurie + */ +#ifndef COBALT_LED_H +#define COBALT_LED_H + +/* the set of all leds available on Cobalt systems */ +#define LED_SHUTDOWN (1 << 0) +#define LED_WEBLIGHT (1 << 1) +#define LED_COBALTLOGO (1 << 2) +#define LED_ETH0_TXRX (1 << 3) +#define LED_ETH0_LINK (1 << 4) +#define LED_ETH1_TXRX (1 << 5) +#define LED_ETH1_LINK (1 << 6) +#define LED_DISK0 (1 << 7) +#define LED_DISK1 (1 << 8) +#define LED_DISK2 (1 << 9) +#define LED_DISK3 (1 << 10) +#define LED_SYSFAULT (1 << 11) +#define LED_MONTEREY_UNUSED0 (1 << 12) +#define LED_MONTEREY_UNUSED1 (1 << 13) +/* LED_MONTEREY_UNUSED2 is below */ +#define LED_HEART LED_MONTEREY_UNUSED0 +#define LED_SPARE LED_MONTEREY_UNUSED1 +#define LED_SLED0 (1 << 14) +#define LED_SLED1 (1 << 15) +#define LED_SLED2 (1 << 16) +#define LED_SLED3 (1 << 17) +#define LED_MONTEREY_UNUSED2 (1 << 18) +#define LED_SPARE2 LED_MONTEREY_UNUSED2 + +#ifdef __KERNEL__ + +extern void cobalt_led_set(const unsigned int leds); +extern void cobalt_led_set_bits(const unsigned int leds); +extern void cobalt_led_clear_bits(const unsigned int leds); +extern void cobalt_led_set_lazy(const unsigned int leds); +extern void cobalt_led_set_bits_lazy(const unsigned int leds); +extern void cobalt_led_clear_bits_lazy(const unsigned int leds); +extern unsigned int cobalt_led_get(void); + +extern int cobalt_fpled_register(unsigned int (*fn)(void *), void *data); +extern int cobalt_fpled_unregister(unsigned int (*fn)(void *), void *data); + +#endif /* __KERNEL__ */ + +#endif /* COBALT_LED_H */ + diff -Naur linux-2.6.18.2.orig/include/cobalt/misc.h linux-2.6.18.2/include/cobalt/misc.h --- linux-2.6.18.2.orig/include/cobalt/misc.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/misc.h 2006-11-15 21:27:17.024148165 -0800 @@ -0,0 +1,12 @@ +/* $Id: cobalt-misc.h,v 1.1 2001/04/04 03:36:43 thockin Exp $ */ +/* Copyright 2001 Sun Microsystems, Inc. */ +#ifndef COBALT_MISC_H +#define COBALT_MISC_H + +void cobalt_flush(void); +void cobalt_nmi(unsigned char reason, struct pt_regs *regs); +void cobalt_restart(void); +void cobalt_halt(void); +void cobalt_power_off(void); + +#endif diff -Naur linux-2.6.18.2.orig/include/cobalt/net.h linux-2.6.18.2/include/cobalt/net.h --- linux-2.6.18.2.orig/include/cobalt/net.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/net.h 2006-11-15 21:20:19.746033053 -0800 @@ -0,0 +1,12 @@ +/* $Id: cobalt-net.h,v 1.3 2002/10/25 01:02:42 thockin Exp $ */ +/* Copyright 2001 Sun Microsystems, Inc. */ +#ifndef COBALT_NET_H +#define COBALT_NET_H + +#include +#include + +void cobalt_net_register(struct net_device *ndev); +void cobalt_net_unregister(struct net_device *ndev); + +#endif diff -Naur linux-2.6.18.2.orig/include/cobalt/nvram.h linux-2.6.18.2/include/cobalt/nvram.h --- linux-2.6.18.2.orig/include/cobalt/nvram.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/nvram.h 2006-11-15 21:20:19.746033053 -0800 @@ -0,0 +1,125 @@ +/* + * $Id: cobalt-nvram.h,v 1.21 2002/11/02 00:57:06 thockin Exp $ + * cobalt-nvram.h : defines for the various fields in the cobalt NVRAM + * + * Copyright 2001 Sun Microsystems, Inc. + */ + +#ifndef COBALT_NVRAM_H +#define COBALT_NVRAM_H + +#include + +#define COBT_CMOS_INFO_MAX 0x7f /* top address allowed */ +#define COBT_CMOS_BIOS_DRIVE_INFO 0x12 /* drive info would go here */ + +#define COBT_CMOS_CKS_START NVRAM_OFFSET(0x0e) +#define COBT_CMOS_CKS_END NVRAM_OFFSET(0x7f) + +/* flag bytes - 16 flags for now, leave room for more */ +#define COBT_CMOS_FLAG_BYTE_0 NVRAM_OFFSET(0x10) +#define COBT_CMOS_FLAG_BYTE_1 NVRAM_OFFSET(0x11) + +/* flags in flag bytes - up to 16 */ +#define COBT_CMOS_FLAG_MIN 0x0001 +#define COBT_CMOS_CONSOLE_FLAG 0x0001 /* console on/off */ +#define COBT_CMOS_DEBUG_FLAG 0x0002 /* ROM debug messages */ +#define COBT_CMOS_AUTO_PROMPT_FLAG 0x0004 /* boot to ROM prompt? */ +#define COBT_CMOS_CLEAN_BOOT_FLAG 0x0008 /* set by a clean shutdown */ +#define COBT_CMOS_HW_NOPROBE_FLAG 0x0010 /* go easy on the probing */ +#define COBT_CMOS_SYSFAULT_FLAG 0x0020 /* system fault detected */ +#define COBT_CMOS_OOPSPANIC_FLAG 0x0040 /* panic on oops */ +#define COBT_CMOS_DELAY_CACHE_FLAG 0x0080 /* delay cache initialization */ +#define COBT_CMOS_NOLOGO_FLAG 0x0100 /* hide "C" logo @ boot */ +#define COBT_CMOS_VERSION_FLAG 0x0200 /* the version field is valid */ +#define COBT_CMOS_FLAG_MAX 0x0200 + +/* leave byte 0x12 blank - Linux looks for drive info here */ + +/* CMOS structure version, valid if COBT_CMOS_VERSION_FLAG is true */ +#define COBT_CMOS_VERSION NVRAM_OFFSET(0x13) +#define COBT_CMOS_VER_BTOCODE 1 /* min. version needed for btocode */ + +/* index of default boot method */ +#define COBT_CMOS_BOOT_METHOD NVRAM_OFFSET(0x20) +#define COBT_CMOS_BOOT_METHOD_DISK 0 +#define COBT_CMOS_BOOT_METHOD_ROM 1 +#define COBT_CMOS_BOOT_METHOD_NET 2 +#define COBT_CMOS_BOOT_METHOD_NNET 3 +#define COBT_CMOS_BOOT_METHOD_OTHER 4 + +#define COBT_CMOS_BOOT_DEV_MIN NVRAM_OFFSET(0x21) +/* major #, minor # of first through fourth boot device */ +#define COBT_CMOS_BOOT_DEV0_MAJ NVRAM_OFFSET(0x21) +#define COBT_CMOS_BOOT_DEV0_MIN NVRAM_OFFSET(0x22) +#define COBT_CMOS_BOOT_DEV1_MAJ NVRAM_OFFSET(0x23) +#define COBT_CMOS_BOOT_DEV1_MIN NVRAM_OFFSET(0x24) +#define COBT_CMOS_BOOT_DEV2_MAJ NVRAM_OFFSET(0x25) +#define COBT_CMOS_BOOT_DEV2_MIN NVRAM_OFFSET(0x26) +#define COBT_CMOS_BOOT_DEV3_MAJ NVRAM_OFFSET(0x27) +#define COBT_CMOS_BOOT_DEV3_MIN NVRAM_OFFSET(0x28) +#define COBT_CMOS_BOOT_DEV_MAX NVRAM_OFFSET(0x28) + +/* checksum of bytes 0xe-0x7f */ +#define COBT_CMOS_CHECKSUM NVRAM_OFFSET(0x2e) + +/* running uptime counter, units of 5 minutes (32 bits =~ 41000 years) */ +#define COBT_CMOS_UPTIME_0 NVRAM_OFFSET(0x30) +#define COBT_CMOS_UPTIME_1 NVRAM_OFFSET(0x31) +#define COBT_CMOS_UPTIME_2 NVRAM_OFFSET(0x32) +#define COBT_CMOS_UPTIME_3 NVRAM_OFFSET(0x33) + +/* count of successful boots (32 bits) */ +#define COBT_CMOS_BOOTCOUNT_0 NVRAM_OFFSET(0x38) +#define COBT_CMOS_BOOTCOUNT_1 NVRAM_OFFSET(0x39) +#define COBT_CMOS_BOOTCOUNT_2 NVRAM_OFFSET(0x3a) +#define COBT_CMOS_BOOTCOUNT_3 NVRAM_OFFSET(0x3b) + +/* 13 bytes: system serial number, same as on the back of the system */ +#define COBT_CMOS_SYS_SERNUM_LEN 13 +#define COBT_CMOS_SYS_SERNUM_0 NVRAM_OFFSET(0x40) +#define COBT_CMOS_SYS_SERNUM_1 NVRAM_OFFSET(0x41) +#define COBT_CMOS_SYS_SERNUM_2 NVRAM_OFFSET(0x42) +#define COBT_CMOS_SYS_SERNUM_3 NVRAM_OFFSET(0x43) +#define COBT_CMOS_SYS_SERNUM_4 NVRAM_OFFSET(0x44) +#define COBT_CMOS_SYS_SERNUM_5 NVRAM_OFFSET(0x45) +#define COBT_CMOS_SYS_SERNUM_6 NVRAM_OFFSET(0x46) +#define COBT_CMOS_SYS_SERNUM_7 NVRAM_OFFSET(0x47) +#define COBT_CMOS_SYS_SERNUM_8 NVRAM_OFFSET(0x48) +#define COBT_CMOS_SYS_SERNUM_9 NVRAM_OFFSET(0x49) +#define COBT_CMOS_SYS_SERNUM_10 NVRAM_OFFSET(0x4a) +#define COBT_CMOS_SYS_SERNUM_11 NVRAM_OFFSET(0x4b) +#define COBT_CMOS_SYS_SERNUM_12 NVRAM_OFFSET(0x4c) +/* checksum for serial num - 1 byte */ +#define COBT_CMOS_SYS_SERNUM_CSUM NVRAM_OFFSET(0x4f) + +#define COBT_CMOS_ROM_REV_MAJ NVRAM_OFFSET(0x50) +#define COBT_CMOS_ROM_REV_MIN NVRAM_OFFSET(0x51) +#define COBT_CMOS_ROM_REV_REV NVRAM_OFFSET(0x52) + +#define COBT_CMOS_BTO_CODE_0 NVRAM_OFFSET(0x53) +#define COBT_CMOS_BTO_CODE_1 NVRAM_OFFSET(0x54) +#define COBT_CMOS_BTO_CODE_2 NVRAM_OFFSET(0x55) +#define COBT_CMOS_BTO_CODE_3 NVRAM_OFFSET(0x56) + +#define COBT_CMOS_BTO_IP_CSUM NVRAM_OFFSET(0x57) +#define COBT_CMOS_BTO_IP_0 NVRAM_OFFSET(0x58) +#define COBT_CMOS_BTO_IP_1 NVRAM_OFFSET(0x59) +#define COBT_CMOS_BTO_IP_2 NVRAM_OFFSET(0x5a) +#define COBT_CMOS_BTO_IP_3 NVRAM_OFFSET(0x5b) + +/* byte for load/run methods */ +#define COBT_CMOS_BOOT_TYPE NVRAM_OFFSET(0x5c) +#define COBT_CMOS_BOOT_KERN_DEV 0x01 /* use kernel on major/minor */ +#define COBT_CMOS_BOOT_KERN_ROM 0x02 /* use rom kernel */ +#define COBT_CMOS_BOOT_KERN_NET 0x03 /* use net kernel */ +#define COBT_CMOS_BOOT_KERN_MASK 0x0f /* mask for above */ + +#define COBT_CMOS_BOOT_FS_DEV 0x10 /* use major/minor number dev */ +#define COBT_CMOS_BOOT_FS_NET 0x30 /* use net fs */ +#define COBT_CMOS_BOOT_FS_MASK 0xf0 /* mask for above */ + +/* HA mode we're in (Twin Peaks) */ +#define COBT_CMOS_HA_MODE NVRAM_OFFSET(0x62) + +#endif /* COBALT_NVRAM_H */ diff -Naur linux-2.6.18.2.orig/include/cobalt/ruler.h linux-2.6.18.2/include/cobalt/ruler.h --- linux-2.6.18.2.orig/include/cobalt/ruler.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/ruler.h 2006-11-15 21:20:19.746033053 -0800 @@ -0,0 +1,15 @@ +/* $Id: cobalt-ruler.h,v 1.4 2001/06/08 20:46:44 thockin Exp $ */ +/* Copyright 2001 Sun Microsystems, Inc. */ +#ifndef COBALT_RULER_H +#define COBALT_RULER_H + +#include + +void cobalt_ruler_register(ide_drive_t *hwif); +void cobalt_ruler_unregister(ide_drive_t *hwif); + +//typedef int (*cob_busproc_t) (struct hwif_s *, int); + +//int *cob_busproc_t (ide_drive_t *, int); + +#endif diff -Naur linux-2.6.18.2.orig/include/cobalt/sensors.h linux-2.6.18.2/include/cobalt/sensors.h --- linux-2.6.18.2.orig/include/cobalt/sensors.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/sensors.h 2006-11-15 21:20:19.746033053 -0800 @@ -0,0 +1,29 @@ +/* $Id: cobalt-sensors.h,v 1.2 2001/09/25 18:10:29 thockin Exp $ */ +/* Copyright 2001 Sun Microsystems, Inc. */ +#ifndef COBALT_SENSORS_H +#define COBALT_SENSORS_H + +#include + +extern unsigned int cobalt_nthermals; +extern unsigned int cobalt_nvoltages; + +/* return NULL if the sensor doesn't exist, fill buf if it does */ +char *__cobalt_thermal_read(unsigned int sensor, char *buf, int len); +char *__cobalt_voltage_read(unsigned int sensor, char *buf, int len); + +static inline char * +cobalt_thermal_read(unsigned int sensor) +{ + char buf[32]; + return __cobalt_thermal_read(sensor, buf, sizeof(buf)-1); +} + +static inline char * +cobalt_voltage_read(unsigned int sensor) +{ + char buf[32]; + return __cobalt_voltage_read(sensor, buf, sizeof(buf)-1); +} + +#endif diff -Naur linux-2.6.18.2.orig/include/cobalt/serialnum.h linux-2.6.18.2/include/cobalt/serialnum.h --- linux-2.6.18.2.orig/include/cobalt/serialnum.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/serialnum.h 2006-11-15 21:20:19.746033053 -0800 @@ -0,0 +1,16 @@ +/* + * $Id: cobalt-serialnum.h,v 1.1 2001/03/07 01:58:24 thockin Exp $ + * cobalt-serialnum.h : access to the DS2401 serial number + * + * Copyright 2000 Cobalt Networks, Inc. + * Copyright 2001 Sun Microsystems, Inc. + */ +#ifndef COBALT_SERIALNUM_H +#define COBALT_SERIALNUM_H + +#include + +char *cobalt_serialnum_get(void); +unsigned long cobalt_hostid_get(void); + +#endif /* COBALT_SERIALNUM_H */ diff -Naur linux-2.6.18.2.orig/include/cobalt/superio.h linux-2.6.18.2/include/cobalt/superio.h --- linux-2.6.18.2.orig/include/cobalt/superio.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/superio.h 2006-11-15 21:20:19.766032388 -0800 @@ -0,0 +1,225 @@ +/* + * $Id: cobalt-superio.h,v 1.11 2001/11/02 12:00:03 duncan Exp $ + * cobalt-superio.h : SuperIO support for Sun/Cobalt servers + * + * Copyright 2000 Cobalt Networks, Inc. + * Copyright 2001 Sun Microsystems, Inc. + */ +#ifndef COBALT_SUPERIO_H +#define COBALT_SUPERIO_H + +#include +#include + +/* the lock that protects superio accesses */ +extern spinlock_t cobalt_superio_lock; + +/* + * The main functions of the SuperIO are accessed via index/data registers + * These are the same for both SuperIO chip families + */ +#define SUPERIO_INDEX_PORT 0x2e +#define SUPERIO_DATA_PORT 0x2f + +/* + * This index allows you to select a logical device + */ +#define SUPERIO_LOGICAL_DEV 0x07 + +/* + * Type of SUPERIO + */ +#define SUPERIO_SID 0x20 + +/* + * Some indices that are common to all logical devices + */ +#define SUPERIO_ACTIVATE 0x30 +#define SUPERIO_ADDR_HIGH 0x60 +#define SUPERIO_ADDR_LOW 0x61 +#define SUPERIO_INTR_PIN 0x70 +#define SUPERIO_INTR_TYPE 0x71 + + +/* + * PC87317 SuperIO is found on XTR + */ + +/* + * logical devices - write to LOGICAL_DEV index + */ +#define PC87317_DEV_RTC 0x02 +#define PC87317_DEV_GPIO 0x07 +#define PC87317_DEV_PM 0x08 + +/* withing the PM dev, are the following indices */ +#define PC87317_PMDEV_WDTO 0x05 +#define PC87317_PMDEV_GPELO 0x0e +#define PC87317_PMDEV_GPEHI 0x0f + +/* within the APC bank of RTC */ +#define PC87317_APCR1 0x40 +#define PC87317_APCR2 0x41 +#define PC87317_APCR3 0x49 +#define PC87317_APCR4 0x4a +#define PC87317_APCR5 0x4b +#define PC87317_APCR6 0x4c +#define PC87317_APCR7 0x4d + +#define PC87317_RTC_BANK_MAIN 0 +#define PC87317_RTC_BANK_RTC 1 +#define PC87317_RTC_BANK_APC 2 +#define PC87317_RTC_CRA 0x0a +#define PC87317_RTC_BANK_0 0x20 +#define PC87317_RTC_BANK_1 0x30 +#define PC87317_RTC_BANK_2 0x40 + +#define PC87317_PWRBUTTON 0x01 +#define PC87317_PM1_STATUS 0x01 + +/* offsets from GPEHI/GPELO */ +#define PC87317_GPE_GP1_STS0 0x00 +#define PC87317_GPE_GP1_STS1 0x01 +#define PC87317_GPE_GP1_STS2 0x02 +#define PC87317_GPE_GP1_STS3 0x03 +#define PC87317_GPE_GP1_EN0 0x04 +#define PC87317_GPE_GP1_EN1 0x05 +#define PC87317_GPE_GP1_EN2 0x06 +#define PC87317_GPE_GP1_EN3 0x07 +#define PC87317_GPE_GP2_EN0 0x08 +#define PC87317_GPE_SMI_CMD 0x0c + +/* + * PC87417 family is found on alpine + */ + +#define PC87417_DEV_SWC 0x4 +#define PC87417_DEV_GPIO 0x7 +#define PC87417_DEV_RTC 0x10 + +/* registers in the SWC dev */ +#define PC87417_SWC_PWONCTL 0x9 +/* + * within the System Wake Control device, there are banks, write the bank you + * want to SWC_BANK + */ +#define PC87417_SWC_BANK 0xf +#define PC87417_SWCBANK_ACPI 0x2 +#define PC87417_SWCBANK_WDT 0x3 + +/* + * the SWC WDT bank has 3 main registers + */ +#define PC87417_WDT_CONTROL 0x10 +#define PC87417_WDT_TIMEOUT 0x11 +#define PC87417_WDT_CONFIG 0x12 + +/* + * within the SWC, two regs serve as index/data for wake-event enabling + */ +#define PC87417_SWC_WKEVENT 0x0 +#define PC87417_SWC_WKSTATE 0x1 +#define PC87417_SWCWKEVENT_GPIOE42 0xa +#define PC87417_SWCWKEVENT_RTC 0x18 + + +/* + * types of superIOs + */ +enum superio_type_t +{ + SIO_TYPE_UNKNOWN, + SIO_TYPE_PC8731X, + SIO_TYPE_PC8741X, +}; + + + +#define PC8731X_SID 0xd0 +#define PC9731X_SID 0xdf +#define PC8741X_SID 0xee + +static inline int superio_type( void ) +{ + u8 reg; + unsigned long flags; + enum superio_type_t type; + + spin_lock_irqsave(&cobalt_superio_lock, flags); + + outb(SUPERIO_SID, SUPERIO_INDEX_PORT); + reg = inb( SUPERIO_DATA_PORT ); + switch( reg ) + { + case PC8731X_SID: + case PC9731X_SID: + type = SIO_TYPE_PC8731X; + break; + + case PC8741X_SID: + type = SIO_TYPE_PC8741X; + break; + + default: + type = SIO_TYPE_UNKNOWN; + break; + } + + spin_unlock_irqrestore(&cobalt_superio_lock, flags); + + return type; +} + +/* + * stuff to make life easier + */ + +/* read the base address of a particular superio logical device */ +#define superio_ldev_base(ldev) superio_ldev_base_n(ldev, 0) +static inline unsigned short +superio_ldev_base_n(int ldev, int n) +{ + unsigned long flags; + unsigned short addr; + + spin_lock_irqsave(&cobalt_superio_lock, flags); + + /* select the logical device */ + outb(SUPERIO_LOGICAL_DEV, SUPERIO_INDEX_PORT); + outb(ldev, SUPERIO_DATA_PORT); + + /* read the 2-byte base address */ + outb(SUPERIO_ADDR_HIGH+(n*2), SUPERIO_INDEX_PORT); + addr = inb(SUPERIO_DATA_PORT) << 8; + outb(SUPERIO_ADDR_LOW+(n*2), SUPERIO_INDEX_PORT); + addr |= inb(SUPERIO_DATA_PORT); + + spin_unlock_irqrestore(&cobalt_superio_lock, flags); + + return addr; +} + +static inline void +superio_set_rtc_bank(const unsigned int page) +{ + if (cobt_is_monterey()) { + unsigned char val; + + val = CMOS_READ(0xa); + val &= ~0x70; + switch (page) { + case PC87317_RTC_BANK_MAIN: + val |= 0x20; + break; + case PC87317_RTC_BANK_RTC: + val |= 0x30; + break; + case PC87317_RTC_BANK_APC: + val |= 0x40; + break; + } + CMOS_WRITE(val, 0xa); + } +} + +#endif /* COBALT_SUPERIO_H */ diff -Naur linux-2.6.18.2.orig/include/cobalt/systype.h linux-2.6.18.2/include/cobalt/systype.h --- linux-2.6.18.2.orig/include/cobalt/systype.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/systype.h 2006-11-15 21:20:19.766032388 -0800 @@ -0,0 +1,99 @@ +/* + * $Id: cobalt-systype.h,v 1.8 2002/08/08 22:46:50 carls Exp $ + * cobalt-systype.h : figure out what Cobalt system we are on + * + * Copyright 2000 Cobalt Networks, Inc. + * Copyright 2001-2002 Sun Microsystems, Inc. + */ +#ifndef COBALT_SYSTYPE_H +#define COBALT_SYSTYPE_H + +#include + +typedef enum { + COBT_UNINITIALIZED, + COBT_UNKNOWN, + COBT_PACIFICA, + COBT_CARMEL, + COBT_MONTEREY, + COBT_ALPINE, + COBT_BIGBEAR, +} cobt_sys_t; + +extern cobt_sys_t cobt_type; +extern cobt_sys_t cobalt_systype_probe(void); +extern unsigned long cobt_rev; + +/* + * one test for each major board-type + * COBT_SUPPORT_* from cobalt.h + */ + +/* pacifica is the RaQ 3 and RaQ 4 platform */ +static inline int +cobt_is_pacifica(void) +{ + if (!COBT_SUPPORT_GEN_III) { + return 0; + } + if (cobt_type == COBT_UNINITIALIZED) { + cobalt_systype_probe(); + } + return (cobt_type == COBT_PACIFICA); +} + +/* carmel is the Qube 3 platform */ +static inline int +cobt_is_carmel(void) +{ + if (!COBT_SUPPORT_GEN_III) { + return 0; + } + if (cobt_type == COBT_UNINITIALIZED) { + cobalt_systype_probe(); + } + return (cobt_type == COBT_CARMEL); +} + +/* monterey is the RaQ XTR platform */ +static inline int +cobt_is_monterey(void) +{ + if (!COBT_SUPPORT_GEN_V) { + return 0; + } + if (cobt_type == COBT_UNINITIALIZED) { + cobalt_systype_probe(); + } + return (cobt_type == COBT_MONTEREY); +} + +static inline int +cobt_is_alpine(void) +{ + if (!COBT_SUPPORT_GEN_V) { + return 0; + } + if (cobt_type == COBT_UNINITIALIZED) { + cobalt_systype_probe(); + } + return (cobt_type == COBT_ALPINE); +} + +static inline int +cobt_is_bigbear(void) +{ + if (!COBT_SUPPORT_GEN_V) { + return 0; + } + if (cobt_type == COBT_UNINITIALIZED) { + cobalt_systype_probe(); + } + return (cobt_type == COBT_BIGBEAR); +} + +/* one for each major generation */ +#define cobt_is_3k() (cobt_is_pacifica() || cobt_is_carmel()) +#define cobt_is_5k() (cobt_is_monterey() || cobt_is_alpine() || cobt_is_bigbear()) + +#endif diff -Naur linux-2.6.18.2.orig/include/cobalt/wdt.h linux-2.6.18.2/include/cobalt/wdt.h --- linux-2.6.18.2.orig/include/cobalt/wdt.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.18.2/include/cobalt/wdt.h 2006-11-15 21:20:19.766032388 -0800 @@ -0,0 +1,16 @@ +/* $Id: cobalt-wdt.h,v 1.1 2001/03/07 01:58:24 thockin Exp $ */ +/* Copyright 2001 Sun Microsystems, Inc. */ +#ifndef COBALT_WDT_H +#define COBALT_WDT_H + +#include + +void cobalt_wdt_refresh(unsigned long refresh_timer); +void cobalt_wdt_trigger_reboot(void); + +void cobalt_wdt_disable(void); +void cobalt_wdt_reenable(void); + +void cobalt_wdt_cleardog(void); + +#endif diff -Naur linux-2.6.18.2.orig/init/main.c linux-2.6.18.2/init/main.c --- linux-2.6.18.2.orig/init/main.c 2006-11-03 17:33:58.000000000 -0800 +++ linux-2.6.18.2/init/main.c 2006-11-15 21:20:19.770032256 -0800 @@ -650,6 +650,10 @@ * * Now we can finally start doing some real work.. */ +#ifdef CONFIG_COBALT_RAQ +extern int cobalt_init(void); +#endif + static void __init do_basic_setup(void) { /* drivers will send hotplug events */ @@ -662,6 +666,10 @@ #endif do_initcalls(); + +#ifdef CONFIG_COBALT_RAQ + cobalt_init(); +#endif } static void do_pre_smp_initcalls(void)