diff -Nurb linux-2.6.23.1/Makefile linux-2.6.23.1-cobalt3-tw/Makefile --- linux-2.6.23.1/Makefile 2007-10-12 09:43:44.000000000 -0700 +++ linux-2.6.23.1-cobalt3-tw/Makefile 2007-11-24 12:55:41.000000000 -0800 @@ -206,6 +206,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 @@ -711,6 +712,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 -Nurb linux-2.6.23.1/arch/i386/kernel/Makefile linux-2.6.23.1-cobalt3-tw/arch/i386/kernel/Makefile --- linux-2.6.23.1/arch/i386/kernel/Makefile 2007-10-12 09:43:44.000000000 -0700 +++ linux-2.6.23.1-cobalt3-tw/arch/i386/kernel/Makefile 2007-11-24 12:55:41.000000000 -0800 @@ -55,6 +55,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 -Nurb linux-2.6.23.1/arch/i386/kernel/cobalt.c linux-2.6.23.1-cobalt3-tw/arch/i386/kernel/cobalt.c --- linux-2.6.23.1/arch/i386/kernel/cobalt.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23.1-cobalt3-tw/arch/i386/kernel/cobalt.c 2007-11-24 12:55:41.000000000 -0800 @@ -0,0 +1,279 @@ +/* $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 + +#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 -Nurb linux-2.6.23.1/arch/i386/kernel/process.c linux-2.6.23.1-cobalt3-tw/arch/i386/kernel/process.c --- linux-2.6.23.1/arch/i386/kernel/process.c 2007-10-12 09:43:44.000000000 -0700 +++ linux-2.6.23.1-cobalt3-tw/arch/i386/kernel/process.c 2007-11-24 12:55:41.000000000 -0800 @@ -53,6 +53,11 @@ #include #endif +#ifdef CONFIG_COBALT_RAQ +#include +#include +#endif + #include #include @@ -498,6 +503,12 @@ { int i; +#ifdef CONFIG_COBALT_RAQ + wait_for_flush(); + cobalt_restart(); +#endif + + /* changed the size calculations - should hopefully work better. lbt */ dump->magic = CMAGIC; dump->start_code = 0; diff -Nurb linux-2.6.23.1/arch/i386/kernel/reboot.c linux-2.6.23.1-cobalt3-tw/arch/i386/kernel/reboot.c --- linux-2.6.23.1/arch/i386/kernel/reboot.c 2007-10-12 09:43:44.000000000 -0700 +++ linux-2.6.23.1-cobalt3-tw/arch/i386/kernel/reboot.c 2007-11-24 12:55:41.000000000 -0800 @@ -20,6 +20,11 @@ #include #include +#ifdef CONFIG_COBALT_RAQ +#include +#include +#endif + /* * Power off function, if any */ @@ -300,7 +305,13 @@ { #ifdef CONFIG_SMP int reboot_cpu_id; - +#endif +#ifdef CONFIG_COBALT_RAQ + wait_for_flush(); + //cobalt_halt(); + cobalt_lcd_print("It is now safe", "to remove power"); +#endif +#ifdef CONFIG_SMP /* The boot cpu is always logical cpu 0 */ reboot_cpu_id = 0; @@ -338,6 +349,9 @@ static void native_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); @@ -360,8 +374,35 @@ machine_real_restart(jump_to_bios, sizeof(jump_to_bios)); } +/* kill some time at halt/reboot to allow drives with large cache to sync */ +void wait_for_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"); +} + static void native_machine_restart(char * __unused) { +#ifdef CONFIG_COBALT_RAQ + wait_for_flush(); + cobalt_restart(); +#endif + machine_shutdown(); machine_emergency_restart(); } diff -Nurb linux-2.6.23.1/arch/i386/kernel/traps.c linux-2.6.23.1-cobalt3-tw/arch/i386/kernel/traps.c --- linux-2.6.23.1/arch/i386/kernel/traps.c 2007-10-12 09:43:44.000000000 -0700 +++ linux-2.6.23.1-cobalt3-tw/arch/i386/kernel/traps.c 2007-11-24 12:55:41.000000000 -0800 @@ -65,6 +65,10 @@ int panic_on_unrecovered_nmi; +#ifdef CONFIG_COBALT_RAQ +#include +#endif + asmlinkage int system_call(void); /* Do we ignore FPU interrupts ? */ @@ -656,9 +660,13 @@ static __kprobes 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 for unknown reason %02x on " "CPU %d.\n", reason, smp_processor_id()); printk(KERN_EMERG "You have some hardware problem, likely on the PCI bus.\n"); +#endif #if defined(CONFIG_EDAC) if(edac_handler_set()) { diff -Nurb linux-2.6.23.1/drivers/Kconfig linux-2.6.23.1-cobalt3-tw/drivers/Kconfig --- linux-2.6.23.1/drivers/Kconfig 2007-10-12 09:43:44.000000000 -0700 +++ linux-2.6.23.1-cobalt3-tw/drivers/Kconfig 2007-11-24 12:55:41.000000000 -0800 @@ -84,6 +84,8 @@ source "drivers/auxdisplay/Kconfig" +source "drivers/cobalt/Kconfig" + source "drivers/kvm/Kconfig" source "drivers/uio/Kconfig" diff -Nurb linux-2.6.23.1/drivers/Makefile linux-2.6.23.1-cobalt3-tw/drivers/Makefile --- linux-2.6.23.1/drivers/Makefile 2007-10-12 09:43:44.000000000 -0700 +++ linux-2.6.23.1-cobalt3-tw/drivers/Makefile 2007-11-24 12:55:41.000000000 -0800 @@ -69,6 +69,8 @@ obj-$(CONFIG_WATCHDOG) += char/watchdog/ obj-$(CONFIG_PHONE) += telephony/ obj-$(CONFIG_MD) += md/ +obj-$(CONFIG_COBALT_RAQ) += cobalt/ + obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_ISDN) += isdn/ obj-$(CONFIG_EDAC) += edac/ diff -Nurb linux-2.6.23.1/drivers/char/Kconfig linux-2.6.23.1-cobalt3-tw/drivers/char/Kconfig --- linux-2.6.23.1/drivers/char/Kconfig 2007-10-12 09:43:44.000000000 -0700 +++ linux-2.6.23.1-cobalt3-tw/drivers/char/Kconfig 2007-11-24 12:55:41.000000000 -0800 @@ -833,7 +833,7 @@ will get access to the real time clock (or hardware clock) built into your computer. -config COBALT_LCD +config COBALT_MIPS_LCD bool "Support for Cobalt LCD" depends on MIPS_COBALT help diff -Nurb linux-2.6.23.1/drivers/char/Makefile linux-2.6.23.1-cobalt3-tw/drivers/char/Makefile --- linux-2.6.23.1/drivers/char/Makefile 2007-10-12 09:43:44.000000000 -0700 +++ linux-2.6.23.1-cobalt3-tw/drivers/char/Makefile 2007-11-24 12:55:41.000000000 -0800 @@ -85,7 +85,7 @@ obj-$(CONFIG_I8K) += i8k.o obj-$(CONFIG_DS1620) += ds1620.o obj-$(CONFIG_HW_RANDOM) += hw_random/ -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 -Nurb linux-2.6.23.1/drivers/char/misc.c linux-2.6.23.1-cobalt3-tw/drivers/char/misc.c --- linux-2.6.23.1/drivers/char/misc.c 2007-10-12 09:43:44.000000000 -0700 +++ linux-2.6.23.1-cobalt3-tw/drivers/char/misc.c 2007-11-24 12:55:41.000000000 -0800 @@ -50,6 +50,17 @@ #include #include +#ifdef CONFIG_COBALT_RAQ +#include +#include +#include +#include +#include +#include +#include +#endif + + /* * Head entry for the doubly linked miscdevice list */ @@ -64,6 +75,13 @@ extern int pmu_device_init(void); +#ifdef CONFIG_COBALT_RAQ +extern int cobalt_init(void); +#endif +#ifdef CONFIG_COBALT_MIPS_LCD +extern int lcd_init(void); +#endif + #ifdef CONFIG_PROC_FS static void *misc_seq_start(struct seq_file *seq, loff_t *pos) { @@ -273,7 +291,9 @@ misc_class = class_create(THIS_MODULE, "misc"); if (IS_ERR(misc_class)) return PTR_ERR(misc_class); - +#ifdef CONFIG_COBALT_MIPS_LCD + lcd_init(); +#endif if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) { printk("unable to get major %d for misc devices\n", MISC_MAJOR); diff -Nurb linux-2.6.23.1/drivers/char/nvram.c linux-2.6.23.1-cobalt3-tw/drivers/char/nvram.c --- linux-2.6.23.1/drivers/char/nvram.c 2007-10-12 09:43:44.000000000 -0700 +++ linux-2.6.23.1-cobalt3-tw/drivers/char/nvram.c 2007-11-24 13:09:24.000000000 -0800 @@ -42,12 +42,18 @@ #define PC 1 #define ATARI 2 +#define COBALT 3 /* select machine configuration */ #if defined(CONFIG_ATARI) # define MACH ATARI #elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) /* and others?? */ +# if defined(CONFIG_COBALT_RAQ) +# include +# define MACH COBALT +# else # define MACH PC +# endif #else # error Cannot build nvram driver for this machine configuration. #endif @@ -69,6 +75,18 @@ #endif +#if MACH == COBALT + +#define CHECK_DRIVER_INIT() 1 + +#define NVRAM_BYTES (128-NVRAM_FIRST_BYTE) + +#define mach_check_checksum cobalt_check_checksum +#define mach_set_checksum cobalt_set_checksum +#define mach_proc_infos cobalt_proc_infos + +#endif + #if MACH == ATARI /* Special parameters for RTC in Atari machines */ @@ -585,6 +603,177 @@ #endif /* MACH == PC */ +#if MACH == COBALT + +/* the cobalt CMOS has a wider range of its checksum */ +static int cobalt_check_checksum(void) +{ + int i; + unsigned short sum = 0; + unsigned short expect; + + for (i = COBT_CMOS_CKS_START; i <= COBT_CMOS_CKS_END; ++i) { + if ((i == COBT_CMOS_CHECKSUM) || (i == (COBT_CMOS_CHECKSUM+1))) + continue; + + sum += __nvram_read_byte(i); + } + expect = __nvram_read_byte(COBT_CMOS_CHECKSUM) << 8 | + __nvram_read_byte(COBT_CMOS_CHECKSUM+1); + return ((sum & 0xffff) == expect); +} + +static void cobalt_set_checksum(void) +{ + int i; + unsigned short sum = 0; + + for (i = COBT_CMOS_CKS_START; i <= COBT_CMOS_CKS_END; ++i) { + if ((i == COBT_CMOS_CHECKSUM) || (i == (COBT_CMOS_CHECKSUM+1))) + continue; + + sum += __nvram_read_byte(i); + } + + __nvram_write_byte(sum >> 8, COBT_CMOS_CHECKSUM); + __nvram_write_byte(sum & 0xff, COBT_CMOS_CHECKSUM+1); +} + +#ifdef CONFIG_PROC_FS + +static int cobalt_proc_infos(unsigned char *nvram, char *buffer, int *len, + off_t *begin, off_t offset, int size) +{ + int i; + unsigned int checksum; + unsigned int flags; + char sernum[14]; + char *key = "cNoEbTaWlOtR!"; + unsigned char bto_csum; + + spin_lock_irq(&rtc_lock); + checksum = __nvram_check_checksum(); + spin_unlock_irq(&rtc_lock); + + PRINT_PROC("Checksum status: %svalid\n", checksum ? "" : "not "); + + flags = nvram[COBT_CMOS_FLAG_BYTE_0] << 8 + | nvram[COBT_CMOS_FLAG_BYTE_1]; + + PRINT_PROC("Console: %s\n", + flags & COBT_CMOS_CONSOLE_FLAG ? "on": "off"); + + PRINT_PROC("Firmware Debug Messages: %s\n", + flags & COBT_CMOS_DEBUG_FLAG ? "on": "off"); + + PRINT_PROC("Auto Prompt: %s\n", + flags & COBT_CMOS_AUTO_PROMPT_FLAG ? "on": "off"); + + PRINT_PROC("Shutdown Status: %s\n", + flags & COBT_CMOS_CLEAN_BOOT_FLAG ? "clean": "dirty"); + + PRINT_PROC("Hardware Probe: %s\n", + flags & COBT_CMOS_HW_NOPROBE_FLAG ? "partial": "full"); + + PRINT_PROC("System Fault: %sdetected\n", + flags & COBT_CMOS_SYSFAULT_FLAG ? "": "not "); + + PRINT_PROC("Panic on OOPS: %s\n", + flags & COBT_CMOS_OOPSPANIC_FLAG ? "yes": "no"); + + PRINT_PROC("Delayed Cache Initialization: %s\n", + flags & COBT_CMOS_DELAY_CACHE_FLAG ? "yes": "no"); + + PRINT_PROC("Show Logo at Boot: %s\n", + flags & COBT_CMOS_NOLOGO_FLAG ? "no": "yes"); + + PRINT_PROC("Boot Method: "); + switch (nvram[COBT_CMOS_BOOT_METHOD]) { + case COBT_CMOS_BOOT_METHOD_DISK: + PRINT_PROC("disk\n"); + break; + + case COBT_CMOS_BOOT_METHOD_ROM: + PRINT_PROC("rom\n"); + break; + + case COBT_CMOS_BOOT_METHOD_NET: + PRINT_PROC("net\n"); + break; + + default: + PRINT_PROC("unknown\n"); + break; + } + + PRINT_PROC("Primary Boot Device: %d:%d\n", + nvram[COBT_CMOS_BOOT_DEV0_MAJ], + nvram[COBT_CMOS_BOOT_DEV0_MIN] ); + PRINT_PROC("Secondary Boot Device: %d:%d\n", + nvram[COBT_CMOS_BOOT_DEV1_MAJ], + nvram[COBT_CMOS_BOOT_DEV1_MIN] ); + PRINT_PROC("Tertiary Boot Device: %d:%d\n", + nvram[COBT_CMOS_BOOT_DEV2_MAJ], + nvram[COBT_CMOS_BOOT_DEV2_MIN] ); + + PRINT_PROC("Uptime: %d\n", + nvram[COBT_CMOS_UPTIME_0] << 24 | + nvram[COBT_CMOS_UPTIME_1] << 16 | + nvram[COBT_CMOS_UPTIME_2] << 8 | + nvram[COBT_CMOS_UPTIME_3]); + + PRINT_PROC("Boot Count: %d\n", + nvram[COBT_CMOS_BOOTCOUNT_0] << 24 | + nvram[COBT_CMOS_BOOTCOUNT_1] << 16 | + nvram[COBT_CMOS_BOOTCOUNT_2] << 8 | + nvram[COBT_CMOS_BOOTCOUNT_3]); + + /* 13 bytes of serial num */ + for (i=0 ; i<13 ; i++) { + sernum[i] = nvram[COBT_CMOS_SYS_SERNUM_0 + i]; + } + sernum[13] = '\0'; + + checksum = 0; + for (i=0 ; i<13 ; i++) { + checksum += sernum[i] ^ key[i]; + } + checksum = ((checksum & 0x7f) ^ (0xd6)) & 0xff; + + PRINT_PROC("Serial Number: %s", sernum); + if (checksum != nvram[COBT_CMOS_SYS_SERNUM_CSUM]) { + PRINT_PROC(" (invalid checksum)"); + } + PRINT_PROC("\n"); + + PRINT_PROC("Rom Revison: %d.%d.%d\n", nvram[COBT_CMOS_ROM_REV_MAJ], + nvram[COBT_CMOS_ROM_REV_MIN], nvram[COBT_CMOS_ROM_REV_REV]); + + PRINT_PROC("BTO Server: %d.%d.%d.%d", nvram[COBT_CMOS_BTO_IP_0], + nvram[COBT_CMOS_BTO_IP_1], nvram[COBT_CMOS_BTO_IP_2], + nvram[COBT_CMOS_BTO_IP_3]); + bto_csum = nvram[COBT_CMOS_BTO_IP_0] + nvram[COBT_CMOS_BTO_IP_1] + + nvram[COBT_CMOS_BTO_IP_2] + nvram[COBT_CMOS_BTO_IP_3]; + if (bto_csum != nvram[COBT_CMOS_BTO_IP_CSUM]) { + PRINT_PROC(" (invalid checksum)"); + } + PRINT_PROC("\n"); + + if (flags & COBT_CMOS_VERSION_FLAG + && nvram[COBT_CMOS_VERSION] >= COBT_CMOS_VER_BTOCODE) { + PRINT_PROC("BTO Code: 0x%x\n", + nvram[COBT_CMOS_BTO_CODE_0] << 24 | + nvram[COBT_CMOS_BTO_CODE_1] << 16 | + nvram[COBT_CMOS_BTO_CODE_2] << 8 | + nvram[COBT_CMOS_BTO_CODE_3]); + } + + return 1; +} +#endif /* CONFIG_PROC_FS */ + +#endif /* MACH == COBALT */ + #if MACH == ATARI static int diff -Nurb linux-2.6.23.1/drivers/cobalt/Kconfig linux-2.6.23.1-cobalt3-tw/drivers/cobalt/Kconfig --- linux-2.6.23.1/drivers/cobalt/Kconfig 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23.1-cobalt3-tw/drivers/cobalt/Kconfig 2007-11-24 12:55:41.000000000 -0800 @@ -0,0 +1,156 @@ +# +# 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 + depends on PROC_FS + default y + ---help--- + Creates entries in /proc/cobalt which provide useful information about + your RaQ/Qube. Best to say Y here. + +#config COBALT_BOOTLOADER +# bool "Cobalt Bootloader Support" +# depends on COBALT_RAQ +# default n +# ---help--- +# If you are going to being burning this kernel to the RaQ/Qube's ROM to +# act as a loader kernel, this is required to load the system kernel and +# hand off control to it. It is recommended you say N here unless you +# know what you're getting yourself into. +# +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_POWERMODE + bool "Powermode Restore support" + depends on COBALT_OLDPROC + depends on COBALT_GEN_V + default y + ---help--- + On GenV RaQs you can specify what action to take should a + power failure occur. This code allows you to set that action + via /proc/cobalt/powermode. Your options are "on", "off", and + "same", except on Monterey hardware (XTR's) where there is no + support for "on". + + 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 -Nurb linux-2.6.23.1/drivers/cobalt/Makefile linux-2.6.23.1-cobalt3-tw/drivers/cobalt/Makefile --- linux-2.6.23.1/drivers/cobalt/Makefile 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23.1-cobalt3-tw/drivers/cobalt/Makefile 2007-11-24 12:55:41.000000000 -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 +obj-$(CONFIG_COBALT_POWERMODE) += powermode.o diff -Nurb linux-2.6.23.1/drivers/cobalt/README linux-2.6.23.1-cobalt3-tw/drivers/cobalt/README --- linux-2.6.23.1/drivers/cobalt/README 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23.1-cobalt3-tw/drivers/cobalt/README 2007-11-24 12:55:41.000000000 -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 -Nurb linux-2.6.23.1/drivers/cobalt/acpi.c linux-2.6.23.1-cobalt3-tw/drivers/cobalt/acpi.c --- linux-2.6.23.1/drivers/cobalt/acpi.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23.1-cobalt3-tw/drivers/cobalt/acpi.c 2007-11-24 12:55:41.000000000 -0800 @@ -0,0 +1,1993 @@ + /* + * 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 + +#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 -Nurb linux-2.6.23.1/drivers/cobalt/fans.c linux-2.6.23.1-cobalt3-tw/drivers/cobalt/fans.c --- linux-2.6.23.1/drivers/cobalt/fans.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23.1-cobalt3-tw/drivers/cobalt/fans.c 2007-11-24 12:55:41.000000000 -0800 @@ -0,0 +1,418 @@ +/* $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 + */ +#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 -Nurb linux-2.6.23.1/drivers/cobalt/i2c.c linux-2.6.23.1-cobalt3-tw/drivers/cobalt/i2c.c --- linux-2.6.23.1/drivers/cobalt/i2c.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23.1-cobalt3-tw/drivers/cobalt/i2c.c 2007-11-24 12:55:41.000000000 -0800 @@ -0,0 +1,518 @@ +/* + * $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 + +#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 -Nurb linux-2.6.23.1/drivers/cobalt/init.c linux-2.6.23.1-cobalt3-tw/drivers/cobalt/init.c --- linux-2.6.23.1/drivers/cobalt/init.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23.1-cobalt3-tw/drivers/cobalt/init.c 2007-11-24 12:55:41.000000000 -0800 @@ -0,0 +1,117 @@ +/* $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 +#ifdef CONFIG_COBALT_POWERMODE + cobalt_powermode_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 -Nurb linux-2.6.23.1/drivers/cobalt/lcd.c linux-2.6.23.1-cobalt3-tw/drivers/cobalt/lcd.c --- linux-2.6.23.1/drivers/cobalt/lcd.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23.1-cobalt3-tw/drivers/cobalt/lcd.c 2007-11-24 12:55:41.000000000 -0800 @@ -0,0 +1,858 @@ +/* + * $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 + */ + +#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 + +void cobalt_lcd_print(char * line1, char * line2) +{ + int i; + int len; + + if( !lcd_present ) + return; + +#ifdef CONFIG_COBALT_LCD_TWIDDLE + cobalt_lcd_stop_twiddle(); +#endif + + lcddev_write_inst( (DD_R00) | LCD_Addr); + len = strlen( line1 ); + 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 -Nurb linux-2.6.23.1/drivers/cobalt/led.c linux-2.6.23.1-cobalt3-tw/drivers/cobalt/led.c --- linux-2.6.23.1/drivers/cobalt/led.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.23.1-cobalt3-tw/drivers/cobalt/led.c 2007-11-24 12:55:41.000000000 -0800 @@ -0,0 +1,502 @@ + /* + * $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). + */ + +#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_