public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] support for Cobalt Networks (x86 only) systems (for real this  time)
@ 2001-06-01  2:47 Tim Hockin
  2001-06-01  8:10 ` Jeff Garzik
                   ` (2 more replies)
  0 siblings, 3 replies; 46+ messages in thread
From: Tim Hockin @ 2001-06-01  2:47 UTC (permalink / raw)
  To: alan; +Cc: Linux Kernel Mailing List

[-- Attachment #1: Type: text/plain, Size: 517 bytes --]

apparently, LKML silently (!) bounces messages > a certain size.  So I'll
try smaller patches.  This is part 1/2 of the general Cobalt support.

Alan,

Aattached is a (large, but self contained) patch for Cobalt Networks suport
for x86 systems (RaQ3, RaQ4, Qube3, RaQXTR).  Please let me know if there
is anything that would prevent this from general inclusion in the next
release.

(patch against 2.4.5)

Thanks

Tim
-- 
Tim Hockin
Systems Software Engineer
Sun Microsystems, Cobalt Server Appliances
thockin@sun.com

[-- Attachment #2: cobalt-drivers-1.diff --]
[-- Type: text/plain, Size: 53732 bytes --]

diff -ruN dist-2.4.5/drivers/cobalt/acpi.c cobalt-2.4.5/drivers/cobalt/acpi.c
--- dist-2.4.5/drivers/cobalt/acpi.c	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/acpi.c	Thu May 31 14:32:15 2001
@@ -0,0 +1,218 @@
+/* 
+ * cobalt acpi driver 
+ * Copyright (c) 2000, Cobalt Networks, Inc.
+ * Copyright (c) 2001, Sun Microsystems, Inc.
+ * $Id: acpi.c,v 1.10 2001/05/30 07:19:47 thockin Exp $
+ *
+ * author: asun@cobalt.com, thockin@sun.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
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <linux/cobalt.h>
+#include <linux/cobalt-systype.h>
+#include <linux/cobalt-acpi.h>
+#include <linux/cobalt-superio.h>
+
+#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	0x0cd6
+#define OSB4_DATA_PORT	0x0cd7
+
+/* for registering ACPI handlers */
+struct acpi_handler {
+	void (*function)(int irq, void *dev_id, struct pt_regs *regs);
+	struct acpi_handler *next;
+	struct acpi_handler *prev;
+};
+struct acpi_handler *acpi_handler_list;
+
+static spinlock_t acpi_lock = SPIN_LOCK_UNLOCKED;
+/* these two are for gen V */
+static u16 osb4_port;
+static u16 superio_port;
+
+static u16 
+get_reg(u16 index, u16 data, u8 port)
+{
+	if (cobt_is_5k()) {
+		u16 reg;
+
+		outb(port, index);
+		reg = inb(data);
+		outb(port + 1, index);
+		reg |= inb(data) << 8;
+		return reg;
+	}
+
+	return 0;
+}
+
+static void 
+acpi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned long flags, events;
+	struct acpi_handler *p;
+
+	spin_lock_irqsave(&acpi_lock, flags);
+
+	if (cobt_is_5k()) {
+		/* save the superio events */
+		events = inb(superio_port) | (inb(superio_port + 1) << 8);
+	
+		/* clear SCI interrupt generation */
+		outb(OSB4_EVENT, osb4_port); 
+		outb(SUPERIO_EVENT, superio_port);
+		outb(SUPERIO_EVENT, superio_port + 1);
+	}
+
+	/* call the ACPI handlers */
+	p = acpi_handler_list;
+	while (p) {
+		p->function(irq, dev_id, regs);
+		p = p->next;
+	}
+
+	spin_unlock_irqrestore(&acpi_lock, flags);
+}
+
+int
+cobalt_acpi_register_handler(void (*function)(int, void *, struct pt_regs *))
+{
+	struct acpi_handler *newh;
+	unsigned long flags;
+
+	newh = kmalloc(sizeof(*newh), GFP_ATOMIC);
+	if (!newh) {
+		EPRINTK("can't allocate memory for handler %p\n", function);
+		return -1;
+	}
+
+	spin_lock_irqsave(&acpi_lock, flags);
+
+	/* head insert */
+	newh->function = function;
+	newh->next = acpi_handler_list;
+	newh->prev = NULL;
+	if (acpi_handler_list) {
+		acpi_handler_list->prev = newh;
+	}
+	acpi_handler_list = newh;	
+
+	spin_unlock_irqrestore(&acpi_lock, flags);
+
+	return 0;
+}
+
+int
+cobalt_acpi_unregister_handler(void (*function)(int, void *, struct pt_regs *))
+{
+	struct acpi_handler *p;
+	unsigned long flags;
+	int r = -1;
+
+	spin_lock_irqsave(&acpi_lock, flags);
+
+	p = acpi_handler_list;
+	while (p) {
+		if (p->function == function) {
+			if (p->prev) {
+				p->prev->next = p->next;
+			}
+			if (p->next) {
+				p->next->prev = p->prev;
+			}
+			r = 0;
+			break;
+		}
+		p = p->next;
+	}
+
+	spin_unlock_irqrestore(&acpi_lock, flags);
+
+	return r;
+}
+
+int __init 
+cobalt_acpi_init(void)
+{
+	int err, reg;
+	u16 addr;
+	unsigned long flags;
+
+	if (cobt_is_5k()) {
+		/* setup osb4 i/o regions */
+		if ((reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x20)))
+			request_region(reg, 4, "OSB4 (pm1a_evt_blk)");
+		if ((reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x22)))
+			request_region(reg, 2, "OSB4 (pm1a_cnt_blk)");
+		if ((reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x24)))
+			request_region(reg, 4, "OSB4 (pm_tmr_blk)");
+		if ((reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x26)))
+			request_region(reg, 6, "OSB4 (p_cnt_blk)");
+		if ((reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x28)))
+			request_region(reg, 8, "OSB4 (gpe0_blk)");
+		osb4_port = reg;
+
+		spin_lock_irqsave(&cobalt_superio_lock, flags);
+	
+		/* superi/o -- select pm logical device and get base address */
+		outb(SUPERIO_LOGICAL_DEV, SUPERIO_INDEX_PORT);
+		outb(SUPERIO_DEV_PM, SUPERIO_DATA_PORT);
+		outb(SUPERIO_ADDR_HIGH, SUPERIO_INDEX_PORT);
+		addr = inb(SUPERIO_DATA_PORT) << 8;
+		outb(SUPERIO_ADDR_LOW, SUPERIO_INDEX_PORT);
+		addr |= inb(SUPERIO_DATA_PORT); 
+		if (addr) {
+			/* get registers */
+			if ((reg = get_reg(addr, addr + 1, 0x08))) {
+				request_region(reg, 4, "SUPERIO (pm1_evt_blk)");
+				superio_port = reg;
+			}
+			if ((reg = get_reg(addr, addr + 1, 0x0c))) 
+				request_region(reg, 2, "SUPERIO (pm1_cnt_blk)");
+			if ((reg = get_reg(addr, addr + 1, 0x0e)))
+				request_region(reg, 16, "SUPERIO (gpe_blk)");
+		}
+	
+		spin_unlock_irqrestore(&cobalt_superio_lock, flags);
+	
+		/* 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("couldn't assign ACPI IRQ (%d)\n", ACPI_IRQ);
+			return -1;
+		}
+	}
+
+	return 0;
+}
diff -ruN dist-2.4.5/drivers/cobalt/Config.in cobalt-2.4.5/drivers/cobalt/Config.in
--- dist-2.4.5/drivers/cobalt/Config.in	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/Config.in	Thu May 31 14:32:15 2001
@@ -0,0 +1,23 @@
+#
+# Cobalt Networks hardware support
+#
+mainmenu_option next_comment
+comment 'Cobalt Networks support'
+bool 'Support for Cobalt Networks x86 servers' CONFIG_COBALT
+
+if [ "$CONFIG_COBALT" != "n" ]; then
+   bool 'Gen III (3000 series) system support' CONFIG_COBALT_GEN_III
+   bool 'Gen V (5000 series) system support' CONFIG_COBALT_GEN_V
+   bool 'Create legacy /proc files' CONFIG_COBALT_OLDPROC
+
+   comment 'Cobalt hardware options'
+
+   bool 'Front panel (LCD) support' CONFIG_COBALT_LCD
+   bool 'Software controlled LED support' CONFIG_COBALT_LED
+   bool 'Serial number support' CONFIG_COBALT_SERNUM
+   bool 'Watchdog timer support' CONFIG_COBALT_WDT
+   bool 'Thermal sensor support' CONFIG_COBALT_THERMAL
+   bool 'Fan tachometer support' CONFIG_COBALT_FANS
+   bool 'Disk drive ruler support' CONFIG_COBALT_RULER
+fi
+endmenu
diff -ruN dist-2.4.5/drivers/cobalt/fans.c cobalt-2.4.5/drivers/cobalt/fans.c
--- dist-2.4.5/drivers/cobalt/fans.c	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/fans.c	Thu May 31 14:32:15 2001
@@ -0,0 +1,159 @@
+/* $Id: fans.c,v 1.3 2001/05/30 07:19:47 thockin Exp $
+ * Copyright (c) 2000-2001 Sun Microsystems, Inc 
+ *
+ * This should be SMP safe.  There are no critical regions or data.  All 
+ * this ever does in inb(). --TPH
+ */
+#include <linux/config.h>
+#ifdef CONFIG_COBALT_FANS
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/time.h>
+#include <asm/io.h>
+#include <linux/cobalt.h>
+#include <linux/cobalt-systype.h>
+
+#define GEN_V_FANS_MAX		6
+
+#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_COBALT_OLDPROC
+static struct proc_dir_entry *proc_faninfo;
+#endif
+static struct proc_dir_entry *proc_cfaninfo;
+#endif
+static int fan_read_proc(char *buf, char **start, off_t pos, int len,
+	int *eof, void *x);
+
+
+int __init 
+cobalt_fan_init(void)
+{
+#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_COBALT_OLDPROC
+	proc_faninfo = create_proc_read_entry("faninfo", S_IFREG | S_IRUGO, 
+		NULL, fan_read_proc, NULL);
+        if (!proc_faninfo) {
+		EPRINTK("can't create /proc/faninfo\n");
+	}
+#endif
+	proc_cfaninfo = create_proc_read_entry("faninfo", S_IFREG | S_IRUGO, 
+		proc_cobalt, fan_read_proc, NULL);
+        if (!proc_cfaninfo) {
+		EPRINTK("can't create /proc/cobalt/faninfo\n");
+	}
+#endif
+
+	printk("Cobalt Networks fan tachometers v1.2\n");
+
+	return 0;
+}
+
+/*
+ * Samples fan tachometer square wave to calculate and report RPM
+ */
+static int 
+get_faninfo(char *buffer)
+{
+	if (cobt_is_5k()) {
+		int a, ap, b, bp;
+		int halfcycles[] = {0,0,0,0,0,0};
+		/* these are masks for the fans, cpufanbits are on 
+		 * GPIO port 1, fanbits are on port 2 */
+		int cpufanbits[] = {0x2, 0x4};
+		int fanbits[] = {0x10, 0x40, 0x20, 0x80};
+		int i;
+		int len=0;
+		struct timeval utime;
+		unsigned long elapsed, start;
+	
+		get_fast_time(&utime);
+		start = utime.tv_usec;
+
+		/* initialize 'previous' values. we do edge detection by
+		 * looking for transitions from previous values */
+		ap = inb(0x600);
+		bp = inb(0x604);
+
+		/* We are counting the number of halfcycles in a square wave
+		 * that pass in a given amount of time to determine frequency */
+		do {
+			a = inb(0x600);
+			for (i=0; i<2; i++) {
+				if ((a ^ ap) & cpufanbits[i]) {
+					halfcycles[i]++;
+				}
+			}
+
+			b = inb(0x604);
+			for (i=0; i<4; i++) {
+				if ((b ^ bp) & fanbits[i]) {
+					halfcycles[i+2]++;
+				}
+			}	
+			ap = a;
+			bp = b;
+
+			get_fast_time(&utime);
+			if (utime.tv_usec > start) {
+				elapsed = utime.tv_usec - start;
+			} else {
+				elapsed = utime.tv_usec + 1000001 - start;
+			}
+		} while (elapsed < 50000); /* count for 50ms */
+
+		/* Fan rpm = 60 / ( t x 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
+		 *      = 15,000,000 * halfcycles / elapsed
+		 *
+		 * Note, by this method and sampling for 50ms, our accuracy
+		 *  is +/- 300 rpm.  The fans are spec'ed for 8000 +/- 1000 rpm 
+		 */
+		for (i = 0; i < GEN_V_FANS_MAX; i++)
+			len += sprintf(buffer+len, "fan %x     : %lu\n", i, 
+				halfcycles[i] * 15000000 / elapsed);
+		return len;
+	} else {
+		/* software is keyed off this string - do not change it ! */
+		return sprintf(buffer, "Fan monitoring not supported.\n");
+	}
+}
+
+static int 
+fan_read_proc(char *buf, char **start, off_t pos, int len,
+	int *eof, void *x)
+{
+	int plen;
+
+	plen = get_faninfo(buf);
+
+	if (pos >= plen) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = buf + pos;
+	plen -= pos;
+
+	if (len > plen) {
+		return plen;
+	} else {
+		return len;
+	}
+}
+
+#endif /* CONFIG_COBALT_FANS */
diff -ruN dist-2.4.5/drivers/cobalt/i2c.c cobalt-2.4.5/drivers/cobalt/i2c.c
--- dist-2.4.5/drivers/cobalt/i2c.c	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/i2c.c	Thu May 31 14:32:15 2001
@@ -0,0 +1,402 @@
+/*
+ * $Id: i2c.c,v 1.6 2001/05/30 07:19:47 thockin Exp $
+ * i2c.c : Cobalt I2C driver support
+ *
+ * Copyright (C) 2000 Cobalt Networks, Inc. 
+ * Copyright (C) 2001 Sun Microsystems, Inc. 
+ *
+ * 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 <stddef.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/cobalt.h>
+#include <linux/cobalt-i2c.h>
+#include <linux/cobalt-systype.h>
+
+#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
+
+#define COBALT_I2C_INFO			"Cobalt Networks I2C bus"
+
+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)
+
+static spinlock_t i2c_lock = SPIN_LOCK_UNLOCKED;
+
+static int 
+i2c_wait_for_smi(void)
+{
+	unsigned int timeout=0xffff;
+	int status;
+
+	while (timeout) {
+		outb(0xff, 0x80); /* wait */
+		status = inb(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;
+			}
+		}
+
+		timeout--;
+	}
+
+	/* still busy - punch the abort bit */
+	WPRINTK("i2c timeout: status=0x%x, resetting...\n", status);
+
+	if (cobt_is_3k()) {
+		outb(4, i2c_data->io_port + I2C_3K_CMD);
+	} else if (cobt_is_5k()) {
+		outb(2, i2c_data->io_port + I2C_5K_HOST_CONTROL);
+		outb(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(0xff, I2C_REG(status));
+
+	/* device address */
+	outb((dev|r) & 0xff, I2C_REG(addr));
+
+	/* I2C index */
+	outb(index & 0xff, I2C_REG(index));
+
+	return 0;
+}
+	
+static inline int 
+i2c_cmd(const unsigned char command)
+{
+	if (cobt_is_3k()) {
+		outb(command, i2c_data->io_port + I2C_3K_CMD); 
+		outb(0xff, i2c_data->io_port + I2C_3K_START);
+	} else if (cobt_is_5k()) {
+		outb(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 __init 
+cobalt_i2c_init(void)
+{
+	struct pci_dev *i2cdev = NULL;
+
+	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) {
+			EPRINTK("can't find OSB4 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) {
+		printk(KERN_INFO "%s\n", COBALT_I2C_INFO);
+	} else {
+		EPRINTK("i2c IO port not found\n");
+	}
+
+	return 0;
+}
+
+int 
+cobalt_i2c_reset(void)
+{
+	int r;
+	unsigned long flags;
+
+	spin_lock_irqsave(&i2c_lock, flags);
+
+	if (cobt_is_3k()) {
+		/* clear status */
+		outb(0xff, i2c_data->io_port + I2C_3K_STATUS);
+		/* reset SMB devs */
+		outb(0x08, i2c_data->io_port + I2C_3K_CMD);
+		/* start command */
+		outb(0xff, i2c_data->io_port + I2C_3K_START);
+	} else if (cobt_is_5k()) {
+		/* clear status */
+		outb(0xff, i2c_data->io_port + I2C_5K_HOST_STATUS);
+		outb(0x2, i2c_data->io_port + I2C_5K_HOST_CONTROL);
+	}
+
+	r = i2c_wait_for_smi();
+
+	spin_unlock_irqrestore(&i2c_lock, flags);
+
+	return r;
+}
+
+int 
+cobalt_i2c_read_byte(const int dev, const int index)
+{
+	int val = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&i2c_lock, flags);
+
+	if (i2c_setup(dev, index, I2C_READ) < 0 
+	 || i2c_cmd(I2C_CMD(rw_byte)) < 0) {
+		val = -1;
+	}
+
+	if (val == 0) {
+		val = inb(I2C_REG(data_low));
+	}
+
+	spin_unlock_irqrestore(&i2c_lock, flags);
+
+	return val;
+}
+
+int 
+cobalt_i2c_read_word(const int dev, const int index)
+{
+	int val = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&i2c_lock, flags);
+	
+	if (i2c_setup(dev, index, I2C_READ) < 0 
+	 || i2c_cmd(I2C_CMD(rw_word)) < 0) {
+		val = -1;
+	}
+
+	if (val == 0) {
+		val  = inb(I2C_REG(data_low));
+		val += inb(I2C_REG(data_high)) << 8;
+	}
+
+	spin_unlock_irqrestore(&i2c_lock, flags);
+
+	return val;
+}
+
+int 
+cobalt_i2c_read_block(const int dev, const int index, 
+	unsigned char *data, int count)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&i2c_lock, flags);
+	
+	if (i2c_setup(dev, index, I2C_READ) < 0) { 
+		spin_unlock_irqrestore(&i2c_lock, flags);
+		return -1;
+	}
+
+	outb(count & 0xff, I2C_REG(data_low));
+	outb(count & 0xff, I2C_REG(data_high));
+
+	if (i2c_cmd(I2C_CMD(rw_block)) < 0) {
+		spin_unlock_irqrestore(&i2c_lock, flags);
+		return -1;
+	}
+
+	while (count) {
+		/* read a byte of block data */
+		*data = inb(I2C_REG(data_block));
+		data++;
+		count--;
+	}
+
+	spin_unlock_irqrestore(&i2c_lock, flags);
+
+	return 0;	
+}
+
+int 
+cobalt_i2c_write_byte(const int dev, const int index, const u8 val)
+{
+	int r = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&i2c_lock, flags);
+
+	if (i2c_setup(dev, index, I2C_WRITE) < 0) {
+		r = -1;
+	}
+
+	if (r == 0) {
+		outb(val & 0xff, I2C_REG(data_low));
+
+		if (i2c_cmd(I2C_CMD(rw_byte)) < 0) {
+		    r = -1;
+		}
+	}
+
+	spin_unlock_irqrestore(&i2c_lock, flags);
+
+	return r;	
+}
+
+int 
+cobalt_i2c_write_word(const int dev, const int index, const u16 val)
+{
+	int r = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&i2c_lock, flags);
+
+	if (i2c_setup(dev, index, I2C_WRITE) < 0) {
+		r = -1;
+	}
+
+	if (r == 0) {
+		outb(val & 0xff, I2C_REG(data_low));
+		outb((val >> 8) & 0xff, I2C_REG(data_high));
+
+		if (i2c_cmd(I2C_CMD(rw_word)) < 0) {
+			r = -1;
+		}
+	}
+
+	spin_unlock_irqrestore(&i2c_lock, flags);
+
+	return r;	
+}
+
+int 
+cobalt_i2c_write_block(int dev, int index, unsigned char *data, int count)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&i2c_lock, flags);
+
+	if (i2c_setup(dev, index, I2C_WRITE) < 0) {
+		spin_unlock_irqrestore(&i2c_lock, flags);
+		return -1;
+	}
+
+	outb(count & 0xff, I2C_REG(data_low));
+	outb(count & 0xff, I2C_REG(data_high));
+
+	if (i2c_cmd(I2C_CMD(rw_block)) < 0) {
+		spin_unlock_irqrestore(&i2c_lock, flags);
+		return -1;
+	}
+
+	while (count) {
+		/* write a byte of block data */
+		outb(*data, I2C_REG(data_block));
+		data++;
+		count--;
+	}
+
+	spin_unlock_irqrestore(&i2c_lock, flags);
+
+	return 0;	
+}
diff -ruN dist-2.4.5/drivers/cobalt/init.c cobalt-2.4.5/drivers/cobalt/init.c
--- dist-2.4.5/drivers/cobalt/init.c	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/init.c	Thu May 31 14:32:15 2001
@@ -0,0 +1,79 @@
+/* $Id: init.c,v 1.6 2001/05/30 07:19:48 thockin Exp $ */
+/*
+ *   Copyright (c) 2001  Sun Microsystems
+ *   Generic initialization, to reduce pollution of other files
+ */
+#include <linux/config.h>
+#ifdef CONFIG_COBALT
+
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/cobalt.h>
+
+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 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_thermal_init(void);
+extern int cobalt_fan_init(void);
+extern int cobalt_acpi_init(void);
+extern int cobalt_ruler_init(void);
+
+struct proc_dir_entry *proc_cobalt;
+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();
+	cobalt_i2c_init();
+	cobalt_acpi_init();
+	cobalt_net_init();
+#ifdef CONFIG_COBALT_LED
+	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_WDT
+	cobalt_wdt_init();
+#endif
+#ifdef CONFIG_COBALT_THERMAL
+	cobalt_thermal_init();
+#endif
+#ifdef CONFIG_COBALT_FANS
+	cobalt_fan_init();
+#endif
+
+	return 0;
+}
+
+static int __init
+cobalt_proc_init(void)
+{
+	proc_cobalt = proc_mkdir("cobalt", 0);
+	if (!proc_cobalt) {
+		EPRINTK("can't create /proc/cobalt\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_COBALT */
diff -ruN dist-2.4.5/drivers/cobalt/lcd.c cobalt-2.4.5/drivers/cobalt/lcd.c
--- dist-2.4.5/drivers/cobalt/lcd.c	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/lcd.c	Thu May 31 14:32:15 2001
@@ -0,0 +1,645 @@
+/*
+ * $Id: lcd.c,v 1.9 2001/05/30 07:19:48 thockin 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
+ *
+ * This should be SMP safe.  We don't deal with interrupts or timers at all,
+ * so plain old spin_lock() is sufficient.  We're hardly performance critical,
+ * so we lock around lcd_ioctl() and just where needed by other external
+ * functions.  There is a static global waiters variable that is atomic_t, and
+ * so should be safe. --TPH
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_COBALT_LCD
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/netdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/in6.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include <linux/delay.h>
+
+#include <linux/cobalt.h>
+#include <linux/cobalt-systype.h>
+#include <linux/cobalt-lcd.h>
+#include <linux/cobalt-superio.h>
+#include <linux/cobalt-i2c.h>
+
+#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);
+
+/* 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 spinlock_t lcd_lock = SPIN_LOCK_UNLOCKED;
+/* this is for polling the power button on gen V */
+static u16 pm_event_port;
+
+/* 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 = {
+	COBALT_LCD_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 unsigned char 
+lcddev_read_inst(void)
+{
+        unsigned char a = 0;
+
+	if (cobt_is_3k()) {
+		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 */
+	} else if (cobt_is_5k()) {
+		a = cobalt_i2c_read_byte(COBALT_I2C_DEV_LCD_INST | 
+			COBALT_I2C_READ, 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) {
+		if ((lcddev_read_inst() & 0x80) != 0x80)
+			break;
+	}
+}
+
+/* Write a control instruction to the LCD */
+static inline void 
+lcddev_write_inst(unsigned char data)
+{
+	lcddev_poll_wait();
+
+	if (cobt_is_3k()) {
+		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 */
+	} else if (cobt_is_5k()) {
+		cobalt_i2c_write_byte(COBALT_I2C_DEV_LCD_INST | 
+			COBALT_I2C_WRITE, 0, data);
+	}
+
+	/* 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_3k()) {
+		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 */
+	} else if (cobt_is_5k()) {
+		cobalt_i2c_write_byte(COBALT_I2C_DEV_LCD_DATA | 
+			COBALT_I2C_WRITE, 0, data);
+	}
+
+	/* 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_3k()) {
+		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 */
+	} else if (cobt_is_5k()) {
+		a = cobalt_i2c_read_byte(COBALT_I2C_DEV_LCD_DATA | 
+			COBALT_I2C_READ, 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_3k()) {
+		outb(0x29, LCD_CONTROL_ADDRESS); /* Sel=0, Bi=1 */
+		r = inb(LCD_DATA_ADDRESS);
+	} else if (cobt_is_5k()) {
+		/* first, check to see if the power button has been pushed */
+		if (pm_event_port 
+		 && (inb(pm_event_port + SUPERIO_PM1_STATUS) 
+		     & SUPERIO_PWRBUTTON)) {
+			outb(SUPERIO_PWRBUTTON, 
+				pm_event_port + SUPERIO_PM1_STATUS);
+			return BUTTON_Power;
+		}
+
+		{
+			unsigned char inst = cobalt_i2c_read_byte(0x41, 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: r = BUTTON_NONE_B; break;
+			default: r = inst;
+			}
+		}
+	}
+	
+	return r;
+}
+
+static inline int 
+button_pressed(void)
+{
+        unsigned char b;
+	
+	spin_lock(&lcd_lock);
+	b = read_buttons();
+	spin_unlock(&lcd_lock);
+
+	switch (b) {
+	case BUTTON_Next:
+	case BUTTON_Next_B:
+	case BUTTON_Reset_B:
+	case BUTTON_Power:
+		return b;
+	default:
+	}
+
+        return 0;
+}
+
+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);
+
+	spin_lock(&lcd_lock);
+
+	switch (cmd) {
+	/* Turn the LCD on */
+	case LCD_On:
+		lcddev_write_inst(0x0F);
+		break;		
+
+	/* Turn the LCD off */
+	case LCD_Off:
+		lcddev_write_inst(0x08);
+		break;
+
+	/* Reset the LCD */
+	case LCD_Reset:
+		lcddev_write_inst(0x3F);
+		lcddev_write_inst(0x3F);
+		lcddev_write_inst(0x3F);
+		lcddev_write_inst(0x3F);
+		lcddev_write_inst(0x01);
+		lcddev_write_inst(0x06);
+		break;
+
+	/* Clear the LCD */
+	case LCD_Clear:
+       		lcddev_write_inst(0x01);     
+		break;
+
+	/* Move the cursor one position to the left */
+	case LCD_Cursor_Left:
+		lcddev_write_inst(0x10);
+		break;
+
+	/* Move the cursor one position to the right */
+	case LCD_Cursor_Right:
+		lcddev_write_inst(0x14);
+		break;	
+
+	/* Turn the cursor off */
+	case LCD_Cursor_Off:
+                lcddev_write_inst(0x0C);
+	        break;
+
+	/* Turn the cursor on */
+        case LCD_Cursor_On:
+                lcddev_write_inst(0x0F);
+                break;
+
+	/* Turn blinking off? I don't know what this does - TJS */
+        case LCD_Blink_Off:
+                lcddev_write_inst(0x0E);
+                break;
+
+	/* Get the current cursor position */
+        case LCD_Get_Cursor_Pos:
+                display.cursor_address = lcddev_read_inst();
+                display.cursor_address = display.cursor_address & 0x07F;
+                copy_to_user((struct lcd_display *)arg, &display, dlen);
+                break;
+
+	/* Set the cursor position */
+        case LCD_Set_Cursor_Pos:
+                copy_from_user(&display, (struct lcd_display *)arg, dlen);
+                a = display.cursor_address | LCD_Addr;
+                lcddev_write_inst(a);
+                break;
+
+	/* Get the value at the current cursor position? - TJS */
+        case LCD_Get_Cursor:
+                display.character = lcddev_read_data();
+                copy_to_user((struct lcd_display *)arg, &display, dlen);
+                lcddev_write_inst(0x10);
+                break;
+
+	/* Set the character at the cursor position? - TJS */
+	case LCD_Set_Cursor:
+                copy_from_user(&display, (struct lcd_display *)arg, dlen);
+                lcddev_write_data(display.character);
+                lcddev_write_inst(0x10);
+                break;
+
+	/* Dunno what this does - TJS */ 
+	case LCD_Disp_Left:
+		lcddev_write_inst(0x18);
+		break;
+
+	/* Dunno what this does - TJS */ 
+	case LCD_Disp_Right:
+		lcddev_write_inst(0x1C);
+		break;
+
+	/* Dunno what this does - TJS */ 
+	case LCD_Home:
+		lcddev_write_inst(0x02);
+		break;
+
+	/* Write a string to the LCD */
+	case LCD_Write:
+                copy_from_user(&display, (struct lcd_display *)arg, dlen);
+
+		/* First line */
+                lcddev_write_inst(0x80);
+		for (index = 0; index < display.size1; index++)
+			lcddev_write_data(display.line1[index]);
+
+		/* Second line */
+		lcddev_write_inst(0xC0);	
+		for (index = 0; index < display.size2; index++)
+			lcddev_write_data(display.line2[index]);
+		break;	
+
+	/* Read what's on the LCD */
+        case LCD_Read:
+                for (address = DD_R00; address <= DD_R01; address++) {
+                        lcddev_write_inst(address | LCD_Addr);
+                        display.line1[address] = lcddev_read_data();
+                }
+                display.line1[DD_R01] = '\0';
+
+                for (address = DD_R10; address <= DD_R11; address++) {
+                        lcddev_write_inst(address | LCD_Addr);
+                        display.line2[address - DD_R10] = lcddev_read_data();
+		}
+                display.line2[DD_R01] = '\0';
+
+                copy_to_user((struct lcd_display *)arg, &display, dlen);
+                break;
+
+	case LCD_Raw_Inst:
+                copy_from_user(&display, (struct lcd_display *)arg, dlen);
+                lcddev_write_inst(display.character);
+                break;
+
+        case LCD_Raw_Data:
+                copy_from_user(&display, (struct lcd_display *)arg, dlen);
+                lcddev_write_data(display.character);
+                break;
+
+	case LCD_Type:
+		if (cobt_is_3k()) {
+	        	put_user(LCD_TYPE_PARALLEL_B, (int *)arg);
+		} else if (cobt_is_5k()) {
+	        	put_user(LCD_TYPE_I2C, (int *)arg);
+		}
+		break;
+
+	/* Read the buttons */
+        case BUTTON_Read:
+                button_display.buttons = read_buttons();
+                copy_to_user((struct lcd_display *)arg, &button_display, dlen);
+                break;
+	
+	/* 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_lazy(cobalt_led_get() | arg);
+		break;
+
+	case LED32_Bit_Clear:
+                cobalt_led_set_lazy(cobalt_led_get() & ~arg);
+		break;
+
+	/* set all the leds */
+	case LED_Set:
+                copy_from_user(&display, (struct lcd_display *)arg, dlen);
+                cobalt_led_set_lazy(display.leds);
+		break;
+
+	/* set a single led */
+	case LED_Bit_Set:
+                copy_from_user(&display, (struct lcd_display *)arg, dlen);
+                cobalt_led_set_lazy(cobalt_led_get() | display.leds);
+		break;
+
+	/* clear an led */
+	case LED_Bit_Clear:
+                copy_from_user(&display, (struct lcd_display *)arg, dlen);
+                cobalt_led_set_lazy(cobalt_led_get() & ~display.leds);
+		break;
+
+	default:
+	}
+
+	spin_unlock(&lcd_lock);
+
+	return 0;
+}
+
+static int 
+cobalt_lcd_open(struct inode *inode, struct file *file)
+{
+	if (!lcd_present)
+		return -ENXIO;
+	else
+		return 0;
+}
+
+/* LED 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 buttons_now;
+	static atomic_t lcd_waiters = ATOMIC_INIT(0);
+
+	if (atomic_read(&lcd_waiters) > 0)
+		return -EINVAL;
+	atomic_inc(&lcd_waiters);
+
+	while (((buttons_now = button_pressed()) == 0) &&
+	       !(signal_pending(current)))
+	{
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout((2 * HZ));
+	}
+	atomic_dec(&lcd_waiters);
+
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+
+	return buttons_now;
+}
+
+/* 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;
+
+	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(&lcd_lock);
+	for (addr = min; addr <= max; addr++) {
+                lcddev_write_inst(addr | LCD_Addr);
+                udelay(150);
+                line[addr-min] = lcddev_read_data();
+                udelay(150);
+	}
+	spin_unlock(&lcd_lock);
+	line[addr-min] = '\0';
+
+	return line;
+}
+
+static int 
+cobalt_lcd_read_proc(char *buf, char **start, off_t offset,
+				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);
+	
+	if (offset >= plen) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = buf + offset;
+	plen -= offset;
+
+	if (len > plen) {
+		return plen;
+	} else {
+        	return len;
+	}
+}
+
+int __init 
+cobalt_lcd_init(void)
+{	
+	if (disable_lcd) {
+		printk(KERN_INFO "%s version %d.%d [DISABLED]\n",
+		       LCD_DRIVER, LCD_DRIVER_VMAJ, LCD_DRIVER_VMIN);
+		return 0;
+	}
+
+	misc_register(&lcd_dev);
+    
+	/* flag ourselves as present */
+	lcd_present = 1;
+
+	/* initialize the device */
+	lcddev_init();
+
+	if (cobt_is_5k()) {
+		u16 pm_port;
+		unsigned long flags;
+
+		spin_lock_irqsave(&cobalt_superio_lock, flags);
+		
+		/* superio power button -- get ioport */
+		outb(SUPERIO_LOGICAL_DEV, SUPERIO_INDEX_PORT);
+		outb(SUPERIO_DEV_PM, SUPERIO_DATA_PORT);
+		outb(SUPERIO_ADDR_HIGH, SUPERIO_INDEX_PORT);
+		pm_port = inb(SUPERIO_DATA_PORT) << 8;
+		outb(SUPERIO_ADDR_LOW, SUPERIO_INDEX_PORT);
+		pm_port |= inb(SUPERIO_DATA_PORT);
+		if (pm_port) {
+			/* get the PM1 event base address */
+			outb(0x08, pm_port);
+			pm_event_port = inb(pm_port + 1);
+			outb(0x09, pm_port);
+			pm_event_port |= inb(pm_port + 1) << 8;
+			if (!pm_event_port) {
+				EPRINTK("pm_event_port is 0\n");
+			}
+		} else {
+			EPRINTK("pm_port is 0\n");
+		}
+
+		spin_unlock_irqrestore(&cobalt_superio_lock, flags);
+	}
+
+#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
+
+	printk(KERN_INFO "%s version %d.%d\n", LCD_DRIVER,
+	       LCD_DRIVER_VMAJ, LCD_DRIVER_VMIN);
+
+	return 0;
+}
+
+#endif /* CONFIG_COBALT_LCD */
diff -ruN dist-2.4.5/drivers/cobalt/led.c cobalt-2.4.5/drivers/cobalt/led.c
--- dist-2.4.5/drivers/cobalt/led.c	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/led.c	Thu May 31 14:32:15 2001
@@ -0,0 +1,447 @@
+/*
+ * $Id: led.c,v 1.12 2001/05/30 07:19:48 thockin 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
+ *
+ * This should be SMP safe.  There is one definite critical region: the
+ * handler list (led_handler_lock).  The led_state is atomic, so should be 
+ * safe against simultaneous writes.  Bit banging of lights is currently 
+ * also a protected region (led_hw_lock).
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_COBALT_LED
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include <linux/delay.h>
+
+#include <linux/cobalt.h>
+#include <linux/cobalt-systype.h>
+#include <linux/cobalt-led.h>
+#include <linux/cobalt-i2c.h>
+#include <linux/cobalt-superio.h>
+
+#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_HZ		(HZ/20)
+
+/* 
+ * this is the abstracted state of active LEDs - see the defines for LED_* 
+ *
+ * NOTE: this is an atomic integer, and does not have full 32 bit size : the
+ * best we can count on is 24 bits, says the headers for atomic ops
+ */
+static atomic_t led_state = ATOMIC_INIT(0);
+
+/* 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 u16 superio_rtc_port; /* this is the IO port for the power button */
+static struct timer_list timer;
+
+static spinlock_t led_hw_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 RAQXTR_FPLED_ETH0_TXRX		0x8000
+#define RAQXTR_FPLED_ETH0_LINK		0x1000
+#define RAQXTR_FPLED_ETH1_TXRX		0x4000
+#define RAQXTR_FPLED_ETH1_LINK		0x0800
+#define RAQXTR_FPLED_DISK0		0x2000
+#define RAQXTR_FPLED_DISK1		0x0200
+#define RAQXTR_FPLED_DISK2		0x0080
+#define RAQXTR_FPLED_DISK3		0x0040
+#define RAQXTR_FPLED_DISK_FAULT		0x0010
+#define RAQXTR_FPLED_WEB 		0x0400
+#define RAQXTR_FPLED_HEART		0x0100
+#define RAQXTR_FPLED_SPARE		0x0020
+
+#define FPLED_MASK 			(LED_ETH0_TXRX | LED_ETH0_LINK | \
+					 LED_ETH1_TXRX | LED_ETH1_LINK | \
+		   			 LED_DISK0 | LED_DISK1 | \
+					 LED_DISK2 | LED_DISK3 | \
+					 LED_WEBLIGHT) 
+
+static void 
+xtr_set_logo_led(const int newstate)
+{
+	if (cobt_is_5k()) {
+		u8 val; 
+		unsigned long flags;
+
+		/* get the RTC lock */
+		spin_lock_irqsave(&rtc_lock, flags);
+
+		/* set bank 2 */
+		outb(SUPERIO_RTC_CRA, superio_rtc_port);
+		val = inb(superio_rtc_port + 1) & 0x8f;
+		outb(val | SUPERIO_RTC_BANK_2, superio_rtc_port + 1);
+
+		/* get the current state of APCR4 */
+		outb(SUPERIO_APCR4, superio_rtc_port);
+		val = inb(superio_rtc_port + 1) & 0x3f;
+
+		if (newstate) {
+			val |= (newstate & LED_SYSFAULT) ? 0x80 : 0x0;
+		} else { 
+			val |= 0x40; /* logo is off */
+		}
+		outb(val, superio_rtc_port + 1);
+
+		/* go back to bank 0 */
+		outb(SUPERIO_RTC_CRA, superio_rtc_port);
+		val = inb(superio_rtc_port + 1) & 0x8f;
+		outb(val | SUPERIO_RTC_BANK_0, superio_rtc_port + 1);
+
+		spin_unlock_irqrestore(&rtc_lock, flags);
+	}
+}
+
+/* 
+ * actually set the leds (icky details hidden within) 
+ * this protects against itself with led_hw_lock
+ * */
+static void 
+set_led_hw(const unsigned int newstate)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&led_hw_lock, flags);
+
+	if (cobt_is_raq3() && 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_qube3() && 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_raqxtr()) {
+		unsigned int tmp = 0;
+		if (newstate & LED_WEBLIGHT) {
+			tmp |= RAQXTR_FPLED_WEB;
+		}
+		if (newstate & LED_ETH0_TXRX) {
+			tmp |= RAQXTR_FPLED_ETH0_TXRX;
+		}
+		if (newstate & LED_ETH0_LINK) {
+			tmp |= RAQXTR_FPLED_ETH0_LINK;
+		}
+		if (newstate & LED_ETH1_TXRX) {
+			tmp |= RAQXTR_FPLED_ETH1_TXRX;
+		}
+		if (newstate & LED_ETH1_LINK) {
+			tmp |= RAQXTR_FPLED_ETH1_LINK;
+		}
+		if (newstate & LED_DISK0) {
+			tmp |= RAQXTR_FPLED_DISK0;
+		}
+		if (newstate & LED_DISK1) {
+			tmp |= RAQXTR_FPLED_DISK1;
+		}
+		if (newstate & LED_DISK2) {
+			tmp |= RAQXTR_FPLED_DISK2;
+		}
+		if (newstate & LED_DISK3) {
+			tmp |= RAQXTR_FPLED_DISK3;
+		}
+		if (newstate & LED_HEART) {
+			tmp |= RAQXTR_FPLED_HEART;
+		}
+		if (newstate & LED_SPARE) {
+			tmp |= RAQXTR_FPLED_SPARE;
+		}
+	  	cobalt_i2c_write_byte(COBALT_I2C_DEV_LED_I, 0, tmp & 0xff);
+	  	cobalt_i2c_write_byte(COBALT_I2C_DEV_LED_II, 0, tmp >> 8);
+		xtr_set_logo_led(newstate & LED_COBALTLOGO);
+	}
+
+	spin_unlock_irqrestore(&led_hw_lock, flags);
+}
+
+static inline void
+do_led_set(const unsigned int leds)
+{
+	atomic_set(&led_state, leds);
+	set_led_hw(leds);
+}
+
+static inline unsigned int
+do_led_get(void)
+{
+	return atomic_read(&led_state);
+}
+
+/* blip the front panel leds */
+static void led_timer_func(unsigned long data)
+{
+	if (cobt_is_5k()) {
+		unsigned int leds = 0;
+		struct led_handler *p;
+		unsigned long flags;
+
+		/* snapshot the stored state */
+		leds = do_led_get();
+
+		/* call all registered callbacks */
+		spin_lock_irqsave(&led_handler_lock, flags);
+		p = led_handler_list;
+		while (p) {
+			leds |= p->function(p->data);
+			p = p->next;
+		}
+		spin_unlock_irqrestore(&led_handler_lock, flags);
+		
+		/* set the led state, which sets led_state */
+		do_led_set(leds);
+
+		/* reset the stored state */
+		atomic_set(&led_state, leds & ~FPLED_MASK);
+
+		/* re-arm ourself */
+		mod_timer(&timer, jiffies + FPLED_HZ);
+	}
+}
+
+void 
+cobalt_led_set(const unsigned int leds)
+{
+	do_led_set(leds);
+}
+
+void 
+cobalt_led_set_lazy(const unsigned int leds)
+{
+	if (cobt_is_3k()) {
+		/* no timer on GEN_III, thus no _lazy() form */
+		do_led_set(leds);
+	} else if (cobt_is_5k()) {
+		/* the next led timer run will catch these changes */
+		atomic_set(&led_state, leds);
+	}
+}
+
+unsigned int 
+cobalt_led_get(void)
+{
+	unsigned int r;
+
+	r = do_led_get();
+
+	return r;
+}
+
+int 
+cobalt_fpled_register(unsigned int (*function)(void *), void *data)
+{
+	int r = -1;
+
+	if (cobt_is_5k()) {
+		struct led_handler *newh;
+		unsigned long flags;
+
+		newh = kmalloc(sizeof(*newh), GFP_ATOMIC);
+		if (!newh) {
+			EPRINTK("can't allocate memory for handler %p(%p)\n",
+				function, data);
+			return -1;
+		}
+
+		spin_lock_irqsave(&led_handler_lock, flags);
+	
+		/* head insert */
+		newh->function = function;
+		newh->data = data;
+		newh->next = led_handler_list;
+		newh->prev = NULL;
+		if (led_handler_list) {
+			led_handler_list->prev = newh;
+		}
+		led_handler_list = newh;
+	
+		spin_unlock_irqrestore(&led_handler_lock, flags);
+		r = 0;
+	}
+
+	return r;
+}
+
+int 
+cobalt_fpled_unregister(unsigned int (*function)(void *), void *data)
+{
+	int r = -1;
+
+	if (cobt_is_5k()) {
+		struct led_handler *p;
+		unsigned long flags;
+	
+		spin_lock_irqsave(&led_handler_lock, flags);
+
+		p = led_handler_list;
+		while (p) {
+			if (p->function == function && p->data == data) {
+				if (p->prev) {
+					p->prev->next = p->next;
+				}
+				if (p->next) {
+					p->next->prev = p->prev;
+				}
+				r = 0;
+				break;
+			}
+			p = p->next;
+		}
+
+		spin_unlock_irqrestore(&led_handler_lock, flags);
+	}
+
+	return r;
+}
+
+int __init 
+cobalt_led_init(void)
+{	
+	unsigned int leds = LED_SHUTDOWN | LED_COBALTLOGO;
+
+	if (cobt_is_3k()) {
+		/* LEDs for RaQ3/4 and Qube3 are on the PMU */
+		led_dev = pci_find_device(PCI_VENDOR_ID_AL,
+					  PCI_DEVICE_ID_AL_M7101,
+					  NULL);
+		if (!led_dev) {
+			EPRINTK("can't find PMU for LED control\n");
+			return -1;
+		}
+	} else if (cobt_is_5k()) {
+		unsigned long flags;
+
+		/*
+		 * get the superio port for the power button
+		 */
+
+		spin_lock_irqsave(&cobalt_superio_lock, flags);
+	
+		/* select apc */
+		outb(SUPERIO_LOGICAL_DEV, SUPERIO_INDEX_PORT);
+		outb(SUPERIO_DEV_RTC, SUPERIO_DATA_PORT);
+
+		/* determine base address */
+		outb(SUPERIO_ADDR_HIGH, SUPERIO_INDEX_PORT);
+		superio_rtc_port = inb(SUPERIO_DATA_PORT) << 8;
+		outb(SUPERIO_ADDR_LOW, SUPERIO_INDEX_PORT);
+		superio_rtc_port |= inb(SUPERIO_DATA_PORT);
+		if (!superio_rtc_port) {
+			EPRINTK("superio_rtc_port is 0\n");
+		}
+		spin_unlock_irqrestore(&cobalt_superio_lock, flags);
+
+		/* setup up timer for fp leds */
+		init_timer(&timer);
+		timer.expires = jiffies + FPLED_HZ;
+		timer.data = 0;
+		timer.function = &led_timer_func;
+		add_timer(&timer);
+	}
+
+	/* set the initial state */
+	leds |= cobalt_cmos_read_flag(CMOS_SYSFAULT_FLAG) ? LED_SYSFAULT : 0;
+	do_led_set(leds);
+
+	printk(KERN_INFO "%s version %d.%d\n", LED_DRIVER,
+		LED_DRIVER_VMAJ, LED_DRIVER_VMIN);
+
+	return 0;
+}
+
+#endif /* CONFIG_COBALT_LED */
diff -ruN dist-2.4.5/drivers/cobalt/Makefile cobalt-2.4.5/drivers/cobalt/Makefile
--- dist-2.4.5/drivers/cobalt/Makefile	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/Makefile	Thu May 31 14:32:15 2001
@@ -0,0 +1,19 @@
+#
+# Makefile for the Sun/Cobalt device drivers
+#
+
+O_TARGET := cobalt.o
+
+export-objs	:= init.o i2c.o lcd.o led.o systype.o serialnum.o wdt.o \
+		   thermal.o fans.o
+
+obj-$(CONFIG_COBALT)		+= init.o systype.o i2c.o acpi.o net.o
+obj-$(CONFIG_COBALT_SERNUM)	+= serialnum.o
+obj-$(CONFIG_COBALT_LCD)	+= lcd.o
+obj-$(CONFIG_COBALT_LED)	+= led.o
+obj-$(CONFIG_COBALT_WDT)	+= wdt.o
+obj-$(CONFIG_COBALT_THERMAL)	+= thermal.o
+obj-$(CONFIG_COBALT_FANS)	+= fans.o
+obj-$(CONFIG_COBALT_RULER)	+= ruler.o
+
+include $(TOPDIR)/Rules.make
diff -ruN dist-2.4.5/drivers/cobalt/net.c cobalt-2.4.5/drivers/cobalt/net.c
--- dist-2.4.5/drivers/cobalt/net.c	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/net.c	Thu May 31 14:32:15 2001
@@ -0,0 +1,128 @@
+/* 
+ * cobalt net wrappers
+ * Copyright (c) 2000, Cobalt Networks, Inc.
+ * $Id: net.c,v 1.5 2001/04/27 03:13:53 thockin Exp $
+ * author: thockin@sun.com
+ *
+ * This should be SMP safe.  The only critical data is the list of devices.
+ * The LED handler runs at timer-interrupt, so we must use the IRQ safe forms
+ * of the locks. --TPH
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <asm/io.h>
+#include <linux/cobalt.h>
+#include <linux/cobalt-net.h>
+#include <linux/cobalt-led.h>
+
+#define MAX_COBT_NETDEVS	2
+static struct net_device *netdevs[MAX_COBT_NETDEVS];
+static int n_netdevs;
+static spinlock_t cobaltnet_lock = SPIN_LOCK_UNLOCKED;
+
+#if defined(CONFIG_COBALT_LED)
+static unsigned int
+net_led_handler(void *data)
+{
+	int i;
+	unsigned int leds = 0;
+	static int txrxmap[MAX_COBT_NETDEVS] = {LED_ETH0_TXRX, LED_ETH1_TXRX};
+	static int linkmap[MAX_COBT_NETDEVS] = {LED_ETH0_LINK, LED_ETH1_LINK};
+	unsigned long flags;
+	static unsigned long net_old[MAX_COBT_NETDEVS];
+
+	spin_lock_irqsave(&cobaltnet_lock, flags);
+
+	for (i = 0; i < n_netdevs; i++) {
+		unsigned long txrxstate;
+		struct net_device *dev = netdevs[i];
+		if (!dev) {
+			continue;
+		}
+		/* check for link */
+		if (dev->link_check && dev->link_check(dev)) {
+			leds |= linkmap[i];
+		}
+		/* check for tx/rx */
+		txrxstate = dev->trans_start ^ dev->last_rx;
+		if (txrxstate != net_old[i]) {
+			leds |= txrxmap[i];
+			net_old[i] = txrxstate;
+		}
+	}
+
+	spin_unlock_irqrestore(&cobaltnet_lock, flags);
+
+	return leds;
+}
+#endif
+
+/* 
+ * We try to be VERY explicit here.  Fine for now, may eventually break down.
+ */
+void 
+cobalt_net_register(struct net_device *ndev)
+{
+	unsigned long flags;
+	int i;
+	
+        if (!ndev) {
+	       return;
+	}
+
+	/* we'll track the first MAX_COBT_NETDEVS NICs */
+	if (n_netdevs >= MAX_COBT_NETDEVS) {
+	       return;
+	}
+
+	spin_lock_irqsave(&cobaltnet_lock, flags);
+
+	/* find a free slot */
+	for (i = 0; i < n_netdevs; i++) {
+		if (!netdevs[i]) {
+			netdevs[i] = ndev;
+			n_netdevs++;
+		}
+	}
+
+	spin_unlock_irqrestore(&cobaltnet_lock, flags);
+}
+
+void 
+cobalt_net_unregister(struct net_device *ndev)
+{
+	int i;
+	unsigned long flags;
+	
+        if (!ndev) {
+	       return;
+	}
+
+	spin_lock_irqsave(&cobaltnet_lock, flags);
+
+	/* try to remove it from the list */
+	for (i = 0; i < n_netdevs; i++) {
+		if (netdevs[i] == ndev) {
+			netdevs[i] = NULL;
+		}
+	}
+
+	spin_unlock_irqrestore(&cobaltnet_lock, flags);
+}
+
+int __init
+cobalt_net_init(void)
+{
+#if defined(CONFIG_COBALT_LED)
+	/* register an LED handler */
+	cobalt_fpled_register(net_led_handler, NULL);
+#endif
+
+	return 0;
+}

