summaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-02-04 07:40:19 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-02-04 07:40:19 +0000
commit33263fc5f9ac8e8cb2b22d06af3ce5ac1dd815e4 (patch)
tree2d1b86a40bef0958a68cf1a2eafbeb0667a70543 /drivers/misc
parent216f5f51aa02f8b113aa620ebc14a9631a217a00 (diff)
Merge with Linux 2.3.32.
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/acpi.c252
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);