patch-2.3.24 linux/drivers/misc/acpi.c

Next file: linux/drivers/net/Config.in
Previous file: linux/drivers/macintosh/macserial.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.23/linux/drivers/misc/acpi.c linux/drivers/misc/acpi.c
@@ -29,8 +29,13 @@
 #include <linux/sched.h>
 #include <linux/wait.h>
 #include <linux/spinlock.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
+#include <linux/sysctl.h>
+#include <linux/delay.h>
 #include <linux/acpi.h>
 
 /*
@@ -49,7 +54,26 @@
 #define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue * x = NULL
 #endif
 
+static int acpi_do_ulong(ctl_table *ctl,
+			 int write,
+			 struct file *file,
+			 void *buffer,
+			 size_t *len);
+static int acpi_do_event_reg(ctl_table *ctl,
+			     int write,
+			     struct file *file,
+			     void *buffer,
+			     size_t *len);
+static int acpi_do_event(ctl_table *ctl,
+			 int write,
+			 struct file *file,
+			 void *buffer,
+			 size_t *len);
+
+static struct ctl_table_header *acpi_sysctl = NULL;
+
 static struct acpi_facp *acpi_facp = NULL;
+static int acpi_fake_facp = 0;
 static unsigned long acpi_facp_addr = 0;
 static unsigned long acpi_dsdt_addr = 0;
 
@@ -57,15 +81,64 @@
 static volatile u32 acpi_pm1_status = 0;
 static volatile u32 acpi_gpe_status = 0;
 static volatile u32 acpi_gpe_level = 0;
-static DECLARE_WAIT_QUEUE_HEAD(acpi_wait_event);
+static DECLARE_WAIT_QUEUE_HEAD(acpi_event_wait);
 
 /* Make it impossible to enter L2/L3 until after we've initialized */
 static unsigned long acpi_p_lvl2_lat = ~0UL;
 static unsigned long acpi_p_lvl3_lat = ~0UL;
 
 /* Initialize to guaranteed harmless port read */