^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCH] support for Cobalt Networks (x86 only) systems (for real this  time)
@ 2001-06-01  2:49 Tim Hockin
  2001-06-01  4:47 ` Dax Kelson
  2001-06-01  7:47 ` Jeff Garzik
  0 siblings, 2 replies; 46+ messages in thread
From: Tim Hockin @ 2001-06-01  2:49 UTC (permalink / raw)
  To: alan; +Cc: Linux Kernel Mailing List

[-- Attachment #1: Type: text/plain, Size: 517 bytes --]

apparently, LKML silently (!) bounces messages > a certain size.  So I'll
try smaller patches.  This is part 2/2 of the general Cobalt support.

Alan,

Aattached is a (large, but self contained) patch for Cobalt Networks suport
for x86 systems (RaQ3, RaQ4, Qube3, RaQXTR).  Please let me know if there
is anything that would prevent this from general inclusion in the next
release.

(patch against 2.4.5)

Thanks

Tim
-- 
Tim Hockin
Systems Software Engineer
Sun Microsystems, Cobalt Server Appliances
thockin@sun.com

[-- Attachment #2: cobalt-drivers-2.diff --]
[-- Type: text/plain, Size: 65797 bytes --]

diff -ruN dist-2.4.5/drivers/cobalt/README cobalt-2.4.5/drivers/cobalt/README
--- dist-2.4.5/drivers/cobalt/README	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/README	Thu May 31 14:32:15 2001
@@ -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 systype
+detected the specified generation.  This leaves the code free from #ifdef
+cruft, but lets the compiler throw out unsupported generation-specific code
+with if (0) detection.
+
+--
diff -ruN dist-2.4.5/drivers/cobalt/ruler.c cobalt-2.4.5/drivers/cobalt/ruler.c
--- dist-2.4.5/drivers/cobalt/ruler.c	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/ruler.c	Thu May 31 14:32:15 2001
@@ -0,0 +1,393 @@
+/* 
+ * cobalt ruler driver 
+ * Copyright (c) 2000, Cobalt Networks, Inc.
+ * $Id: ruler.c,v 1.10 2001/05/30 07:19:48 thockin Exp $
+ *
+ * author: asun@cobalt.com, thockin@sun.com
+ *
+ * This should be SMP safe.  There are two critical pieces of data, and thsu
+ * two locks.  The ruler_lock protects the arrays of channels(hwifs) and
+ * busproc function pointers.  These are only ever written in the
+ * register/unregister functions but read in several other places.  A
+ * read/write lock is appropriate.  The second lock is the lock on the sled
+ * led state and the I2C_DEV_RULER.  It gets called from timer context, so
+ * irqsave it. The global switches and sled_leds are atomic_t. --TPH
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/ide.h>
+#include <linux/hdreg.h>
+#include <linux/notifier.h>
+#include <linux/sysctl.h>
+#include <linux/reboot.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+#include <asm/io.h>
+#include <linux/cobalt.h>
+#include <linux/cobalt-systype.h>
+#include <linux/cobalt-i2c.h>
+#include <linux/cobalt-acpi.h>
+#include <linux/cobalt-led.h>
+
+#define RULER_TIMEOUT		(HZ >> 1)  /* .5s */
+#define MAX_COBT_DRIVES		4
+#define LED_SLED0		(1 << 3)
+#define LED_SLED1		(1 << 2)
+#define LED_SLED2		(1 << 1)
+#define LED_SLED3		(1 << 0)
+
+/* all of this is for gen V */
+static struct timer_list cobalt_ruler_timer;
+static rwlock_t ruler_lock = RW_LOCK_UNLOCKED;
+static spinlock_t rled_lock = SPIN_LOCK_UNLOCKED;
+static ide_hwif_t *channels[MAX_COBT_DRIVES];
+static ide_busproc_t *busprocs[MAX_COBT_DRIVES];
+/* NOTE: switches is a bitmask of DETACHED sleds */
+static atomic_t switches = ATOMIC_INIT(0);	
+static atomic_t sled_leds = ATOMIC_INIT(0);
+static int sled_led_map[] = {LED_SLED0, LED_SLED1, LED_SLED2, LED_SLED3};
+static int ruler_detect;
+
+static inline u8
+read_switches(void)
+{
+	u8 state = 0;
+	if (cobt_is_5k()) {
+		int tries = 3;
+
+		/* i2c can be busy, and this can read wrong - try a few times */
+		while (tries--) {
+			state = cobalt_i2c_read_byte(COBALT_I2C_DEV_DRV_SWITCH, 
+				0);
+			if ((state & 0xf0) != 0xf0) {
+				break;
+			}
+		}
+	}
+
+	return state;
+}
+
+/*
+ * deal with sled leds: LED on means OK to remove
+ * NOTE: all the reset lines are kept high. 
+ * NOTE: the reset lines are in the reverse order of the switches. 
+ */
+static void
+set_sled_leds(u8 leds)
+{
+	if (cobt_is_5k()) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&rled_lock, flags);
+
+		atomic_set(&sled_leds, leds);
+		leds |= 0xf0;
+		cobalt_i2c_write_byte(COBALT_I2C_DEV_RULER, 0, leds);
+
+		spin_unlock_irqrestore(&rled_lock, flags);
+	}
+}
+
+static inline u8
+get_sled_leds(void)
+{
+	return atomic_read(&sled_leds);
+}
+
+/* this must be called with the ruler_lock held for read */
+static int
+do_busproc(int idx, ide_hwif_t *hwif, int arg)
+{
+	if (cobt_is_5k()) {
+		/* sed sled LEDs */
+		switch (arg) {
+			case BUSSTATE_ON:
+				set_sled_leds(get_sled_leds() & 
+					~sled_led_map[idx]);
+				break;
+			case BUSSTATE_OFF:
+			case BUSSTATE_TRISTATE:
+				set_sled_leds(get_sled_leds() | 
+					sled_led_map[idx]);
+				break;
+			default:
+				WPRINTK("unknown busproc argument (%d)\n", arg);
+		}
+	}
+
+	/* do the real work */
+	return busprocs[idx](hwif, arg);
+}
+
+static void 
+ruler_timer_fn(unsigned long data)
+{
+	if (cobt_is_5k()) {
+		u8 state;
+		int i;
+		unsigned int now, expected, bit, swcur;
+
+		state = read_switches();
+		if ((state & 0xf0) == 0xf0) {
+			return;
+		}
+		swcur = atomic_read(&switches);
+	
+		state &= 0xf;
+		read_lock(&ruler_lock);
+		for (i = 0; i < MAX_COBT_DRIVES; i++) {
+		  	bit = 1 << i;
+			now = state & bit;
+			expected = swcur & bit;
+			if (now == expected) {
+				/* no changes to worry about */
+				continue;
+			}
+
+		  	if (now) {
+				/* a freshly detached drive */
+				atomic_set(&switches, swcur | bit);
+		    		if (channels[i]) {
+					printk("disabling ide ruler "
+						"channel %d\n", i);
+					do_busproc(i, channels[i], 
+						BUSSTATE_TRISTATE);
+		    		} else {
+					WPRINTK("drive detach on bad "
+						"channel (%d)\n", i);
+				}
+				set_sled_leds(get_sled_leds() | 
+					sled_led_map[i]);
+		  	} else {
+				/* 
+				 * do we want to do anything when a re-attach 
+				 * is detected?
+				 */
+			}
+		}
+		read_unlock(&ruler_lock);
+	}
+}
+
+static void 
+ruler_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	if (cobt_is_5k() && ruler_detect) {
+		u8 state;
+
+		state = read_switches();
+		if ((state & 0xf0) != 0xf0) {
+			/* this is protected inside mod_timer */
+			mod_timer(&cobalt_ruler_timer, jiffies + RULER_TIMEOUT);
+		}
+		 
+		/* empirical: delay enough to debounce */
+		udelay(10);
+	}
+}
+
+#if defined(CONFIG_COBALT_LED)
+/* figure which LEDs to blink */
+static unsigned int
+ide_led_handler(void *data)
+{
+	unsigned int leds = 0;
+
+	if (cobt_is_5k()) {
+		int i;
+		static int ledmap[MAX_COBT_DRIVES] = { 
+			LED_DISK0, LED_DISK1, LED_DISK2, LED_DISK3
+		};
+		static unsigned long old[MAX_COBT_DRIVES];
+
+		read_lock(&ruler_lock);
+
+		for (i = 0; i < MAX_COBT_DRIVES; i++) {
+			if (channels[i] && channels[i]->drives[0].present 
+			 && channels[i]->drives[0].service_start != old[i]) {
+				leds |= ledmap[i];
+				old[i] = channels[i]->drives[0].service_start;
+			}
+		}
+
+		read_unlock(&ruler_lock);
+	}
+
+	return leds;
+}
+#endif
+
+/* this is essentially an exported function - it is in the hwif structs */
+static int
+ruler_busproc_fn(ide_hwif_t *hwif, int arg)
+{
+	int r = 0;
+
+	if (cobt_is_5k()) {
+		int idx;
+
+		read_lock(&ruler_lock);
+
+		for (idx = 0; idx < MAX_COBT_DRIVES; idx++) {
+			if (channels[idx] == hwif) {
+				break;
+			}
+		}
+
+		if (idx >= MAX_COBT_DRIVES) {
+			/* not a hwif we manage? */
+			return 0;
+		}
+
+		r = do_busproc(idx, hwif, arg);
+	
+		read_unlock(&ruler_lock);
+	
+	}
+
+	return r;
+}
+	
+/* 
+ * We try to be VERY explicit here.  Fine for now, may eventually break down.
+ */
+void 
+cobalt_ruler_register(ide_hwif_t *hwif)
+{
+	if (cobt_is_5k()) {
+		struct pci_dev *dev;
+		int idx;
+		unsigned long flags;
+
+		if (!hwif) {
+			return;
+		}
+
+		/* Cobalt rulers only have HPT370 controllers on bus 1 */
+		dev = hwif->pci_dev;
+		if (dev->vendor != PCI_VENDOR_ID_TTI
+		 || dev->device != PCI_DEVICE_ID_TTI_HPT366
+		 || dev->bus->number != 1) {
+			/* ignore it */
+			return;
+		}
+
+		/* IDE ruler has controllers at dev 3 and 4, ONLY */
+		if (dev->devfn == PCI_DEVFN(3,0)) {
+			idx = hwif->channel;
+		} else if (dev->devfn == PCI_DEVFN(4,0)) {
+			idx = 2 + hwif->channel;
+		} else {
+			return;
+		}
+
+		if (idx >= MAX_COBT_DRIVES) {
+		       return;
+		}
+
+		write_lock_irqsave(&ruler_lock, flags);
+
+		/* save a pointer to the hwif, and trap it's busproc() */
+		channels[idx] = hwif;
+		if (hwif->busproc) {
+			busprocs[idx] = hwif->busproc;
+			hwif->busproc = ruler_busproc_fn;
+		}
+
+		write_unlock_irqrestore(&ruler_lock, flags);
+
+		/* the associated switch should be closed */
+		if (hwif->drives[0].present) {
+			/* set the sled LED off - not safe to remove */
+			set_sled_leds(get_sled_leds() & ~sled_led_map[idx]);
+		}
+	}
+}
+
+void 
+cobalt_ruler_unregister(ide_hwif_t *hwif)
+{
+	if (cobt_is_5k()) {
+		int i;
+		unsigned long flags;
+
+		write_lock_irqsave(&ruler_lock, flags);
+
+		for (i = 0; i < MAX_COBT_DRIVES; i++) {
+			if (channels[i] == hwif) {
+				channels[i] = NULL;
+				hwif->busproc = busprocs[i];
+				busprocs[i] = NULL;
+			}
+		}
+
+		write_unlock_irqrestore(&ruler_lock, flags);
+	}
+}
+
+void 
+cobalt_ruler_powerdown(void)
+{
+	if (cobt_is_5k()) {
+		unsigned long flags;
+		spin_lock_irqsave(&rled_lock, flags);
+		cobalt_i2c_write_byte(COBALT_I2C_DEV_RULER, 0, 0x0);
+		atomic_set(&sled_leds, 0);
+		spin_unlock_irqrestore(&rled_lock, flags);
+	}
+}
+
+int __init 
+cobalt_ruler_init(void)
+{
+	if (cobt_is_5k()) {
+		int err;
+		u8 tmp;
+
+		/* initialize switches */
+		tmp = read_switches();
+		ruler_detect = ((tmp & 0xf0) == 0xf0) ? 0 : 1;
+		tmp &= 0xf;
+		atomic_set(&switches, tmp);
+
+		/* initialize space for hwif variables */
+		memset(channels, 0, sizeof(channels));
+
+		/* initialize our timer */
+		init_timer(&cobalt_ruler_timer);
+		cobalt_ruler_timer.function = ruler_timer_fn;
+	
+		/* register an interrupt handler */
+		err = cobalt_acpi_register_handler(ruler_interrupt);
+		if (err) {
+			EPRINTK("can't register interrupt handler %p\n", 
+				ruler_interrupt);
+		}
+
+		/* set initial sled LED state */
+		set_sled_leds(LED_SLED0 | LED_SLED1 | LED_SLED2 | LED_SLED3);
+
+		/* register for a blinky LEDs callback */
+#if defined(CONFIG_COBALT_LED)
+		err = cobalt_fpled_register(ide_led_handler, NULL);
+		if (err) {
+			EPRINTK("can't register LED handler %p\n", 
+				ide_led_handler);
+		}
+#endif
+	
+		printk(KERN_INFO "Cobalt Networks ruler driver v1.2\n");
+	}
+
+	return 0;
+}
diff -ruN dist-2.4.5/drivers/cobalt/serialnum.c cobalt-2.4.5/drivers/cobalt/serialnum.c
--- dist-2.4.5/drivers/cobalt/serialnum.c	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/serialnum.c	Thu May 31 14:32:15 2001
@@ -0,0 +1,476 @@
+/* $Id: serialnum.c,v 1.4 2001/05/30 07:19:48 thockin Exp $ */
+/*
+ *
+ *   Author: Philip Gladstone, Axent Technologies
+ *           modified for Nat Semi PC[89]7317 by asun@cobalt.com
+ *           ported to 2.4.x by thockin@sun.com
+ *   Copyright (c) 2000  Axent Technologies, Cobalt Networks
+ *   Copyright (c) 2001  Axent Technologies, Sun Microsystems
+ *
+ *   This module interrogates the DS2401 Silicon Serial Number chip
+ *   that is attached to all x86 Cobalt systems.
+ *
+ *   It exports /proc/cobalt/hostid which is four bytes generated from of 
+ *   the id. It can be linked to /var/adm/hostid or /etc/hostid for the 
+ *   hostid command to use.
+ *
+ *   It exports /proc/cobalt/serialnumber which is the entire 64 bit value 
+ *   read back (in ascii).
+ *
+ *   For the guts of the 1 wire protocol used herein, please see the DS2401
+ *   specification.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * This driver is SMP safe by nature. --TPH
+ */
+#include <linux/config.h>
+#ifdef CONFIG_COBALT_SERNUM
+
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/cobalt.h>
+#include <linux/cobalt-systype.h>
+#include <linux/cobalt-superio.h>
+#include <linux/cobalt-serialnum.h>
+
+/* dependent on systype */
+static unsigned int sn_direction;
+static unsigned int sn_output;
+static unsigned int sn_input;
+static unsigned int sn_mask;
+
+/* 3k style systems */
+#define III_SN_DIRECTION	0x7d
+#define III_SN_OUTPUT		0x7e
+#define III_SN_INPUT		0x7f
+#define III_SN_MASK		0x08
+static struct pci_dev *id_dev;
+
+/* 5k style systems */
+#define V_SN_DIRECTION		(sn_io_base + 0x01)
+#define V_SN_OUTPUT		(sn_io_base + 0x00)
+#define V_SN_INPUT		(sn_io_base + 0x00)
+#define V_SN_MASK		(sn_io_base + 0x01)
+static unsigned int sn_io_base;
+
+#define SSN_SIZE     8	/* bytes */
+static char ssn_string[SSN_SIZE * 2 + 1];
+static unsigned long hostid;
+static int debug;
+#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_COBALT_OLDPROC
+static struct proc_dir_entry *proc_hostid;
+static struct proc_dir_entry *proc_serialnum;
+#endif
+static struct proc_dir_entry *proc_chostid;
+static struct proc_dir_entry *proc_cserialnum;
+#endif
+
+static int
+hostid_read(char *buf, char **start, off_t pos, int len, int *eof, void *x)
+{
+	int plen = sizeof(hostid);
+
+	if (pos >= plen) {
+		*eof = 1;
+		return 0;
+	}
+
+	memcpy(buf, &hostid, sizeof(hostid));
+	*start = buf + pos;
+	plen -= pos;
+
+	if (len > plen) {
+		return plen;
+	} else {
+		return len;
+	}
+}
+
+static int
+serialnum_read(char *buf, char **start, off_t pos, int len, int *eof, void *x)
+{
+	int plen = sizeof(ssn_string);
+
+	if (pos >= plen+1) {
+		*eof = 1;
+		return 0;
+	}
+
+	sprintf(buf, "%s\n", ssn_string);
+	*start = buf + pos;
+	plen -= pos;
+
+	if (len > plen) {
+		return plen;
+	} else {
+		return len;
+	}
+}
+
+/* set up the requisite IO bits */
+static int __init 
+io_init(void)
+{
+	unsigned char data;
+
+	if (cobt_is_3k()) {
+		/* The GPIO tied to the ID chip is on the PMU */
+		id_dev = pci_find_device(PCI_VENDOR_ID_AL, 
+			PCI_DEVICE_ID_AL_M7101, NULL);
+		if (!id_dev) {
+			EPRINTK("can't find PMU for serialnumber access\n");
+			return -ENXIO;
+		}
+
+		/* Set input mode on GPIO3 */
+		pci_read_config_byte(id_dev, sn_direction, &data);
+		if (debug > 1) {
+			WPRINTK("read of register 0x%x = 0x%x\n",
+				sn_direction, data);
+		}
+		if (data & sn_mask) {
+			pci_write_config_byte(id_dev, sn_direction, 
+				data & ~sn_mask);
+		}
+
+		/* Set the output value to be 0 */
+		pci_read_config_byte(id_dev, sn_output, &data);
+		if (debug > 1) {
+			WPRINTK("read of register 0x%x = 0x%x\n",
+				sn_output, data);
+		}
+		if (data & sn_mask) {
+			pci_write_config_byte(id_dev, sn_output, 
+				data & ~sn_mask);
+		}
+	} else if (cobt_is_5k()) {
+		u16 addr;
+		unsigned long flags;
+
+		spin_lock_irqsave(&cobalt_superio_lock, flags);
+		outb(SUPERIO_LOGICAL_DEV, SUPERIO_INDEX_PORT);
+		outb(SUPERIO_DEV_GPIO, SUPERIO_DATA_PORT);
+		outb(SUPERIO_ADDR_HIGH, SUPERIO_INDEX_PORT);
+		addr = inb(SUPERIO_DATA_PORT) << 8;
+		outb(SUPERIO_ADDR_LOW, SUPERIO_INDEX_PORT);
+		addr |= inb(SUPERIO_DATA_PORT);
+		spin_unlock_irqrestore(&cobalt_superio_lock, flags);
+		if (addr) {
+			u8 val;
+
+			sn_io_base = addr;
+
+			/* set output value to 0 */
+			val = inb(sn_direction);
+			outb(val | sn_mask, sn_direction);
+			data = inb(sn_output);
+			if (data & sn_mask) {
+				outb(data & ~sn_mask, sn_output);
+			}
+			/* set to input */
+			outb(val & ~sn_mask, sn_direction);
+		}
+	} else {
+		return -ENXIO;
+	}
+
+	/* Let things calm down */
+	udelay(500);
+	return 0;
+}
+
+/* write out a bit */
+static void __init
+io_write(int delay)
+{
+	if (cobt_is_3k()) {
+		unsigned char data;
+		/* Set output mode on GPIO3 */
+		pci_read_config_byte(id_dev, sn_direction, &data);
+		pci_write_config_byte(id_dev, sn_direction, data | sn_mask);
+		udelay(delay);
+
+		/* Set input mode */
+		pci_write_config_byte(id_dev, sn_direction, data & ~sn_mask);
+	} else if (cobt_is_5k()) {
+		unsigned char direction;
+
+		/* change to output and back */
+		direction = inb(sn_direction); 
+		outb(direction | sn_mask, sn_direction);
+		udelay(delay);
+		outb(direction & ~sn_mask, sn_direction);
+	}
+}
+
+/* read in a bit */
+static int __init
+io_read(void)
+{
+	unsigned char data = 0;
+
+	/* Get the input value */
+	if (cobt_is_3k()) {
+		pci_read_config_byte(id_dev, sn_input, &data);
+	} else if (cobt_is_5k()) {
+		data = inb(sn_input);
+	}
+
+	return (data & sn_mask) ? 1 : 0;
+}
+
+static void __init
+io_write_byte(unsigned char c)
+{
+	int i;
+	unsigned long flags;
+
+	save_flags(flags);
+
+	for (i = 0; i < 8; i++, c >>= 1) {
+		cli();
+		if (c & 1) {
+			/* Transmit a 1 */
+			io_write(5);
+			udelay(80);
+		} else {
+			/* Transmit a 0 */
+			io_write(80);
+			udelay(10);
+		}
+		restore_flags(flags);
+	}
+}
+
+static int __init
+io_read_byte(void)
+{
+	int i;
+	int c = 0;
+	unsigned long flags;
+
+	save_flags(flags);
+
+	for (i = 0; i < 8; i++) {
+		cli();
+		io_write(1);	/* Start the read */
+		udelay(2);
+		if (io_read()) {
+			c |= 1 << i;
+		}
+		udelay(60);
+		restore_flags(flags);
+	}
+
+	return c;
+}
+
+static int __init
+get_ssn(unsigned char *buf)
+{
+	int i;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	/* Master Reset Pulse */
+	for (i = 0; i < 600; i += 30) {
+		if (io_read()) {
+			break;
+		}
+	}
+
+	if (i >= 600) {
+		if (debug) {
+			EPRINTK("the data line seems to be held low\n");
+		}
+		return -ENXIO;
+	}
+
+	io_write(600);
+
+	for (i = 0; i < 300; i += 15) {
+		udelay(15);
+		if (io_read() == 0) {
+			/* We got a presence pulse */
+			udelay(600);	/* Wait for things to quiet down */
+			break;
+		}
+	}
+	restore_flags(flags);
+
+	if (i >= 300) {
+		if (debug)
+			EPRINTK("no presence pulse detected\n");
+		return -ENXIO;
+	}
+
+	io_write_byte(0x33);
+
+	for (i = 0; i < 8; i++) {
+		int rc;
+
+		rc = io_read_byte();
+		if (rc < 0) {
+			return rc;
+		}
+
+		*buf++ = rc;
+	}
+
+	return 0;
+}
+
+int __init
+cobalt_serialnum_init(void)
+{
+	char *version;
+	char vstring[32];
+	unsigned char ssn[SSN_SIZE];
+	int rc;
+	int i;
+
+	/* pick proper variables */
+	if (cobt_is_3k()) {
+		sn_direction = III_SN_DIRECTION;
+		sn_output = III_SN_OUTPUT;
+		sn_input = III_SN_INPUT;
+		sn_mask = III_SN_MASK;
+	} else if (cobt_is_5k()) {
+		sn_direction = V_SN_DIRECTION;
+		sn_output = V_SN_OUTPUT;
+		sn_input = V_SN_INPUT;
+		sn_mask = V_SN_MASK;
+	} else {
+		return -1;
+	}
+
+	/* get version from CVS */
+	version = strchr("$Revision: 1.4 $", ':') + 2;
+	if (version) {
+		char *p;
+
+		strncpy(vstring, version, sizeof(vstring));
+		if ((p = strchr(vstring, ' '))) {
+			*p = '\0';
+		}
+	} else {
+		strncpy(vstring, "unknown", sizeof(vstring));
+	}
+
+	/* set up for proper IO */
+	rc = io_init();
+	if (rc) {
+		return rc;
+	}
+
+	/*
+	 * NOTE: the below algorithm CAN NOT be changed.  We have many systems
+	 * out there registered with the serial number AS DERIVED by this
+	 * algorithm.
+	 */
+
+	rc = get_ssn(ssn);
+	if (rc) {
+		return rc;
+	}
+
+	/* Convert to ssn_string */
+	for (i = 7; i >= 0; i--) {
+		sprintf(ssn_string + (7 - i) * 2, "%02x", ssn[i]);
+	}
+
+	/* get four bytes for a pretty unique (not guaranteed) hostid */
+	hostid = *(unsigned long *)ssn ^ *(unsigned long *)(ssn+4);
+
+#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_COBALT_OLDPROC
+	proc_hostid = create_proc_read_entry("hostid", 0, NULL, 
+		hostid_read, NULL);
+	if (!proc_hostid) {
+		EPRINTK("can't create /proc/hostid\n");
+	}
+	proc_serialnum = create_proc_read_entry("serialnumber", 0, NULL,
+		serialnum_read, NULL);
+	if (!proc_serialnum) {
+		EPRINTK("can't create /proc/serialnumber\n");
+	}
+#endif
+	proc_chostid = create_proc_read_entry("hostid", 0, proc_cobalt, 
+		hostid_read, NULL);
+	if (!proc_chostid) {
+		EPRINTK("can't create /proc/cobalt/hostid\n");
+	}
+	proc_cserialnum = create_proc_read_entry("serialnumber", 0, 
+		proc_cobalt, serialnum_read, NULL);
+	if (!proc_cserialnum) {
+		EPRINTK("can't create /proc/cobalt/serialnumber\n");
+	}
+#endif
+
+	/* done */
+	printk(KERN_INFO "Cobalt Networks serial number version %s: %s\n", 
+		vstring, ssn_string);
+
+	return 0;
+}
+
+char *
+cobalt_serialnum_get(void)
+{
+	return ssn_string;
+}
+
+unsigned long
+cobalt_hostid_get(void)
+{
+	return hostid;
+}
+
+#ifdef MODULE
+MODULE_PARM(debug, "i");
+
+int
+init_module(void)
+{
+	return cobalt_serialnum_init();
+}
+
+void
+cleanup_module(void)
+{
+#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_COBALT_OLDPROC
+	remove_proc_entry("hostid", NULL);
+	remove_proc_entry("serialnumber", NULL);
+#endif
+	remove_proc_entry("hostid", proc_cobalt);
+	remove_proc_entry("serialnumber", proc_cobalt);
+#endif
+}
+#endif /* MODULE */
+
+#endif /* CONFIG_COBALT_SERNUM */
diff -ruN dist-2.4.5/drivers/cobalt/systype.c cobalt-2.4.5/drivers/cobalt/systype.c
--- dist-2.4.5/drivers/cobalt/systype.c	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/systype.c	Thu May 31 14:32:15 2001
@@ -0,0 +1,141 @@
+/*
+ * $Id: systype.c,v 1.4 2001/05/30 07:19:48 thockin Exp $
+ * systype.c : routines for figuring out which Cobalt system this is
+ *
+ * Copyright 2001 Sun Microsystems, Inc.
+ *
+ * By:  Tim Hockin
+ *	Adrian Sun
+ *	Duncan Laurie
+ *
+ * This driver is SMP safe by nature. --TPH
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_COBALT
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+
+#include <linux/cobalt.h>
+#include <linux/cobalt-systype.h>
+
+cobt_sys_t cobt_type = COBT_UNKNOWN;
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *proc_systype;
+#endif
+static int systype_read_proc(char *buf, char **start, off_t pos, int len,
+	int *eof, void *x);
+static char *systype_str(cobt_sys_t type);
+
+int __init 
+cobalt_systype_init(void)
+{
+	static int init_done = 0;
+	struct pci_dev *pdev;
+
+	if (init_done) {
+		return 0;
+	}
+	init_done = 1;
+
+#if defined(CONFIG_COBALT_GEN_III)
+	/* board identifier for RaQ3/4 vs Qube3 is on the PMU @ 0x7f */
+	pdev = pci_find_device(PCI_VENDOR_ID_AL,
+		PCI_DEVICE_ID_AL_M7101, NULL);
+	if (pdev) {
+		/* 
+		 * check to see what board we are on
+		 * ( RaQ 3, RaQ 4, Qube 3 )
+		 */
+		unsigned char val;
+
+		/* momentarily set DOGB# to input */
+		pci_read_config_byte(pdev, 0x7d, &val);
+		pci_write_config_byte(pdev, 0x7d, val & ~0x20);
+
+		/* read the GPIO register */
+		pci_read_config_byte(pdev, 0x7f, &val);
+		/* RaQ3/4 boards have DOGB (0x20) high, 
+		 * Qube3 has DOGB low */ 
+		cobt_type = (val & 0x20) ? COBT_RAQ3 : COBT_QUBE3;
+
+		/* change DOGB back to output */
+		pci_read_config_byte(pdev, 0x7d, &val);
+		pci_write_config_byte(pdev, 0x7d, val | 0x20);
+	}
+#endif
+#if defined(CONFIG_COBALT_GEN_V)
+	/* is it a gen V ? */
+	pdev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS,
+		PCI_DEVICE_ID_SERVERWORKS_LE, NULL);
+	if (pdev) {
+		cobt_type = COBT_RAQXTR;
+	}
+#endif /* CONFIG_COBALT_GEN */
+
+#ifdef CONFIG_PROC_FS
+	proc_systype = create_proc_read_entry("systype", S_IFREG | S_IRUGO, 
+		proc_cobalt, systype_read_proc, NULL);
+	if (!proc_systype) {
+		EPRINTK("can't create /proc/cobalt/systype\n");
+	}
+#endif
+
+	printk("Cobalt system type: ");
+	if (cobt_type == COBT_UNKNOWN) {
+		printk("unknown (trouble will ensue)\n");
+		return -1;
+	} else {
+		printk("%s\n", systype_str(cobt_type));
+	}
+
+	return 0;
+}
+
+static int 
+systype_read_proc(char *buf, char **start, off_t pos, int len,
+	int *eof, void *x)
+{
+	int plen = 0;
+
+	plen += sprintf(buf+plen, "%s\n", systype_str(cobt_type));
+
+        if (pos >= plen) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = buf + pos;
+	plen -= pos;
+
+	if (len > plen) {
+		return plen;
+	} else {
+		return len;
+	}
+}
+
+static char *
+systype_str(cobt_sys_t type)
+{
+	switch (type) {
+		case COBT_RAQ3:
+			return "RaQ 3/4";
+			break;
+		case COBT_QUBE3:
+			return "Qube 3";
+			break;
+		case COBT_RAQXTR:
+			return "RaQ XTR";
+			break;
+		case COBT_UNKNOWN:
+		default:
+			return "unknown";
+			break;
+	}
+}
+#endif /* CONFIG_COBALT */
diff -ruN dist-2.4.5/drivers/cobalt/thermal.c cobalt-2.4.5/drivers/cobalt/thermal.c
--- dist-2.4.5/drivers/cobalt/thermal.c	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/thermal.c	Thu May 31 14:32:15 2001
@@ -0,0 +1,144 @@
+/* $Id: thermal.c,v 1.3 2001/05/30 07:19:48 thockin Exp $
+ * Copyright (c) 2000-2001 Sun Microsystems, Inc 
+ *
+ * This is SMP safe.  The only protectable operationis a single I2C access,
+ * which gets protected by I2C.  This may change in the future with different
+ * thermal sensors. --TPH
+ */
+#include <linux/config.h>
+#ifdef CONFIG_COBALT_THERMAL
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/cobalt.h>
+#include <linux/cobalt-systype.h>
+#include <linux/cobalt-i2c.h>
+#include <linux/cobalt-thermal.h>
+#include <asm/io.h>
+
+unsigned int cobalt_thermal_id_max;
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *proc_therm;
+#endif
+
+static char *degrees(int);
+static int thermal_read_proc(char *buf, char **start, off_t pos, int len, 
+	int *eof, void *x);
+
+int __init 
+cobalt_thermal_init(void)
+{
+	if (cobt_is_3k()) {
+		cobalt_thermal_id_max = 0; /* 3k's have 1 thermal */
+	} else if (cobt_is_5k()) {
+		cobalt_thermal_id_max = 3; /* monterey has 4 temp sensors */
+	}
+
+#ifdef CONFIG_PROC_FS
+	/* make a file in /proc */
+	proc_therm = create_proc_read_entry("thermal_sensors", S_IFREG, 
+		proc_cobalt, thermal_read_proc, NULL);
+        if (!proc_therm) {
+		EPRINTK("can't create /proc/cobalt/thermal_sensors\n");
+	}
+#endif
+
+	printk("Cobalt Networks thermal sensors v1.4\n");
+
+	return 0;
+}
+
+#define LM77_TEMP		0x0
+#define LM77_STATUS		0x1
+#define LM77_HYST		0x2
+#define LM77_CRIT		0x3
+#define LM77_LOW		0x4
+#define LM77_HIGH		0x5
+
+#ifndef min
+#define min(a,b)  ((a) < (b) ? (a) : (b))
+#endif
+
+char *
+cobalt_temp_read(unsigned int sensor)
+{
+	int tmp;
+	int val = 0;
+	int tries = 2;
+	static int last_val = 0;
+
+	if (sensor > cobalt_thermal_id_max) {
+		return NULL;
+	}
+
+	/* sometimes it reads as zero... try again */
+	while (tries--) {
+		/* LM77 returns the bytes backwards - <shrug> */
+		/* address = base + deviceid + 1 for read */
+		tmp = cobalt_i2c_read_word(COBALT_I2C_DEV_TEMP +
+			(sensor<<1) + 1, LM77_TEMP);
+		if (tmp < 0) {
+			/* read failed, return the last known value */
+			return degrees(last_val);
+		}
+
+        	val = (tmp<<8 & 0xff00) + (tmp>>8 & 0x00ff);
+		if (val) {
+			last_val = val;
+			break;
+		}
+	}
+	return degrees(val);
+}
+
+/* build a string - measured in degrees C */
+static char *
+degrees(int val)
+{
+	static char temp[16] = "unknown";
+
+	sprintf(temp, "%d", val>>4);
+	if (val & 0x0008) {
+		sprintf(temp+strlen(temp), ".5");
+	}
+	return temp;
+}
+
+static int 
+thermal_read_proc(char *buf, char **start, off_t pos, int len, 
+	int *eof, void *x)
+{
+	int i;
+	int plen = 0;
+
+	for (i = 0; i <= cobalt_thermal_id_max; i++) {
+		char *c;
+		c = cobalt_temp_read(i);
+		if (!c) {
+			continue;
+		}
+		plen += sprintf(buf+plen, "%d: %s\n", i, c);
+	}
+
+	if (pos >= plen) {
+		*eof = 1;
+		return 0;
+	}
+
+	*start = buf + pos;
+	plen -= pos;
+
+	if (len > plen) {
+		return plen;
+	} else {
+		return len;
+	}
+}
+
+#endif /* CONFIG_COBALT_THERMAL */
diff -ruN dist-2.4.5/drivers/cobalt/wdt.c cobalt-2.4.5/drivers/cobalt/wdt.c
--- dist-2.4.5/drivers/cobalt/wdt.c	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/drivers/cobalt/wdt.c	Thu May 31 14:32:15 2001
@@ -0,0 +1,371 @@
+/* $Id: wdt.c,v 1.8 2001/05/30 22:34:57 thockin Exp $ */
+/* 
+ * Cobalt kernel WDT timer driver
+ * Tim Hockin <thockin@cobaltnet.com>
+ * Adrian Sun <asun@cobalt.com>
+ * Chris Johnson <cjohnson@cobalt.com>
+ * Copyright (c)1999-2000, Cobalt Networks
+ * Copyright (c)2001, Sun Microsystems
+ *
+ * This should be SMP safe.  Every external function (except trigger_reboot)
+ * grabs the wdt lock.  No function in this file may call any exported
+ * function (excepting trigger_reboot).  The disable counter is an atomic, so
+ * there should be no issues there. --TPH
+ */
+#include <linux/config.h>
+#ifdef CONFIG_COBALT_WDT
+
+#include <linux/module.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/cobalt.h>
+#include <linux/cobalt-systype.h>
+#include <linux/cobalt-wdt.h>
+#include <linux/cobalt-superio.h>
+#include <asm/io.h>
+#include <asm/msr.h>
+
+#define DOGB		0x20
+#define ALI_7101_WDT	0x92
+#define ALI_WDT_ARM	0x01
+#define WDT_3K_TIMEOUT (HZ >> 4)	/* 1/16 second */
+
+#define SUPERIO_TIMEOUT  (0x01)		/* 1 minute */
+#define WDT_5K_TIMEOUT (HZ << 3)	/* 8 seconds */
+
+struct timer_list cobalt_wdt_timer;
+static unsigned long wdt_timeout;
+static unsigned long long tsc_per_wdt;
+static atomic_t cobalt_wdt_disable_count = ATOMIC_INIT(0);
+static int initialized;
+static spinlock_t wdt_lock = SPIN_LOCK_UNLOCKED;
+
+/* gen III */
+static struct pci_dev *cobalt_pmu;
+static int use_pic;
+/* gen V */
+static u16 superio_pm_port;
+
+static void do_refresh(void);
+static void do_cleardog(void);
+static void do_disable(void);
+static void do_reenable(void);
+	
+static unsigned long  __init 
+chipset_setup(void)
+{
+	if (cobt_is_3k()) {
+		/* 
+		 * Set up the PMU for 3k boards. It has a max
+		 * of a 1 second timeout. 
+		 */
+		struct pci_dev *south;
+		char tmp;
+
+		/* PMU (1543 ver A1-E) has a built-in WDT.  Set it to 1 sec */
+		cobalt_pmu = pci_find_device(PCI_VENDOR_ID_AL, 
+			PCI_DEVICE_ID_AL_M7101, NULL);
+		if (!cobalt_pmu) {
+			EPRINTK("can't find south bridge for WDT\n");
+			return 0;
+		}
+		pci_write_config_byte(cobalt_pmu, ALI_7101_WDT, 0x02);
+	
+		/* why it is called 1543, but DevId is 1533 I'll never know */
+		south = pci_find_device(PCI_VENDOR_ID_AL, 
+			PCI_DEVICE_ID_AL_M1533, NULL);
+		if (!south) {
+			EPRINTK("can't find south bridge for WDT\n");
+			use_pic = 1;
+		} else {
+		        /* reversion # is here - must match ???1001?(b)
+			 * else use PIC for WDT */
+			pci_read_config_byte(south, 0x5e, &tmp);
+			use_pic = ((tmp & 0x1e) != 0x12);
+		}
+
+		if (!use_pic) {
+			/* set DOGB GPIO pin to OUTPUT - JIC */
+			pci_read_config_byte(cobalt_pmu, 0x7d, &tmp);
+			pci_write_config_byte(cobalt_pmu, 0x7d, tmp | DOGB);
+		}
+		return WDT_3K_TIMEOUT;
+	} else if (cobt_is_5k()) {
+		unsigned long flags;
+
+		/* 
+		 * Set up the Nat. Semi SuperI/O for Monterey. It has a 
+		 * minimum of a 1 minute timeout. 
+		 */
+	
+		spin_lock_irqsave(&cobalt_superio_lock, flags);
+		
+		/* superi/o -- select pm logical device and get base address */
+		outb(SUPERIO_LOGICAL_DEV, SUPERIO_INDEX_PORT);
+		outb(SUPERIO_DEV_PM, SUPERIO_DATA_PORT);
+		outb(SUPERIO_ADDR_HIGH, SUPERIO_INDEX_PORT);
+		superio_pm_port = inb(SUPERIO_DATA_PORT) << 8;
+		outb(SUPERIO_ADDR_LOW, SUPERIO_INDEX_PORT);
+		superio_pm_port |= inb(SUPERIO_DATA_PORT); 
+		if (superio_pm_port) {
+			outb(SUPERIO_WDT_DEV, superio_pm_port);
+			outb(SUPERIO_TIMEOUT, superio_pm_port + 1); 
+		}
+	
+		spin_unlock_irqrestore(&cobalt_superio_lock, flags);
+
+		return WDT_5K_TIMEOUT;
+	}
+
+	return 0;
+}
+
+void __init 
+cobalt_wdt_init(void)
+{
+	unsigned long long start, stop;
+
+	wdt_timeout = chipset_setup();
+
+	/* figure out time */
+	rdtscll(start);
+	udelay(100);
+	rdtscll(stop);
+
+	/*
+	 * (int) (stop - start) * 10 == tsc per msec
+	 * 1000 / HZ == msec per tick
+	 * wdt_timeout == ticks per watchdog rearm
+	 */
+	tsc_per_wdt = (int) (stop - start) * 10 * (1000 * wdt_timeout / HZ);
+
+#if !defined(CONFIG_COBALT_BOOTLOADER)
+	/* set the timer going */
+	init_timer(&cobalt_wdt_timer);
+	cobalt_wdt_timer.function = cobalt_wdt_refresh;
+	cobalt_wdt_timer.data = 1;
+	cobalt_wdt_timer.expires = jiffies + wdt_timeout;
+	add_timer(&cobalt_wdt_timer);
+
+	/* the first timer tick will set it going */
+	printk("Cobalt Networks watchdog v1.4");
+	if (cobt_is_3k() && use_pic) {
+		printk(" (old revision board: using PIC controller)");
+	}
+	printk("\n");
+#endif /* BOOTLOADER */
+
+	initialized = 1;
+}
+
+static inline void
+hw_disable(void)
+{
+	if (cobt_is_3k()) {
+		char tmp;
+
+		/* read the value, disable (reset) WDT */
+		pci_read_config_byte(cobalt_pmu, ALI_7101_WDT, &tmp);
+		pci_write_config_byte(cobalt_pmu, ALI_7101_WDT, 
+			(tmp & ~ALI_WDT_ARM));
+	} else if (cobt_is_5k()) {
+		unsigned long flags;
+		spin_lock_irqsave(&cobalt_superio_lock, flags);
+		outb(SUPERIO_WDT_DEV, superio_pm_port);
+		outb(0x0, superio_pm_port + 1);
+		spin_unlock_irqrestore(&cobalt_superio_lock, flags);
+	}
+}
+
+static inline void
+hw_enable(void)
+{
+	if (cobt_is_3k()) {
+		unsigned char tmp;
+		/* read the value, disable (reset) WDT, enable WDT */
+		pci_read_config_byte(cobalt_pmu, ALI_7101_WDT, &tmp);
+		pci_write_config_byte(cobalt_pmu, ALI_7101_WDT, 
+			(tmp | ALI_WDT_ARM));
+		if (use_pic) {
+			/* transition GPIO 5 (DOGB) to arm/clear timer */
+			pci_read_config_byte(cobalt_pmu, 0x7e, &tmp);
+			pci_write_config_byte(cobalt_pmu, 0x7e, tmp ^ DOGB);
+		}
+	} else if (cobt_is_5k()) {
+		unsigned long flags;
+		spin_lock_irqsave(&cobalt_superio_lock, flags);
+		outb(SUPERIO_WDT_DEV, superio_pm_port);
+		outb(SUPERIO_TIMEOUT, superio_pm_port + 1);
+		spin_unlock_irqrestore(&cobalt_superio_lock, flags);
+	}
+}
+
+static void
+do_refresh(void)
+{
+	if (!initialized) {
+		return;
+	}
+
+	do_cleardog();
+	
+	/* re-arm the timer - this is locked in mod_timer() */
+	mod_timer(&cobalt_wdt_timer, jiffies + wdt_timeout);
+}
+
+EXPORT_SYMBOL(cobalt_wdt_refresh);
+void 
+cobalt_wdt_refresh(unsigned long refresh_timer)
+{
+	unsigned long flags;
+
+#ifdef CONFIG_COBALT_BOOTLOADER
+	return;
+#endif
+
+	spin_lock_irqsave(&wdt_lock, flags);
+	do_refresh();
+	spin_unlock_irqrestore(&wdt_lock, flags);
+}
+
+static void
+do_cleardog(void)
+{
+	static unsigned long long last_tsc = 0;
+	unsigned long long tmp;
+
+	if (!initialized || (atomic_read(&cobalt_wdt_disable_count) > 0)) {
+		return;
+	}
+
+	/* only bother if we're due */
+	rdtscll(tmp);
+	if ((int)(tmp - last_tsc) < tsc_per_wdt) {
+		return;
+	}
+
+	hw_disable();
+	hw_enable();
+
+	rdtscll(last_tsc);
+}
+
+EXPORT_SYMBOL(cobalt_wdt_cleardog);
+void 
+cobalt_wdt_cleardog(void)
+{
+	unsigned long flags;
+
+#ifdef CONFIG_COBALT_BOOTLOADER
+	return;
+#endif
+
+	spin_lock_irqsave(&wdt_lock, flags);
+	do_cleardog();
+	spin_unlock_irqrestore(&wdt_lock, flags);
+}
+
+/* 
+ * this is called from machine_restart. it should not be used on
+ * 5k machines. 
+ */
+EXPORT_SYMBOL(cobalt_wdt_trigger_reboot);
+void 
+cobalt_wdt_trigger_reboot(void)
+{
+	if (cobt_is_3k()) {
+		if (!cobalt_pmu) {
+			WPRINTK("no PMU found!\n");
+			WPRINTK("reboot not possible!\n");
+			return;
+		}
+
+#if !defined(CONFIG_COBALT_BOOTLOADER)
+		/* stop feeding it */
+		del_timer(&cobalt_wdt_timer);
+#endif
+
+		/* kiss your rear goodbye... */
+		initialized = 0;
+		hw_disable();
+		hw_enable();
+	}
+}
+
+static void
+do_disable(void)
+{
+	if (!initialized) {
+		return;
+	}
+
+	if (atomic_read(&cobalt_wdt_disable_count) == 0) {
+		atomic_inc(&cobalt_wdt_disable_count);
+		del_timer(&cobalt_wdt_timer);
+		hw_disable();
+	}
+}
+
+EXPORT_SYMBOL(cobalt_wdt_disable);
+void 
+cobalt_wdt_disable(void)
+{
+	unsigned long flags;
+
+#ifdef CONFIG_COBALT_BOOTLOADER
+	return;
+#endif
+
+	if (cobt_is_3k() && use_pic) {
+		WPRINTK("in PIC mode - cannot disable\n");
+		return;
+	}
+
+	spin_lock_irqsave(&wdt_lock, flags);
+	do_disable();
+	spin_unlock_irqrestore(&wdt_lock, flags);
+}
+
+static void
+do_reenable(void)
+{
+	int dcnt;
+
+	if (!initialized) { 
+		return;
+	}
+
+	atomic_dec(&cobalt_wdt_disable_count);
+	dcnt = atomic_read(&cobalt_wdt_disable_count);
+
+	if (dcnt == 0) {
+		do_refresh();
+	} else if (dcnt < 0) {
+		WPRINTK("too many enables\n");
+		atomic_set(&cobalt_wdt_disable_count, 0);
+	}
+}
+
+
+EXPORT_SYMBOL(cobalt_wdt_reenable);
+void 
+cobalt_wdt_reenable(void)
+{
+	unsigned long flags;
+
+#ifdef CONFIG_COBALT_BOOTLOADER
+	return;
+#endif
+
+	spin_lock_irqsave(&wdt_lock, flags);
+	do_reenable();
+	spin_unlock_irqrestore(&wdt_lock, flags);
+}
+
+#endif /* CONFIG_COBALT_WDT */
diff -ruN dist-2.4.5/include/linux/cobalt-acpi.h cobalt-2.4.5/include/linux/cobalt-acpi.h
--- dist-2.4.5/include/linux/cobalt-acpi.h	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/include/linux/cobalt-acpi.h	Thu May 31 14:33:16 2001
@@ -0,0 +1,16 @@
+/*
+ * $Id: $
+ * cobalt-acpi.h : support for ACPI interrupts
+ *
+ * Copyright 2000 Cobalt Networks, Inc.
+ * Copyright 2001 Sun Microsystems, Inc.
+ */
+#ifndef COBALT_ACPI_H
+#define COBALT_ACPI_H
+
+extern int cobalt_acpi_register_handler(void (*function)
+	(int, void *, struct pt_regs *));
+extern int cobalt_acpi_unregister_handler(void (*function)
+	(int, void *, struct pt_regs *));
+
+#endif /* COBALT_ACPI_H */
diff -ruN dist-2.4.5/include/linux/cobalt.h cobalt-2.4.5/include/linux/cobalt.h
--- dist-2.4.5/include/linux/cobalt.h	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/include/linux/cobalt.h	Thu May 31 14:33:16 2001
@@ -0,0 +1,76 @@
+/* $Id: cobalt.h,v 1.5 2001/05/30 07:19:48 thockin Exp $ */
+/* Copyright 2001 Sun Microsystems, Inc. */
+#ifndef COBALT_H
+#define COBALT_H
+
+/* generational support - for easy checking */
+#ifdef CONFIG_COBALT_GEN_III
+# define COBT_SUPPORT_GEN_III 1
+#else
+# define COBT_SUPPORT_GEN_III 0
+#endif
+
+#ifdef CONFIG_COBALT_GEN_V
+# define COBT_SUPPORT_GEN_V 1
+#else
+# define COBT_SUPPORT_GEN_V 0
+#endif
+
+/* keep this test up to date with new generation defines */
+#if !defined(CONFIG_COBALT_GEN_III) && !defined(CONFIG_COBALT_GEN_V)
+/* barf if no generation has been selected */
+#error You asked for CONFIG_COBALT, but no CONFIG_COBALT_GEN !
+#endif
+
+/* macros for consistent errors/warnings */
+#define EPRINTK(fmt, args...)  \
+	printk(KERN_ERR "%s:%s" fmt , __FILE__ , __FUNCTION__ , ##args)
+
+#define WPRINTK(fmt, args...)  \
+	printk(KERN_WARNING "%s:%s" fmt , __FILE__ , __FUNCTION__ , ##args)
+
+/* accesses for CMOS */
+#include <linux/mc146818rtc.h>
+#define CMOS_FLAG_MIN           0x0001
+#define CMOS_SYSFAULT_FLAG      0x0020
+#define CMOS_OOPSPANIC_FLAG     0x0040
+#define CMOS_FLAG_MAX           0x0040
+
+#define CMOS_ADDR_PORT          0x70
+#define CMOS_DATA_PORT          0x71
+#define CMOS_INFO_MAX           0xff
+#define CMOS_FLAG_BYTE_0        0x10
+#define CMOS_FLAG_BYTE_1        0x11
+
+static inline u8
+cobalt_cmos_read_byte(int index)
+{
+	u8 data;
+	unsigned long flags;
+
+	if (index > CMOS_INFO_MAX)
+		return 0;
+
+	spin_lock_irqsave(&rtc_lock, flags);
+	outb(index, CMOS_ADDR_PORT);
+	data = inb(CMOS_DATA_PORT);
+	spin_unlock_irqrestore(&rtc_lock, flags);
+
+	return data;
+}
+
+static inline int
+cobalt_cmos_read_flag(const unsigned int flag)
+{
+	u16 cmosfl;
+
+	cmosfl = cobalt_cmos_read_byte(CMOS_FLAG_BYTE_0) << 8;
+	cmosfl |= cobalt_cmos_read_byte(CMOS_FLAG_BYTE_1);
+
+	return (cmosfl & flag) ? 1 : 0;
+}
+
+/* the root of /proc/cobalt */
+extern struct proc_dir_entry *proc_cobalt;
+
+#endif
diff -ruN dist-2.4.5/include/linux/cobalt-i2c.h cobalt-2.4.5/include/linux/cobalt-i2c.h
--- dist-2.4.5/include/linux/cobalt-i2c.h	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/include/linux/cobalt-i2c.h	Thu May 31 14:33:16 2001
@@ -0,0 +1,37 @@
+/*
+ * $Id: cobalt-i2c.h,v 1.1 2001/03/07 01:58:24 thockin Exp $
+ * cobalt-i2c.h : I2C support for LCD/Front Panel
+ *
+ * Copyright 2000 Cobalt Networks, Inc.
+ * Copyright 2001 Sun Microsystems, Inc.
+ */
+#ifndef COBALT_I2C_H
+#define COBALT_I2C_H
+
+#include <linux/types.h>
+#include <linux/cobalt.h>
+
+#define COBALT_I2C_DEV_LED_I		0x40
+#define COBALT_I2C_DEV_LED_II		0x42
+#define COBALT_I2C_DEV_LCD_DATA		0x4a
+#define COBALT_I2C_DEV_LCD_INST		0x48
+#define COBALT_I2C_DEV_FP_BUTTONS	0x41
+#define COBALT_I2C_DEV_DRV_SWITCH	0x45
+#define COBALT_I2C_DEV_RULER		0x46
+#define COBALT_I2C_DEV_TEMP		0x90
+#define COBALT_I2C_READ			0x01
+#define COBALT_I2C_WRITE		0x00
+
+extern int cobalt_i2c_reset(void);
+extern int cobalt_i2c_read_byte(const int dev, const int index);
+extern int cobalt_i2c_read_word(const int dev, const int index);
+extern int cobalt_i2c_read_block(const int dev, const int index,
+				 unsigned char *data, int count);
+extern int cobalt_i2c_write_byte(const int dev, const int index,
+				 const u8 val);
+extern int cobalt_i2c_write_word(const int dev, const int index,
+				 const u16 val);
+extern int cobalt_i2c_write_block(const int dev, const int index,
+				  unsigned char *data, int count);
+
+#endif /* COBALT_I2C_H */
diff -ruN dist-2.4.5/include/linux/cobalt-lcd.h cobalt-2.4.5/include/linux/cobalt-lcd.h
--- dist-2.4.5/include/linux/cobalt-lcd.h	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/include/linux/cobalt-lcd.h	Thu May 31 14:33:16 2001
@@ -0,0 +1,90 @@
+/*
+ * $Id: cobalt-lcd.h,v 1.2 2001/04/11 03:53:41 thockin Exp $
+ * cobalt-lcd.h : some useful defines for the Cobalt LCD driver
+ *                (must be useable from both kernel and user space)
+ *
+ * Copyright 1996-2000 Cobalt Networks, Inc.
+ * Copyright 2001 Sun Microsystems, Inc.
+ *
+ * By:	Andrew Bose	
+ *	Timothy Stonis (x86 version)
+ *	Tim Hockin
+ *	Adrian Sun
+ *	Erik Gilling
+ *	Duncan Laurie
+ */
+#ifndef COBALT_LCD_H
+#define COBALT_LCD_H
+
+#include <linux/cobalt.h>
+#include <linux/cobalt-led.h>
+
+/* this is the generic device minor assigned to /dev/lcd */
+#define COBALT_LCD_MINOR	156
+#define COBALT_LCD_LINELEN	40
+
+struct lcd_display {
+        unsigned long buttons;
+        int size1;
+        int size2;
+        unsigned char line1[COBALT_LCD_LINELEN];
+        unsigned char line2[COBALT_LCD_LINELEN];
+        unsigned char cursor_address;
+        unsigned char character;
+        unsigned char leds;
+        unsigned char *RomImage;
+};
+
+/* different lcd types */
+#define LCD_TYPE_UNKNOWN	0
+#define LCD_TYPE_PARALLEL	1
+#define LCD_TYPE_PARALLEL_B	2
+#define LCD_TYPE_I2C		3
+
+/* Function command codes for ioctl */
+#define LCD_On			1
+#define LCD_Off			2
+#define LCD_Clear		3
+#define LCD_Reset		4
+#define LCD_Cursor_Left		5
+#define LCD_Cursor_Right	6
+#define LCD_Disp_Left		7
+#define LCD_Disp_Right		8
+#define LCD_Get_Cursor		9
+#define LCD_Set_Cursor		10
+#define LCD_Home		11
+#define LCD_Read		12		
+#define LCD_Write		13	
+#define LCD_Cursor_Off		14
+#define LCD_Cursor_On		15
+#define LCD_Get_Cursor_Pos	16
+#define LCD_Set_Cursor_Pos	17
+#define LCD_Blink_Off		18
+#define LCD_Raw_Inst		19
+#define LCD_Raw_Data		20
+#define LCD_Type                21
+
+/* LED controls */
+#define LED_Set			40	
+#define LED_Bit_Set		41
+#define LED_Bit_Clear		42
+#define LED32_Set		43	
+#define LED32_Bit_Set		44
+#define LED32_Bit_Clear		45
+
+/* button ioctls */
+#define BUTTON_Read		50  
+
+/* Button defs */
+#define BUTTON_Next		0x3D
+#define BUTTON_Next_B		0x7E
+#define BUTTON_Reset_B		0xFC
+#define BUTTON_NONE_B           0xFE
+#define BUTTON_Left_B           0xFA
+#define BUTTON_Right_B          0xDE
+#define BUTTON_Up_B             0xF6
+#define BUTTON_Down_B           0xEE
+#define BUTTON_Enter_B          0xBE
+#define BUTTON_Power            0x01
+
+#endif /* COBALT_LCD_H */
diff -ruN dist-2.4.5/include/linux/cobalt-led.h cobalt-2.4.5/include/linux/cobalt-led.h
--- dist-2.4.5/include/linux/cobalt-led.h	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/include/linux/cobalt-led.h	Thu May 31 14:33:16 2001
@@ -0,0 +1,45 @@
+/*
+ * $Id: cobalt-led.h,v 1.1 2001/04/11 03:53:41 thockin Exp $
+ * cobalt-led.c
+ *
+ * Copyright 1996-2000 Cobalt Networks, Inc.
+ * Copyright 2001 Sun Microsystems, Inc.
+ *
+ * By:	Andrew Bose	
+ *	Timothy Stonis (x86 version)
+ *	Tim Hockin
+ *	Adrian Sun
+ *	Erik Gilling
+ *	Duncan Laurie
+ */
+#ifndef COBALT_LED_H
+#define COBALT_LED_H
+
+#include <linux/ide.h>
+
+/* the set of all leds available on Cobalt systems */
+#define LED_SHUTDOWN		(1 << 0)
+#define LED_WEBLIGHT		(1 << 1)
+#define LED_COBALTLOGO		(1 << 2)
+#define LED_ETH0_TXRX		(1 << 3)
+#define LED_ETH0_LINK		(1 << 4)
+#define LED_ETH1_TXRX		(1 << 5)
+#define LED_ETH1_LINK		(1 << 6)
+#define LED_DISK0		(1 << 7)
+#define LED_DISK1		(1 << 8)
+#define LED_DISK2		(1 << 9)
+#define LED_DISK3		(1 << 10)
+#define LED_SYSFAULT		(1 << 11)
+#define LED_HEART		(1 << 12)
+#define LED_SPARE		(1 << 13)
+
+#ifdef __KERNEL__
+extern void cobalt_led_set(const unsigned int leds);
+extern void cobalt_led_set_lazy(const unsigned int leds);
+extern unsigned int cobalt_led_get(void);
+extern int cobalt_fpled_register(unsigned int (*fn)(void *), void *data);
+extern int cobalt_fpled_unregister(unsigned int (*fn)(void *), void *data);
+#endif
+
+#endif /* COBALT_LED_H */
+
diff -ruN dist-2.4.5/include/linux/cobalt-misc.h cobalt-2.4.5/include/linux/cobalt-misc.h
--- dist-2.4.5/include/linux/cobalt-misc.h	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/include/linux/cobalt-misc.h	Thu May 31 14:33:16 2001
@@ -0,0 +1,11 @@
+/* $Id: $ */
+/* Copyright 2001 Sun Microsystems, Inc. */
+#ifndef COBALT_MISC_H
+#define COBALT_MISC_H
+
+void cobalt_nmi(unsigned char reason, struct pt_regs *regs);
+void cobalt_restart(void);
+void cobalt_halt(void);
+void cobalt_power_off(void);
+
+#endif
diff -ruN dist-2.4.5/include/linux/cobalt-net.h cobalt-2.4.5/include/linux/cobalt-net.h
--- dist-2.4.5/include/linux/cobalt-net.h	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/include/linux/cobalt-net.h	Thu May 31 14:33:16 2001
@@ -0,0 +1,11 @@
+/* $Id: cobalt-net.h,v 1.1 2001/04/13 02:15:35 thockin Exp $ */
+/* Copyright 2001 Sun Microsystems, Inc. */
+#ifndef COBALT_NET_H
+#define COBALT_NET_H
+
+#include <linux/netdevice.h>
+
+void cobalt_net_register(struct net_device *ndev);
+void cobalt_net_unregister(struct net_device *ndev);
+
+#endif
diff -ruN dist-2.4.5/include/linux/cobalt-ruler.h cobalt-2.4.5/include/linux/cobalt-ruler.h
--- dist-2.4.5/include/linux/cobalt-ruler.h	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/include/linux/cobalt-ruler.h	Thu May 31 14:33:16 2001
@@ -0,0 +1,12 @@
+/* $Id: cobalt-ruler.h,v 1.2 2001/04/27 03:13:53 thockin Exp $ */
+/* Copyright 2001 Sun Microsystems, Inc. */
+#ifndef COBALT_RULER_H
+#define COBALT_RULER_H
+
+#include <linux/ide.h>
+
+void cobalt_ruler_register(ide_hwif_t *hwif);
+void cobalt_ruler_unregister(ide_hwif_t *hwif);
+void cobalt_ruler_powerdown(void);
+
+#endif
diff -ruN dist-2.4.5/include/linux/cobalt-serialnum.h cobalt-2.4.5/include/linux/cobalt-serialnum.h
--- dist-2.4.5/include/linux/cobalt-serialnum.h	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/include/linux/cobalt-serialnum.h	Thu May 31 14:33:16 2001
@@ -0,0 +1,16 @@
+/*
+ * $Id: $
+ * cobalt-serialnum.h : access to the DS2401 serial number
+ *
+ * Copyright 2000 Cobalt Networks, Inc.
+ * Copyright 2001 Sun Microsystems, Inc.
+ */
+#ifndef COBALT_SERIALNUM_H
+#define COBALT_SERIALNUM_H
+
+#include <linux/cobalt.h>
+
+char *cobalt_serialnum_get(void);
+unsigned long cobalt_hostid_get(void);
+
+#endif /* COBALT_SERIALNUM_H */
diff -ruN dist-2.4.5/include/linux/cobalt-superio.h cobalt-2.4.5/include/linux/cobalt-superio.h
--- dist-2.4.5/include/linux/cobalt-superio.h	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/include/linux/cobalt-superio.h	Thu May 31 14:33:16 2001
@@ -0,0 +1,41 @@
+/*
+ * $Id: cobalt-superio.h,v 1.1 2001/03/07 01:58:24 thockin Exp $
+ * cobalt-superio.h : SuperIO support for Sun/Cobalt servers
+ *
+ * Copyright 2000 Cobalt Networks, Inc.
+ * Copyright 2001 Sun Microsystems, Inc.
+ */
+#ifndef COBALT_SUPERIO_H
+#define COBALT_SUPERIO_H
+
+#include <linux/cobalt.h>
+
+#define SUPERIO_INDEX_PORT      0x2e
+#define SUPERIO_DATA_PORT       0x2f
+
+#define SUPERIO_LOGICAL_DEV     0x07
+#define SUPERIO_DEV_RTC         0x02
+#define SUPERIO_DEV_GPIO        0x07
+#define SUPERIO_DEV_PM          0x08
+
+#define SUPERIO_ACTIVATE        0x30
+#define SUPERIO_ADDR_HIGH       0x60
+#define SUPERIO_ADDR_LOW        0x61
+#define SUPERIO_INTR_PIN        0x70
+#define SUPERIO_INTR_TYPE       0x71
+
+#define SUPERIO_APCR1           0x40
+#define SUPERIO_APCR4           0x4a
+
+#define SUPERIO_RTC_CRA         0x0a
+#define SUPERIO_RTC_BANK_0      0x20
+#define SUPERIO_RTC_BANK_2      0x40
+
+#define SUPERIO_PWRBUTTON       0x01
+#define SUPERIO_PM1_STATUS      0x01
+
+#define SUPERIO_WDT_DEV         0x05
+
+extern spinlock_t cobalt_superio_lock;
+
+#endif /* COBALT_SUPERIO_H */
diff -ruN dist-2.4.5/include/linux/cobalt-systype.h cobalt-2.4.5/include/linux/cobalt-systype.h
--- dist-2.4.5/include/linux/cobalt-systype.h	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/include/linux/cobalt-systype.h	Thu May 31 14:33:16 2001
@@ -0,0 +1,29 @@
+/*
+ * $Id: cobalt-systype.h,v 1.1 2001/03/07 01:58:24 thockin Exp $
+ * cobalt-systype.h : figure out what Cobalt system we are on
+ *
+ * Copyright 2000 Cobalt Networks, Inc.
+ * Copyright 2001 Sun Microsystems, Inc.
+ */
+#ifndef COBALT_SYSTYPE_H
+#define COBALT_SYSTYPE_H
+
+#include <linux/cobalt.h>
+
+typedef enum {
+	COBT_UNKNOWN,
+	COBT_RAQ3,
+	COBT_QUBE3,
+	COBT_RAQXTR,
+} cobt_sys_t;
+
+extern cobt_sys_t cobt_type;
+/* one for each major board-type - COBT_SUPPORT_* from <linux/cobalt.h> */
+#define cobt_is_raq3()	 (COBT_SUPPORT_GEN_III && cobt_type == COBT_RAQ3)
+#define cobt_is_qube3()	 (COBT_SUPPORT_GEN_III && cobt_type == COBT_QUBE3)
+#define cobt_is_raqxtr() (COBT_SUPPORT_GEN_V && cobt_type == COBT_RAQXTR)
+/* one for each major generation */
+#define cobt_is_3k()	 (cobt_is_raq3() || cobt_is_qube3())
+#define cobt_is_5k()	 (cobt_is_raqxtr())
+
+#endif
diff -ruN dist-2.4.5/include/linux/cobalt-thermal.h cobalt-2.4.5/include/linux/cobalt-thermal.h
--- dist-2.4.5/include/linux/cobalt-thermal.h	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/include/linux/cobalt-thermal.h	Thu May 31 14:33:16 2001
@@ -0,0 +1,12 @@
+/* $Id: $ */
+/* Copyright 2001 Sun Microsystems, Inc. */
+#ifndef COBALT_THERMAL_H
+#define COBALT_THERMAL_H
+
+#include <linux/cobalt.h>
+
+extern unsigned int cobalt_thermal_id_max;
+
+char *cobalt_temp_read(unsigned int sensor);
+
+#endif
diff -ruN dist-2.4.5/include/linux/cobalt-wdt.h cobalt-2.4.5/include/linux/cobalt-wdt.h
--- dist-2.4.5/include/linux/cobalt-wdt.h	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/include/linux/cobalt-wdt.h	Thu May 31 14:33:16 2001
@@ -0,0 +1,16 @@
+/* $Id: $ */
+/* Copyright 2001 Sun Microsystems, Inc. */
+#ifndef COBALT_WDT_H
+#define COBALT_WDT_H
+
+#include <linux/cobalt.h>
+
+void cobalt_wdt_refresh(unsigned long refresh_timer);
+void cobalt_wdt_trigger_reboot(void);
+
+void cobalt_wdt_disable(void);
+void cobalt_wdt_reenable(void);
+
+void cobalt_wdt_cleardog(void);
+
+#endif
diff -ruN dist-2.4.5/arch/i386/config.in cobalt-2.4.5/arch/i386/config.in
--- dist-2.4.5/arch/i386/config.in	Thu May 24 15:14:08 2001
+++ cobalt-2.4.5/arch/i386/config.in	Thu May 31 14:31:37 2001
@@ -192,6 +192,7 @@
    define_bool CONFIG_X86_LOCAL_APIC y
    define_bool CONFIG_PCI y
 else
+   source drivers/cobalt/Config.in
    if [ "$CONFIG_SMP" = "y" ]; then
       define_bool CONFIG_X86_IO_APIC y
       define_bool CONFIG_X86_LOCAL_APIC y
diff -ruN dist-2.4.5/Documentation/Configure.help cobalt-2.4.5/Documentation/Configure.help
--- dist-2.4.5/Documentation/Configure.help	Thu May 24 15:03:06 2001
+++ cobalt-2.4.5/Documentation/Configure.help	Thu May 31 14:31:29 2001
@@ -13035,6 +13035,121 @@
 
   If unsure, say N.
 
+Cobalt Networks support
+CONFIG_COBALT
+  Support for Cobalt Networks x86-based servers.
+
+Gen III (3000 series) system support
+CONFIG_COBALT_GEN_III
+  This option enables support for the 3000 series of Cobalt Networks 
+  systems. This includes the RaQ 3, RaQ 4, and Qube 3 product lines.
+
+  This platform uses an AMD K6-2 processor, an ALI M1541/1533 chipset, 
+  an optional NCR 53c875 SCSI controller, and two Intel 82559ER or 
+  National Semiconductor DP83815 NICs.
+
+  Getting this option wrong will likely result in a kernel that does 
+  not boot. Selecting support for more than 1 system series will add
+  bloat to your kernel, but will not cause anything bad to happen.
+
+  If you have a Cobalt Networks System, but aren't sure what kind, 
+  say Y here.
+  
+Gen V (5000 series) system support
+CONFIG_COBALT_GEN_V
+  This option enables support for the 5000 series of Cobalt Networks 
+  systems. This includes the RaQ XTR product line.
+
+  This platform uses Intel Pentium III Coppermine FCPGA CPUs, the 
+  ServerWorks LE chipset (with registered ECC DIMMs only!), two 
+  HighPoint HPT370 IDE controllers, and two National Semiconductor 
+  DP83815 NICs.
+
+  Getting this option wrong will likely result in a kernel that does 
+  not boot. Selecting support for more than 1 system series will add
+  bloat to your kernel, but will not cause anything bad to happen.
+
+  If you have a Cobalt Networks System, but aren't sure what kind, 
+  say Y here.
+
+Create legacy /proc files
+CONFIG_COBALT_OLDPROC
+  This option forces some Cobalt Networks drivers to support legacy 
+  files in /proc.  Older versions of these drivers exported files 
+  directly in /proc, as opposed to the newer /proc/cobalt.  If you say 
+  N to this option, the old filenames will no longer be exported.  
+  Regardless of your selection here, files in /proc/cobalt will be 
+  exported.  Of course, you have to include support for /proc fs, too.
+
+  It is safe to say Y here.
+
+Front panel LCD support
+CONFIG_COBALT_LCD
+  This enables support for the Cobalt Networks front panel.  This is 
+  for the LCD panel and buttons.  The primary method for connection is 
+  via the parallel port (IO base 0x370), but newer systems use an
+  I2C bus.  
+  
+  If you have a Cobalt Networks system, you should say Y here.
+
+Software controlled LED support
+CONFIG_COBALT_LED
+  This enables support for the software-controlled LEDs on Cobalt 
+  Networks systems.  This includes the fault light and front panel LEDs
+  on the RaQ XTR, the lightbar on the Qube 3, and others.
+
+  If you have a Cobalt Networks system, you should say Y here.
+
+Silicon serial number support
+CONFIG_COBALT_SERNUM
+  This enables support for the on-board serial number on Cobalt Networks 
+  systems.  This is a universally-unique 64-bit serial number.  Some 
+  systems use a Dallas DS2401 chip, others have an I2C based EEPROM.
+ 
+  If you select Y here, the files /proc/cobalt/hostid and 
+  /proc/cobalt/serialnumber will be created.  The hostid file contains a 
+  32 bit integer generated from the serial number, in binary form.  The 
+  serialnumber file contains the hexadecimal representation of the serial 
+  number, in ASCII.
+  
+  If you have a Cobalt Networks system, you should say Y here.
+
+Chipset watchdog timer support
+CONFIG_COBALT_WDT
+  This enables support for the watchdog timer built into Cobalt chipsets.
+  The timer wakes up periodically, to make find out if system has hung, 
+  or disabled interrupts too long. The result of detecting a hang is a 
+  hard reboot.
+
+  If you have a Cobalt Networks system, you should say Y here.
+
+Thermal sensor support
+CONFIG_COBALT_THERMAL
+  This enables support for the thermal sensor(s) built into Cobalt
+  Networks systems.  This driver exports /proc/cobalt/thermal_sensors.
+
+  If you have a Cobalt Networks system, you should say Y here.
+
+Fan tachometer support
+CONFIG_COBALT_FANS
+  This enables support for the fan tachometers built into some Cobalt 
+  Networks systems.  This driver exports /proc/cobalt/faninfo.  Some 
+  Cobalt software depends on this feature, and enabling it does not cause 
+  any risks.
+
+  If you have a Cobalt Networks system, you should say Y here, unless you
+  are absolutely sure.
+
+Disk drive ruler support
+CONFIG_COBALT_RULER
+  This enables support for the cobalt hard drive ruler, found on some
+  Cobalt systems, including the RaQ XTR.  This is the device that enables
+  swapping of drives.  It is not needed for basic disk operation.  
+  Enabling this on a system with no ruler will have no adverse effects.
+
+  If you have a Cobalt Networks system, you should say Y here,
+  unless you are absolutely sure.
+
 I2C support
 CONFIG_I2C
   I2C (pronounce: I-square-C) is a slow serial bus protocol used in
diff -ruN dist-2.4.5/arch/i386/kernel/Makefile cobalt-2.4.5/arch/i386/kernel/Makefile
--- dist-2.4.5/arch/i386/kernel/Makefile	Fri Dec 29 14:35:47 2000
+++ cobalt-2.4.5/arch/i386/kernel/Makefile	Thu May 31 14:31:37 2001
@@ -40,5 +40,6 @@
 obj-$(CONFIG_X86_LOCAL_APIC)	+= apic.o
 obj-$(CONFIG_X86_IO_APIC)	+= io_apic.o mpparse.o
 obj-$(CONFIG_X86_VISWS_APIC)	+= visws_apic.o
+obj-$(CONFIG_COBALT)	+= cobalt.o
 
 include $(TOPDIR)/Rules.make
diff -ruN dist-2.4.5/arch/i386/kernel/cobalt.c cobalt-2.4.5/arch/i386/kernel/cobalt.c
--- dist-2.4.5/arch/i386/kernel/cobalt.c	Wed Dec 31 16:00:00 1969
+++ cobalt-2.4.5/arch/i386/kernel/cobalt.c	Thu May 31 14:31:37 2001
@@ -0,0 +1,168 @@
+/* $Id: cobalt.c,v 1.4 2001/04/17 05:38:51 asun Exp $ */
+#include <linux/config.h>
+
+#ifdef CONFIG_COBALT
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/reboot.h>
+#include <linux/pci.h>
+#include <linux/cobalt-misc.h>
+#include <linux/cobalt-led.h>
+#include <linux/cobalt-superio.h>
+#include <linux/cobalt-ruler.h>
+
+static inline void ledonoff(unsigned long on, unsigned long off);
+
+void 
+cobalt_nmi(unsigned char reason, struct pt_regs *regs)
+{
+#if defined(CONFIG_COBALT_GEN_V)
+	struct pci_dev *cnb_dev;
+	u8 err;
+
+	printk("NMI:");
+	cnb_dev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS,
+		PCI_DEVICE_ID_SERVERWORKS_LE, NULL);
+	if (!cnb_dev) {
+		printk("%s: can't find north bridge for NMI status\n", 
+			__FILE__);
+		return;
+	}
+
+	pci_read_config_byte(cnb_dev, 0x47, &err);
+	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) {
+		u32 address;
+		u8 row, dimm, ecc;
+
+		pci_read_config_dword(cnb_dev, 0x94, &address);
+		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 0x%x, DIMM type %d "
+		       "(col addr=0x%x, row addr=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); 
+#endif
+}
+
+void 
+cobalt_restart(void)
+{
+#if defined(CONFIG_COBALT_GEN_III)
+	/* kick watchdog */
+	cobalt_wdt_trigger_reboot();
+#elif defined (CONFIG_COBALT_GEN_V)
+	cobalt_ruler_powerdown();
+
+	/* set "Enable Hard Reset" bit to 1 */
+	outb(0x02, 0x0cf9);
+
+	/* the 0-to-1 transition of bit 2 will cause reset of processor */
+	outb(0x06, 0x0cf9);
+#endif
+
+	/* we should not get here unless there is a BAD error */
+	printk("%s: can not restart - halting\n", __FILE__);
+	machine_halt();
+}
+
+void
+cobalt_halt(void)
+{
+	int haltok = current_cpu_data.hlt_works_ok;
+
+#if defined(CONFIG_COBALT_GEN_V) 
+	/* we have soft power-off */
+	machine_power_off();
+#endif
+
+	/* 
+	 * 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");
+		}
+	}
+}
+
+static inline void 
+ledonoff(unsigned long on, unsigned long off)
+{
+#if defined(CONFIG_COBALT_LCD)
+	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
+}
+
+void
+cobalt_power_off(void)
+{
+#if defined(CONFIG_COBALT_GEN_V)
+	u16 addr;
+	u8 val;
+
+	/* use card control register 7 to select logical device 2 (APC) */
+	outb(SUPERIO_LOGICAL_DEV, SUPERIO_INDEX_PORT);
+	outb(SUPERIO_DEV_RTC, SUPERIO_DATA_PORT);
+
+	/* get the base register */
+	outb(SUPERIO_ADDR_HIGH, SUPERIO_INDEX_PORT);
+	addr = inb(SUPERIO_DATA_PORT) << 8;
+	outb(SUPERIO_ADDR_LOW, SUPERIO_INDEX_PORT);
+	addr |= inb(SUPERIO_DATA_PORT);
+	if (addr) {
+		/* set up bank 2 */
+		outb(SUPERIO_RTC_CRA, addr);
+		val = inb(addr + 1) & 0x8f;
+		outb(val | SUPERIO_RTC_BANK_2, addr + 1);
+
+		/* power off the machine with APCR1 */
+		outb(SUPERIO_APCR1, addr);
+		val = inb(addr + 1);
+		outb(0x20 | val, addr + 1);
+	}
+#endif
+}
+
+#endif /* CONFIG_COBALT */
diff -ruN dist-2.4.5/arch/i386/kernel/process.c cobalt-2.4.5/arch/i386/kernel/process.c
--- dist-2.4.5/arch/i386/kernel/process.c	Fri Feb  9 11:29:44 2001
+++ cobalt-2.4.5/arch/i386/kernel/process.c	Thu May 31 14:31:38 2001
@@ -46,6 +46,9 @@
 #ifdef CONFIG_MATH_EMULATION
 #include <asm/math_emu.h>
 #endif
+#ifdef CONFIG_COBALT
+#include <linux/cobalt-misc.h>
+#endif
 
 #include <linux/irq.h>
 
@@ -354,6 +357,10 @@
 	disable_IO_APIC();
 #endif
 
+#ifdef CONFIG_COBALT
+	cobalt_restart();
+#endif
+
 	if(!reboot_thru_bios) {
 		/* rebooting needs to touch the page at absolute addr 0 */
 		*((unsigned short *)__va(0x472)) = reboot_mode;
@@ -376,10 +383,17 @@
 
 void machine_halt(void)
 {
+#ifdef CONFIG_COBALT
+	cobalt_halt();
+#endif
 }
 
 void machine_power_off(void)
 {
+#ifdef CONFIG_COBALT
+	cobalt_power_off();
+#endif
+
 	if (pm_power_off)
 		pm_power_off();
 }
diff -ruN dist-2.4.5/arch/i386/kernel/setup.c cobalt-2.4.5/arch/i386/kernel/setup.c
--- dist-2.4.5/arch/i386/kernel/setup.c	Fri May 25 17:07:09 2001
+++ cobalt-2.4.5/arch/i386/kernel/setup.c	Thu May 31 14:31:38 2001
@@ -104,6 +104,10 @@
 #include <asm/dma.h>
 #include <asm/mpspec.h>
 #include <asm/mmu_context.h>
+#ifdef CONFIG_COBALT
+#include <linux/cobalt-thermal.h>
+#endif
+
 /*
  * Machine setup..
  */
@@ -2441,9 +2445,13 @@
 			     x86_cap_flags[i] != NULL )
 				p += sprintf(p, " %s", x86_cap_flags[i]);
 
-		p += sprintf(p, "\nbogomips\t: %lu.%02lu\n\n",
+		p += sprintf(p, "\nbogomips\t: %lu.%02lu\n",
 			     c->loops_per_jiffy/(500000/HZ),
 			     (c->loops_per_jiffy/(5000/HZ)) % 100);
+#ifdef CONFIG_COBALT
+		p += sprintf(p, "temperature\t: %s\n", cobalt_temp_read(n));
+#endif
+		p += sprintf(p, "\n");
 	}
 	return p - buffer;
 }
