diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-01-21 22:34:01 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-01-21 22:34:01 +0000 |
commit | 9e30c3705aed9fbec4c3304570e4d6e707856bcb (patch) | |
tree | b19e6acb5a67af31a4e7742e05c2166dc3f1444c /drivers/misc | |
parent | 72919904796333a20c6a5d5c380091b42e407aa9 (diff) |
Merge with Linux 2.3.22.
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/acpi.c | 271 |
1 files changed, 185 insertions, 86 deletions
diff --git a/drivers/misc/acpi.c b/drivers/misc/acpi.c index 99911093b..e6878dde6 100644 --- a/drivers/misc/acpi.c +++ b/drivers/misc/acpi.c @@ -28,6 +28,7 @@ #include <linux/miscdevice.h> #include <linux/sched.h> #include <linux/wait.h> +#include <linux/spinlock.h> #include <asm/uaccess.h> #include <asm/io.h> #include <linux/acpi.h> @@ -52,21 +53,40 @@ static struct acpi_facp *acpi_facp = NULL; static unsigned long acpi_facp_addr = 0; static unsigned long acpi_dsdt_addr = 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 DECLARE_WAIT_QUEUE_HEAD(acpi_wait_event); +/* 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; + + +/* + * Get the value of the PM1 control register (SCI_EN, ...) + */ +static u32 acpi_read_pm1_control(struct acpi_facp *facp) +{ + u32 value = inw(facp->pm1a_cnt); + if (facp->pm1b_cnt) + value |= inw(facp->pm1b_cnt); + return value; +} + /* * Get the value of the fixed event status register */ static u32 acpi_read_pm1_status(struct acpi_facp *facp) { u32 value = inw(facp->pm1a_evt); - - if (facp->pm1b_evt) { + if (facp->pm1b_evt) value |= inw(facp->pm1b_evt); - } return value; } @@ -76,9 +96,8 @@ static u32 acpi_read_pm1_status(struct acpi_facp *facp) static void acpi_write_pm1_status(struct acpi_facp *facp, u32 value) { outw(value, facp->pm1a_evt); - if (facp->pm1b_evt) { + if (facp->pm1b_evt) outw(value, facp->pm1b_evt); - } } /* @@ -88,9 +107,8 @@ 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) { + if (facp->pm1b_evt) value |= inw(facp->pm1b_evt + offset); - } return value; } @@ -100,11 +118,9 @@ static u32 acpi_read_pm1_enable(struct acpi_facp *facp) static void acpi_write_pm1_enable(struct acpi_facp *facp, u32 value) { int offset = facp->pm1_evt_len >> 1; - outw(value, facp->pm1a_evt + offset); - if (facp->pm1b_evt) { + if (facp->pm1b_evt) outw(value, facp->pm1b_evt + offset); - } } /* @@ -117,14 +133,12 @@ static u32 acpi_read_gpe_status(struct acpi_facp *facp) if (facp->gpe1) { size = facp->gpe1_len >> 1; - for (i = size - 1; i >= 0; i--) { + for (i = size - 1; i >= 0; i--) value = (value << 8) | inb(facp->gpe1 + i); - } } size = facp->gpe0_len >> 1; - for (i = size - 1; i >= 0; i--) { + for (i = size - 1; i >= 0; i--) value = (value << 8) | inb(facp->gpe0 + i); - } return value; } @@ -165,9 +179,8 @@ static u32 acpi_read_gpe_enable(struct acpi_facp *facp) } } size = facp->gpe0_len >> 1; - for (i = size - 1; i >= 0; i--) { + for (i = size - 1; i >= 0; i--) value = (value << 8) | inb(facp->gpe0 + offset + i); - } return value; } @@ -201,14 +214,15 @@ 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); // remap entire table table = (struct acpi_table *) - ioremap_nocache((unsigned long) addr, table_size); + ioremap_nocache((unsigned long) addr, + table_size); } } return table; @@ -219,9 +233,8 @@ static struct acpi_table *__init acpi_map_table(u32 addr) */ static void acpi_unmap_table(struct acpi_table *table) { - if (table) { + if (table) iounmap(table); - } } /* @@ -313,6 +326,8 @@ static void acpi_unmap_tables(void) static void acpi_irq(int irq, void *dev_id, struct pt_regs *regs) { u32 pm1_status, gpe_status, gpe_level, gpe_edge; + unsigned long flags; + // detect and clear fixed events pm1_status = (acpi_read_pm1_status(acpi_facp) & acpi_read_pm1_enable(acpi_facp)); @@ -323,7 +338,7 @@ static void acpi_irq(int irq, void *dev_id, struct pt_regs *regs) & acpi_read_gpe_enable(acpi_facp)); gpe_level = gpe_status & acpi_gpe_level; if (gpe_level) { - // disable level-triggered events + // disable level-triggered events (re-enabled after handling) acpi_write_gpe_enable( acpi_facp, acpi_read_gpe_enable(acpi_facp) & ~gpe_level); @@ -334,10 +349,12 @@ static void acpi_irq(int irq, void *dev_id, struct pt_regs *regs) while (acpi_read_gpe_status(acpi_facp) & gpe_edge) acpi_write_gpe_status(acpi_facp, gpe_edge); } - - // notify process reading /dev/acpi + + // notify process waiting on /dev/acpi + spin_lock_irqsave(&acpi_event_lock, flags); acpi_pm1_status |= pm1_status; acpi_gpe_status |= gpe_status; + spin_unlock_irqrestore(&acpi_event_lock, flags); wake_up_interruptible(&acpi_wait_event); } @@ -360,6 +377,39 @@ static int acpi_release(struct inode *inode, struct file *file) } /* + * Is ACPI enabled or not? + */ +static inline int acpi_is_enabled(struct acpi_facp *facp) +{ + return ((acpi_read_pm1_control(facp) & ACPI_SCI_EN) ? 1:0); +} + +/* + * Enable SCI + */ +static int acpi_enable(struct acpi_facp *facp) +{ + outb(facp->acpi_enable, facp->smi_cmd); + return (acpi_is_enabled(facp) ? 0:-1); +} + +/* + * Disable SCI + */ +static int acpi_disable(struct acpi_facp *facp) +{ + // disable and clear any pending events + acpi_write_gpe_enable(facp, 0); + while (acpi_read_gpe_status(facp)) + acpi_write_gpe_status(facp, acpi_read_gpe_status(facp)); + acpi_write_pm1_enable(facp, 0); + acpi_write_pm1_status(facp, acpi_read_pm1_status(facp)); + + outb(facp->acpi_disable, facp->smi_cmd); + return (acpi_is_enabled(facp) ? -1:0); +} + +/* * Handle command to /dev/acpi */ static int acpi_ioctl(struct inode *inode, @@ -369,6 +419,9 @@ static int acpi_ioctl(struct inode *inode, { int status = -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + switch (cmd) { case ACPI_FIND_TABLES: status = verify_area(VERIFY_WRITE, @@ -391,7 +444,7 @@ static int acpi_ioctl(struct inode *inode, = (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); @@ -405,12 +458,30 @@ static int acpi_ioctl(struct inode *inode, ~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; + + if (pm1_enable || gpe_enable) { + // enable ACPI unless it is already + if (!acpi_is_enabled(acpi_facp) + && acpi_enable(acpi_facp)) { + status = -EBUSY; + } + } + else { + // disable ACPI unless it is already + if (acpi_is_enabled(acpi_facp) + && acpi_disable(acpi_facp)) { + status = -EBUSY; + } + } + + if (!status) + { + acpi_write_pm1_enable(acpi_facp, pm1_enable); + acpi_write_gpe_enable(acpi_facp, gpe_enable); + acpi_gpe_level = gpe_level; + } } break; case ACPI_WAIT_EVENT: @@ -427,13 +498,13 @@ static int acpi_ioctl(struct inode *inode, unsigned long flags; // we need an atomic exchange here - save_flags(flags); - cli(); + spin_lock_irqsave(&acpi_event_lock, flags); pm1_status = acpi_pm1_status; acpi_pm1_status = 0; gpe_status = acpi_gpe_status; acpi_gpe_status = 0; - restore_flags(flags); + spin_unlock_irqrestore(&acpi_event_lock, + flags); if (pm1_status || gpe_status) break; @@ -453,6 +524,41 @@ static int acpi_ioctl(struct inode *inode, return status; } +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; +} + static struct file_operations acpi_fops = { NULL, /* llseek */ @@ -481,46 +587,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; +/* + * Claim ACPI I/O ports + */ +static int acpi_claim_ioports(struct acpi_facp *facp) +{ + // we don't get a guarantee of contiguity for any of the ACPI registers + request_region(facp->pm1a_evt, facp->pm1_evt_len, "acpi"); + if (facp->pm1b_evt) + request_region(facp->pm1b_evt, facp->pm1_evt_len, "acpi"); + request_region(facp->pm1a_cnt, facp->pm1_cnt_len, "acpi"); + if (facp->pm1b_cnt) + request_region(facp->pm1b_cnt, facp->pm1_cnt_len, "acpi"); + if (facp->pm2_cnt) + request_region(facp->pm2_cnt, facp->pm2_cnt_len, "acpi"); + request_region(facp->pm_tmr, facp->pm_tm_len, "acpi"); + request_region(facp->gpe0, facp->gpe0_len, "acpi"); + if (facp->gpe1) + request_region(facp->gpe1, facp->gpe1_len, "acpi"); -/* Initialize to guaranteed harmless port read */ -static u16 acpi_p_lvl2 = 0x80; -static u16 acpi_p_lvl3 = 0x80; + return 0; +} -static void acpi_idle_handler(void) +/* + * Free ACPI I/O ports + */ +static int acpi_release_ioports(struct acpi_facp *facp) { - unsigned long time; - static int sleep_level = 1; + // we don't get a guarantee of contiguity for any of the ACPI registers + release_region(facp->pm1a_evt, facp->pm1_evt_len); + if (facp->pm1b_evt) + release_region(facp->pm1b_evt, facp->pm1_evt_len); + release_region(facp->pm1a_cnt, facp->pm1_cnt_len); + if (facp->pm1b_cnt) + release_region(facp->pm1b_cnt, facp->pm1_cnt_len); + if (facp->pm2_cnt) + release_region(facp->pm2_cnt, facp->pm2_cnt_len); + release_region(facp->pm_tmr, facp->pm_tm_len); + release_region(facp->gpe0, facp->gpe0_len); + if (facp->gpe1) + release_region(facp->gpe1, facp->gpe1_len); - 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; + return 0; } /* @@ -528,9 +636,9 @@ static void acpi_idle_handler(void) */ static int __init acpi_init(void) { - if (acpi_map_tables()) { + if (acpi_map_tables()) return -ENODEV; - } + if (request_irq(acpi_facp->sci_int, acpi_irq, SA_INTERRUPT | SA_SHIRQ, @@ -541,9 +649,11 @@ static int __init acpi_init(void) acpi_unmap_tables(); return -ENODEV; } - if (misc_register(&acpi_device)) { + + acpi_claim_ioports(acpi_facp); + + 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 @@ -564,20 +674,9 @@ static int __init acpi_init(void) static void __exit acpi_exit(void) { misc_deregister(&acpi_device); - - // 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_pm1_enable(acpi_facp, 0); - acpi_write_pm1_status(acpi_facp, acpi_read_pm1_status(acpi_facp)); - - // disable SCI and free interrupt - outb(acpi_facp->acpi_disable, acpi_facp->smi_cmd); + acpi_disable(acpi_facp); + acpi_release_ioports(acpi_facp); free_irq(acpi_facp->sci_int, NULL); - acpi_unmap_tables(); } |