diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-04 07:40:19 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-04 07:40:19 +0000 |
commit | 33263fc5f9ac8e8cb2b22d06af3ce5ac1dd815e4 (patch) | |
tree | 2d1b86a40bef0958a68cf1a2eafbeb0667a70543 /drivers/misc | |
parent | 216f5f51aa02f8b113aa620ebc14a9631a217a00 (diff) |
Merge with Linux 2.3.32.
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/acpi.c | 252 |
1 files changed, 183 insertions, 69 deletions
diff --git a/drivers/misc/acpi.c b/drivers/misc/acpi.c index fb7654f7e..ffb05161a 100644 --- a/drivers/misc/acpi.c +++ b/drivers/misc/acpi.c @@ -27,6 +27,7 @@ #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/sched.h> +#include <linux/time.h> #include <linux/wait.h> #include <linux/spinlock.h> #include <linux/ioport.h> @@ -54,7 +55,14 @@ #define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue * x = NULL #endif -static int acpi_idle_thread(void *context); +/* + * Yes, it's unfortunate that we are relying on get_cmos_time + * because it is slow (> 1 sec.) and i386 only. It might be better + * to use some of the code from drivers/char/rtc.c in the near future + */ +extern unsigned long get_cmos_time(void); + +static int acpi_control_thread(void *context); static int acpi_do_ulong(ctl_table *ctl, int write, struct file *file, @@ -70,13 +78,13 @@ static int acpi_do_event(ctl_table *ctl, 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); +static int acpi_do_sleep(ctl_table *ctl, + int write, + struct file *file, + void *buffer, + size_t *len); -DECLARE_WAIT_QUEUE_HEAD(acpi_idle_wait); +DECLARE_WAIT_QUEUE_HEAD(acpi_control_wait); static struct ctl_table_header *acpi_sysctl = NULL; @@ -86,22 +94,29 @@ static struct acpi_facs *acpi_facs = NULL; static unsigned long acpi_facp_addr = 0; static unsigned long acpi_dsdt_addr = 0; +// current system sleep state (S0 - S4) +static acpi_sstate_t acpi_sleep_state = ACPI_S0; +// time sleep began +static unsigned long acpi_sleep_start = 0; + static spinlock_t acpi_event_lock = SPIN_LOCK_UNLOCKED; static volatile u32 acpi_pm1_status = 0; static volatile u32 acpi_gpe_status = 0; static volatile u32 acpi_gpe_level = 0; +static volatile acpi_sstate_t acpi_event_state = ACPI_S0; 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; +/* Make it impossible to enter C2/C3 until after we've initialized */ +static unsigned long acpi_p_lvl2_lat = ACPI_INFINITE_LAT; +static unsigned long acpi_p_lvl3_lat = ACPI_INFINITE_LAT; + +static unsigned long acpi_p_blk = 0; -/* Initialize to guaranteed harmless port read */ -static unsigned long acpi_p_lvl2 = ACPI_P_LVL_DISABLED; -static unsigned long acpi_p_lvl3 = ACPI_P_LVL_DISABLED; +static int acpi_p_lvl2_tested = 0; +static int acpi_p_lvl3_tested = 0; // bits 8-15 are SLP_TYPa, bits 0-7 are SLP_TYPb static unsigned long acpi_slp_typ[] = @@ -138,12 +153,8 @@ static struct ctl_table acpi_table[] = {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), + {ACPI_P_BLK, "p_blk", + &acpi_p_blk, sizeof(acpi_p_blk), 0600, NULL, &acpi_do_ulong}, {ACPI_P_LVL2_LAT, "p_lvl2_lat", @@ -154,14 +165,19 @@ static struct ctl_table acpi_table[] = &acpi_p_lvl3_lat, sizeof(acpi_p_lvl3_lat), 0644, NULL, &acpi_do_ulong}, + {ACPI_S0_SLP_TYP, "s0_slp_typ", + &acpi_slp_typ[ACPI_S0], sizeof(acpi_slp_typ[ACPI_S0]), + 0600, NULL, &acpi_do_ulong}, + + {ACPI_S1_SLP_TYP, "s1_slp_typ", + &acpi_slp_typ[ACPI_S1], sizeof(acpi_slp_typ[ACPI_S1]), + 0600, NULL, &acpi_do_ulong}, + {ACPI_S5_SLP_TYP, "s5_slp_typ", - &acpi_slp_typ[5], sizeof(acpi_slp_typ[5]), + &acpi_slp_typ[ACPI_S5], sizeof(acpi_slp_typ[ACPI_S5]), 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 + {ACPI_SLEEP, "sleep", NULL, 0, 0600, NULL, &acpi_do_sleep}, {0} }; @@ -187,6 +203,17 @@ static u32 acpi_read_pm1_control(struct acpi_facp *facp) } /* + * Set the value of the PM1 control register (BM_RLD, ...) + */ +static void acpi_write_pm1_control(struct acpi_facp *facp, u32 value) +{ + if (facp->pm1a_cnt) + outw(value, facp->pm1a_cnt); + if (facp->pm1b_cnt) + outw(value, facp->pm1b_cnt); +} + +/* * Get the value of the fixed event status register */ static u32 acpi_read_pm1_status(struct acpi_facp *facp) @@ -522,14 +549,13 @@ static int __init acpi_find_piix4(void) 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->p_lvl2_lat = (__u16) ACPI_INFINITE_LAT; + acpi_facp->p_lvl3_lat = (__u16) ACPI_INFINITE_LAT; acpi_facp_addr = virt_to_phys(acpi_facp); acpi_dsdt_addr = 0; - acpi_p_lvl2 = base + ACPI_PIIX4_P_LVL2; - acpi_p_lvl3 = base + ACPI_PIIX4_P_LVL3; + acpi_p_blk = base + ACPI_PIIX4_P_BLK; return 0; } @@ -569,6 +595,7 @@ static void acpi_irq(int irq, void *dev_id, struct pt_regs *regs) acpi_pm1_status |= pm1_status; acpi_gpe_status |= gpe_status; spin_unlock_irqrestore(&acpi_event_lock, flags); + acpi_event_state = acpi_sleep_state; wake_up_interruptible(&acpi_event_wait); } @@ -602,19 +629,39 @@ static int acpi_disable(struct acpi_facp *facp) acpi_write_pm1_enable(facp, 0); acpi_write_pm1_status(facp, acpi_read_pm1_status(facp)); - if (facp->smi_cmd) - outb(facp->acpi_disable, facp->smi_cmd); - return (acpi_is_enabled(facp) ? -1:0); + /* writing acpi_disable to smi_cmd would be appropriate + * here but this causes a nasty crash on many systems + */ + + return 0; } /* - * Idle loop + * Idle loop (uniprocessor only) */ static void acpi_idle_handler(void) { static int sleep_level = 1; - u32 timer, pm2_cnt; - unsigned long time; + u32 pm1_cnt, timer, pm2_cnt, bm_active; + unsigned long time, usec; + + // return to C0 on bus master request (necessary for C3 only) + pm1_cnt = acpi_read_pm1_control(acpi_facp); + if (sleep_level == 3) { + if (!(pm1_cnt & ACPI_BM_RLD)) { + pm1_cnt |= ACPI_BM_RLD; + acpi_write_pm1_control(acpi_facp, pm1_cnt); + } + } + else { + if (pm1_cnt & ACPI_BM_RLD) { + pm1_cnt &= ~ACPI_BM_RLD; + acpi_write_pm1_control(acpi_facp, pm1_cnt); + } + } + + // clear bus master activity flag + acpi_write_pm1_status(acpi_facp, ACPI_BM); // get current time (fallback to CPU cycles if no PM timer) timer = acpi_facp->pm_tmr; @@ -629,19 +676,19 @@ static void acpi_idle_handler(void) __asm__ __volatile__("sti ; hlt": : :"memory"); break; case 2: - inb(acpi_p_lvl2); + inb(acpi_p_blk + ACPI_P_LVL2); break; case 3: pm2_cnt = acpi_facp->pm2_cnt; if (pm2_cnt) { - /* Disable PCI arbitration while sleeping, - to avoid DMA corruption? */ + /* Disable PCI arbitration while sleeping, + to avoid DMA corruption? */ outb(inb(pm2_cnt) | ACPI_ARB_DIS, pm2_cnt); - inb(acpi_p_lvl3); + inb(acpi_p_blk + ACPI_P_LVL3); outb(inb(pm2_cnt) & ~ACPI_ARB_DIS, pm2_cnt); } else { - inb(acpi_p_lvl3); + inb(acpi_p_blk + ACPI_P_LVL3); } break; } @@ -652,12 +699,29 @@ static void acpi_idle_handler(void) else time = ACPI_CPU_TO_TMR_TICKS(get_cycles() - time); - if (time > acpi_p_lvl3_lat) - sleep_level = 3; - else if (time > acpi_p_lvl2_lat) - sleep_level = 2; - else - sleep_level = 1; + // check for bus master activity + bm_active = (acpi_read_pm1_status(acpi_facp) & ACPI_BM); + + // record working C2/C3 + if (sleep_level == 2 && !acpi_p_lvl2_tested) { + acpi_p_lvl2_tested = 1; + printk(KERN_INFO "ACPI: C2 works\n"); + } + else if (sleep_level == 3 && !acpi_p_lvl3_tested) { + acpi_p_lvl3_tested = 1; + printk(KERN_INFO "ACPI: C3 works\n"); + } + + // pick next C-state based on time spent sleeping, + // C-state latencies, and bus master activity + sleep_level = 1; + if (acpi_p_blk) { + usec = ACPI_TMR_TICKS_TO_uS(time); + if (usec > acpi_p_lvl3_lat && !bm_active) + sleep_level = 3; + else if (usec > acpi_p_lvl2_lat) + sleep_level = 2; + } } /* @@ -689,11 +753,30 @@ static int acpi_enter_dx(acpi_dstate_t state) } /* + * Update system time from real-time clock + */ +static void acpi_update_clock(void) +{ + if (acpi_sleep_start) { + unsigned long delta; + struct timeval tv; + + delta = get_cmos_time() - acpi_sleep_start; + do_gettimeofday(&tv); + tv.tv_sec += delta; + do_settimeofday(&tv); + + acpi_sleep_start = 0; + } +} + + +/* * Enter system sleep state */ -static void acpi_enter_sx(int state) +static void acpi_enter_sx(acpi_sstate_t state) { - unsigned long slp_typ = acpi_slp_typ[state]; + unsigned long slp_typ = acpi_slp_typ[(int) state]; if (slp_typ != ACPI_SLP_TYP_DISABLED) { u16 typa, typb, value; @@ -704,6 +787,15 @@ static void acpi_enter_sx(int state) typa = ((typa << ACPI_SLP_TYP_SHIFT) & ACPI_SLP_TYP_MASK); typb = ((typb << ACPI_SLP_TYP_SHIFT) & ACPI_SLP_TYP_MASK); + if (state != ACPI_S0) { + acpi_sleep_start = get_cmos_time(); + acpi_enter_dx(ACPI_D3); + acpi_sleep_state = state; + } + + // clear wake status + acpi_write_pm1_status(acpi_facp, ACPI_WAK); + // set SLP_TYPa/b and SLP_EN if (acpi_facp->pm1a_cnt) { value = inw(acpi_facp->pm1a_cnt) & ~ACPI_SLP_TYP_MASK; @@ -713,6 +805,18 @@ static void acpi_enter_sx(int state) value = inw(acpi_facp->pm1b_cnt) & ~ACPI_SLP_TYP_MASK; outw(value | typb | ACPI_SLP_EN, acpi_facp->pm1b_cnt); } + + if (state == ACPI_S0) { + acpi_sleep_state = state; + acpi_enter_dx(ACPI_D0); + acpi_sleep_start = 0; + } + else if (state == ACPI_S1) { + // wait until S1 is entered + while (!(acpi_read_pm1_status(acpi_facp) & ACPI_WAK)) ; + // finished sleeping, update system time + acpi_update_clock(); + } } } @@ -721,7 +825,7 @@ static void acpi_enter_sx(int state) */ static void acpi_power_off_handler(void) { - acpi_enter_sx(5); + acpi_enter_sx(ACPI_S5); } /* @@ -929,7 +1033,8 @@ static int acpi_do_event(ctl_table *ctl, size_t *len) { u32 pm1_status = 0, gpe_status = 0; - char str[4 * sizeof(u32) + 7]; + acpi_sstate_t event_state = 0; + char str[27]; int size; if (write) @@ -949,6 +1054,7 @@ static int acpi_do_event(ctl_table *ctl, gpe_status = acpi_gpe_status; acpi_gpe_status = 0; spin_unlock_irqrestore(&acpi_event_lock, flags); + event_state = acpi_event_state; if (pm1_status || gpe_status) break; @@ -959,7 +1065,10 @@ static int acpi_do_event(ctl_table *ctl, return -ERESTARTSYS; } - size = sprintf(str, "0x%08x 0x%08x\n", pm1_status, gpe_status); + size = sprintf(str, "0x%08x 0x%08x 0x%01x\n", + pm1_status, + gpe_status, + event_state); copy_to_user(buffer, str, size); *len = size; file->f_pos += size; @@ -968,13 +1077,13 @@ static int acpi_do_event(ctl_table *ctl, } /* - * Sleep or wake system + * Enter system sleep state */ -static int acpi_do_sleep_wake(ctl_table *ctl, - int write, - struct file *file, - void *buffer, - size_t *len) +static int acpi_do_sleep(ctl_table *ctl, + int write, + struct file *file, + void *buffer, + size_t *len) { if (!write) { if (file->f_pos) { @@ -984,13 +1093,8 @@ static int acpi_do_sleep_wake(ctl_table *ctl, } else { - // just shutdown some devices for now - if (ctl->data) { - acpi_enter_dx(ACPI_D3); - } - else { - acpi_enter_dx(ACPI_D0); - } + acpi_enter_sx(ACPI_S1); + acpi_enter_sx(ACPI_S0); } file->f_pos += *len; return 0; @@ -1008,6 +1112,15 @@ static int __init acpi_init(void) return -ENODEV; } + if (acpi_facp->p_lvl2_lat + && acpi_facp->p_lvl2_lat <= ACPI_MAX_P_LVL2_LAT) { + acpi_p_lvl2_lat = acpi_facp->p_lvl2_lat; + } + if (acpi_facp->p_lvl3_lat + && acpi_facp->p_lvl3_lat <= ACPI_MAX_P_LVL3_LAT) { + acpi_p_lvl3_lat = acpi_facp->p_lvl3_lat; + } + if (acpi_facp->sci_int && request_irq(acpi_facp->sci_int, acpi_irq, @@ -1023,10 +1136,12 @@ static int __init acpi_init(void) acpi_claim_ioports(acpi_facp); acpi_sysctl = register_sysctl_table(acpi_dir_table, 1); - pid = kernel_thread(acpi_idle_thread, + pid = kernel_thread(acpi_control_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + acpi_power_off = acpi_power_off_handler; + /* * 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 @@ -1037,7 +1152,6 @@ static int __init acpi_init(void) return 0; #endif - acpi_power_off = acpi_power_off_handler; acpi_idle = acpi_idle_handler; return 0; @@ -1111,18 +1225,18 @@ void acpi_wakeup(struct acpi_dev *dev) /* * Manage idle devices */ -static int acpi_idle_thread(void *context) +static int acpi_control_thread(void *context) { exit_mm(current); exit_files(current); strcpy(current->comm, "acpi"); for(;;) { - interruptible_sleep_on(&acpi_idle_wait); + interruptible_sleep_on(&acpi_control_wait); if (signal_pending(current)) break; - // find all idle devices and set idle timer based on policy + // find all idle devices and set idle timer } return 0; @@ -1133,7 +1247,7 @@ __initcall(acpi_init); /* * Module visible symbols */ -EXPORT_SYMBOL(acpi_idle_wait); +EXPORT_SYMBOL(acpi_control_wait); EXPORT_SYMBOL(acpi_register); EXPORT_SYMBOL(acpi_unregister); EXPORT_SYMBOL(acpi_wakeup); |