diff -ruN dist-2.4.5/arch/i386/kernel/traps.c cobalt-2.4.5/arch/i386/kernel/traps.c
--- dist-2.4.5/arch/i386/kernel/traps.c	Mon May  7 14:15:21 2001
+++ cobalt-2.4.5/arch/i386/kernel/traps.c	Thu May 31 14:31:38 2001
@@ -46,6 +46,9 @@
 #include <asm/cobalt.h>
 #include <asm/lithium.h>
 #endif
+#ifdef CONFIG_COBALT
+#include <linux/cobalt-misc.h>
+#endif
 
 #include <linux/irq.h>
 
@@ -347,12 +350,18 @@
 
 static void mem_parity_error(unsigned char reason, struct pt_regs * regs)
 {
+#if defined(CONFIG_COBALT_GEN_V)
+	cobalt_nmi(reason, regs);
+#else
 	printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n");
 	printk("You probably have a hardware problem with your RAM chips\n");
+#endif
 
-	/* Clear and disable the memory parity error line. */
-	reason = (reason & 0xf) | 4;
-	outb(reason, 0x61);
+	/* Clear and re-enable the memory parity error line. */
+	reason &= 0xf;
+	outb(reason | 4, 0x61);
+	outb(reason & ~4, 0x61);
+	
 }
 
 static void io_check_error(unsigned char reason, struct pt_regs * regs)