-static u16 acpi_p_lvl2 = 0x80;
-static u16 acpi_p_lvl3 = 0x80;
+static unsigned long acpi_p_lvl2 = 0x80;
+static unsigned long acpi_p_lvl3 = 0x80;
+
+static struct ctl_table acpi_table[] =
+{
+	{ACPI_FACP, "facp",
+	 &acpi_facp_addr, sizeof(acpi_facp_addr),
+	 0400, NULL, &acpi_do_ulong},
+
+	{ACPI_DSDT, "dsdt",
+	 &acpi_dsdt_addr, sizeof(acpi_dsdt_addr),
+	 0400, NULL, &acpi_do_ulong},
+
+	{ACPI_PM1_ENABLE, "pm1_enable",
+	 NULL, 0,
+	 0600, NULL, &acpi_do_event_reg},
+
+	{ACPI_GPE_ENABLE, "gpe_enable",
+	 NULL, 0,
+	 0600, NULL, &acpi_do_event_reg},
+
+	{ACPI_GPE_LEVEL, "gpe_level",
+	 NULL, 0,
+	 0600, NULL, &acpi_do_event_reg},
+
+	{ACPI_EVENT, "event", NULL, 0, 0400, NULL, &acpi_do_event},
+
+	{ACPI_P_LVL2, "p_lvl2",
+	 &acpi_p_lvl2, sizeof(acpi_p_lvl2),
+	 0600, NULL, &acpi_do_ulong},
+
+	{ACPI_P_LVL3, "p_lvl3",
+	 &acpi_p_lvl3, sizeof(acpi_p_lvl3),
+	 0600, NULL, &acpi_do_ulong},
+
+	{ACPI_P_LVL2_LAT, "p_lvl2_lat",
+	 &acpi_p_lvl2_lat, sizeof(acpi_p_lvl2_lat),
+	 0600, NULL, &acpi_do_ulong},
+
+	{ACPI_P_LVL3_LAT, "p_lvl3_lat",
+	 &acpi_p_lvl3_lat, sizeof(acpi_p_lvl3_lat),
+	 0600, NULL, &acpi_do_ulong},
+
+	{0}
+};
+
+static struct ctl_table acpi_dir_table[] =
+{
+	{CTL_ACPI, "acpi", NULL, 0, 0500, acpi_table},
+	{0}
+};
 
 
 /*
@@ -73,7 +146,9 @@
  */
 static u32 acpi_read_pm1_control(struct acpi_facp *facp)
 {
-	u32 value = inw(facp->pm1a_cnt);
+	u32 value = 0;
+	if (facp->pm1a_cnt)
+		value = inw(facp->pm1a_cnt);
 	if (facp->pm1b_cnt)
 		value |= inw(facp->pm1b_cnt);
 	return value;
@@ -84,7 +159,9 @@
  */
 static u32 acpi_read_pm1_status(struct acpi_facp *facp)
 {
-	u32 value = inw(facp->pm1a_evt);
+	u32 value = 0;
+	if (facp->pm1a_evt)
+		value = inw(facp->pm1a_evt);
 	if (facp->pm1b_evt)
 		value |= inw(facp->pm1b_evt);
 	return value;
@@ -95,7 +172,8 @@
  */
 static void acpi_write_pm1_status(struct acpi_facp *facp, u32 value)
 {
-	outw(value, facp->pm1a_evt);
+	if (facp->pm1a_evt)
+		outw(value, facp->pm1a_evt);
 	if (facp->pm1b_evt)
 		outw(value, facp->pm1b_evt);
 }
@@ -106,7 +184,9 @@
 static u32 acpi_read_pm1_enable(struct acpi_facp *facp)
 {
 	int offset = facp->pm1_evt_len >> 1;
-	u32 value = inw(facp->pm1a_evt + offset);
+	u32 value = 0;
+	if (facp->pm1a_evt)
+		value = inw(facp->pm1a_evt + offset);
 	if (facp->pm1b_evt)
 		value |= inw(facp->pm1b_evt + offset);
 	return value;
@@ -118,7 +198,8 @@
 static void acpi_write_pm1_enable(struct acpi_facp *facp, u32 value)
 {
 	int offset = facp->pm1_evt_len >> 1;
-	outw(value, facp->pm1a_evt + offset);
+	if (facp->pm1a_evt)
+		outw(value, facp->pm1a_evt + offset);
 	if (facp->pm1b_evt)
 		outw(value, facp->pm1b_evt + offset);
 }
@@ -136,9 +217,11 @@
 		for (i = size - 1; i >= 0; i--)
 			value = (value << 8) | inb(facp->gpe1 + i);
 	}
-	size = facp->gpe0_len >> 1;
-	for (i = size - 1; i >= 0; i--)
-		value = (value << 8) | inb(facp->gpe0 + i);
+	if (facp->gpe0) {
+		size = facp->gpe0_len >> 1;
+		for (i = size - 1; i >= 0; i--)
+			value = (value << 8) | inb(facp->gpe0 + i);
+	}
 	return value;
 }
 
@@ -149,10 +232,12 @@
 {
 	int i, size;
 
-	size = facp->gpe0_len >> 1;
-	for (i = 0; i < size; i++) {
-		outb(value & 0xff, facp->gpe0 + i);
-		value >>= 8;
+	if (facp->gpe0) {
+		size = facp->gpe0_len >> 1;
+		for (i = 0; i < size; i++) {
+			outb(value & 0xff, facp->gpe0 + i);
+			value >>= 8;
+		}
 	}
 	if (facp->gpe1) {
 		size = facp->gpe1_len >> 1;
@@ -178,9 +263,11 @@
 			value = (value << 8) | inb(facp->gpe1 + offset + i);
 		}
 	}
-	size = facp->gpe0_len >> 1;
-	for (i = size - 1; i >= 0; i--)
-		value = (value << 8) | inb(facp->gpe0 + offset + i);
+	if (facp->gpe0) {
+		size = facp->gpe0_len >> 1;
+		for (i = size - 1; i >= 0; i--)
+			value = (value << 8) | inb(facp->gpe0 + offset + i);
+	}
 	return value;
 }
 
@@ -192,9 +279,11 @@
 	int i, offset;
 
 	offset = facp->gpe0_len >> 1;
-	for (i = 0; i < offset; i++) {
-		outb(value & 0xff, facp->gpe0 + offset + i);
-		value >>= 8;
+	if (facp->gpe0) {
+		for (i = 0; i < offset; i++) {
+			outb(value & 0xff, facp->gpe0 + offset + i);
+			value >>= 8;
+		}
 	}
 	if (facp->gpe1) {
 		offset = facp->gpe1_len >> 1;
@@ -238,9 +327,9 @@
 }
 
 /*
- * Locate and map ACPI tables (FACP, DSDT, ...)
+ * Locate and map ACPI tables
  */
-static int __init acpi_map_tables(void)
+static int __init acpi_find_tables(void)
 {
 	struct acpi_rsdp *rsdp;
 	struct acpi_table *rsdt;
@@ -251,8 +340,8 @@
 	// search BIOS memory for RSDP
 	for (i = ACPI_BIOS_ROM_BASE; i < ACPI_BIOS_ROM_END; i += 16) {
 		rsdp = (struct acpi_rsdp *) phys_to_virt(i);
-		if (rsdp->signature[0] == ACPI_RSDP1_SIG &&
-		    rsdp->signature[1] == ACPI_RSDP2_SIG) {
+		if (rsdp->signature[0] == ACPI_RSDP1_SIG
+		    && rsdp->signature[1] == ACPI_RSDP2_SIG) {
 			char oem[7];
 			int j;
 
@@ -270,14 +359,13 @@
 			break;
 		}
 	}
-	if (i >= ACPI_BIOS_ROM_END) {
-		printk(KERN_ERR "ACPI: no RSDP found\n");
+	if (i >= ACPI_BIOS_ROM_END)
 		return -ENODEV;
-	}
+
 	// fetch RSDT from RSDP
 	rsdt = acpi_map_table(rsdp->rsdt);
 	if (!rsdt || rsdt->signature != ACPI_RSDT_SIG) {
-		printk(KERN_ERR "ACPI: no RSDT found\n");
+		printk(KERN_ERR "ACPI: missing RSDT\n");
 		acpi_unmap_table(rsdt);
 		return -ENODEV;
 	}
@@ -288,11 +376,11 @@
 	while (rsdt_entry_count) {
 		struct acpi_table *dt = acpi_map_table(*rsdt_entry);
 		if (dt && dt->signature == ACPI_FACP_SIG) {
-			acpi_facp = (struct acpi_facp *) dt;
+			acpi_facp = (struct acpi_facp*) dt;
 			acpi_facp_addr = *rsdt_entry;
 			acpi_dsdt_addr = acpi_facp->dsdt;
-			break;
-		} else {
+		}
+		else {
 			acpi_unmap_table(dt);
 		}
 		rsdt_entry++;
@@ -302,22 +390,89 @@
 	acpi_unmap_table(rsdt);
 
 	if (!acpi_facp) {
-		printk(KERN_ERR "ACPI: no FACP found\n");
+		printk(KERN_ERR "ACPI: missing FACP\n");
 		return -ENODEV;
 	}
 	return 0;
 }
 
 /*
- * Unmap ACPI tables (FACP, DSDT, ...)
+ * Unmap or destroy ACPI tables
  */
-static void acpi_unmap_tables(void)
+static void acpi_destroy_tables(void)
 {
-	acpi_idle = NULL;
+	if (!acpi_fake_facp)
+		acpi_unmap_table((struct acpi_table*) acpi_facp);
+	else
+		kfree(acpi_facp);
+}
+
+/*
+ * Locate PIIX4 device and create a fake FACP
+ */
+static int __init acpi_find_piix4(void)
+{
+	struct pci_dev *dev;
+	u32 base;
+	u16 cmd;
+	u8 pmregmisc;
+
+	dev = pci_find_device(PCI_VENDOR_ID_INTEL,
+			      PCI_DEVICE_ID_INTEL_82371AB_3,
+			      NULL);
+	if (!dev)
+		return -ENODEV;
+	
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	if (!(cmd & PCI_COMMAND_IO))
+		return -ENODEV;
+	
+	pci_read_config_byte(dev, ACPI_PIIX4_PMREGMISC, &pmregmisc);
+	if (!(pmregmisc & ACPI_PIIX4_PMIOSE))
+		return -ENODEV;
+	
+	pci_read_config_dword(dev, 0x40, &base);
+	if (!(base & PCI_BASE_ADDRESS_SPACE_IO))
+		return -ENODEV;
+	
+	base &= PCI_BASE_ADDRESS_IO_MASK;
+	if (!base)
+		return -ENODEV;
+
+	printk(KERN_INFO "ACPI: found PIIX4 at 0x%04x\n", base);
+
+	acpi_facp = kmalloc(sizeof(struct acpi_facp), GFP_KERNEL);
+	if (!acpi_facp)
+		return -ENOMEM;
+
+	acpi_fake_facp = 1;
+	memset(acpi_facp, 0, sizeof(struct acpi_facp));
+	acpi_facp->int_model = ACPI_PIIX4_INT_MODEL;
+	acpi_facp->sci_int = ACPI_PIIX4_SCI_INT;
+	acpi_facp->smi_cmd = ACPI_PIIX4_SMI_CMD;
+	acpi_facp->acpi_enable = ACPI_PIIX4_ACPI_ENABLE;
+	acpi_facp->acpi_disable = ACPI_PIIX4_ACPI_DISABLE;
+	acpi_facp->s4bios_req = ACPI_PIIX4_S4BIOS_REQ;
+	acpi_facp->pm1a_evt = base + ACPI_PIIX4_PM1_EVT;
+	acpi_facp->pm1a_cnt = base + ACPI_PIIX4_PM1_CNT;
+	acpi_facp->pm2_cnt = ACPI_PIIX4_PM2_CNT;
+	acpi_facp->pm_tmr = base + ACPI_PIIX4_PM_TMR;
+	acpi_facp->gpe0 = base + ACPI_PIIX4_GPE0;
+	acpi_facp->pm1_evt_len = ACPI_PIIX4_PM1_EVT_LEN;
+	acpi_facp->pm1_cnt_len = ACPI_PIIX4_PM1_CNT_LEN;
+	acpi_facp->pm2_cnt_len = ACPI_PIIX4_PM2_CNT_LEN;
+	acpi_facp->pm_tm_len = ACPI_PIIX4_PM_TM_LEN;
+	acpi_facp->gpe0_len = ACPI_PIIX4_GPE0_LEN;
+	acpi_facp->p_lvl2_lat = ~0;
+	acpi_facp->p_lvl3_lat = ~0;
+
+	acpi_facp_addr = virt_to_phys(acpi_facp);
 	acpi_dsdt_addr = 0;
-	acpi_facp_addr = 0;
-	acpi_unmap_table((struct acpi_table *) acpi_facp);
-	acpi_facp = NULL;
+
+	acpi_p_lvl2 = base + ACPI_PIIX4_P_LVL2;
+	acpi_p_lvl3 = base + ACPI_PIIX4_P_LVL3;
+
+	return 0;
 }
 
 /*
@@ -355,25 +510,7 @@
 	acpi_pm1_status |= pm1_status;
 	acpi_gpe_status |= gpe_status;
 	spin_unlock_irqrestore(&acpi_event_lock, flags);
-	wake_up_interruptible(&acpi_wait_event);
-}
-
-/*
- * Handle open of /dev/acpi
- */
-static int acpi_open(struct inode *inode, struct file *file)
-{
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-/*
- * Handle close of /dev/acpi
- */
-static int acpi_release(struct inode *inode, struct file *file)
-{
-	MOD_DEC_USE_COUNT;
-	return 0;
+	wake_up_interruptible(&acpi_event_wait);
 }
 
 /*
@@ -389,7 +526,8 @@
  */
 static int acpi_enable(struct acpi_facp *facp)
 {
-	outb(facp->acpi_enable, facp->smi_cmd);
+	if (facp->smi_cmd)
+		outb(facp->acpi_enable, facp->smi_cmd);
 	return (acpi_is_enabled(facp) ? 0:-1);
 }
 
@@ -404,132 +542,29 @@
 		acpi_write_gpe_status(facp, acpi_read_gpe_status(facp));
 	acpi_write_pm1_enable(facp, 0);
 	acpi_write_pm1_status(facp, acpi_read_pm1_status(facp));
-	
-	outb(facp->acpi_disable, facp->smi_cmd);
+
+	if (facp->smi_cmd)
+		outb(facp->acpi_disable, facp->smi_cmd);
 	return (acpi_is_enabled(facp) ? -1:0);
 }
 
 /*
- * Handle command to /dev/acpi
+ * Idle loop
  */
-static int acpi_ioctl(struct inode *inode,
-		      struct file *file,
-		      unsigned cmd,
-		      unsigned long arg)
-{
-	int status = -EINVAL;
-
-	if (!capable(CAP_SYS_ADMIN))
-		return -EPERM;
-
-	switch (cmd) {
-	case ACPI_FIND_TABLES:
-		status = verify_area(VERIFY_WRITE,
-				     (void *) arg,
-				     sizeof(struct acpi_find_tables));
-		if (!status) {
-			struct acpi_find_tables *rqst
-				= (struct acpi_find_tables *) arg;
-			put_user(acpi_facp_addr, &rqst->facp);
-			put_user(acpi_dsdt_addr, &rqst->dsdt);
-			status = 0;
-		}
-		break;
-	case ACPI_ENABLE_EVENT:
-		status = verify_area(VERIFY_READ,
-				     (void *) arg,
-				     sizeof(struct acpi_enable_event));
-		if (!status) {
-			struct acpi_enable_event *rqst
-				= (struct acpi_enable_event *) arg;
-			u32 pm1_enable, gpe_enable, gpe_level;
-			u32 pm1_enabling, gpe_enabling;
-
-			get_user(pm1_enable, &rqst->pm1_enable);
-			get_user(gpe_enable, &rqst->gpe_enable);
-			get_user(gpe_level, &rqst->gpe_level);
-			gpe_level &= gpe_enable;
-			
-			// clear previously disabled events before enabling
-			pm1_enabling = (pm1_enable
-					& ~acpi_read_pm1_enable(acpi_facp));
-			acpi_write_pm1_status(acpi_facp, pm1_enabling);
-			gpe_enabling = (gpe_enable &
-					~acpi_read_gpe_enable(acpi_facp));
-			while (acpi_read_gpe_status(acpi_facp) & gpe_enabling)
-				acpi_write_gpe_status(acpi_facp, gpe_enabling);
-
-			status = 0;
-
-			if (pm1_enable || gpe_enable) {
-				// enable ACPI unless it is already
-				if (!acpi_is_enabled(acpi_facp)
-				    && acpi_enable(acpi_facp)) {
-					status = -EBUSY;
-				}
-			}
-			else {
-				// disable ACPI unless it is already
-				if (acpi_is_enabled(acpi_facp)
-				    && acpi_disable(acpi_facp)) {
-					status = -EBUSY;
-				}
-			}
-
-			if (!status)
-			{
-				acpi_write_pm1_enable(acpi_facp, pm1_enable);
-				acpi_write_gpe_enable(acpi_facp, gpe_enable);
-				acpi_gpe_level = gpe_level;
-			}
-		}
-		break;
-	case ACPI_WAIT_EVENT:
-		status = verify_area(VERIFY_WRITE,
-				     (void *) arg,
-				     sizeof(struct acpi_wait_event));
-		if (!status) {
-			struct acpi_wait_event *rqst
-				= (struct acpi_wait_event *) arg;
-			u32 pm1_status = 0;
-			u32 gpe_status = 0;
-			
-			for (;;) {
-				unsigned long flags;
-				
-				// we need an atomic exchange here
-				spin_lock_irqsave(&acpi_event_lock, flags);
-				pm1_status = acpi_pm1_status;
-				acpi_pm1_status = 0;
-				gpe_status = acpi_gpe_status;
-				acpi_gpe_status = 0;
-				spin_unlock_irqrestore(&acpi_event_lock,
-						       flags);
-
-				if (pm1_status || gpe_status)
-					break;
-
-				// wait for an event to arrive
-				interruptible_sleep_on(&acpi_wait_event);
-				if (signal_pending(current))
-					return -ERESTARTSYS;
-			}
-
-			put_user(pm1_status, &rqst->pm1_status);
-			put_user(gpe_status, &rqst->gpe_status);
-			status = 0;
-		}
-		break;
-	}
-	return status;
-}
-
 static void acpi_idle_handler(void)
 {
-	unsigned long time;
 	static int sleep_level = 1;
+	u32 timer, pm2_cnt;
+	unsigned long time;
+
+	// get current time (fallback to CPU cycles if no PM timer)
+	timer = acpi_facp->pm_tmr;
+	if (timer)
+		time = inl(timer);
+	else
+		time = get_cycles();
 
-	time = inl(acpi_facp->pm_tmr);
+	// sleep
 	switch (sleep_level) {
 	case 1:
 		__asm__ __volatile__("sti ; hlt": : :"memory");
@@ -538,18 +573,25 @@
 		inb(acpi_p_lvl2);
 		break;
 	case 3:
-		/* Disable PCI arbitration while sleeping,
-		   to avoid DMA corruption? */
-		if (acpi_facp->pm2_cnt) {
-			unsigned int port = acpi_facp->pm2_cnt;
-			outb(inb(port) | ACPI_ARB_DIS, port);
+		pm2_cnt = acpi_facp->pm2_cnt;
+		if (pm2_cnt) {
+			/* Disable PCI arbitration while sleeping,
+			   to avoid DMA corruption? */
+			outb(inb(pm2_cnt) | ACPI_ARB_DIS, pm2_cnt);
+			inb(acpi_p_lvl3);
+			outb(inb(pm2_cnt) & ~ACPI_ARB_DIS, pm2_cnt);
+		}
+		else {
 			inb(acpi_p_lvl3);
-			outb(inb(port) & ~ACPI_ARB_DIS, port);
-			break;
 		}
-		inb(acpi_p_lvl3);
+		break;
 	}
-	time = (inl(acpi_facp->pm_tmr) - time) & ACPI_TMR_MASK;
+
+	// calculate time spent sleeping (fallback to CPU cycles)
+	if (timer)
+		time = (inl(timer) - time) & ACPI_TMR_MASK;
+	else
+		time = ACPI_CPU_TO_TMR_TICKS(get_cycles() - time);
 
 	if (time > acpi_p_lvl3_lat)
 		sleep_level = 3;
@@ -559,50 +601,24 @@
 		sleep_level = 1;
 }
 
-static struct file_operations acpi_fops =
-{
-	NULL,			/* llseek */
-	NULL,			/* read */
-	NULL,			/* write */
-	NULL,			/* readdir */
-	NULL,			/* poll */
-	acpi_ioctl,		/* ioctl */
-	NULL,			/* mmap */
-	acpi_open,		/* open */
-	NULL,			/* flush */
-	acpi_release,		/* release */
-	NULL,			/* fsync */
-	NULL,			/* fasync */
-	NULL,			/* check_media_change */
-	NULL,			/* revalidate */
-	NULL,			/* lock */
-};
-
-static struct miscdevice acpi_device =
-{
-	ACPI_MINOR_DEV,
-	"acpi",
-	&acpi_fops,
-	NULL,
-	NULL
-};
-
 /*
  * Claim ACPI I/O ports
  */
 static int acpi_claim_ioports(struct acpi_facp *facp)
 {
 	// we don't get a guarantee of contiguity for any of the ACPI registers
-	request_region(facp->pm1a_evt, facp->pm1_evt_len, "acpi");
+	if (facp->pm1a_evt)
+		request_region(facp->pm1a_evt, facp->pm1_evt_len, "acpi");
 	if (facp->pm1b_evt)
 		request_region(facp->pm1b_evt, facp->pm1_evt_len, "acpi");
-	request_region(facp->pm1a_cnt, facp->pm1_cnt_len, "acpi");
+	if (facp->pm1a_cnt)
+		request_region(facp->pm1a_cnt, facp->pm1_cnt_len, "acpi");
 	if (facp->pm1b_cnt)
 		request_region(facp->pm1b_cnt, facp->pm1_cnt_len, "acpi");
-	if (facp->pm2_cnt)
-		request_region(facp->pm2_cnt, facp->pm2_cnt_len, "acpi");
-	request_region(facp->pm_tmr, facp->pm_tm_len, "acpi");
-	request_region(facp->gpe0, facp->gpe0_len, "acpi");
+	if (facp->pm_tmr)
+		request_region(facp->pm_tmr, facp->pm_tm_len, "acpi");
+	if (facp->gpe0)
+		request_region(facp->gpe0, facp->gpe0_len, "acpi");
 	if (facp->gpe1)
 		request_region(facp->gpe1, facp->gpe1_len, "acpi");
 
@@ -615,16 +631,18 @@
 static int acpi_release_ioports(struct acpi_facp *facp)
 {
 	// we don't get a guarantee of contiguity for any of the ACPI registers
-	release_region(facp->pm1a_evt, facp->pm1_evt_len);
+	if (facp->pm1a_evt)
+		release_region(facp->pm1a_evt, facp->pm1_evt_len);
 	if (facp->pm1b_evt)
 		release_region(facp->pm1b_evt, facp->pm1_evt_len);
-	release_region(facp->pm1a_cnt, facp->pm1_cnt_len);
+	if (facp->pm1a_cnt)
+		release_region(facp->pm1a_cnt, facp->pm1_cnt_len);
 	if (facp->pm1b_cnt)
 		release_region(facp->pm1b_cnt, facp->pm1_cnt_len);
-	if (facp->pm2_cnt)
-		release_region(facp->pm2_cnt, facp->pm2_cnt_len);
-	release_region(facp->pm_tmr, facp->pm_tm_len);
-	release_region(facp->gpe0, facp->gpe0_len);
+	if (facp->pm_tmr)
+		release_region(facp->pm_tmr, facp->pm_tm_len);
+	if (facp->gpe0)
+		release_region(facp->gpe0, facp->gpe0_len);
 	if (facp->gpe1)
 		release_region(facp->gpe1, facp->gpe1_len);
 
@@ -632,28 +650,224 @@
 }
 
 /*
+ * Examine/modify value
+ */
+static int acpi_do_ulong(ctl_table *ctl,
+			 int write,
+			 struct file *file,
+			 void *buffer,
+			 size_t *len)
+{
+	char str[2 * sizeof(unsigned long) + 4], *strend;
+	unsigned long val;
+	int size;
+
+	if (!write) {
+		if (file->f_pos) {
+			*len = 0;
+			return 0;
+		}
+
+		val = *(unsigned long*) ctl->data;
+		size = sprintf(str, "0x%08lx\n", val);
+		if (*len >= size) {
+			copy_to_user(buffer, str, size);
+			*len = size;
+		}
+		else
+			*len = 0;
+	}
+	else {
+		size = sizeof(str) - 1;
+		if (size > *len)
+			size = *len;
+		copy_from_user(str, buffer, size);
+		str[size] = '\0';
+		val = simple_strtoul(str, &strend, 0);
+		if (strend == str)
+			return -EINVAL;
+		*(unsigned long*) ctl->data = val;
+	}
+
+	file->f_pos += *len;
+	return 0;
+}
+
+/*
+ * Examine/modify event register
+ */
+static int acpi_do_event_reg(ctl_table *ctl,
+			     int write,
+			     struct file *file,
+			     void *buffer,
+			     size_t *len)
+{
+	char str[2 * sizeof(u32) + 4], *strend;
+	u32 val, enabling;
+	int size;
+
+	if (!write) {
+		if (file->f_pos) {
+			*len = 0;
+			return 0;
+		}
+
+		val = 0;
+		switch (ctl->ctl_name) {
+		case ACPI_PM1_ENABLE:
+			val = acpi_read_pm1_enable(acpi_facp);
+			break;
+		case ACPI_GPE_ENABLE:
+			val = acpi_read_gpe_enable(acpi_facp);
+			break;
+		case ACPI_GPE_LEVEL:
+			val = acpi_gpe_level;
+			break;
+		}
+		
+		size = sprintf(str, "0x%08x\n", val);
+		if (*len >= size) {
+			copy_to_user(buffer, str, size);
+			*len = size;
+		}
+		else
+			*len = 0;
+	}
+	else
+	{
+		// fetch user value
+		size = sizeof(str) - 1;
+		if (size > *len)
+			size = *len;
+		copy_from_user(str, buffer, size);
+		str[size] = '\0';
+		val = (u32) simple_strtoul(str, &strend, 0);
+		if (strend == str)
+			return -EINVAL;
+
+		// store value in register
+		switch (ctl->ctl_name) {
+		case ACPI_PM1_ENABLE:
+			// clear previously disabled events
+			enabling = (val
+				    & ~acpi_read_pm1_enable(acpi_facp));
+			acpi_write_pm1_status(acpi_facp, enabling);
+			
+			if (val) {
+				// enable ACPI unless it is already
+				if (!acpi_is_enabled(acpi_facp))
+					acpi_enable(acpi_facp);
+			}
+			else if (!acpi_read_gpe_enable(acpi_facp)) {
+				// disable ACPI unless it is already
+				if (acpi_is_enabled(acpi_facp))
+					acpi_disable(acpi_facp);
+			}
+			
+			acpi_write_pm1_enable(acpi_facp, val);
+			break;
+		case ACPI_GPE_ENABLE:
+			// clear previously disabled events
+			enabling = (val
+				    & ~acpi_read_gpe_enable(acpi_facp));
+			while (acpi_read_gpe_status(acpi_facp) & enabling)
+				acpi_write_gpe_status(acpi_facp, enabling);
+			
+			if (val) {
+				// enable ACPI unless it is already
+				if (!acpi_is_enabled(acpi_facp))
+					acpi_enable(acpi_facp);
+			}
+			else if (!acpi_read_pm1_enable(acpi_facp)) {
+				// disable ACPI unless it is already
+				if (acpi_is_enabled(acpi_facp))
+					acpi_disable(acpi_facp);
+			}
+			
+			acpi_write_gpe_enable(acpi_facp, val);
+			break;
+		case ACPI_GPE_LEVEL:
+			acpi_gpe_level = val;
+			break;
+		}
+	}
+
+	file->f_pos += *len;
+	return 0;
+}
+
+/*
+ * Wait for next event
+ */
+static int acpi_do_event(ctl_table *ctl,
+			 int write,
+			 struct file *file,
+			 void *buffer,
+			 size_t *len)
+{
+	u32 pm1_status = 0, gpe_status = 0;
+	char str[4 * sizeof(u32) + 6];
+	int size;
+
+	if (write)
+		return -EPERM;
+	if (*len < sizeof(str)) {
+		*len = 0;
+		return 0;
+	}
+
+	for (;;) {
+		unsigned long flags;
+		
+		// we need an atomic exchange here
+		spin_lock_irqsave(&acpi_event_lock, flags);
+		pm1_status = acpi_pm1_status;
+		acpi_pm1_status = 0;
+		gpe_status = acpi_gpe_status;
+		acpi_gpe_status = 0;
+		spin_unlock_irqrestore(&acpi_event_lock, flags);
+		
+		if (pm1_status || gpe_status)
+			break;
+		
+		// wait for an event to arrive
+		interruptible_sleep_on(&acpi_event_wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+	}
+
+	size = sprintf(str, "0x%08x 0x%08x\n", pm1_status, gpe_status);
+	copy_to_user(buffer, str, size);
+	*len = size;
+	file->f_pos += size;
+
+	return 0;
+}
+
+/*
  * Initialize and enable ACPI
  */
 static int __init acpi_init(void)
 {
-	if (acpi_map_tables())
+	if (acpi_find_tables() && acpi_find_piix4()) {
+		// no ACPI tables and not PIIX4
 		return -ENODEV;
+	}
 
-	if (request_irq(acpi_facp->sci_int,
-			acpi_irq,
-			SA_INTERRUPT | SA_SHIRQ,
-			"acpi",
-			NULL)) {
+	if (acpi_facp->sci_int
+	    && request_irq(acpi_facp->sci_int,
+			   acpi_irq,
+			   SA_INTERRUPT | SA_SHIRQ,
+			   "acpi",
+			   NULL)) {
 		printk(KERN_ERR "ACPI: SCI (IRQ%d) allocation failed\n",
 		       acpi_facp->sci_int);
-		acpi_unmap_tables();
+		acpi_destroy_tables();
 		return -ENODEV;
 	}
 
 	acpi_claim_ioports(acpi_facp);
-
-	if (misc_register(&acpi_device))
-		printk(KERN_ERR "ACPI: misc. register failed\n");
+	acpi_sysctl = register_sysctl_table(acpi_dir_table, 1);
 
 	/*
 	 * Set up the ACPI idle function. Note that we can't really
@@ -664,7 +878,9 @@
 	if (smp_num_cpus > 1)
 		return 0;
 #endif
+
 	acpi_idle = acpi_idle_handler;
+
 	return 0;
 }
 
@@ -673,11 +889,16 @@
  */
 static void __exit acpi_exit(void)
 {
-	misc_deregister(&acpi_device);
+	acpi_idle = NULL;
+
+	unregister_sysctl_table(acpi_sysctl);
 	acpi_disable(acpi_facp);
 	acpi_release_ioports(acpi_facp);
-	free_irq(acpi_facp->sci_int, NULL);
-	acpi_unmap_tables();
+
+	if (acpi_facp->sci_int)
+		free_irq(acpi_facp->sci_int, NULL);
+
+	acpi_destroy_tables();
 }
 
 #ifdef MODULE

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)