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

Next file: linux/drivers/net/3c507.c
Previous file: linux/drivers/misc/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.26/linux/drivers/misc/acpi.c linux/drivers/misc/acpi.c
@@ -54,6 +54,7 @@
 #define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue * x = NULL
 #endif
 
+static int acpi_idle_thread(void *context);
 static int acpi_do_ulong(ctl_table *ctl,
 			 int write,
 			 struct file *file,
@@ -69,6 +70,13 @@
 			 struct file *file,
 			 void *buffer,
 			 size_t *len);
+static int acpi_do_sleep_wake(ctl_table *ctl,
+			      int write,
+			      struct file *file,
+			      void *buffer,
+			      size_t *len);
+
+DECLARE_WAIT_QUEUE_HEAD(acpi_idle_wait);
 
 static struct ctl_table_header *acpi_sysctl = NULL;
 
@@ -84,6 +92,9 @@
 static volatile u32 acpi_gpe_level = 0;
 static DECLARE_WAIT_QUEUE_HEAD(acpi_event_wait);
 
+static spinlock_t acpi_devs_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(acpi_devs);
+
 /* 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;
@@ -147,6 +158,11 @@
 	 &acpi_slp_typ[5], sizeof(acpi_slp_typ[5]),
 	 0600, NULL, &acpi_do_ulong},
 
+#if 0
+	{123, "sleep", (void*) 1, 0, 0600, NULL, &acpi_do_sleep_wake},
+	{124, "wake", NULL, 0, 0600, NULL, &acpi_do_sleep_wake},
+#endif
+
 	{0}
 };
 
@@ -319,15 +335,23 @@
 	if (addr) {
 		// map table header to determine size
 		table = (struct acpi_table *)
-			ioremap_nocache((unsigned long) addr,
-					sizeof(struct acpi_table));
+			ioremap((unsigned long) addr,
+				sizeof(struct acpi_table));
 		if (table) {
 			unsigned long table_size = table->length;
 			iounmap(table);
 			// remap entire table
 			table = (struct acpi_table *)
-				ioremap_nocache((unsigned long) addr,
-						table_size);
+				ioremap((unsigned long) addr, table_size);
+		}
+
+		if (!table) {
+			/* ioremap is a pain, it returns NULL if the
+			 * table starts within mapped physical memory.
+			 * Hopefully, no table straddles a mapped/unmapped
+			 * physical memory boundary, ugh
+			 */
+			table = (struct acpi_table*) phys_to_virt(addr);
 		}
 	}
 	return table;
@@ -338,6 +362,7 @@
  */
 static void acpi_unmap_table(struct acpi_table *table)
 {
+	// iounmap ignores addresses within physical memory
 	if (table)
 		iounmap(table);
 }
@@ -380,8 +405,14 @@
 
 	// fetch RSDT from RSDP
 	rsdt = acpi_map_table(rsdp->rsdt);
-	if (!rsdt || rsdt->signature != ACPI_RSDT_SIG) {
-		printk(KERN_ERR "ACPI: missing RSDT\n");
+	if (!rsdt) {
+		printk(KERN_ERR "ACPI: missing RSDT at 0x%p\n",
+		       (void*) rsdp->rsdt);
+		return -ENODEV;
+	}
+	else if (rsdt->signature != ACPI_RSDT_SIG) {
+		printk(KERN_ERR "ACPI: bad RSDT at 0x%p (%08x)\n",
+		       (void*) rsdp->rsdt, (unsigned) rsdt->signature);
 		acpi_unmap_table(rsdt);
 		return -ENODEV;
 	}
@@ -396,9 +427,15 @@
 			acpi_facp_addr = *rsdt_entry;
 			acpi_dsdt_addr = acpi_facp->dsdt;
 
+			// map FACS if it exists
 			if (acpi_facp->facs) {
-				acpi_facs = (struct acpi_facs*)
-					acpi_map_table(acpi_facp->facs);
+				dt = acpi_map_table(acpi_facp->facs);
+				if (dt && dt->signature == ACPI_FACS_SIG) {
+					acpi_facs = (struct acpi_facs*) dt;
+				}
+				else {
+					acpi_unmap_table(dt);
+				}
 			}
 		}
 		else {
@@ -624,6 +661,34 @@
 }
 
 /*
+ * Put all devices into specified D-state
+ */
+static int acpi_enter_dx(acpi_dstate_t state)
+{
+	int status = 0;
+	struct list_head *i = acpi_devs.next;
+
+	while (i != &acpi_devs)	{
+		struct acpi_dev *dev = list_entry(i, struct acpi_dev, entry);
+		if (dev->state != state) {
+			int dev_status = 0;
+			if (dev->info.transition)
+				dev_status = dev->info.transition(dev, state);
+			if (!dev_status) {
+				// put hardware into D-state
+				dev->state = state;
+			}
+			if (dev_status)
+				status = dev_status;
+		}
+
+		i = i->next;
+	}
+
+	return status;
+}
+
+/*
  * Enter system sleep state
  */
 static void acpi_enter_sx(int state)