diff -ruN dist-2.4.5/drivers/Makefile cobalt-2.4.5/drivers/Makefile
--- dist-2.4.5/drivers/Makefile	Wed May 16 10:27:02 2001
+++ cobalt-2.4.5/drivers/Makefile	Thu May 31 14:32:02 2001
@@ -7,7 +7,7 @@
 
 
 mod-subdirs :=	dio mtd sbus video macintosh usb input telephony sgi i2o ide \
-		scsi md ieee1394 pnp isdn atm fc4 net/hamradio i2c acpi
+		scsi md ieee1394 pnp isdn atm fc4 net/hamradio i2c acpi cobalt
 
 subdir-y :=	parport char block net sound misc media cdrom
 subdir-m :=	$(subdir-y)
@@ -42,6 +42,7 @@
 subdir-$(CONFIG_HAMRADIO)	+= net/hamradio
 subdir-$(CONFIG_I2C)		+= i2c
 subdir-$(CONFIG_ACPI)		+= acpi
+subdir-$(CONFIG_COBALT)		+= cobalt
 
 include $(TOPDIR)/Rules.make
 
diff -ruN dist-2.4.5/drivers/char/mem.c cobalt-2.4.5/drivers/char/mem.c
--- dist-2.4.5/drivers/char/mem.c	Sat May 19 18:11:36 2001
+++ cobalt-2.4.5/drivers/char/mem.c	Thu May 31 14:32:09 2001
@@ -26,6 +26,9 @@
 #include <asm/io.h>
 #include <asm/pgalloc.h>
 
