summaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-12-04 03:58:56 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-12-04 03:58:56 +0000
commit1d67e90f19a7acfd9a05dc59678e7d0c5090bd0d (patch)
tree357efc7b93f8f5102110d20d293f41360ec212fc /drivers/misc
parentaea27b2e18d69af87e673972246e66657b4fa274 (diff)
Merge with Linux 2.3.21.
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Config.in9
-rw-r--r--drivers/misc/Makefile4
-rw-r--r--drivers/misc/acpi.c219
-rw-r--r--drivers/misc/piix4_acpi.c214
4 files changed, 201 insertions, 245 deletions
diff --git a/drivers/misc/Config.in b/drivers/misc/Config.in
index 9467c5e45..5b793e214 100644
--- a/drivers/misc/Config.in
+++ b/drivers/misc/Config.in
@@ -4,13 +4,6 @@
mainmenu_option next_comment
comment 'Misc devices'
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate 'Generic ACPI support' CONFIG_ACPI
-fi
-
-# PIIX4 ACPI requires PCI for setup and a hardcoded TSC for timing
-if [ "$CONFIG_PCI" = "y" -a "$CONFIG_X86_TSC" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool 'PIIX4 ACPI support' CONFIG_PIIX4_ACPI
-fi
+tristate 'Generic ACPI support' CONFIG_ACPI
endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 8368614ad..6183d1231 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -26,10 +26,6 @@ else
endif
endif
-ifdef CONFIG_PIIX4_ACPI
-O_OBJS += piix4_acpi.o
-endif
-
include $(TOPDIR)/Rules.make
fastdep:
diff --git a/drivers/misc/acpi.c b/drivers/misc/acpi.c
index 27137fa40..99911093b 100644
--- a/drivers/misc/acpi.c
+++ b/drivers/misc/acpi.c
@@ -18,6 +18,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+/*
+ * See http://www.geocities.com/SiliconValley/Hardware/3165/
+ * for the user-level ACPI stuff
+ */
+
#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
@@ -46,6 +51,10 @@
static struct acpi_facp *acpi_facp = NULL;
static unsigned long acpi_facp_addr = 0;
static unsigned long acpi_dsdt_addr = 0;
+
+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);
/*
@@ -73,6 +82,19 @@ static void acpi_write_pm1_status(struct acpi_facp *facp, u32 value)
}
/*
+ * Get the value of the fixed event enable register
+ */
+static u32 acpi_read_pm1_enable(struct acpi_facp *facp)
+{
+ int offset = facp->pm1_evt_len >> 1;
+ u32 value = inw(facp->pm1a_evt + offset);
+ if (facp->pm1b_evt) {
+ value |= inw(facp->pm1b_evt + offset);
+ }
+ return value;
+}
+
+/*
* Set the value of the fixed event enable register (enable events)
*/
static void acpi_write_pm1_enable(struct acpi_facp *facp, u32 value)
@@ -128,6 +150,28 @@ static void acpi_write_gpe_status(struct acpi_facp *facp, u32 value)
}
/*
+ * Get the value of the general-purpose event enable register
+ */
+static u32 acpi_read_gpe_enable(struct acpi_facp *facp)
+{
+ u32 value = 0;
+ int i, size, offset;
+
+ offset = facp->gpe0_len >> 1;
+ if (facp->gpe1) {
+ size = facp->gpe1_len >> 1;
+ for (i = size - 1; i >= 0; i--) {
+ 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);
+ }
+ return value;
+}
+
+/*
* Set the value of the general-purpose event enable register (enable events)
*/
static void acpi_write_gpe_enable(struct acpi_facp *facp, u32 value)
@@ -157,7 +201,8 @@ static struct acpi_table *__init acpi_map_table(u32 addr)
if (addr) {
// map table header to determine size
table = (struct acpi_table *)
- ioremap_nocache((unsigned long) addr, sizeof(struct acpi_table));
+ ioremap_nocache((unsigned long) addr,
+ sizeof(struct acpi_table));
if (table) {
unsigned long table_size = table->length;
iounmap(table);
@@ -201,10 +246,13 @@ static int __init acpi_map_tables(void)
// strip trailing space and print OEM identifier
memcpy_fromio(oem, rsdp->oem, 6);
oem[6] = '\0';
- for (j = 5; j > 0 && (oem[j] == '\0' || oem[j] == ' '); j--) {
+ for (j = 5;
+ j > 0 && (oem[j] == '\0' || oem[j] == ' ');
+ j--) {
oem[j] = '\0';
}
- printk(KERN_INFO "ACPI: \"%s\" found at 0x%p\n", oem, (void *) i);
+ printk(KERN_INFO "ACPI: \"%s\" found at 0x%p\n",
+ oem, (void *) i);
break;
}
@@ -252,6 +300,7 @@ static int __init acpi_map_tables(void)
*/
static void acpi_unmap_tables(void)
{
+ acpi_idle = NULL;
acpi_dsdt_addr = 0;
acpi_facp_addr = 0;
acpi_unmap_table((struct acpi_table *) acpi_facp);
@@ -263,17 +312,32 @@ static void acpi_unmap_tables(void)
*/
static void acpi_irq(int irq, void *dev_id, struct pt_regs *regs)
{
- u32 status;
-
- // detect and disable any fixed events
- status = acpi_read_pm1_status(acpi_facp);
- acpi_write_pm1_enable(acpi_facp, ~status);
-
- // detect and disable any general-purpose events
- status = acpi_read_gpe_status(acpi_facp);
- acpi_write_gpe_enable(acpi_facp, ~status);
-
+ u32 pm1_status, gpe_status, gpe_level, gpe_edge;
+ // detect and clear fixed events
+ pm1_status = (acpi_read_pm1_status(acpi_facp)
+ & acpi_read_pm1_enable(acpi_facp));
+ acpi_write_pm1_status(acpi_facp, pm1_status);
+
+ // detect and handle general-purpose events
+ gpe_status = (acpi_read_gpe_status(acpi_facp)
+ & acpi_read_gpe_enable(acpi_facp));
+ gpe_level = gpe_status & acpi_gpe_level;
+ if (gpe_level) {
+ // disable level-triggered events
+ acpi_write_gpe_enable(
+ acpi_facp,
+ acpi_read_gpe_enable(acpi_facp) & ~gpe_level);
+ }
+ gpe_edge = gpe_status & ~gpe_level;
+ if (gpe_edge) {
+ // clear edge-triggered events
+ while (acpi_read_gpe_status(acpi_facp) & gpe_edge)
+ acpi_write_gpe_status(acpi_facp, gpe_edge);
+ }
+
// notify process reading /dev/acpi
+ acpi_pm1_status |= pm1_status;
+ acpi_gpe_status |= gpe_status;
wake_up_interruptible(&acpi_wait_event);
}
@@ -311,17 +375,79 @@ static int acpi_ioctl(struct inode *inode,
(void *) arg,
sizeof(struct acpi_find_tables));
if (!status) {
- struct acpi_find_tables *rqst = (struct acpi_find_tables *) arg;
+ 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);
+
+ acpi_write_pm1_enable(acpi_facp, pm1_enable);
+ acpi_write_gpe_enable(acpi_facp, gpe_enable);
+ acpi_gpe_level = gpe_level;
+
+ status = 0;
+ }
+ break;
case ACPI_WAIT_EVENT:
- interruptible_sleep_on(&acpi_wait_event);
- if (signal_pending(current))
- return -ERESTARTSYS;
- status = 0;
+ 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
+ save_flags(flags);
+ cli();
+ pm1_status = acpi_pm1_status;
+ acpi_pm1_status = 0;
+ gpe_status = acpi_gpe_status;
+ acpi_gpe_status = 0;
+ restore_flags(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;
@@ -355,6 +481,48 @@ static struct miscdevice acpi_device =
NULL
};
+/* 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 void acpi_idle_handler(void)
+{
+ unsigned long time;
+ static int sleep_level = 1;
+
+ time = inl(acpi_facp->pm_tmr);
+ switch (sleep_level) {
+ case 1:
+ __asm__ __volatile__("sti ; hlt": : :"memory");
+ break;
+ case 2:
+ 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);
+ inb(acpi_p_lvl3);
+ outb(inb(port) & ~ACPI_ARB_DIS, port);
+ break;
+ }
+ inb(acpi_p_lvl3);
+ }
+ time = (inl(acpi_facp->pm_tmr) - time) & ACPI_TMR_MASK;
+
+ if (time > acpi_p_lvl3_lat)
+ sleep_level = 3;
+ else if (time > acpi_p_lvl2_lat)
+ sleep_level = 2;
+ else
+ sleep_level = 1;
+}
+
/*
* Initialize and enable ACPI
*/
@@ -376,6 +544,17 @@ static int __init acpi_init(void)
if (misc_register(&acpi_device)) {
printk(KERN_ERR "ACPI: misc. register failed\n");
}
+
+ /*
+ * 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
+ * device..
+ */
+#ifdef __SMP__
+ if (smp_num_cpus > 1)
+ return 0;
+#endif
+ acpi_idle = acpi_idle_handler;
return 0;
}
@@ -389,7 +568,8 @@ static void __exit acpi_exit(void)
// disable and clear any pending events
acpi_write_gpe_enable(acpi_facp, 0);
while (acpi_read_gpe_status(acpi_facp)) {
- acpi_write_gpe_status(acpi_facp, acpi_read_gpe_status(acpi_facp));
+ acpi_write_gpe_status(acpi_facp,
+ acpi_read_gpe_status(acpi_facp));
}
acpi_write_pm1_enable(acpi_facp, 0);
acpi_write_pm1_status(acpi_facp, acpi_read_pm1_status(acpi_facp));
@@ -405,6 +585,7 @@ static void __exit acpi_exit(void)
module_init(acpi_init)
module_exit(acpi_exit)
+
#else
__initcall(acpi_init);
diff --git a/drivers/misc/piix4_acpi.c b/drivers/misc/piix4_acpi.c
deleted file mode 100644
index 9cd3625b9..000000000
--- a/drivers/misc/piix4_acpi.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * linux/drivers/misc/piix4_acpi.c
- *
- * (C) Copyright 1999 Linus Torvalds
- *
- * A PM driver for the ACPI portion of the Intel PIIX4
- * chip.
- *
- * This has been known to occasionally work on some laptops.
- *
- * It probably only works on Intel PII machines that support
- * the STPCLK protocol.
- */
-
-#include <linux/sched.h>
-#include <linux/pci.h>
-#include <linux/init.h>
-
-#include <asm/io.h>
-
-extern void (*acpi_idle)(void);
-
-/*
- * This first part should be common to all ACPI
- * CPU sleep functionality. Assuming we get the
- * timing heuristics in a better shape than "none" ;)
- */
-
-typedef void (*sleep_fn_t)(void);
-
-/*
- * Our "sleep mode" is a fixed point number
- * with two binary places, ranging between
- * [0 .. 3[
- */
-#define Cx_SHIFT 2
-#define MAXMODE ((3 << Cx_SHIFT)-1)
-
-/*
- * NOTE!
- *
- * Right now this always defaults to C3, which is just broken.
- * The exit latency is usually too high for much busy IO activity,
- * and generally it's not always the best thing to do.
- *
- * We should just read the cycle counter around all the cases,
- * and if we pause for a long time we go to a deeper sleep, while
- * a short wait makes us go into a lighter sleep.
- */
-static void common_acpi_idle(sleep_fn_t *sleep)
-{
- int mode = MAXMODE;
-
- while (1) {
- while (!current->need_resched) {
- unsigned int time;
-
- time = get_cycles();
- sleep[(mode) >> Cx_SHIFT]();
- time = get_cycles() - time;
-
- /*
- * Yeah, yeah, yeah.
- * if (time > Large && mode < MAXMODE) mode++;
- * if (time < Small && mode > 0) mode--;
- * Yadda-yadda-yadda.
- *
- * "Large" is on the order of half a timer tick.
- * "Small" is on the order of Large >> 2 or so.
- *
- * Somebody should _really_ look at the exact
- * details. The ACPI bios would give some made-up
- * numbers, they might be useful (or maybe not:
- * they are probably tuned for whatever Windows
- * does, so don't take them for granted).
- */
- }
- schedule();
- check_pgt_cache();
- }
-}
-
-/* Ok, here starts the magic PIIX4 knowledge */
-
-/*
- * Ehh.. We "know" about the northbridge
- * bus arbitration stuff. Maybe somebody
- * should actually verify this some day?
- */
-#define NORTHBRIDGE_CONTROL 0x22
-#define NB_ARBITRATE 0x01
-
-/*
- * PIIX4 ACPI IO offsets and defines
- */
-#define PMEN 0x02
-#define PMCNTRL 0x04
-#define PMTMR 0x08
-#define GPSTS 0x0c
-#define GPEN 0x0E
-
-#define PCNTRL 0x10
-#define CC_EN 0x0200
-#define BST_EN 0x0400
-#define SLEEP_EN 0x0800
-#define STPCLK_EN 0x1000
-#define CLKRUN_EN 0x2000
-
-#define PLVL2 0x14
-#define PLVL3 0x15
-
-/*
- * PIIX4 ACPI PCI configurations offsets and defines
- */
-#define DEVACTB 0x58
-#define BRLD_EN_IRQ0 0x01
-#define BRLD_EN_IRQ 0x02
-
-#define PMREGMISC 0x80
-#define PMIOSE 0x01
-
-static unsigned int piix4_base_address = 0;
-
-static void piix4_c1_sleep(void)
-{
- asm volatile("sti ; hlt" : : : "memory");
-}
-
-static void piix4_c2_sleep(void)
-{
- outl(CLKRUN_EN | CC_EN, piix4_base_address + PCNTRL);
- inb(piix4_base_address + PLVL2);
-}
-
-static void piix4_c3_sleep(void)
-{
- __cli();
- outl(CLKRUN_EN | CC_EN | STPCLK_EN | SLEEP_EN, piix4_base_address + PCNTRL);
- outb(NB_ARBITRATE, NORTHBRIDGE_CONTROL);
- inb(piix4_base_address + PLVL3);
- outb(0, NORTHBRIDGE_CONTROL);
- __sti();
-}
-
-static sleep_fn_t piix4_sleep[] = {
- piix4_c1_sleep, /* low-latency C1 (ie "sti ; hlt") */
- piix4_c2_sleep, /* medium latency C2 (ie LVL2 stopckl) */
- piix4_c3_sleep /* high-latency C3 (ie LVL3 sleep) */
-};
-
-static void piix4_acpi_idle(void)
-{
- common_acpi_idle(piix4_sleep);
-}
-
-static int __init piix4_acpi_init(void)
-{
- /* This is the PIIX4 ACPI device */
- struct pci_dev *dev;
- u32 base, val;
- u16 cmd;
- u8 pmregmisc;
-
-#ifdef __SMP__
- /*
- * We can't really do idle things with multiple CPU's, I'm
- * afraid. We'd need a per-CPU ACPI device.
- */
- if (smp_num_cpus > 1)
- return -1;
-#endif
- dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
-
- if (!dev)
- return -1;
-
- /*
- * Read the IO base value, and verify that it makes sense
- *
- * We could enable this if it wasn't enabled before, but
- * let's walk before we run..
- */
- pci_read_config_word(dev, PCI_COMMAND, &cmd);
- if (!(cmd & PCI_COMMAND_IO))
- return -1;
-
- pci_read_config_byte(dev, PMREGMISC, &pmregmisc);
- if (!(pmregmisc & PMIOSE))
- return -1;
-
- pci_read_config_dword(dev, 0x40, &base);
- if (!(base & PCI_BASE_ADDRESS_SPACE_IO))
- return -1;
-
- base &= PCI_BASE_ADDRESS_IO_MASK;
- if (!base)
- return -1;
-
- printk("Found PIIX4 ACPI device at %04x\n", base);
- piix4_base_address = base;
-
- /* Enable stopcklock, sleep and bursts, along with clock control */
- outl(CLKRUN_EN | CC_EN | STPCLK_EN | SLEEP_EN, piix4_base_address + PCNTRL);
-
- /* Make all unmasked interrupts be BREAK events */
- pci_read_config_dword(dev, DEVACTB, &val);
- pci_write_config_dword(dev, DEVACTB, val | BRLD_EN_IRQ0 | BRLD_EN_IRQ);
-
- /* Set up the new idle handler.. */
- acpi_idle = piix4_acpi_idle;
- return 0;
-}
-
-__initcall(piix4_acpi_init);