@@ -903,10 +968,41 @@
 }
 
 /*
+ * Sleep or wake system
+ */
+static int acpi_do_sleep_wake(ctl_table *ctl,
+			      int write,
+			      struct file *file,
+			      void *buffer,
+			      size_t *len)
+{
+	if (!write) {
+		if (file->f_pos) {
+			*len = 0;
+			return 0;
+		}
+	}
+	else
+	{
+		// just shutdown some devices for now
+		if (ctl->data) {
+			acpi_enter_dx(ACPI_D3);
+		}
+		else {
+			acpi_enter_dx(ACPI_D0);
+		}
+	}
+	file->f_pos += *len;
+	return 0;
+}
+
+/*
  * Initialize and enable ACPI
  */
 static int __init acpi_init(void)
 {
+	int pid;
+
 	if (acpi_find_tables() && acpi_find_piix4()) {
 		// no ACPI tables and not PIIX4
 		return -ENODEV;
@@ -917,7 +1013,7 @@
 			   acpi_irq,
 			   SA_INTERRUPT | SA_SHIRQ,
 			   "acpi",
-			   NULL)) {
+			   acpi_facp)) {
 		printk(KERN_ERR "ACPI: SCI (IRQ%d) allocation failed\n",
 		       acpi_facp->sci_int);
 		acpi_destroy_tables();
@@ -927,6 +1023,10 @@
 	acpi_claim_ioports(acpi_facp);
 	acpi_sysctl = register_sysctl_table(acpi_dir_table, 1);
 
+	pid = kernel_thread(acpi_idle_thread,
+			    NULL,
+			    CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+
 	/*
 	 * Set up the ACPI idle function. Note that we can't really
 	 * do this with multiple CPU's, we'd need a per-CPU ACPI
@@ -956,18 +1056,84 @@
 	acpi_release_ioports(acpi_facp);
 
 	if (acpi_facp->sci_int)
-		free_irq(acpi_facp->sci_int, NULL);
+		free_irq(acpi_facp->sci_int, acpi_facp);
 
 	acpi_destroy_tables();
 }
 
-#ifdef MODULE
+/*
+ * Register a device with the ACPI subsystem
+ */
+struct acpi_dev* acpi_register(struct acpi_dev_info *info, unsigned long adr)
+{
+	struct acpi_dev *dev = NULL;
+	if (info) {
+		dev = kmalloc(sizeof(struct acpi_dev), GFP_KERNEL);
+		if (dev) {
+			unsigned long flags;
+			
+			memset(dev, 0, sizeof(*dev));
+			memcpy(&dev->info, info, sizeof(dev->info));
+			dev->adr = adr;
+			
+			spin_lock_irqsave(&acpi_devs_lock, flags);
+			list_add(&dev->entry, &acpi_devs);
+			spin_unlock_irqrestore(&acpi_devs_lock, flags);
+		}
+	}
+	return dev;
+}
 
-module_init(acpi_init)
-module_exit(acpi_exit)
+/*
+ * Unregister a device with ACPI
+ */
+void acpi_unregister(struct acpi_dev *dev)
+{
+	if (dev) {
+		unsigned long flags;
 
-#else
+		spin_lock_irqsave(&acpi_devs_lock, flags);
+		list_del(&dev->entry);
+		spin_unlock_irqrestore(&acpi_devs_lock, flags);
+
+		kfree(dev);
+	}
+}
+
+/*
+ * Wake up a device
+ */
+void acpi_wakeup(struct acpi_dev *dev)
+{
+	// run _PS0 or tell parent bus to wake device up
+}
+
+/*
+ * Manage idle devices
+ */
+static int acpi_idle_thread(void *context)
+{
+	exit_mm(current);
+	exit_files(current);
+	strcpy(current->comm, "acpi");
+	
+	for(;;) {
+		interruptible_sleep_on(&acpi_idle_wait);
+		if (signal_pending(current))
+			break;
+
+		// find all idle devices and set idle timer based on policy
+	}
+
+	return 0;
+}
 
 __initcall(acpi_init);
 
-#endif
+/*
+ * Module visible symbols
+ */
+EXPORT_SYMBOL(acpi_idle_wait);
+EXPORT_SYMBOL(acpi_register);
+EXPORT_SYMBOL(acpi_unregister);
+EXPORT_SYMBOL(acpi_wakeup);

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