+#ifdef CONFIG_COBALT
+extern int cobalt_init(void);
+#endif
 #ifdef CONFIG_I2C
 extern int i2c_init_all(void);
 #endif
@@ -644,6 +647,9 @@
 #endif
 #if defined(CONFIG_ADB)
 	adbdev_init();
+#endif
+#ifdef CONFIG_COBALT
+	cobalt_init();
 #endif
 	return 0;
 }
diff -ruN dist-2.4.5/Makefile cobalt-2.4.5/Makefile
--- dist-2.4.5/Makefile	Fri May 25 09:51:33 2001
+++ cobalt-2.4.5/Makefile	Thu May 31 14:31:28 2001
@@ -177,6 +177,7 @@
 DRIVERS-$(CONFIG_PHONE) += drivers/telephony/telephony.o
 DRIVERS-$(CONFIG_ACPI) += drivers/acpi/acpi.o
 DRIVERS-$(CONFIG_MD) += drivers/md/mddev.o
+DRIVERS-$(CONFIG_COBALT) += drivers/cobalt/cobalt.o
 
 DRIVERS := $(DRIVERS-y)
 
@@ -193,6 +194,10 @@
 	drivers/zorro/devlist.h drivers/zorro/gen-devlist \
 	drivers/sound/bin2hex drivers/sound/hex2hex \
 	drivers/atm/fore200e_mkfirm drivers/atm/{pca,sba}*{.bin,.bin1,.bin2} \
