diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-07-15 03:32:22 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-07-15 03:32:22 +0000 |
commit | f1da2c3860e301527d56a1ef0b56c649ee7c4b1b (patch) | |
tree | 562b5d2e8b9cb62eb983d78ff6bcf9789e08fcf6 /drivers/acpi/cpu.c | |
parent | 00f11569ac8ca73cbcdef8822de1583e79aee571 (diff) |
Merge with Linux 2.4.0-test5-pre1. This works again on Origin UP.
The IP22 cache bugs which are plaguing some machines are still unfixed.
Diffstat (limited to 'drivers/acpi/cpu.c')
-rw-r--r-- | drivers/acpi/cpu.c | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/drivers/acpi/cpu.c b/drivers/acpi/cpu.c new file mode 100644 index 000000000..f1feeb12b --- /dev/null +++ b/drivers/acpi/cpu.c @@ -0,0 +1,307 @@ +/* + * cpu.c - Processor handling + * + * Copyright (C) 2000 Andrew Henroid + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/pm.h> +#include <linux/acpi.h> +#include "acpi.h" +#include "driver.h" + +unsigned long acpi_c2_exit_latency = ACPI_INFINITE; +unsigned long acpi_c3_exit_latency = ACPI_INFINITE; +unsigned long acpi_c2_enter_latency = ACPI_INFINITE; +unsigned long acpi_c3_enter_latency = ACPI_INFINITE; + +static unsigned long acpi_pblk = ACPI_INVALID; +static int acpi_c2_tested = 0; +static int acpi_c3_tested = 0; + +/* + * Clear busmaster activity flag + */ +static inline void +acpi_clear_bm_activity(struct acpi_facp *facp) +{ + acpi_write_pm1_status(facp, ACPI_BM); +} + +/* + * Returns 1 if there has been busmaster activity + */ +static inline int +acpi_bm_activity(struct acpi_facp *facp) +{ + return acpi_read_pm1_status(facp) & ACPI_BM; +} + +/* + * Set system to sleep through busmaster requests + */ +static void +acpi_sleep_on_busmaster(struct acpi_facp *facp) +{ + u32 pm1_cntr = acpi_read_pm1_control(facp); + if (pm1_cntr & ACPI_BM_RLD) { + pm1_cntr &= ~ACPI_BM_RLD; + acpi_write_pm1_control(facp, pm1_cntr); + } +} + +/* + * Set system to wake on busmaster requests + */ +static void +acpi_wake_on_busmaster(struct acpi_facp *facp) +{ + u32 pm1_cntr = acpi_read_pm1_control(facp); + if (!(pm1_cntr & ACPI_BM_RLD)) { + pm1_cntr |= ACPI_BM_RLD; + acpi_write_pm1_control(facp, pm1_cntr); + } + acpi_clear_bm_activity(facp); +} + +/* The ACPI timer is just the low 24 bits */ +#define TIME_BEGIN(tmr) inl(tmr) +#define TIME_END(tmr, begin) ((inl(tmr) - (begin)) & 0x00ffffff) + +/* + * Idle loop (uniprocessor only) + */ +static void +acpi_idle(void) +{ + static int sleep_level = 1; + struct acpi_facp *facp = &acpi_facp; + + if (!facp + || facp->hdr.signature != ACPI_FACP_SIG + || !facp->pm_tmr + || !acpi_pblk) + goto not_initialized; + + /* + * start from the previous sleep level.. + */ + if (sleep_level == 1) + goto sleep1; + if (sleep_level == 2) + goto sleep2; + sleep3: + sleep_level = 3; + if (!acpi_c3_tested) { + printk(KERN_DEBUG "ACPI C3 works\n"); + acpi_c3_tested = 1; + } + acpi_wake_on_busmaster(facp); + if (facp->pm2_cnt) + goto sleep3_with_arbiter; + + for (;;) { + unsigned long time; + unsigned int pm_tmr = facp->pm_tmr; + + __cli(); + if (current->need_resched) + goto out; + if (acpi_bm_activity(facp)) + goto sleep2; + + time = TIME_BEGIN(pm_tmr); + inb(acpi_pblk + ACPI_P_LVL3); + /* Dummy read, force synchronization with the PMU */ + inl(pm_tmr); + time = TIME_END(pm_tmr, time); + + __sti(); + if (time < acpi_c3_exit_latency) + goto sleep2; + } + + sleep3_with_arbiter: + for (;;) { + unsigned long time; + u8 arbiter; + unsigned int pm2_cntr = facp->pm2_cnt; + unsigned int pm_tmr = facp->pm_tmr; + + __cli(); + if (current->need_resched) + goto out; + if (acpi_bm_activity(facp)) + goto sleep2; + + time = TIME_BEGIN(pm_tmr); + arbiter = inb(pm2_cntr) & ~ACPI_ARB_DIS; + /* Disable arbiter, park on CPU */ + outb(arbiter | ACPI_ARB_DIS, pm2_cntr); + inb(acpi_pblk + ACPI_P_LVL3); + /* Dummy read, force synchronization with the PMU */ + inl(pm_tmr); + time = TIME_END(pm_tmr, time); + /* Enable arbiter again.. */ + outb(arbiter, pm2_cntr); + + __sti(); + if (time < acpi_c3_exit_latency) + goto sleep2; + } + + sleep2: + sleep_level = 2; + if (!acpi_c2_tested) { + printk(KERN_DEBUG "ACPI C2 works\n"); + acpi_c2_tested = 1; + } + acpi_wake_on_busmaster(facp); /* Required to track BM activity.. */ + for (;;) { + unsigned long time; + unsigned int pm_tmr = facp->pm_tmr; + + __cli(); + if (current->need_resched) + goto out; + + time = TIME_BEGIN(pm_tmr); + inb(acpi_pblk + ACPI_P_LVL2); + /* Dummy read, force synchronization with the PMU */ + inl(pm_tmr); + time = TIME_END(pm_tmr, time); + + __sti(); + if (time < acpi_c2_exit_latency) + goto sleep1; + if (acpi_bm_activity(facp)) { + acpi_clear_bm_activity(facp); + continue; + } + if (time > acpi_c3_enter_latency) + goto sleep3; + } + + sleep1: + sleep_level = 1; + acpi_sleep_on_busmaster(facp); + for (;;) { + unsigned long time; + unsigned int pm_tmr = facp->pm_tmr; + + __cli(); + if (current->need_resched) + goto out; + time = TIME_BEGIN(pm_tmr); + safe_halt(); + time = TIME_END(pm_tmr, time); + if (time > acpi_c2_enter_latency) + goto sleep2; + } + + not_initialized: + for (;;) { + __cli(); + if (current->need_resched) + goto out; + safe_halt(); + } + + out: + __sti(); +} + +/* + * Get processor information + */ +static ACPI_STATUS +acpi_find_cpu(ACPI_HANDLE handle, u32 level, void *ctx, void **value) +{ + ACPI_OBJECT obj; + ACPI_CX_STATE lat[4]; + ACPI_CPU_THROTTLING_STATE throttle[ACPI_MAX_THROTTLE]; + ACPI_BUFFER buf; + int i, count; + + buf.length = sizeof(obj); + buf.pointer = &obj; + if (!ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buf))) + return AE_OK; + + printk(KERN_INFO "ACPI: PBLK %d @ 0x%04x:%d\n", + obj.processor.proc_id, + obj.processor.pblk_address, + obj.processor.pblk_length); + if (acpi_pblk != ACPI_INVALID + || !obj.processor.pblk_address + || obj.processor.pblk_length != 6) + return AE_OK; + + acpi_pblk = obj.processor.pblk_address; + + buf.length = sizeof(lat); + buf.pointer = lat; + if (!ACPI_SUCCESS(acpi_get_processor_cx_info(handle, &buf))) + return AE_OK; + + if (lat[2].latency < MAX_CX_STATE_LATENCY) { + printk(KERN_INFO "ACPI: C2 supported\n"); + acpi_c2_exit_latency = lat[2].latency; + } + if (lat[3].latency < MAX_CX_STATE_LATENCY) { + printk(KERN_INFO "ACPI: C3 supported\n"); + acpi_c3_exit_latency = lat[3].latency; + } + + memset(throttle, 0, sizeof(throttle)); + buf.length = sizeof(throttle); + buf.pointer = throttle; + + if (!ACPI_SUCCESS(acpi_get_processor_throttling_info(handle, &buf))) + return AE_OK; + + for (i = 0, count = 0; i < ACPI_MAX_THROTTLE; i++) { + if (throttle[i].percent_of_clock) + count++; + } + count--; + if (count > 0) + printk(KERN_INFO "ACPI: %d throttling states\n", count); + + return AE_OK; +} + +int +acpi_cpu_init(void) +{ + acpi_walk_namespace(ACPI_TYPE_PROCESSOR, + ACPI_ROOT_OBJECT, + ACPI_INT32_MAX, + acpi_find_cpu, + NULL, + NULL); + +#ifdef CONFIG_SMP + if (smp_num_cpus == 1) + pm_idle = acpi_idle; +#else + pm_idle = acpi_idle; +#endif + + return 0; +} |