From 1d67e90f19a7acfd9a05dc59678e7d0c5090bd0d Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Sat, 4 Dec 1999 03:58:56 +0000 Subject: Merge with Linux 2.3.21. --- drivers/misc/Config.in | 9 +- drivers/misc/Makefile | 4 - drivers/misc/acpi.c | 219 ++++++++++++++++++++++++++++++++++++++++++---- drivers/misc/piix4_acpi.c | 214 -------------------------------------------- 4 files changed, 201 insertions(+), 245 deletions(-) delete mode 100644 drivers/misc/piix4_acpi.c (limited to 'drivers/misc') 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 #include #include @@ -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); /* @@ -72,6 +81,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) */ @@ -127,6 +149,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) */ @@ -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 -#include -#include - -#include - -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); -- cgit v1.2.3