+	drivers/scsi/aic7xxx/aicasm/aicasm_gram.c \
+	drivers/scsi/aic7xxx/aicasm/aicasm_scan.c \
+	drivers/scsi/aic7xxx/aicasm/y.tab.h \
+	drivers/scsi/aic7xxx/aicasm/aicasm \
 	net/khttpd/make_times_h \
 	net/khttpd/times.h \
 	submenu*

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for real this  time)
       [not found] <mailman.991363680.24671.linux-kernel2news@redhat.com>
@ 2001-06-01  4:09 ` Pete Zaitcev
  2001-06-01  6:57   ` Tim Hockin
  0 siblings, 1 reply; 46+ messages in thread
From: Pete Zaitcev @ 2001-06-01  4:09 UTC (permalink / raw)
  To: thockin, alan; +Cc: Linux Kernel Mailing List

> Aattached is a (large, but self contained) patch for Cobalt Networks suport
> for x86 systems (RaQ3, RaQ4, Qube3, RaQXTR).  Please let me know if there
> is anything that would prevent this from general inclusion in the next
> release.

Looks interesting. Seemingly literate use of spinlocks.

Off-hand I see old style initialization. Is it right for new driver?

i2c framework is not used, I wonder why. Someone thought that
it was too heavy perhaps? If so, I disagree.

Also, I am curious
if any alignment with lm-sensors is possible, for the sake of
common userland tools? If we managed that, PSARC would eat their
hearts out - they tried to do it since E-250 shipped.

lcd_read bounces reads with -EINVAL when another read is in
progress. Gross.

Nitpicking:

1.:
	p = head;
	while (p) {
		p = p->next;
	}

It is what for(;;) does.

2. Spaces and tabs are mixed in funny ways, makes to cute effects
when quoting diffs.

-- Pete

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for real this  time)
  2001-06-01  2:49 [PATCH] support for Cobalt Networks (x86 only) systems (for real this time) Tim Hockin
@ 2001-06-01  4:47 ` Dax Kelson
  2001-06-01  7:47 ` Jeff Garzik
  1 sibling, 0 replies; 46+ messages in thread
From: Dax Kelson @ 2001-06-01  4:47 UTC (permalink / raw)
  To: Tim Hockin; +Cc: Linux Kernel Mailing List

Tim Hockin said once upon a time (Thu, 31 May 2001):

> Aattached is a (large, but self contained) patch for Cobalt Networks suport
> for x86 systems (RaQ3, RaQ4, Qube3, RaQXTR).  Please let me know if there
> is anything that would prevent this from general inclusion in the next
> release.

I can vouch for these patches stability wise.  I've been running an
earlier version of these patches (for 2.4.4) on a Qube 3 acting as a
firewall for 3 weeks without any problems.

Incidently, that Qube 3 is running Red Hat 7.1 and using reiserfs on all
filesystems except for /boot.

Dax


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for real this  time)
  2001-06-01  4:09 ` Pete Zaitcev
@ 2001-06-01  6:57   ` Tim Hockin
  2001-06-01  7:27     ` Jeff Garzik
  2001-06-01  8:43     ` Pete Zaitcev
  0 siblings, 2 replies; 46+ messages in thread
From: Tim Hockin @ 2001-06-01  6:57 UTC (permalink / raw)
  To: Pete Zaitcev; +Cc: linux-kernel

> Looks interesting. Seemingly literate use of spinlocks.

thanks - I gave it lots of thought.

> Off-hand I see old style initialization. Is it right for new driver?

the old-style init is because it is an old driver.  I want to do a full-on
rework, but haven't had the time.

> i2c framework is not used, I wonder why. Someone thought that
> it was too heavy perhaps? If so, I disagree.

i2c is only in our stuff because the i2c core is not in the standard kernel
yet.  As soon as it is, I will make cobalt_i2c* go away.

> if any alignment with lm-sensors is possible, for the sake of

yes - I have communicated with the lm-sensors crew.  It is very high on my
wishlist.

> lcd_read bounces reads with -EINVAL when another read is in
> progress. Gross.

as I said - I didn't write the LCD driver, I just had to port it up :)  I
want to re-do the whole paradigm of it (it has been ported forward since 
2.0.3x)

> 1.:
> 	p = head;
> 	while (p) {
> 		p = p->next;
> 	}
> 
> It is what for(;;) does.

I don't get it - are you saying you do or don't like the while (p)
approach?  I think it is clearer because it is more true ot the heuristic -
"start at the beginning and walk down the list".

> 2. Spaces and tabs are mixed in funny ways, makes to cute effects
> when quoting diffs.

I've tried to eliminate that when I see it - I'll give the diff a close
examination.

thanks for the feedback - it will be nice to not have to constantly port
all our changes to each kernel release.  There are still some patches (of
course) but I didn't submit them because they are VERY specific to cobalt -
for example in the ide probing calling cobalt_ruler_register().  Ifdefs
protect, but the overall appearance would be rejected, I suspect - no?

Tim

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for real  this  time)
  2001-06-01  6:57   ` Tim Hockin
@ 2001-06-01  7:27     ` Jeff Garzik
  2001-06-01  8:43     ` Pete Zaitcev
  1 sibling, 0 replies; 46+ messages in thread
From: Jeff Garzik @ 2001-06-01  7:27 UTC (permalink / raw)
  To: Tim Hockin; +Cc: Pete Zaitcev, linux-kernel

> > Off-hand I see old style initialization. Is it right for new driver?
> 
> the old-style init is because it is an old driver.  I want to do a full-on
> rework, but haven't had the time.

New-style init by itself shouldn't be hard to do, independent of a full
re-work...


> > 2. Spaces and tabs are mixed in funny ways, makes to cute effects
> > when quoting diffs.
> 
> I've tried to eliminate that when I see it - I'll give the diff a close
> examination.

Why not just run indent over the source before submitting.  That will
regularize this stuff, and ensure that you are close to
Documentation/CodingStyle.

Here is the command I use.  The first two options are the only really
importants ones...

indent -kr -i8 -npsl -pcs -l100 -lc120

(line length is 100 because line length 72/75/80 winds up wrapping long
printk and logic lines when typically the programmer didn't want them to
be wrapped)

-- 
Jeff Garzik      | Disbelief, that's why you fail.
Building 1024    |
MandrakeSoft     |

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for real  this time)
  2001-06-01  2:49 [PATCH] support for Cobalt Networks (x86 only) systems (for real this time) Tim Hockin
  2001-06-01  4:47 ` Dax Kelson
@ 2001-06-01  7:47 ` Jeff Garzik
  1 sibling, 0 replies; 46+ messages in thread
From: Jeff Garzik @ 2001-06-01  7:47 UTC (permalink / raw)
  To: Tim Hockin; +Cc: alan, Linux Kernel Mailing List

General comments:

* Code looks really clean.  Nice work.
* Use module_init/exit.  I know, I know, you heard it before :)
* I dunno if Linus will take it as-is because he has been threatening to
stop taking PCI drives that use old-style PCI init for no good reason. 
(he even made me change a driver that used old-style PCI init for a good
reason :))
* There is a ton of procfs stuff in there.  I suppose !CONFIG_PROC_FS is
not a supported configuration on Cobalt?  :)


Tim Hockin wrote:
> +/* this is essentially an exported function - it is in the hwif structs */
> +static int
> +ruler_busproc_fn(ide_hwif_t *hwif, int arg)
[...]
> +               read_lock(&ruler_lock);
[...]
> +               read_unlock(&ruler_lock);

Should this be read_lock_bh, since other uses are in a timer function or
_irqsave/restore?


> +               /* The GPIO tied to the ID chip is on the PMU */
> +               id_dev = pci_find_device(PCI_VENDOR_ID_AL,
> +                       PCI_DEVICE_ID_AL_M7101, NULL);

as mentioned earlier, pci_register_driver is preferred over "old style"
PCI.


> +               spin_lock_irqsave(&cobalt_superio_lock, flags);
> +               outb(SUPERIO_LOGICAL_DEV, SUPERIO_INDEX_PORT);
> +               outb(SUPERIO_DEV_GPIO, SUPERIO_DATA_PORT);
> +               outb(SUPERIO_ADDR_HIGH, SUPERIO_INDEX_PORT);
> +               addr = inb(SUPERIO_DATA_PORT) << 8;
> +               outb(SUPERIO_ADDR_LOW, SUPERIO_INDEX_PORT);
> +               addr |= inb(SUPERIO_DATA_PORT);
> +               spin_unlock_irqrestore(&cobalt_superio_lock, flags);

Nothing wrong here, just commenting that I wish other superio did this
sometimes in some cases... :)

> +static void __init
> +io_write_byte(unsigned char c)
> +{
> +       int i;
> +       unsigned long flags;
> +
> +       save_flags(flags);
> +
> +       for (i = 0; i < 8; i++, c >>= 1) {
> +               cli();
> +               if (c & 1) {
> +                       /* Transmit a 1 */
> +                       io_write(5);
> +                       udelay(80);
> +               } else {
> +                       /* Transmit a 0 */
> +                       io_write(80);
> +                       udelay(10);
> +               }
> +               restore_flags(flags);
> +       }
> +}

Can save/restore flags be replaced with a spinlock?


> +       /* get version from CVS */
> +       version = strchr("$Revision: 1.4 $", ':') + 2;
> +       if (version) {
> +               char *p;
> +
> +               strncpy(vstring, version, sizeof(vstring));
> +               if ((p = strchr(vstring, ' '))) {
> +                       *p = '\0';
> +               }
> +       } else {
> +               strncpy(vstring, "unknown", sizeof(vstring));
> +       }

ug :)  It would be nice if this could be done at compile time

> +       proc_serialnum = create_proc_read_entry("serialnumber", 0, NULL,
> +               serialnum_read, NULL);
> +       if (!proc_serialnum) {
> +               EPRINTK("can't create /proc/serialnumber\n");
> +       }
> +#endif
> +       proc_chostid = create_proc_read_entry("hostid", 0, proc_cobalt,
> +               hostid_read, NULL);
> +       if (!proc_chostid) {
> +               EPRINTK("can't create /proc/cobalt/hostid\n");
> +       }
> +       proc_cserialnum = create_proc_read_entry("serialnumber", 0,
> +               proc_cobalt, serialnum_read, NULL);
> +       if (!proc_cserialnum) {
> +               EPRINTK("can't create /proc/cobalt/serialnumber\n");

security concern?

We disable CPU processor serial numbers on x86, maybe you want to make
everything except the output of /usr/bin/hostid priveleged?


> diff -ruN dist-2.4.5/drivers/cobalt/wdt.c cobalt-2.4.5/drivers/cobalt/wdt.c
> --- dist-2.4.5/drivers/cobalt/wdt.c     Wed Dec 31 16:00:00 1969
> +++ cobalt-2.4.5/drivers/cobalt/wdt.c   Thu May 31 14:32:15 2001

Shouldn't this be stored with other watchdog timers?

> diff -ruN dist-2.4.5/include/linux/cobalt-acpi.h cobalt-2.4.5/include/linux/cobalt-acpi.h
> --- dist-2.4.5/include/linux/cobalt-acpi.h      Wed Dec 31 16:00:00 1969
> +++ cobalt-2.4.5/include/linux/cobalt-acpi.h    Thu May 31 14:33:16 2001

Another ACPI user... neat!



> +/* the root of /proc/cobalt */
> +extern struct proc_dir_entry *proc_cobalt;

I am wondering if some of this stuff can be a sysctl instead of
procfs???

> +
> +#endif
> diff -ruN dist-2.4.5/include/linux/cobalt-i2c.h cobalt-2.4.5/include/linux/cobalt-i2c.h
> --- dist-2.4.5/include/linux/cobalt-i2c.h       Wed Dec 31 16:00:00 1969
> +++ cobalt-2.4.5/include/linux/cobalt-i2c.h     Thu May 31 14:33:16 2001

Sometimes I wish for a directory structure with:
* arch-specific includes
* platform-specific includes
* generic core includes

Then we could put this stuff in include/cobalt/* or
platform/cobalt/include or somesuch.  


> +extern cobt_sys_t cobt_type;
> +/* one for each major board-type - COBT_SUPPORT_* from <linux/cobalt.h> */
> +#define cobt_is_raq3()  (COBT_SUPPORT_GEN_III && cobt_type == COBT_RAQ3)
> +#define cobt_is_qube3()         (COBT_SUPPORT_GEN_III && cobt_type == COBT_QUBE3)
> +#define cobt_is_raqxtr() (COBT_SUPPORT_GEN_V && cobt_type == COBT_RAQXTR)
> +/* one for each major generation */
> +#define cobt_is_3k()    (cobt_is_raq3() || cobt_is_qube3())
> +#define cobt_is_5k()    (cobt_is_raqxtr())

Is they look like functions, why not make them static inline?



>  static void mem_parity_error(unsigned char reason, struct pt_regs * regs)
>  {
> +#if defined(CONFIG_COBALT_GEN_V)
> +       cobalt_nmi(reason, regs);
> +#else
>         printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n");
>         printk("You probably have a hardware problem with your RAM chips\n");
> +#endif
> 
> -       /* Clear and disable the memory parity error line. */
> -       reason = (reason & 0xf) | 4;
> -       outb(reason, 0x61);
> +       /* Clear and re-enable the memory parity error line. */
> +       reason &= 0xf;
> +       outb(reason | 4, 0x61);
> +       outb(reason & ~4, 0x61);
> +
>  }

Interesting.  I wonder if this positively affects anyone else.


-- 
Jeff Garzik      | Disbelief, that's why you fail.
Building 1024    |
MandrakeSoft     |

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for real  this time)
  2001-06-01  2:47 [PATCH] support for Cobalt Networks (x86 only) systems (for real this time) Tim Hockin
@ 2001-06-01  8:10 ` Jeff Garzik
  2001-06-01  8:46 ` [PATCH] support for Cobalt Networks (x86 only) systems (for real this Alan Cox
       [not found] ` <mailman.991383180.28261.linux-kernel2news@redhat.com>
  2 siblings, 0 replies; 46+ messages in thread
From: Jeff Garzik @ 2001-06-01  8:10 UTC (permalink / raw)
  To: Tim Hockin; +Cc: alan, Linux Kernel Mailing List

Tim Hockin wrote:
> +int __init
> +cobalt_acpi_init(void)
> +{
> +       int err, reg;
> +       u16 addr;
> +       unsigned long flags;
> +
> +       if (cobt_is_5k()) {
> +               /* setup osb4 i/o regions */
> +               if ((reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x20)))
> +                       request_region(reg, 4, "OSB4 (pm1a_evt_blk)");
> +               if ((reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x22)))
> +                       request_region(reg, 2, "OSB4 (pm1a_cnt_blk)");
> +               if ((reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x24)))
> +                       request_region(reg, 4, "OSB4 (pm_tmr_blk)");
> +               if ((reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x26)))
> +                       request_region(reg, 6, "OSB4 (p_cnt_blk)");
> +               if ((reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x28)))
> +                       request_region(reg, 8, "OSB4 (gpe0_blk)");
> +               osb4_port = reg;
> +
> +               spin_lock_irqsave(&cobalt_superio_lock, flags);
> +
> +               /* superi/o -- select pm logical device and get base address */
> +               outb(SUPERIO_LOGICAL_DEV, SUPERIO_INDEX_PORT);
> +               outb(SUPERIO_DEV_PM, SUPERIO_DATA_PORT);
> +               outb(SUPERIO_ADDR_HIGH, SUPERIO_INDEX_PORT);
> +               addr = inb(SUPERIO_DATA_PORT) << 8;
> +               outb(SUPERIO_ADDR_LOW, SUPERIO_INDEX_PORT);
> +               addr |= inb(SUPERIO_DATA_PORT);
> +               if (addr) {
> +                       /* get registers */
> +                       if ((reg = get_reg(addr, addr + 1, 0x08))) {
> +                               request_region(reg, 4, "SUPERIO (pm1_evt_blk)");
> +                               superio_port = reg;
> +                       }
> +                       if ((reg = get_reg(addr, addr + 1, 0x0c)))
> +                               request_region(reg, 2, "SUPERIO (pm1_cnt_blk)");
> +                       if ((reg = get_reg(addr, addr + 1, 0x0e)))
> +                               request_region(reg, 16, "SUPERIO (gpe_blk)");

need to check for request_region failure.  since you have a lot of
request_region calls, you may want to consider breaking them out into a
separate function which returns success or failure, and handles details
of cleaning up after 4 request_regions succeed but the 5th one fails.

	if (!request_region(region1))
		goto out;
	if (!request_region(region2))
		goto cleanup_1;
	if (!request_region(region3))
		goto cleanup_2;
	return 0;

cleanup_2:
	release_region(region2);
cleanup_1:
	release_region(region1);
out:
	return -EBUSY;

> +               /* 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("couldn't assign ACPI IRQ (%d)\n", ACPI_IRQ);
> +                       return -1;
> +               }

return 'err' not -1 here?

> +bool 'Support for Cobalt Networks x86 servers' CONFIG_COBALT
> +
> +if [ "$CONFIG_COBALT" != "n" ]; then
> +   bool 'Gen III (3000 series) system support' CONFIG_COBALT_GEN_III
> +   bool 'Gen V (5000 series) system support' CONFIG_COBALT_GEN_V
> +   bool 'Create legacy /proc files' CONFIG_COBALT_OLDPROC
> +
> +   comment 'Cobalt hardware options'
> +
> +   bool 'Front panel (LCD) support' CONFIG_COBALT_LCD
> +   bool 'Software controlled LED support' CONFIG_COBALT_LED
> +   bool 'Serial number support' CONFIG_COBALT_SERNUM
> +   bool 'Watchdog timer support' CONFIG_COBALT_WDT
> +   bool 'Thermal sensor support' CONFIG_COBALT_THERMAL
> +   bool 'Fan tachometer support' CONFIG_COBALT_FANS
> +   bool 'Disk drive ruler support' CONFIG_COBALT_RULER
> +fi

Style:  s/bool '/bool '  /


> +#ifdef CONFIG_PROC_FS
> +#ifdef CONFIG_COBALT_OLDPROC
> +       proc_faninfo = create_proc_read_entry("faninfo", S_IFREG | S_IRUGO,
> +               NULL, fan_read_proc, NULL);
> +        if (!proc_faninfo) {
> +               EPRINTK("can't create /proc/faninfo\n");
> +       }
> +#endif
> +       proc_cfaninfo = create_proc_read_entry("faninfo", S_IFREG | S_IRUGO,
> +               proc_cobalt, fan_read_proc, NULL);
> +        if (!proc_cfaninfo) {
> +               EPRINTK("can't create /proc/cobalt/faninfo\n");
> +       }
> +#endif
> +
> +       printk("Cobalt Networks fan tachometers v1.2\n");
> +
> +       return 0;
> +}

S_IFREG|S_IRUGO is the default, so simply pass zero mode for this
behavior.

But, each time a user cats this proc file, the user is banging the
hardware.  What happens when a malicious user forks off 100 processes to
continually cat this file?  :)

> +/* 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,
> +};

Needs owner field assigned a value.

> +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);
> +
> +       spin_lock(&lcd_lock);
> +
> +       switch (cmd) {
> +       /* Turn the LCD on */
> +       case LCD_On:
> +               lcddev_write_inst(0x0F);
> +               break;

Security: don't you want CAP_RAW_IO or something before executing any of
these ioctls?

> +       /* Read what's on the LCD */
> +        case LCD_Read:
> +                for (address = DD_R00; address <= DD_R01; address++) {
> +                        lcddev_write_inst(address | LCD_Addr);
> +                        display.line1[address] = lcddev_read_data();
> +                }
> +                display.line1[DD_R01] = '\0';
> +
> +                for (address = DD_R10; address <= DD_R11; address++) {
> +                        lcddev_write_inst(address | LCD_Addr);
> +                        display.line2[address - DD_R10] = lcddev_read_data();
> +               }
> +                display.line2[DD_R01] = '\0';
> +
> +                copy_to_user((struct lcd_display *)arg, &display, dlen);

unchecked copy_to_user.  should be:

	if (copy_to_user(...))
		return -EFAULT;

> +       case LCD_Raw_Inst:
> +                copy_from_user(&display, (struct lcd_display *)arg, dlen);
> +                lcddev_write_inst(display.character);
> +                break;
> +
> +        case LCD_Raw_Data:
> +                copy_from_user(&display, (struct lcd_display *)arg, dlen);

ditto.  other unchecked copy_from_user and put_user cases snipped.



> +       /* clear an led */
> +       case LED_Bit_Clear:
> +                copy_from_user(&display, (struct lcd_display *)arg, dlen);
> +                cobalt_led_set_lazy(cobalt_led_get() & ~display.leds);
> +               break;
> +
> +       default:
> +       }
> +
> +       spin_unlock(&lcd_lock);

bug: you can't hold a spinlock while you are sleeping in copy_from_user

> +/* LED 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 buttons_now;
> +       static atomic_t lcd_waiters = ATOMIC_INIT(0);
> +
> +       if (atomic_read(&lcd_waiters) > 0)
> +               return -EINVAL;
> +       atomic_inc(&lcd_waiters);
> +
> +       while (((buttons_now = button_pressed()) == 0) &&
> +              !(signal_pending(current)))
> +       {
> +               current->state = TASK_INTERRUPTIBLE;
> +               schedule_timeout((2 * HZ));
> +       }
> +       atomic_dec(&lcd_waiters);
> +
> +       if (signal_pending(current))
> +               return -ERESTARTSYS;
> +
> +       return buttons_now;

why not use a semaphore here?
also, you do not have O_NONBLOCK.

> +int
> +cobalt_fpled_register(unsigned int (*function)(void *), void *data)
> +{
> +       int r = -1;
> +
> +       if (cobt_is_5k()) {
> +               struct led_handler *newh;
> +               unsigned long flags;
> +
> +               newh = kmalloc(sizeof(*newh), GFP_ATOMIC);
> +               if (!newh) {
> +                       EPRINTK("can't allocate memory for handler %p(%p)\n",
> +                               function, data);
> +                       return -1;
> +               }

maybe I am misunderstanding the context in which this code is called. 
why not GFP_KERNEL?

> +#define MAX_COBT_NETDEVS       2
> +static struct net_device *netdevs[MAX_COBT_NETDEVS];
> +static int n_netdevs;
> +static spinlock_t cobaltnet_lock = SPIN_LOCK_UNLOCKED;
> +
> +#if defined(CONFIG_COBALT_LED)
> +static unsigned int
> +net_led_handler(void *data)
> +{
> +       int i;
> +       unsigned int leds = 0;
> +       static int txrxmap[MAX_COBT_NETDEVS] = {LED_ETH0_TXRX, LED_ETH1_TXRX};
> +       static int linkmap[MAX_COBT_NETDEVS] = {LED_ETH0_LINK, LED_ETH1_LINK};
> +       unsigned long flags;
> +       static unsigned long net_old[MAX_COBT_NETDEVS];
> +
> +       spin_lock_irqsave(&cobaltnet_lock, flags);
> +
> +       for (i = 0; i < n_netdevs; i++) {
> +               unsigned long txrxstate;
> +               struct net_device *dev = netdevs[i];
> +               if (!dev) {
> +                       continue;
> +               }
> +               /* check for link */
> +               if (dev->link_check && dev->link_check(dev)) {

Obviously dev->link_check is a new and nonstandard addition.

You should use:  netif_carrier_{on,off,ok}


> +void
> +cobalt_net_register(struct net_device *ndev)
> +{
> +       unsigned long flags;
> +       int i;
> +
> +        if (!ndev) {
> +              return;
> +       }
> +
> +       /* we'll track the first MAX_COBT_NETDEVS NICs */
> +       if (n_netdevs >= MAX_COBT_NETDEVS) {
> +              return;
> +       }
> +
> +       spin_lock_irqsave(&cobaltnet_lock, flags);
> +
> +       /* find a free slot */
> +       for (i = 0; i < n_netdevs; i++) {
> +               if (!netdevs[i]) {
> +                       netdevs[i] = ndev;
> +                       n_netdevs++;
> +               }
> +       }
> +
> +       spin_unlock_irqrestore(&cobaltnet_lock, flags);
> +}

Are you sure this is needed?

Each Cobalt NIC should store its struct net_device instance in struct
pci_dev::private_data, using pci_{get,set}_drvdata wrapper.

So, the system already store a list of Cobalt net devices for you.  Just
search the pci_drivers list for all members whose pci_dev::driver value
== &cobalt_net_pci_driver.

This of course assumes all the net devices are PCI, and that the net
device uses current (a.k.a. "new style") PCI driver init.

It is heavily encouraged to convert your PCI drivers to struct
pci_driver, because in 2.5 this will become a more generic struct
driver, and struct pci_dev will become a more generic struct device, to
be used by all kernel drivers.

-- 
Jeff Garzik      | Disbelief, that's why you fail.
Building 1024    |
MandrakeSoft     |

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for real this  time)
  2001-06-01  6:57   ` Tim Hockin
  2001-06-01  7:27     ` Jeff Garzik
@ 2001-06-01  8:43     ` Pete Zaitcev
  2001-06-01 18:29       ` Tim Hockin
  1 sibling, 1 reply; 46+ messages in thread
From: Pete Zaitcev @ 2001-06-01  8:43 UTC (permalink / raw)
  To: Tim Hockin; +Cc: Pete Zaitcev, linux-kernel

> From: Tim Hockin <thockin@hockin.org>
> Date: Thu, 31 May 2001 23:57:48 -0700 (PDT)

> > i2c framework is not used, I wonder why. Someone thought that
> > it was too heavy perhaps? If so, I disagree.
> 
> i2c is only in our stuff because the i2c core is not in the standard kernel
> yet.  As soon as it is, I will make cobalt_i2c* go away.

I am puzzled by this comment. Did you look into drivers/i2c/?
It certainly is a part of a stock kernel. The main user is
the V4L, in drivers/media/video, but I think LM sensors use it too.

-- Pete

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for real this
  2001-06-01  2:47 [PATCH] support for Cobalt Networks (x86 only) systems (for real this time) Tim Hockin
  2001-06-01  8:10 ` Jeff Garzik
@ 2001-06-01  8:46 ` Alan Cox
       [not found] ` <mailman.991383180.28261.linux-kernel2news@redhat.com>
  2 siblings, 0 replies; 46+ messages in thread
From: Alan Cox @ 2001-06-01  8:46 UTC (permalink / raw)
  To: Tim Hockin; +Cc: alan, Linux Kernel Mailing List

> +		/* setup osb4 i/o regions */
> +		if ((reg = get_reg(OSB4_INDEX_PORT, OSB4_DATA_PORT, 0x20)))
> +			request_region(reg, 4, "OSB4 (pm1a_evt_blk)");

Check request_region worked

> +static int 
> +i2c_wait_for_smi(void)

Obvious question - why duplicate the i2c layer

> +/* device structure */
> +static struct miscdevice lcd_dev = {
> +	COBALT_LCD_MINOR,

Is this an officially allocated minor ?

> +	/* Get the current cursor position */
> +        case LCD_Get_Cursor_Pos:
> +                display.cursor_address = lcddev_read_inst();
> +                display.cursor_address = display.cursor_address & 0x07F;
> +                copy_to_user((struct lcd_display *)arg, &display, dlen);

		Sleeping holding a spinlock

> +        case LCD_Set_Cursor_Pos:
> +                copy_from_user(&display, (struct lcd_display *)arg, dlen);

Ditto. Also should check the return and return -EFAULT if so

> +/* LED 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 buttons_now;
> +	static atomic_t lcd_waiters = ATOMIC_INIT(0);
> +
> +	if (atomic_read(&lcd_waiters) > 0)
> +		return -EINVAL;
> +	atomic_inc(&lcd_waiters);

Unsafe. Two people can execute the atomic_read before anyone executes
the atomic_inc. You probably want test_and_set_bit() or a semaphore

> +	while (((buttons_now = button_pressed()) == 0) &&
> +	       !(signal_pending(current)))
> +	{
> +		current->state = TASK_INTERRUPTIBLE;

We have a set_ function for this.. ALso what stops an ioctl occuring at
the same instant ?



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for real  this time)
       [not found] ` <mailman.991383180.28261.linux-kernel2news@redhat.com>
@ 2001-06-01  8:58   ` Pete Zaitcev
  2001-06-01 12:03     ` Bogdan Costescu
  0 siblings, 1 reply; 46+ messages in thread
From: Pete Zaitcev @ 2001-06-01  8:58 UTC (permalink / raw)
  To: Linux Kernel Mailing List

> But, each time a user cats this proc file, the user is banging the
> hardware.  What happens when a malicious user forks off 100 processes to
> continually cat this file?  :)

Nothing good, probably. Same story as /proc/apm, which only
hits BIOS instead (and it's debateable what is better).

> Security: don't you want CAP_RAW_IO or something before executing any of
> these ioctls?

Perhaps it's mode 600 in their distro...

> bug: you can't hold a spinlock while you are sleeping in copy_from_user

Yep... I meant to check for it but forgot.

-- Pete

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for real this time)
  2001-06-01  8:58   ` [PATCH] support for Cobalt Networks (x86 only) systems (for real this time) Pete Zaitcev
@ 2001-06-01 12:03     ` Bogdan Costescu
  2001-06-01 12:20       ` [PATCH] support for Cobalt Networks (x86 only) systems (for realthis time) Jeff Garzik
  0 siblings, 1 reply; 46+ messages in thread
From: Bogdan Costescu @ 2001-06-01 12:03 UTC (permalink / raw)
  To: Pete Zaitcev; +Cc: Linux Kernel Mailing List

On Fri, 1 Jun 2001, Pete Zaitcev wrote:

> > But, each time a user cats this proc file, the user is banging the
> > hardware.  What happens when a malicious user forks off 100 processes to
> > continually cat this file?  :)
>
> Nothing good, probably. Same story as /proc/apm, which only
> hits BIOS instead (and it's debateable what is better).

Hmm, the MII related ioctl's in some net drivers (checked for 3c59x,
tulip, eepro100) are also querying the hardware. And a user is allowed to
ask for this info (but not able to modify it).

Sincerely,

Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan.Costescu@IWR.Uni-Heidelberg.De


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for realthis  time)
  2001-06-01 12:03     ` Bogdan Costescu
@ 2001-06-01 12:20       ` Jeff Garzik
  2001-06-01 12:51         ` [PATCH] support for Cobalt Networks (x86 only) systems (for realthis Alan Cox
  0 siblings, 1 reply; 46+ messages in thread
From: Jeff Garzik @ 2001-06-01 12:20 UTC (permalink / raw)
  To: Bogdan Costescu; +Cc: Pete Zaitcev, Linux Kernel Mailing List

Bogdan Costescu wrote:
> 
> On Fri, 1 Jun 2001, Pete Zaitcev wrote:
> 
> > > But, each time a user cats this proc file, the user is banging the
> > > hardware.  What happens when a malicious user forks off 100 processes to
> > > continually cat this file?  :)
> >
> > Nothing good, probably. Same story as /proc/apm, which only
> > hits BIOS instead (and it's debateable what is better).
> 
> Hmm, the MII related ioctl's in some net drivers (checked for 3c59x,
> tulip, eepro100) are also querying the hardware. And a user is allowed to
> ask for this info (but not able to modify it).

And a malicious user calling those at a high rate is bound to get ugly.

In both of these situations, calling the ioctls without priveleges is
quite useful, so maybe rate-limiting for ioctls and proc files like this
would be a good idea in general.

	Jeff


-- 
Jeff Garzik      | Disbelief, that's why you fail.
Building 1024    |
MandrakeSoft     |

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for realthis
  2001-06-01 12:20       ` [PATCH] support for Cobalt Networks (x86 only) systems (for realthis time) Jeff Garzik
@ 2001-06-01 12:51         ` Alan Cox
  2001-06-01 12:56           ` Jeff Garzik
  0 siblings, 1 reply; 46+ messages in thread
From: Alan Cox @ 2001-06-01 12:51 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Bogdan Costescu, Pete Zaitcev, Linux Kernel Mailing List

> In both of these situations, calling the ioctls without priveleges is
> quite useful, so maybe rate-limiting for ioctls and proc files like this
> would be a good idea in general.

Many of them (like the MII and APM ones) the result can be cached 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for realthis
  2001-06-01 12:51         ` [PATCH] support for Cobalt Networks (x86 only) systems (for realthis Alan Cox
@ 2001-06-01 12:56           ` Jeff Garzik
  2001-06-01 12:58             ` Alan Cox
  0 siblings, 1 reply; 46+ messages in thread
From: Jeff Garzik @ 2001-06-01 12:56 UTC (permalink / raw)
  To: Alan Cox; +Cc: Bogdan Costescu, Pete Zaitcev, Linux Kernel Mailing List

Alan Cox wrote:
> > In both of these situations, calling the ioctls without priveleges is
> > quite useful, so maybe rate-limiting for ioctls and proc files like this
> > would be a good idea in general.

> Many of them (like the MII and APM ones) the result can be cached

Only some of them can be cached...  (some of the MIIs in some drivers
are already cached, in fact)   you can't cache stuff like what your link
partner is advertising at the moment, or what your battery status is at
the moment.

-- 
Jeff Garzik      | Disbelief, that's why you fail.
Building 1024    |
MandrakeSoft     |

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for realthis
  2001-06-01 12:56           ` Jeff Garzik
@ 2001-06-01 12:58             ` Alan Cox
  2001-06-01 13:06               ` Bogdan Costescu
  2001-06-02 22:04               ` [PATCH] support for Cobalt Networks (x86 only) systems (for realthis Jamie Lokier
  0 siblings, 2 replies; 46+ messages in thread
From: Alan Cox @ 2001-06-01 12:58 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: Alan Cox, Bogdan Costescu, Pete Zaitcev,
	Linux Kernel Mailing List

> Only some of them can be cached...  (some of the MIIs in some drivers
> are already cached, in fact)   you can't cache stuff like what your link
> partner is advertising at the moment, or what your battery status is at
> the moment.

I am sure that to an unpriviledged application reporting back the same result
as we saw last time we asked the hardware unless it is over 30 seconds old
will work fine. Maybe 10 for link partner ?


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for realthis
  2001-06-01 12:58             ` Alan Cox
@ 2001-06-01 13:06               ` Bogdan Costescu
  2001-06-01 13:15                 ` [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis Jeff Garzik
  2001-06-01 14:19                 ` [PATCH] support for Cobalt Networks (x86 only) systems (for Alan Cox
  2001-06-02 22:04               ` [PATCH] support for Cobalt Networks (x86 only) systems (for realthis Jamie Lokier
  1 sibling, 2 replies; 46+ messages in thread
From: Bogdan Costescu @ 2001-06-01 13:06 UTC (permalink / raw)
  To: Alan Cox
  Cc: Jeff Garzik, Bogdan Costescu, Pete Zaitcev,
	Linux Kernel Mailing List

On Fri, 1 Jun 2001, Alan Cox wrote:

> I am sure that to an unpriviledged application reporting back the same result
> as we saw last time we asked the hardware unless it is over 30 seconds old
> will work fine. Maybe 10 for link partner ?

No way! If I implement a HA application which depends on link status, I
want the info to be accurate, I don't want to know that 30 seconds ago I
had good link.

IMHO, rate limiting is the only solution.

-- 
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan.Costescu@IWR.Uni-Heidelberg.De


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis
  2001-06-01 13:06               ` Bogdan Costescu
@ 2001-06-01 13:15                 ` Jeff Garzik
  2001-06-01 13:19                   ` David S. Miller
  2001-06-01 13:39                   ` Bogdan Costescu
  2001-06-01 14:19                 ` [PATCH] support for Cobalt Networks (x86 only) systems (for Alan Cox
  1 sibling, 2 replies; 46+ messages in thread
From: Jeff Garzik @ 2001-06-01 13:15 UTC (permalink / raw)
  To: Bogdan Costescu; +Cc: Alan Cox, Pete Zaitcev, Linux Kernel Mailing List, netdev

Bogdan Costescu wrote:
> No way! If I implement a HA application which depends on link status, I
> want the info to be accurate, I don't want to know that 30 seconds ago I
> had good link.

To tangent a little bit, and add netdev to the CC...

The loss and regain of link status should be proactively signalled to
userspace using netlink or something similar.  Currently we have
netif_carrier_{on,off,ok} but it is only passively checked. 
netif_carrier_{on,off} should probably schedule_task() to fire off a
netlink message...

For your HA application specifically, right now, I would suggest making
sure your net driver calls netif_carrier_xxx correctly, then checking
for IFF_RUNNING interface flag.  IFF_RUNNING will disappear if the
interface is up, but there is no carrier [as according to
netif_carrier_ok].

-- 
Jeff Garzik      | Disbelief, that's why you fail.
Building 1024    |
MandrakeSoft     |

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis
  2001-06-01 13:15                 ` [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis Jeff Garzik
@ 2001-06-01 13:19                   ` David S. Miller
  2001-06-01 14:02                     ` Bogdan Costescu
  2001-06-01 13:39                   ` Bogdan Costescu
  1 sibling, 1 reply; 46+ messages in thread
From: David S. Miller @ 2001-06-01 13:19 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: Bogdan Costescu, Alan Cox, Pete Zaitcev,
	Linux Kernel Mailing List, netdev


Jeff Garzik writes:
 > For your HA application specifically, right now, I would suggest making
 > sure your net driver calls netif_carrier_xxx correctly, then checking
 > for IFF_RUNNING interface flag.  IFF_RUNNING will disappear if the
 > interface is up, but there is no carrier [as according to
 > netif_carrier_ok].

Don't such HA apps need to run as root anyways?

Regardless, I agree that, long term, the way to do this is via netlink.

Later,
David S. Miller
davem@redhat.com

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis
  2001-06-01 13:15                 ` [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis Jeff Garzik
  2001-06-01 13:19                   ` David S. Miller
@ 2001-06-01 13:39                   ` Bogdan Costescu
  2001-06-01 14:14                     ` jamal
  1 sibling, 1 reply; 46+ messages in thread
From: Bogdan Costescu @ 2001-06-01 13:39 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Alan Cox, Pete Zaitcev, Linux Kernel Mailing List, netdev

On Fri, 1 Jun 2001, Jeff Garzik wrote:

> The loss and regain of link status should be proactively signalled to
> userspace using netlink or something similar.

[ For the general discussion ]
I fully agree, but I just wanted to give an example of legit use from
user space of _current_ values from hardware.

>  Currently we have
> netif_carrier_{on,off,ok} but it is only passively checked.
> netif_carrier_{on,off} should probably schedule_task() to fire off a
> netlink message...

[ Link status details ]
Just that not all NICs have hardware support (and/or not all drivers use
these facilities) for link status change notification using interrupts.
Right now, most drivers _poll_ for media status and based on the poll
rate, netif_carrier routines are (or should be) called. We can't make the
poll rate very small for the general case, as MII access is time
consuming (same discussion was some months ago when the bonding driver
was updated). However, for users who know that they need this info to be
more accurate (at the expense of CPU time), polling through ioctl's is the
only solution.

[ Back to general discussion ]
So far, to the problem of too often access to hardware, 2 solutions were
proposed:
1. cache the values. You can then let the user shoot him-/her-self in the
   foot by making too many ioctl calls. But this prevent any legit use of
   current hardware state.
2. rate limiting. You don't let the user access the hardware too often (to
   be defined), so he/she can't shoot his-/her-self in the foot. Legit use
   of current hardware state is possible.

IMHO, solution 2 is much better. Can you find situations when it's not ?

-- 
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan.Costescu@IWR.Uni-Heidelberg.De




^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis
  2001-06-01 13:19                   ` David S. Miller
@ 2001-06-01 14:02                     ` Bogdan Costescu
  0 siblings, 0 replies; 46+ messages in thread
From: Bogdan Costescu @ 2001-06-01 14:02 UTC (permalink / raw)
  To: David S. Miller
  Cc: Jeff Garzik, Alan Cox, Pete Zaitcev, Linux Kernel Mailing List,
	netdev

On Fri, 1 Jun 2001, David S. Miller wrote:

> Don't such HA apps need to run as root anyways?

Not necessarily, but eventually you can let root (CAP_NET_ADMIN, anyway)
go through without any limitations, root can bring down the system at will
in other ways.

In addition, the rate limiting solution allows a warning to be issued when
the limit is exceeded, so that the poor sysadmin knows what hit him 8-)

-- 
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan.Costescu@IWR.Uni-Heidelberg.De


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis
  2001-06-01 13:39                   ` Bogdan Costescu
@ 2001-06-01 14:14                     ` jamal
  2001-06-01 14:30                       ` Bogdan Costescu
  0 siblings, 1 reply; 46+ messages in thread
From: jamal @ 2001-06-01 14:14 UTC (permalink / raw)
  To: Bogdan Costescu
  Cc: Jeff Garzik, Alan Cox, Pete Zaitcev, Linux Kernel Mailing List,
	netdev


Jeff, Thanks for copying netdev. Wish more people would do that.

On Fri, 1 Jun 2001, Bogdan Costescu wrote:

> On Fri, 1 Jun 2001, Jeff Garzik wrote:
>
> > The loss and regain of link status should be proactively signalled to
> > userspace using netlink or something similar.
>
> [ For the general discussion ]
> I fully agree, but I just wanted to give an example of legit use from
> user space of _current_ values from hardware.
>
> >  Currently we have
> > netif_carrier_{on,off,ok} but it is only passively checked.
> > netif_carrier_{on,off} should probably schedule_task() to fire off a
> > netlink message...
>
> [ Link status details ]
> Just that not all NICs have hardware support (and/or not all drivers use
> these facilities) for link status change notification using interrupts.
> Right now, most drivers _poll_ for media status and based on the poll
> rate, netif_carrier routines are (or should be) called. We can't make the
> poll rate very small for the general case, as MII access is time
> consuming (same discussion was some months ago when the bonding driver
> was updated). However, for users who know that they need this info to be
> more accurate (at the expense of CPU time), polling through ioctl's is the
> only solution.

Not really.

One idea i have been toying with is to maintain hysteris or threshold of
some form in dev_watchdog;
example: if watchdog timer expires threshold times, you declare the link
dead and send netif_carrier_off netlink message.
On recovery, you send  netif_carrier_on

Assumption:
If the tx path is blocked, more than likely the link is down.

cheers,
jamal


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for
  2001-06-01 13:06               ` Bogdan Costescu
  2001-06-01 13:15                 ` [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis Jeff Garzik
@ 2001-06-01 14:19                 ` Alan Cox
  2001-06-01 14:37                   ` Mark Frazer
  2001-06-01 15:13                   ` [PATCH] support for Cobalt Networks (x86 only) systems (for Bogdan Costescu
  1 sibling, 2 replies; 46+ messages in thread
From: Alan Cox @ 2001-06-01 14:19 UTC (permalink / raw)
  To: Bogdan Costescu
  Cc: Alan Cox, Jeff Garzik, Bogdan Costescu, Pete Zaitcev,
	Linux Kernel Mailing List

> No way! If I implement a HA application which depends on link status, I
> want the info to be accurate, I don't want to know that 30 seconds ago I
> had good link.
> 
> IMHO, rate limiting is the only solution.

Please re-read your comment. Then think about it. Then tell me how rate limiting
differs from caching to the application.

Alan


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis
  2001-06-01 14:14                     ` jamal
@ 2001-06-01 14:30                       ` Bogdan Costescu
  2001-06-02 14:49                         ` jamal
  0 siblings, 1 reply; 46+ messages in thread
From: Bogdan Costescu @ 2001-06-01 14:30 UTC (permalink / raw)
  To: jamal
  Cc: Jeff Garzik, Alan Cox, Pete Zaitcev, Linux Kernel Mailing List,
	netdev

On Fri, 1 Jun 2001, jamal wrote:

> Jeff, Thanks for copying netdev. Wish more people would do that.

Shame on me, I should have thought of that too... I joined lkml only about
2 weeks ago because netdev related topics are sometimes discussed only
there...

> Not really.
>
> One idea i have been toying with is to maintain hysteris or threshold of
> some form in dev_watchdog;

AFAIK, dev_watchdog is right now used only for Tx (if I'm wrong, please
correct me!). So how do you sense link loss if you expect only high Rx
traffic ?

> example: if watchdog timer expires threshold times, you declare the link
> dead and send netif_carrier_off netlink message.
> On recovery, you send  netif_carrier_on

I assume that you mean "on recovery" as in "first succesful hard_start_xmit".

> Assumption:
> If the tx path is blocked, more than likely the link is down.

Yes, but is this a good approximation ? I'm not saying that it's not, I'm
merely asking for counter-arguments.

-- 
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan.Costescu@IWR.Uni-Heidelberg.De



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for
  2001-06-01 14:37                   ` Mark Frazer
@ 2001-06-01 14:37                     ` Alan Cox
  2001-06-01 14:45                       ` Mark Frazer
  2001-06-01 14:46                       ` Bogdan Costescu
  2001-06-01 17:37                     ` Alan Cox
  1 sibling, 2 replies; 46+ messages in thread
From: Alan Cox @ 2001-06-01 14:37 UTC (permalink / raw)
  To: Mark Frazer
  Cc: Alan Cox, Bogdan Costescu, Jeff Garzik, Pete Zaitcev,
	Linux Kernel Mailing List

> I'd argue for rate limiting as the application only gets back new data,
> never a cached value n times in a row.

Which is worse. I cat the proc file a few times and your HA app is unlucky. It
now gets *NO* data for five minutes. If we cache the values it gets approximate
data



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for
  2001-06-01 14:19                 ` [PATCH] support for Cobalt Networks (x86 only) systems (for Alan Cox
@ 2001-06-01 14:37                   ` Mark Frazer
  2001-06-01 14:37                     ` Alan Cox
  2001-06-01 17:37                     ` Alan Cox
  2001-06-01 15:13                   ` [PATCH] support for Cobalt Networks (x86 only) systems (for Bogdan Costescu
  1 sibling, 2 replies; 46+ messages in thread
From: Mark Frazer @ 2001-06-01 14:37 UTC (permalink / raw)
  To: Alan Cox
  Cc: Bogdan Costescu, Jeff Garzik, Pete Zaitcev,
	Linux Kernel Mailing List

Alan Cox <alan@lxorguk.ukuu.org.uk> [01/06/01 10:32]:
> > No way! If I implement a HA application which depends on link status, I
> > want the info to be accurate, I don't want to know that 30 seconds ago I
> > had good link.
> > 
> > IMHO, rate limiting is the only solution.
> 
> Please re-read your comment. Then think about it. Then tell me how rate limiting
> differs from caching to the application.
> 

I'd argue for rate limiting as the application only gets back new data,
never a cached value n times in a row.

With caching, you'd have to let the application know when the cached
value was last read and how long it will be cached for.  With rate
limiting, you'd just block the app and get the new data to the app
once the timer has expired.  Or, if the app doesn't read the data
until after the timer has expired, it will not block at all.

Seems to me that you'd have less redundant application processing with
rate limiting.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for
  2001-06-01 14:37                     ` Alan Cox
@ 2001-06-01 14:45                       ` Mark Frazer
  2001-06-01 14:46                       ` Bogdan Costescu
  1 sibling, 0 replies; 46+ messages in thread
From: Mark Frazer @ 2001-06-01 14:45 UTC (permalink / raw)
  To: Alan Cox
  Cc: Bogdan Costescu, Jeff Garzik, Pete Zaitcev,
	Linux Kernel Mailing List

Alan Cox <alan@lxorguk.ukuu.org.uk> [01/06/01 10:39]:
> > I'd argue for rate limiting as the application only gets back new data,
> > never a cached value n times in a row.
> 
> Which is worse. I cat the proc file a few times and your HA app is unlucky. It
> now gets *NO* data for five minutes. If we cache the values it gets approximate
> data
> 

Is it not possible to have several readers waiting for the same data
and return it to all of them when the timer expires?


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for
  2001-06-01 14:37                     ` Alan Cox
  2001-06-01 14:45                       ` Mark Frazer
@ 2001-06-01 14:46                       ` Bogdan Costescu
  1 sibling, 0 replies; 46+ messages in thread
From: Bogdan Costescu @ 2001-06-01 14:46 UTC (permalink / raw)
  To: Alan Cox
  Cc: Mark Frazer, Jeff Garzik, Pete Zaitcev, Linux Kernel Mailing List

On Fri, 1 Jun 2001, Alan Cox wrote:

> Which is worse. I cat the proc file a few times and your HA app is unlucky. It
> now gets *NO* data for five minutes. If we cache the values it gets approximate
> data

Rate limit: current values, sysadmin knows when something's wrong or
            application knows when something's wrong (depending if you
            block or not when the limit is exceeded).
Caching: approximate values, sysadmin is not notified (unless something
         like ioctl/second is available somewhere), application is not
         notified.

[ Whoa, I can't type at the speed of this thread 8-) ]

-- 
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan.Costescu@IWR.Uni-Heidelberg.De


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for
  2001-06-01 14:19                 ` [PATCH] support for Cobalt Networks (x86 only) systems (for Alan Cox
  2001-06-01 14:37                   ` Mark Frazer
@ 2001-06-01 15:13                   ` Bogdan Costescu
  1 sibling, 0 replies; 46+ messages in thread
From: Bogdan Costescu @ 2001-06-01 15:13 UTC (permalink / raw)
  To: Alan Cox; +Cc: Jeff Garzik, Pete Zaitcev, Linux Kernel Mailing List, netdev


[ OK, this time I cc'ed netdev 8-) ]

On Fri, 1 Jun 2001, Alan Cox wrote:

> Please re-read your comment. Then think about it. Then tell me how rate
> limiting differs from caching to the application.

For caching, the kernel establishes the rate with which the info is
updated. There's nothing wrong, but how is the application to know if the
value is actual or cached (from when, until when) ? That means that a
single application that needs data more often than the caching rate will
get bogus data and not know about it.

With rate limiting, you always get new values, unless the limit is
exceeded. When the limit is exceeded, you log and:
- block any request until some timer is expired. The application can
detect that it's been blocked and react. You can detect if there are
several calls waiting and return the same value to all.
- return error until some timer is expired. The application can again
detect that.
In both cases, the application is also capable of guessing the value of
the delay.

For one application which follows the rules (doesn't need data more often
than the caching rate or doesn't exceed the rate limit) there is no
difference, I agree. But when somebody is playing tricks while you need
data, you have the chance of detecting this by using rate limits.

And yes, I agree that either of them (cache or rate limit) should be
modifiable through proc entry/ioctl/whatever.

-- 
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan.Costescu@IWR.Uni-Heidelberg.De








^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for
  2001-06-01 14:37                   ` Mark Frazer
  2001-06-01 14:37                     ` Alan Cox
@ 2001-06-01 17:37                     ` Alan Cox
  2001-06-02  8:20                       ` Bogdan Costescu
  1 sibling, 1 reply; 46+ messages in thread
From: Alan Cox @ 2001-06-01 17:37 UTC (permalink / raw)
  To: Mark Frazer
  Cc: Alan Cox, Bogdan Costescu, Jeff Garzik, Pete Zaitcev,
	Linux Kernel Mailing List

> I'd argue for rate limiting as the application only gets back new data,
> never a cached value n times in a row.

No the application gets back no data, ever, because a third party application
keeps beating it. You don't even need maliciousness for this, synchronization
effects and locking on the file will ensure it gets you in the end

> With caching, you'd have to let the application know when the cached
> value was last read and how long it will be cached for.  With rate

fstat() mtime. That seems easy enough


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for real  this  time)
  2001-06-01  8:43     ` Pete Zaitcev
@ 2001-06-01 18:29       ` Tim Hockin
  0 siblings, 0 replies; 46+ messages in thread
From: Tim Hockin @ 2001-06-01 18:29 UTC (permalink / raw)
  To: Pete Zaitcev; +Cc: linux-kernel

Pete Zaitcev wrote:

> > i2c is only in our stuff because the i2c core is not in the standard kernel
> > yet.  As soon as it is, I will make cobalt_i2c* go away.
> 
> I am puzzled by this comment. Did you look into drivers/i2c/?
> It certainly is a part of a stock kernel. The main user is
> the V4L, in drivers/media/video, but I think LM sensors use it too.

sorry, I meant to say:  The core is in, but the drivers for the adapters in
question are not.  They are part of lm_sensors, and as such, make it very
hard for us to maintain.  I have encouraged the lm_sensors crew to get at
LEAST the adapters/algorithms submitted for general inclusion.  Once that
is in, I will make cobalt_i2c go away.

Tim
-- 
Tim Hockin
Systems Software Engineer
Sun Microsystems, Cobalt Server Appliances
thockin@sun.com

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for
  2001-06-01 17:37                     ` Alan Cox
@ 2001-06-02  8:20                       ` Bogdan Costescu
  2001-06-02 16:15                         ` Alan Cox
  0 siblings, 1 reply; 46+ messages in thread
From: Bogdan Costescu @ 2001-06-02  8:20 UTC (permalink / raw)
  To: Alan Cox
  Cc: Mark Frazer, Jeff Garzik, Pete Zaitcev, Linux Kernel Mailing List,
	netdev

On Fri, 1 Jun 2001, Alan Cox wrote:

> No the application gets back no data, ever, because a third party application
> keeps beating it. You don't even need maliciousness for this, synchronization
> effects and locking on the file will ensure it gets you in the end

Sure, but as I already wrote, you can detect that something is wrong. Then
shoot the person!

> > With caching, you'd have to let the application know when the cached
> > value was last read and how long it will be cached for.  With rate
>
> fstat() mtime. That seems easy enough

This only answered the first part of the question: when. How do you pass
the "how long" info ?
Does the same applies for the MII ioctl case ?

Now let's talk about implementation issues. For the MII case, you have
several registers that have to be read. The way it generally done is for
the ioctl to pass MII address and register number and receive back the
value.

Caching means that the driver (I don't think that it can be done at
higher levels) has to keep track of accesses to all MII interfaces (yes,
there can be more than one on a NIC) and all of their registers. One
solution is to read all registers at once and start the cache timer for
each MII register access. Another solution is to have each register start
its own cache timer.

OTOH, ioctl rate limiting can be done at higher level and you need only
one timer per netdevice. So, it's done once and all net drivers benefit
from it.

Guess which one I prefer...

-- 
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan.Costescu@IWR.Uni-Heidelberg.De


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis
  2001-06-01 14:30                       ` Bogdan Costescu
@ 2001-06-02 14:49                         ` jamal
  2001-06-03 12:09                           ` Bogdan Costescu
  0 siblings, 1 reply; 46+ messages in thread
From: jamal @ 2001-06-02 14:49 UTC (permalink / raw)
  To: Bogdan Costescu
  Cc: Jeff Garzik, Alan Cox, Pete Zaitcev, Linux Kernel Mailing List,
	netdev



On Fri, 1 Jun 2001, Bogdan Costescu wrote:

> On Fri, 1 Jun 2001, jamal wrote:
>
> > One idea i have been toying with is to maintain hysteris or threshold of
> > some form in dev_watchdog;
>
> AFAIK, dev_watchdog is right now used only for Tx (if I'm wrong, please
> correct me!). So how do you sense link loss if you expect only high Rx
> traffic ?
>

Good question. Makes me think. Thoughts further below.

> > example: if watchdog timer expires threshold times, you declare the link
> > dead and send netif_carrier_off netlink message.
> > On recovery, you send  netif_carrier_on
>
> I assume that you mean "on recovery" as in "first succesful hard_start_xmit".
>

right.

> > Assumption:
> > If the tx path is blocked, more than likely the link is down.
>
> Yes, but is this a good approximation ? I'm not saying that it's not, I'm
> merely asking for counter-arguments.

It is an indirect approximation. Note that if the system data is very
asymetrical as in the case you pointed out, notification will take a long
long time. You need a plan B. Still, the tx watchdogs are a good source of
fault detection in the case of non-availabilty of MII detection and even
with the presence of MII.

I hate making this more complex than it should be:

Since we already have a messaging system within the kernel and
user<->kernel space aka "netlink" -- one could easily add a protocol in
user space which "dynamically heartbeats" the devices. Control should come
from user space; it would be a great idea to avoid ioctls.

"Dynamic" in the above sense means trying to totaly avoid making it a
synchronous poll. The poll rate is a function of how many packets go out
that device per average measurement time. Basically, the period that the
user space app dumps "hello" netlink packets to the kernel is a variable.

cheers,
jamal



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for
  2001-06-02  8:20                       ` Bogdan Costescu
@ 2001-06-02 16:15                         ` Alan Cox
  2001-06-02 19:10                           ` MII access (was [PATCH] support for Cobalt Networks (x86 only) systems) Bogdan Costescu
  0 siblings, 1 reply; 46+ messages in thread
From: Alan Cox @ 2001-06-02 16:15 UTC (permalink / raw)
  To: Bogdan Costescu
  Cc: Alan Cox, Mark Frazer, Jeff Garzik, Pete Zaitcev,
	Linux Kernel Mailing List, netdev

> > keeps beating it. You don't even need maliciousness for this, synchronization
> > effects and locking on the file will ensure it gets you in the end
> 
> Sure, but as I already wrote, you can detect that something is wrong. Then
> shoot the person!

How does that solve the problem ?

> > fstat() mtime. That seems easy enough
> 
> This only answered the first part of the question: when. How do you pass
> the "how long" info ?
> Does the same applies for the MII ioctl case ?

The mtime tells you exactly that.

> Caching means that the driver (I don't think that it can be done at
> higher levels) has to keep track of accesses to all MII interfaces (yes,
> there can be more than one on a NIC) and all of their registers. One

I disagree. A non priviledged app should not be able to poke around in MII
registers anyway. So you only have to cache the generic state of the link.

> each MII register access. Another solution is to have each register start
> its own cache timer.

You don't need timers.

> OTOH, ioctl rate limiting can be done at higher level and you need only
> one timer per netdevice. So, it's done once and all net drivers benefit
> from it.

You don't need any timers if you are caching. Zilch nada none. You know the
last time a query came in. The mtime lets the app know the last time the value
was modified.

Alan



^ permalink raw reply	[flat|nested] 46+ messages in thread

* MII access (was [PATCH] support for Cobalt Networks (x86 only) systems)
  2001-06-02 16:15                         ` Alan Cox
@ 2001-06-02 19:10                           ` Bogdan Costescu
  2001-06-02 19:25                             ` MII access (was [PATCH] support for Cobalt Networks (x86 only) Alan Cox
  0 siblings, 1 reply; 46+ messages in thread
From: Bogdan Costescu @ 2001-06-02 19:10 UTC (permalink / raw)
  To: Alan Cox
  Cc: Mark Frazer, Jeff Garzik, Pete Zaitcev, Linux Kernel Mailing List,
	netdev


[ As this is becoming more and more MII specific, I changed the subject ]

On Sat, 2 Jun 2001, Alan Cox wrote:

> > This only answered the first part of the question: when. How do you pass
> > the "how long" info ?
> > Does the same applies for the MII ioctl case ?
>
> The mtime tells you exactly that.

Alan, please consider this situation:

One application needs to poll link status with 1 second resolution. On a
system where caching is done with an unknown cache expiring time, this
application is sometimes fed incorrect data. So, you need a way to tell
for how long this situation lasts. If you have a proc/ioctl interface for
setting cache expiring time, this same interface can then be used for
reading back this info. This application can then check that this value is
lower than 1 second and if not, notify the user that it cannot run.

As this thread started as a general hardware access problem, would only
_one_ value for all these cases be sufficient ? Or each case should have
its own timeout ? Anyway, for MII, accessing the status at sub-second
intervals might be a legit one, so what measuring units should be used?

> I disagree. A non priviledged app should not be able to poke around in MII
> registers anyway. So you only have to cache the generic state of the link.

At the beginning of this thread, Jeff said "calling the ioctls without
priveleges is quite useful". Now if you say that there is no such case,
the whole problem could simply be solved by checking for the appropriate
priviledges.

I just realized another thing, important (IMHO) if a normal user is still
allowed to access MII: the drivers (checked for 3c59x, eepro100, tulip) do
not verify that the value passed for register number is within the allowed
range and use it as:

        int read_cmd = (0xf6 << 10) | ((phy_id & 0x1f) << 5) | location;

(phy_id is the MII address and location is the register number).

There is also no check that the MII address specified is actually in use
by the driver, but this is used with mii-diag to query a MII which was not
correctly identified (maybe this should be allowed for CAP_NET_ADMIN only ?)

>From one of Don Becker pages:
"MII transceivers have 32 management registers. The first 16 are reserved
for standard-defined uses, and the remaining one are available for
chip-specific features. Only the first seven registers are currently
defined."

Usually, the transceivers return garbage if you read from locations you
are not supposed to (overwritting phy_ad).  But if you begin overwritting
the READ command (0xf6 above)... Something like this should do:

        int read_cmd = (0xf6 << 10) | ((phy_id & 0x1f) << 5) | (location & 0x1f);

> You don't need timers.

Too tired to think straight yesterday... You're right. And if you alloc
32*sizeof(int) (you want to keep jiffies, right ?) per netdevice, I think
that it could even be done outside the driver. Hmm, most of my
previous arguments are no longer valid 8-(

-- 
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan.Costescu@IWR.Uni-Heidelberg.De


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: MII access (was [PATCH] support for Cobalt Networks (x86 only)
  2001-06-02 19:10                           ` MII access (was [PATCH] support for Cobalt Networks (x86 only) systems) Bogdan Costescu
@ 2001-06-02 19:25                             ` Alan Cox
  2001-06-02 21:36                               ` Bogdan Costescu
  0 siblings, 1 reply; 46+ messages in thread
From: Alan Cox @ 2001-06-02 19:25 UTC (permalink / raw)
  To: Bogdan Costescu
  Cc: Alan Cox, Mark Frazer, Jeff Garzik, Pete Zaitcev,
	Linux Kernel Mailing List, netdev

> One application needs to poll link status with 1 second resolution. On a

Then it needs to be privileged

> for how long this situation lasts. If you have a proc/ioctl interface for
> setting cache expiring time, this same interface can then be used for
> reading back this info. This application can then check that this value is
> lower than 1 second and if not, notify the user that it cannot run.

And if the approach is to block until the time for the next read occurs is
done then the program get stuck for 30 seconds, misses its deadline and kills
the cluster - how is this better ??

> Usually, the transceivers return garbage if you read from locations you
> are not supposed to (overwritting phy_ad).  But if you begin overwritting
> the READ command (0xf6 above)... Something like this should do:

Some of them just hang.

> Too tired to think straight yesterday... You're right. And if you alloc
> 32*sizeof(int) (you want to keep jiffies, right ?) per netdevice, I think
> that it could even be done outside the driver. Hmm, most of my
> previous arguments are no longer valid 8-(

Doing the MII monitoring somewhere centralised like the routing daemons would
certainly let more inteillgent management and reporting get done


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: MII access (was [PATCH] support for Cobalt Networks (x86 only)
  2001-06-02 19:25                             ` MII access (was [PATCH] support for Cobalt Networks (x86 only) Alan Cox
@ 2001-06-02 21:36                               ` Bogdan Costescu
  2001-06-02 21:37                                 ` Alan Cox
  0 siblings, 1 reply; 46+ messages in thread
From: Bogdan Costescu @ 2001-06-02 21:36 UTC (permalink / raw)
  To: Alan Cox
  Cc: Mark Frazer, Jeff Garzik, Pete Zaitcev, Linux Kernel Mailing List,
	netdev

On Sat, 2 Jun 2001, Alan Cox wrote:

> > One application needs to poll link status with 1 second resolution. On a
>
> Then it needs to be privileged

Fine. Can you think of a default value for expiring cache ?

> And if the approach is to block until the time for the next read occurs is
> done then the program get stuck for 30 seconds, misses its deadline and kills
> the cluster - how is this better ??

Is not better. Well, when somebody is playing against you, you're in
trouble either way:
- rate limit: - blocking - as above
              - non-blocking - notify the user that you can't get the info
                and probably stop or aquire elevated priviledges and try
                to restart the network
- cache: get outdated info

But when a HA application runs, it's usually preferable to stop (and you
notice it) than to continue with wrong data. Especially if you set the
cache expiry to something like 30 seconds; think in terms of how many
transactions/second today's hardware allows...

> Doing the MII monitoring somewhere centralised like the routing daemons would
> certainly let more inteillgent management and reporting get done

I don't argue over this point, already several people mentioned it. But I
explained the present situation in a previous message: the MII info is
normally read at a low rate and some applications need it more often. It
doesn't matter that it's delivered through ioctl, netlink or any other
way, you have to read it from the hardware and deliver to user-space at
user request. So the "doing the MII monitoring" is the tough part.

-- 
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan.Costescu@IWR.Uni-Heidelberg.De


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: MII access (was [PATCH] support for Cobalt Networks (x86 only)
  2001-06-02 21:36                               ` Bogdan Costescu
@ 2001-06-02 21:37                                 ` Alan Cox
  2001-06-03 11:59                                   ` Bogdan Costescu
  0 siblings, 1 reply; 46+ messages in thread
From: Alan Cox @ 2001-06-02 21:37 UTC (permalink / raw)
  To: Bogdan Costescu
  Cc: Alan Cox, Mark Frazer, Jeff Garzik, Pete Zaitcev,
	Linux Kernel Mailing List, netdev

> > Then it needs to be privileged
> 
> Fine. Can you think of a default value for expiring cache ?

Yeah .. so long as its a default and tunable in /proc.

> 


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (for realthis
  2001-06-01 12:58             ` Alan Cox
  2001-06-01 13:06               ` Bogdan Costescu
@ 2001-06-02 22:04               ` Jamie Lokier
  1 sibling, 0 replies; 46+ messages in thread
From: Jamie Lokier @ 2001-06-02 22:04 UTC (permalink / raw)
  To: Alan Cox
  Cc: Jeff Garzik, Bogdan Costescu, Pete Zaitcev,
	Linux Kernel Mailing List

Alan Cox wrote:
> > Only some of them can be cached...  (some of the MIIs in some drivers
> > are already cached, in fact)   you can't cache stuff like what your link
> > partner is advertising at the moment, or what your battery status is at
> > the moment.
> 
> I am sure that to an unpriviledged application reporting back the same result
> as we saw last time we asked the hardware unless it is over 30 seconds old
> will work fine. Maybe 10 for link partner ?

Please no, 30 seconds is way too long for "your mains power is
knackered, please fiddle with the power connector again" notification.
When I jiggle the wire I need to know if it's making contact within 1
second, and if it's lost when I let it go, I need to know that within
1 second so I can jiggle it some more.

Probably should try some contact cleaner ;-)

Thanks...
-- Jamie

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: MII access (was [PATCH] support for Cobalt Networks (x86 only)
  2001-06-02 21:37                                 ` Alan Cox
@ 2001-06-03 11:59                                   ` Bogdan Costescu
  2001-06-03 12:11                                     ` Jeff Garzik
  0 siblings, 1 reply; 46+ messages in thread
From: Bogdan Costescu @ 2001-06-03 11:59 UTC (permalink / raw)
  To: Alan Cox
  Cc: Mark Frazer, Jeff Garzik, Pete Zaitcev, Linux Kernel Mailing List,
	netdev

On Sat, 2 Jun 2001, Alan Cox wrote:

> > > Then it needs to be privileged
> >
> > Fine. Can you think of a default value for expiring cache ?
>
> Yeah .. so long as its a default and tunable in /proc.

New day, new ideea. The original problem was that unpriviledged users can
access it too often. How about exposing the MII registers as /dev entries?
Then you can have normal access rights for them and no need to worry about
frequency of access.  Probably default would be 600 owned by root and for
HA applications a user or a group can get read (or even write) access.
It's up to the sysadmin to allow it, but has to be renewed after each
boot. But I guess that this is not something to be applied to 2.2 and
2.4...

With clearer mind, I have to make some a correction to one of the previous
messages: the problem of not checking arguments range does not apply to
3c59x which has in the ioctl function '& 0x1f' for both transceiver number
and register number. However, eepro100 and tulip don't do that. (I'm
checking now with 2.4.3 from Mandrake 8, but I don't think that there were
recent changes in these areas).

-- 
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan.Costescu@IWR.Uni-Heidelberg.De


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis
  2001-06-02 14:49                         ` jamal
@ 2001-06-03 12:09                           ` Bogdan Costescu
  2001-06-04 18:43                             ` Jeff Garzik
  2001-06-04 19:08                             ` jamal
  0 siblings, 2 replies; 46+ messages in thread
From: Bogdan Costescu @ 2001-06-03 12:09 UTC (permalink / raw)
  To: jamal
  Cc: Jeff Garzik, Alan Cox, Pete Zaitcev, Linux Kernel Mailing List,
	netdev

On Sat, 2 Jun 2001, jamal wrote:

> Still, the tx watchdogs are a good source of fault detection in the case
> of non-availabilty of MII detection and even with the presence of MII.

Agreed. But my question was a bit different: is there any legit situation
where Tx timeouts can happen in a row _without_ having a link loss ? In
this situation, we'd have false positives...

> "Dynamic" in the above sense means trying to totaly avoid making it a
> synchronous poll. The poll rate is a function of how many packets go out
> that device per average measurement time. Basically, the period that the
> user space app dumps "hello" netlink packets to the kernel is a variable.

Sounds nice, but could this be implemented light enough ?

-- 
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan.Costescu@IWR.Uni-Heidelberg.De


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: MII access (was [PATCH] support for Cobalt Networks (x86 only)
  2001-06-03 11:59                                   ` Bogdan Costescu
@ 2001-06-03 12:11                                     ` Jeff Garzik
  2001-06-05  9:07                                       ` Bogdan Costescu
  0 siblings, 1 reply; 46+ messages in thread
From: Jeff Garzik @ 2001-06-03 12:11 UTC (permalink / raw)
  To: Bogdan Costescu
  Cc: Alan Cox, Mark Frazer, Pete Zaitcev, Linux Kernel Mailing List,
	netdev

Bogdan Costescu wrote:
> With clearer mind, I have to make some a correction to one of the previous
> messages: the problem of not checking arguments range does not apply to
> 3c59x which has in the ioctl function '& 0x1f' for both transceiver number
> and register number. However, eepro100 and tulip don't do that. (I'm
> checking now with 2.4.3 from Mandrake 8, but I don't think that there were
> recent changes in these areas).

half right -- tulip does this for the phy id but not the MII register
number.  I'll fix that up.  Please bug Andrey about fixing up
eepro100...

-- 
Jeff Garzik      | Echelon words of the day, from The Register:
Building 1024    | FRU Lebed HALO Spetznaz Al Amn al-Askari Glock 26 
MandrakeSoft     | Steak Knife Kill the President anarchy echelon
                 | nuclear assassinate Roswell Waco World Trade Center

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis
  2001-06-03 12:09                           ` Bogdan Costescu
@ 2001-06-04 18:43                             ` Jeff Garzik
  2001-06-04 19:08                             ` jamal
  1 sibling, 0 replies; 46+ messages in thread
From: Jeff Garzik @ 2001-06-04 18:43 UTC (permalink / raw)
  To: Bogdan Costescu
  Cc: jamal, Alan Cox, Pete Zaitcev, Linux Kernel Mailing List, netdev

Bogdan Costescu wrote:
> 
> On Sat, 2 Jun 2001, jamal wrote:
> 
> > Still, the tx watchdogs are a good source of fault detection in the case
> > of non-availabilty of MII detection and even with the presence of MII.
> 
> Agreed. But my question was a bit different: is there any legit situation
> where Tx timeouts can happen in a row _without_ having a link loss ? In
> this situation, we'd have false positives...

yes

-- 
Jeff Garzik      | Echelon words of the day, from The Register:
Building 1024    | FRU Lebed HALO Spetznaz Al Amn al-Askari Glock 26 
MandrakeSoft     | Steak Knife Kill the President anarchy echelon
                 | nuclear assassinate Roswell Waco World Trade Center

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis
  2001-06-03 12:09                           ` Bogdan Costescu
  2001-06-04 18:43                             ` Jeff Garzik
@ 2001-06-04 19:08                             ` jamal
  1 sibling, 0 replies; 46+ messages in thread
From: jamal @ 2001-06-04 19:08 UTC (permalink / raw)
  To: Bogdan Costescu
  Cc: Jeff Garzik, Alan Cox, Pete Zaitcev, Linux Kernel Mailing List,
	netdev



On Sun, 3 Jun 2001, Bogdan Costescu wrote:

> On Sat, 2 Jun 2001, jamal wrote:
>
> > Still, the tx watchdogs are a good source of fault detection in the case
> > of non-availabilty of MII detection and even with the presence of MII.
>
> Agreed. But my question was a bit different: is there any legit situation
> where Tx timeouts can happen in a row _without_ having a link loss ? In
> this situation, we'd have false positives...

Two places:
1) no MII indicators
2) shaky hardware and MII bounces. Is it on, is it off? What is going on?
You  could use them to "probe" to make sure that infact the MII indicators
are not false positives.

Your mileage may vary.

>
> > "Dynamic" in the above sense means trying to totaly avoid making it a
> > synchronous poll. The poll rate is a function of how many packets go out
> > that device per average measurement time. Basically, the period that the
> > user space app dumps "hello" netlink packets to the kernel is a variable.
>
> Sounds nice, but could this be implemented light enough ?
>

Not as simple as synchronous polls.
Note, however, simple/light does not imply the best.

cheers,
jamal



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: MII access (was [PATCH] support for Cobalt Networks (x86 only)
  2001-06-03 12:11                                     ` Jeff Garzik
@ 2001-06-05  9:07                                       ` Bogdan Costescu
  2001-06-10  2:07                                         ` eepro100 security fix [was: Re: MII access] Andrey Savochkin
  0 siblings, 1 reply; 46+ messages in thread
From: Bogdan Costescu @ 2001-06-05  9:07 UTC (permalink / raw)
  To: Jeff Garzik
  Cc: Alan Cox, Mark Frazer, Pete Zaitcev, Linux Kernel Mailing List,
	netdev, saw

On Sun, 3 Jun 2001, Jeff Garzik wrote:

> Bogdan Costescu wrote:
> > With clearer mind, I have to make some a correction to one of the previous
> > messages: the problem of not checking arguments range does not apply to
> > 3c59x which has in the ioctl function '& 0x1f' for both transceiver number
> > and register number. However, eepro100 and tulip don't do that. (I'm
> > checking now with 2.4.3 from Mandrake 8, but I don't think that there were
> > recent changes in these areas).
>
> half right -- tulip does this for the phy id but not the MII register
> number.  I'll fix that up.  Please bug Andrey about fixing up
> eepro100...

OK, Andrey is now CC-ed. However, I only checked the 3 mentioned drivers,
while MII ioctl's are used in many more... I was hoping that the
mantainers would jump in!

-- 
Bogdan Costescu

IWR - Interdisziplinaeres Zentrum fuer Wissenschaftliches Rechnen
Universitaet Heidelberg, INF 368, D-69120 Heidelberg, GERMANY
Telephone: +49 6221 54 8869, Telefax: +49 6221 54 8868
E-mail: Bogdan.Costescu@IWR.Uni-Heidelberg.De


^ permalink raw reply	[flat|nested] 46+ messages in thread

* eepro100 security fix [was: Re: MII access]
  2001-06-05  9:07                                       ` Bogdan Costescu
@ 2001-06-10  2:07                                         ` Andrey Savochkin
  0 siblings, 0 replies; 46+ messages in thread
From: Andrey Savochkin @ 2001-06-10  2:07 UTC (permalink / raw)
  To: torvalds
  Cc: Bogdan Costescu, Jeff Garzik, Alan Cox, Mark Frazer, Pete Zaitcev,
	Linux Kernel Mailing List, netdev

[-- Attachment #1: Type: text/plain, Size: 934 bytes --]

Linus,

Please apply the attached patch.
It fixes a security problem of user-controlled access to the card ports from
a non-privileged ioctl which should have read-only semantics.

Best regards
		Andrey

On Tue, Jun 05, 2001 at 11:07:06AM +0200, Bogdan Costescu wrote:
> On Sun, 3 Jun 2001, Jeff Garzik wrote:
> 
> > Bogdan Costescu wrote:
> > > With clearer mind, I have to make some a correction to one of the previous
> > > messages: the problem of not checking arguments range does not apply to
> > > 3c59x which has in the ioctl function '& 0x1f' for both transceiver number
> > > and register number. However, eepro100 and tulip don't do that. (I'm
> > > checking now with 2.4.3 from Mandrake 8, but I don't think that there were
> > > recent changes in these areas).
> >
> > half right -- tulip does this for the phy id but not the MII register
> > number.  I'll fix that up.  Please bug Andrey about fixing up
> > eepro100...

[-- Attachment #2: mii-access1 --]
[-- Type: text/plain, Size: 489 bytes --]

--- drivers/net/eepro100.c.prev	Sat Jan 27 05:07:13 2001
+++ drivers/net/eepro100.c	Wed Jun  6 22:26:03 2001
@@ -1913,7 +1913,7 @@
 		   timer routine.  2000/05/09 SAW */
 		saved_acpi = pci_set_power_state(sp->pdev, 0);
 		t = del_timer_sync(&sp->timer);
-		data[3] = mdio_read(ioaddr, data[0], data[1]);
+		data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
 		if (t)
 			add_timer(&sp->timer); /* may be set to the past  --SAW */
 		pci_set_power_state(sp->pdev, saved_acpi);

^ permalink raw reply	[flat|nested] 46+ messages in thread

end of thread, other threads:[~2001-06-10  2:09 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2001-06-01  2:47 [PATCH] support for Cobalt Networks (x86 only) systems (for real this time) Tim Hockin
2001-06-01  8:10 ` Jeff Garzik
2001-06-01  8:46 ` [PATCH] support for Cobalt Networks (x86 only) systems (for real this Alan Cox
     [not found] ` <mailman.991383180.28261.linux-kernel2news@redhat.com>
2001-06-01  8:58   ` [PATCH] support for Cobalt Networks (x86 only) systems (for real this time) Pete Zaitcev
2001-06-01 12:03     ` Bogdan Costescu
2001-06-01 12:20       ` [PATCH] support for Cobalt Networks (x86 only) systems (for realthis time) Jeff Garzik
2001-06-01 12:51         ` [PATCH] support for Cobalt Networks (x86 only) systems (for realthis Alan Cox
2001-06-01 12:56           ` Jeff Garzik
2001-06-01 12:58             ` Alan Cox
2001-06-01 13:06               ` Bogdan Costescu
2001-06-01 13:15                 ` [PATCH] support for Cobalt Networks (x86 only) systems (forrealthis Jeff Garzik
2001-06-01 13:19                   ` David S. Miller
2001-06-01 14:02                     ` Bogdan Costescu
2001-06-01 13:39                   ` Bogdan Costescu
2001-06-01 14:14                     ` jamal
2001-06-01 14:30                       ` Bogdan Costescu
2001-06-02 14:49                         ` jamal
2001-06-03 12:09                           ` Bogdan Costescu
2001-06-04 18:43                             ` Jeff Garzik
2001-06-04 19:08                             ` jamal
2001-06-01 14:19                 ` [PATCH] support for Cobalt Networks (x86 only) systems (for Alan Cox
2001-06-01 14:37                   ` Mark Frazer
2001-06-01 14:37                     ` Alan Cox
2001-06-01 14:45                       ` Mark Frazer
2001-06-01 14:46                       ` Bogdan Costescu
2001-06-01 17:37                     ` Alan Cox
2001-06-02  8:20                       ` Bogdan Costescu
2001-06-02 16:15                         ` Alan Cox
2001-06-02 19:10                           ` MII access (was [PATCH] support for Cobalt Networks (x86 only) systems) Bogdan Costescu
2001-06-02 19:25                             ` MII access (was [PATCH] support for Cobalt Networks (x86 only) Alan Cox
2001-06-02 21:36                               ` Bogdan Costescu
2001-06-02 21:37                                 ` Alan Cox
2001-06-03 11:59                                   ` Bogdan Costescu
2001-06-03 12:11                                     ` Jeff Garzik
2001-06-05  9:07                                       ` Bogdan Costescu
2001-06-10  2:07                                         ` eepro100 security fix [was: Re: MII access] Andrey Savochkin
2001-06-01 15:13                   ` [PATCH] support for Cobalt Networks (x86 only) systems (for Bogdan Costescu
2001-06-02 22:04               ` [PATCH] support for Cobalt Networks (x86 only) systems (for realthis Jamie Lokier
  -- strict thread matches above, loose matches on Subject: below --
2001-06-01  2:49 [PATCH] support for Cobalt Networks (x86 only) systems (for real this time) Tim Hockin
2001-06-01  4:47 ` Dax Kelson
2001-06-01  7:47 ` Jeff Garzik
     [not found] <mailman.991363680.24671.linux-kernel2news@redhat.com>
2001-06-01  4:09 ` Pete Zaitcev
2001-06-01  6:57   ` Tim Hockin
2001-06-01  7:27     ` Jeff Garzik
2001-06-01  8:43     ` Pete Zaitcev
2001-06-01 18:29       ` Tim Hockin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox