Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6

* 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6: (28 commits)
  ACPI: check battery status on resume for un/plug events during sleep
  ACPICA: Fix incorrect handling of PCI Express Root Bridge _HID
  ACPI: asus_acpi: don't printk on writing garbage to proc files
  ACPI: asus_acpi: fix proc files parsing
  ACPI: SCI interrupt source override
  ACPI: fix printk format warnings
  ACPI: fix section for CPU init functions
  ACPI: update comments in motherboard.c
  ACPI: acpi_pci_link_set() can allocate with either GFP_ATOMIC or GFP_KERNEL
  ACPI: fix potential OOPS in power driver with CONFIG_ACPI_DEBUG
  ACPI: ibm_acpi: delete obsolete documentation
  ACPI: created a dedicated workqueue for notify() execution
  ACPI: Remove deferred execution from global lock acquire wakeup path
  MSI S270 Laptop support: backlight, wlan, bluetooth states
  ACPI: EC: export ec_transaction() for msi-laptop driver
  ACPI: EC: Simplify acpi_hw_low_level*() with inb()/outb().
  ACPI: EC: Unify poll and interrupt gpe handlers
  ACPI: EC: Unify poll and interrupt mode transaction functions
  ACPI: EC: Remove unused variables and duplicated code
  ACPI: EC: Remove unnecessary delay added by previous transation patch.
  ...
This commit is contained in:
Linus Torvalds 2006-10-15 11:02:52 -07:00
commit 0b269d8462
29 changed files with 1094 additions and 1011 deletions

View File

@ -30,9 +30,10 @@ detailed description):
- ACPI sounds - ACPI sounds
- temperature sensors - temperature sensors
- Experimental: embedded controller register dump - Experimental: embedded controller register dump
- Experimental: LCD brightness control - LCD brightness control
- Experimental: volume control - Volume control
- Experimental: fan speed, fan enable/disable - Experimental: fan speed, fan enable/disable
- Experimental: WAN enable and disable
A compatibility table by model and feature is maintained on the web A compatibility table by model and feature is maintained on the web
site, http://ibm-acpi.sf.net/. I appreciate any success or failure site, http://ibm-acpi.sf.net/. I appreciate any success or failure
@ -52,40 +53,7 @@ Installation
If you are compiling this driver as included in the Linux kernel If you are compiling this driver as included in the Linux kernel
sources, simply enable the CONFIG_ACPI_IBM option (Power Management / sources, simply enable the CONFIG_ACPI_IBM option (Power Management /
ACPI / IBM ThinkPad Laptop Extras). The rest of this section describes ACPI / IBM ThinkPad Laptop Extras).
how to install this driver when downloaded from the web site.
First, you need to get a kernel with ACPI support up and running.
Please refer to http://acpi.sourceforge.net/ for help with this
step. How successful you will be depends a lot on you ThinkPad model,
the kernel you are using and any additional patches applied. The
kernel provided with your distribution may not be good enough. I
needed to compile a 2.6.7 kernel with the 20040715 ACPI patch to get
ACPI working reliably on my ThinkPad X40. Old ThinkPad models may not
be supported at all.
Assuming you have the basic ACPI support working (e.g. you can see the
/proc/acpi directory), follow the following steps to install this
driver:
- unpack the archive:
tar xzvf ibm-acpi-x.y.tar.gz; cd ibm-acpi-x.y
- compile the driver:
make
- install the module in your kernel modules directory:
make install
- load the module:
modprobe ibm_acpi
After loading the module, check the "dmesg" output for any error messages.
Features Features
-------- --------
@ -523,13 +491,8 @@ registers contain the current battery capacity, etc. If you experiment
with this, do send me your results (including some complete dumps with with this, do send me your results (including some complete dumps with
a description of the conditions when they were taken.) a description of the conditions when they were taken.)
EXPERIMENTAL: LCD brightness control -- /proc/acpi/ibm/brightness LCD brightness control -- /proc/acpi/ibm/brightness
----------------------------------------------------------------- ---------------------------------------------------
This feature is marked EXPERIMENTAL because the implementation
directly accesses hardware registers and may not work as expected. USE
WITH CAUTION! To use this feature, you need to supply the
experimental=1 parameter when loading the module.
This feature allows software control of the LCD brightness on ThinkPad This feature allows software control of the LCD brightness on ThinkPad
models which don't have a hardware brightness slider. The available models which don't have a hardware brightness slider. The available
@ -542,13 +505,8 @@ commands are:
The <level> number range is 0 to 7, although not all of them may be The <level> number range is 0 to 7, although not all of them may be
distinct. The current brightness level is shown in the file. distinct. The current brightness level is shown in the file.
EXPERIMENTAL: Volume control -- /proc/acpi/ibm/volume Volume control -- /proc/acpi/ibm/volume
----------------------------------------------------- ---------------------------------------
This feature is marked EXPERIMENTAL because the implementation
directly accesses hardware registers and may not work as expected. USE
WITH CAUTION! To use this feature, you need to supply the
experimental=1 parameter when loading the module.
This feature allows volume control on ThinkPad models which don't have This feature allows volume control on ThinkPad models which don't have
a hardware volume knob. The available commands are: a hardware volume knob. The available commands are:
@ -611,6 +569,23 @@ with the following command:
echo 'level <level>' > /proc/acpi/ibm/thermal echo 'level <level>' > /proc/acpi/ibm/thermal
EXPERIMENTAL: WAN -- /proc/acpi/ibm/wan
---------------------------------------
This feature is marked EXPERIMENTAL because the implementation
directly accesses hardware registers and may not work as expected. USE
WITH CAUTION! To use this feature, you need to supply the
experimental=1 parameter when loading the module.
This feature shows the presence and current state of a WAN (Sierra
Wireless EV-DO) device. If WAN is installed, the following commands can
be used:
echo enable > /proc/acpi/ibm/wan
echo disable > /proc/acpi/ibm/wan
It was tested on a Lenovo Thinkpad X60. It should probably work on other
Thinkpad models which come with this module installed.
Multiple Commands, Module Parameters Multiple Commands, Module Parameters
------------------------------------ ------------------------------------

View File

@ -1998,6 +1998,13 @@ M: rubini@ipvvis.unipv.it
L: linux-kernel@vger.kernel.org L: linux-kernel@vger.kernel.org
S: Maintained S: Maintained
MSI LAPTOP SUPPORT
P: Lennart Poettering
M: mzxreary@0pointer.de
L: https://tango.0pointer.de/mailman/listinfo/s270-linux
W: http://0pointer.de/lennart/tchibo.html
S: Maintained
MTRR AND SIMILAR SUPPORT [i386] MTRR AND SIMILAR SUPPORT [i386]
P: Richard Gooch P: Richard Gooch
M: rgooch@atnf.csiro.au M: rgooch@atnf.csiro.au

View File

@ -332,7 +332,7 @@ acpi_parse_ioapic(acpi_table_entry_header * header, const unsigned long end)
/* /*
* Parse Interrupt Source Override for the ACPI SCI * Parse Interrupt Source Override for the ACPI SCI
*/ */
static void acpi_sci_ioapic_setup(u32 gsi, u16 polarity, u16 trigger) static void acpi_sci_ioapic_setup(u32 bus_irq, u32 gsi, u16 polarity, u16 trigger)
{ {
if (trigger == 0) /* compatible SCI trigger is level */ if (trigger == 0) /* compatible SCI trigger is level */
trigger = 3; trigger = 3;
@ -352,13 +352,13 @@ static void acpi_sci_ioapic_setup(u32 gsi, u16 polarity, u16 trigger)
* If GSI is < 16, this will update its flags, * If GSI is < 16, this will update its flags,
* else it will create a new mp_irqs[] entry. * else it will create a new mp_irqs[] entry.
*/ */
mp_override_legacy_irq(gsi, polarity, trigger, gsi); mp_override_legacy_irq(bus_irq, polarity, trigger, gsi);
/* /*
* stash over-ride to indicate we've been here * stash over-ride to indicate we've been here
* and for later update of acpi_fadt * and for later update of acpi_fadt
*/ */
acpi_sci_override_gsi = gsi; acpi_sci_override_gsi = bus_irq;
return; return;
} }
@ -376,7 +376,7 @@ acpi_parse_int_src_ovr(acpi_table_entry_header * header,
acpi_table_print_madt_entry(header); acpi_table_print_madt_entry(header);
if (intsrc->bus_irq == acpi_fadt.sci_int) { if (intsrc->bus_irq == acpi_fadt.sci_int) {
acpi_sci_ioapic_setup(intsrc->global_irq, acpi_sci_ioapic_setup(intsrc->bus_irq, intsrc->global_irq,
intsrc->flags.polarity, intsrc->flags.polarity,
intsrc->flags.trigger); intsrc->flags.trigger);
return 0; return 0;
@ -879,7 +879,7 @@ static int __init acpi_parse_madt_ioapic_entries(void)
* pretend we got one so we can set the SCI flags. * pretend we got one so we can set the SCI flags.
*/ */
if (!acpi_sci_override_gsi) if (!acpi_sci_override_gsi)
acpi_sci_ioapic_setup(acpi_fadt.sci_int, 0, 0); acpi_sci_ioapic_setup(acpi_fadt.sci_int, acpi_fadt.sci_int, 0, 0);
/* Fill in identity legacy mapings where no override */ /* Fill in identity legacy mapings where no override */
mp_config_acpi_legacy_irqs(); mp_config_acpi_legacy_irqs();

View File

@ -10,6 +10,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/cpu.h>
#include <acpi/processor.h> #include <acpi/processor.h>
#include <asm/acpi.h> #include <asm/acpi.h>
@ -41,5 +42,124 @@ void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags,
flags->bm_check = 1; flags->bm_check = 1;
} }
} }
EXPORT_SYMBOL(acpi_processor_power_init_bm_check); EXPORT_SYMBOL(acpi_processor_power_init_bm_check);
/* The code below handles cstate entry with monitor-mwait pair on Intel*/
struct cstate_entry_s {
struct {
unsigned int eax;
unsigned int ecx;
} states[ACPI_PROCESSOR_MAX_POWER];
};
static struct cstate_entry_s *cpu_cstate_entry; /* per CPU ptr */
static short mwait_supported[ACPI_PROCESSOR_MAX_POWER];
#define MWAIT_SUBSTATE_MASK (0xf)
#define MWAIT_SUBSTATE_SIZE (4)
#define CPUID_MWAIT_LEAF (5)
#define CPUID5_ECX_EXTENSIONS_SUPPORTED (0x1)
#define CPUID5_ECX_INTERRUPT_BREAK (0x2)
#define MWAIT_ECX_INTERRUPT_BREAK (0x1)
#define NATIVE_CSTATE_BEYOND_HALT (2)
int acpi_processor_ffh_cstate_probe(unsigned int cpu,
struct acpi_processor_cx *cx, struct acpi_power_register *reg)
{
struct cstate_entry_s *percpu_entry;
struct cpuinfo_x86 *c = cpu_data + cpu;
cpumask_t saved_mask;
int retval;
unsigned int eax, ebx, ecx, edx;
unsigned int edx_part;
unsigned int cstate_type; /* C-state type and not ACPI C-state type */
unsigned int num_cstate_subtype;
if (!cpu_cstate_entry || c->cpuid_level < CPUID_MWAIT_LEAF )
return -1;
if (reg->bit_offset != NATIVE_CSTATE_BEYOND_HALT)
return -1;
percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu);
percpu_entry->states[cx->index].eax = 0;
percpu_entry->states[cx->index].ecx = 0;
/* Make sure we are running on right CPU */
saved_mask = current->cpus_allowed;
retval = set_cpus_allowed(current, cpumask_of_cpu(cpu));
if (retval)
return -1;
cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx);
/* Check whether this particular cx_type (in CST) is supported or not */
cstate_type = (cx->address >> MWAIT_SUBSTATE_SIZE) + 1;
edx_part = edx >> (cstate_type * MWAIT_SUBSTATE_SIZE);
num_cstate_subtype = edx_part & MWAIT_SUBSTATE_MASK;
retval = 0;
if (num_cstate_subtype < (cx->address & MWAIT_SUBSTATE_MASK)) {
retval = -1;
goto out;
}
/* mwait ecx extensions INTERRUPT_BREAK should be supported for C2/C3 */
if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) ||
!(ecx & CPUID5_ECX_INTERRUPT_BREAK)) {
retval = -1;
goto out;
}
percpu_entry->states[cx->index].ecx = MWAIT_ECX_INTERRUPT_BREAK;
/* Use the hint in CST */
percpu_entry->states[cx->index].eax = cx->address;
if (!mwait_supported[cstate_type]) {
mwait_supported[cstate_type] = 1;
printk(KERN_DEBUG "Monitor-Mwait will be used to enter C-%d "
"state\n", cx->type);
}
out:
set_cpus_allowed(current, saved_mask);
return retval;
}
EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_probe);
void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx)
{
unsigned int cpu = smp_processor_id();
struct cstate_entry_s *percpu_entry;
percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu);
mwait_idle_with_hints(percpu_entry->states[cx->index].eax,
percpu_entry->states[cx->index].ecx);
}
EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_enter);
static int __init ffh_cstate_init(void)
{
struct cpuinfo_x86 *c = &boot_cpu_data;
if (c->x86_vendor != X86_VENDOR_INTEL)
return -1;
cpu_cstate_entry = alloc_percpu(struct cstate_entry_s);
return 0;
}
static void __exit ffh_cstate_exit(void)
{
if (cpu_cstate_entry) {
free_percpu(cpu_cstate_entry);
cpu_cstate_entry = NULL;
}
}
arch_initcall(ffh_cstate_init);
__exitcall(ffh_cstate_exit);

View File

@ -236,18 +236,26 @@ EXPORT_SYMBOL_GPL(cpu_idle_wait);
* We execute MONITOR against need_resched and enter optimized wait state * We execute MONITOR against need_resched and enter optimized wait state
* through MWAIT. Whenever someone changes need_resched, we would be woken * through MWAIT. Whenever someone changes need_resched, we would be woken
* up from MWAIT (without an IPI). * up from MWAIT (without an IPI).
*
* New with Core Duo processors, MWAIT can take some hints based on CPU
* capability.
*/ */
void mwait_idle_with_hints(unsigned long eax, unsigned long ecx)
{
if (!need_resched()) {
__monitor((void *)&current_thread_info()->flags, 0, 0);
smp_mb();
if (!need_resched())
__mwait(eax, ecx);
}
}
/* Default MONITOR/MWAIT with no hints, used for default C1 state */
static void mwait_idle(void) static void mwait_idle(void)
{ {
local_irq_enable(); local_irq_enable();
while (!need_resched())
while (!need_resched()) { mwait_idle_with_hints(0, 0);
__monitor((void *)&current_thread_info()->flags, 0, 0);
smp_mb();
if (need_resched())
break;
__mwait(0, 0);
}
} }
void __devinit select_idle_routine(const struct cpuinfo_x86 *c) void __devinit select_idle_routine(const struct cpuinfo_x86 *c)

View File

@ -238,18 +238,26 @@ void cpu_idle (void)
* We execute MONITOR against need_resched and enter optimized wait state * We execute MONITOR against need_resched and enter optimized wait state
* through MWAIT. Whenever someone changes need_resched, we would be woken * through MWAIT. Whenever someone changes need_resched, we would be woken
* up from MWAIT (without an IPI). * up from MWAIT (without an IPI).
*
* New with Core Duo processors, MWAIT can take some hints based on CPU
* capability.
*/ */
void mwait_idle_with_hints(unsigned long eax, unsigned long ecx)
{
if (!need_resched()) {
__monitor((void *)&current_thread_info()->flags, 0, 0);
smp_mb();
if (!need_resched())
__mwait(eax, ecx);
}
}
/* Default MONITOR/MWAIT with no hints, used for default C1 state */
static void mwait_idle(void) static void mwait_idle(void)
{ {
local_irq_enable(); local_irq_enable();
while (!need_resched())
while (!need_resched()) { mwait_idle_with_hints(0,0);
__monitor((void *)&current_thread_info()->flags, 0, 0);
smp_mb();
if (need_resched())
break;
__mwait(0, 0);
}
} }
void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c) void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c)

View File

@ -138,6 +138,7 @@ struct asus_hotk {
S2x, //S200 (J1 reported), Victor MP-XP7210 S2x, //S200 (J1 reported), Victor MP-XP7210
W1N, //W1000N W1N, //W1000N
W5A, //W5A W5A, //W5A
W3V, //W3030V
xxN, //M2400N, M3700N, M5200N, M6800N, S1300N, S5200N xxN, //M2400N, M3700N, M5200N, M6800N, S1300N, S5200N
//(Centrino) //(Centrino)
END_MODEL END_MODEL
@ -376,6 +377,17 @@ static struct model_data model_conf[END_MODEL] = {
.display_get = "\\ADVG"}, .display_get = "\\ADVG"},
{ {
.name = "W3V",
.mt_mled = "MLED",
.mt_wled = "WLED",
.mt_lcd_switch = xxN_PREFIX "_Q10",
.lcd_status = "\\BKLT",
.brightness_set = "SPLV",
.brightness_get = "GPLV",
.display_set = "SDSP",
.display_get = "\\INFB"},
{
.name = "xxN", .name = "xxN",
.mt_mled = "MLED", .mt_mled = "MLED",
/* WLED present, but not controlled by ACPI */ /* WLED present, but not controlled by ACPI */
@ -555,11 +567,11 @@ static int
write_led(const char __user * buffer, unsigned long count, write_led(const char __user * buffer, unsigned long count,
char *ledname, int ledmask, int invert) char *ledname, int ledmask, int invert)
{ {
int value; int rv, value;
int led_out = 0; int led_out = 0;
count = parse_arg(buffer, count, &value); rv = parse_arg(buffer, count, &value);
if (count > 0) if (rv > 0)
led_out = value ? 1 : 0; led_out = value ? 1 : 0;
hotk->status = hotk->status =
@ -572,7 +584,7 @@ write_led(const char __user * buffer, unsigned long count,
printk(KERN_WARNING "Asus ACPI: LED (%s) write failed\n", printk(KERN_WARNING "Asus ACPI: LED (%s) write failed\n",
ledname); ledname);
return count; return rv;
} }
/* /*
@ -607,20 +619,18 @@ static int
proc_write_ledd(struct file *file, const char __user * buffer, proc_write_ledd(struct file *file, const char __user * buffer,
unsigned long count, void *data) unsigned long count, void *data)
{ {
int value; int rv, value;
count = parse_arg(buffer, count, &value); rv = parse_arg(buffer, count, &value);
if (count > 0) { if (rv > 0) {
if (!write_acpi_int if (!write_acpi_int
(hotk->handle, hotk->methods->mt_ledd, value, NULL)) (hotk->handle, hotk->methods->mt_ledd, value, NULL))
printk(KERN_WARNING printk(KERN_WARNING
"Asus ACPI: LED display write failed\n"); "Asus ACPI: LED display write failed\n");
else else
hotk->ledd_status = (u32) value; hotk->ledd_status = (u32) value;
} else if (count < 0) }
printk(KERN_WARNING "Asus ACPI: Error reading user input\n"); return rv;
return count;
} }
/* /*
@ -761,12 +771,12 @@ static int
proc_write_lcd(struct file *file, const char __user * buffer, proc_write_lcd(struct file *file, const char __user * buffer,
unsigned long count, void *data) unsigned long count, void *data)
{ {
int value; int rv, value;
count = parse_arg(buffer, count, &value); rv = parse_arg(buffer, count, &value);
if (count > 0) if (rv > 0)
set_lcd_state(value); set_lcd_state(value);
return count; return rv;
} }
static int read_brightness(void) static int read_brightness(void)
@ -830,18 +840,15 @@ static int
proc_write_brn(struct file *file, const char __user * buffer, proc_write_brn(struct file *file, const char __user * buffer,
unsigned long count, void *data) unsigned long count, void *data)
{ {
int value; int rv, value;
count = parse_arg(buffer, count, &value); rv = parse_arg(buffer, count, &value);
if (count > 0) { if (rv > 0) {
value = (0 < value) ? ((15 < value) ? 15 : value) : 0; value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
/* 0 <= value <= 15 */ /* 0 <= value <= 15 */
set_brightness(value); set_brightness(value);
} else if (count < 0) {
printk(KERN_WARNING "Asus ACPI: Error reading user input\n");
} }
return rv;
return count;
} }
static void set_display(int value) static void set_display(int value)
@ -880,15 +887,12 @@ static int
proc_write_disp(struct file *file, const char __user * buffer, proc_write_disp(struct file *file, const char __user * buffer,
unsigned long count, void *data) unsigned long count, void *data)
{ {
int value; int rv, value;
count = parse_arg(buffer, count, &value); rv = parse_arg(buffer, count, &value);
if (count > 0) if (rv > 0)
set_display(value); set_display(value);
else if (count < 0) return rv;
printk(KERN_WARNING "Asus ACPI: Error reading user input\n");
return count;
} }
typedef int (proc_readfunc) (char *page, char **start, off_t off, int count, typedef int (proc_readfunc) (char *page, char **start, off_t off, int count,
@ -1097,6 +1101,8 @@ static int asus_model_match(char *model)
return A4G; return A4G;
else if (strncmp(model, "W1N", 3) == 0) else if (strncmp(model, "W1N", 3) == 0)
return W1N; return W1N;
else if (strncmp(model, "W3V", 3) == 0)
return W3V;
else if (strncmp(model, "W5A", 3) == 0) else if (strncmp(model, "W5A", 3) == 0)
return W5A; return W5A;
else else
@ -1200,9 +1206,10 @@ static int asus_hotk_get_info(void)
hotk->methods->mt_wled = NULL; hotk->methods->mt_wled = NULL;
/* L5D's WLED is not controlled by ACPI */ /* L5D's WLED is not controlled by ACPI */
else if (strncmp(string, "M2N", 3) == 0 || else if (strncmp(string, "M2N", 3) == 0 ||
strncmp(string, "W3V", 3) == 0 ||
strncmp(string, "S1N", 3) == 0) strncmp(string, "S1N", 3) == 0)
hotk->methods->mt_wled = "WLED"; hotk->methods->mt_wled = "WLED";
/* M2N and S1N have a usable WLED */ /* M2N, S1N and W3V have a usable WLED */
else if (asus_info) { else if (asus_info) {
if (strncmp(asus_info->oem_table_id, "L1", 2) == 0) if (strncmp(asus_info->oem_table_id, "L1", 2) == 0)
hotk->methods->mled_status = NULL; hotk->methods->mled_status = NULL;

View File

@ -64,6 +64,7 @@ extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
static int acpi_battery_add(struct acpi_device *device); static int acpi_battery_add(struct acpi_device *device);
static int acpi_battery_remove(struct acpi_device *device, int type); static int acpi_battery_remove(struct acpi_device *device, int type);
static int acpi_battery_resume(struct acpi_device *device, int status);
static struct acpi_driver acpi_battery_driver = { static struct acpi_driver acpi_battery_driver = {
.name = ACPI_BATTERY_DRIVER_NAME, .name = ACPI_BATTERY_DRIVER_NAME,
@ -71,6 +72,7 @@ static struct acpi_driver acpi_battery_driver = {
.ids = ACPI_BATTERY_HID, .ids = ACPI_BATTERY_HID,
.ops = { .ops = {
.add = acpi_battery_add, .add = acpi_battery_add,
.resume = acpi_battery_resume,
.remove = acpi_battery_remove, .remove = acpi_battery_remove,
}, },
}; };
@ -753,6 +755,18 @@ static int acpi_battery_remove(struct acpi_device *device, int type)
return 0; return 0;
} }
/* this is needed to learn about changes made in suspended state */
static int acpi_battery_resume(struct acpi_device *device, int state)
{
struct acpi_battery *battery;
if (!device)
return -EINVAL;
battery = device->driver_data;
return acpi_battery_check(battery);
}
static int __init acpi_battery_init(void) static int __init acpi_battery_init(void)
{ {
int result; int result;

File diff suppressed because it is too large Load Diff

View File

@ -342,20 +342,8 @@ static u32 acpi_ev_global_lock_handler(void *context)
if (acquired) { if (acquired) {
/* Got the lock, now wake all threads waiting for it */ /* Got the lock, now wake all threads waiting for it */
acpi_gbl_global_lock_acquired = TRUE; acpi_gbl_global_lock_acquired = TRUE;
acpi_ev_global_lock_thread(context);
/* Run the Global Lock thread which will signal all waiting threads */
status =
acpi_os_execute(OSL_GLOBAL_LOCK_HANDLER,
acpi_ev_global_lock_thread, context);
if (ACPI_FAILURE(status)) {
ACPI_EXCEPTION((AE_INFO, status,
"Could not queue Global Lock thread"));
return (ACPI_INTERRUPT_NOT_HANDLED);
}
} }
return (ACPI_INTERRUPT_HANDLED); return (ACPI_INTERRUPT_HANDLED);

View File

@ -225,13 +225,12 @@ acpi_ev_pci_config_region_setup(acpi_handle handle,
if (! if (!
(ACPI_STRNCMP (ACPI_STRNCMP
(object_hID.value, PCI_ROOT_HID_STRING, (object_hID.value, PCI_ROOT_HID_STRING,
sizeof(PCI_ROOT_HID_STRING)) sizeof(PCI_ROOT_HID_STRING)))
|| ||
!(ACPI_STRNCMP !(ACPI_STRNCMP
(object_hID.value, (object_hID.value,
PCI_EXPRESS_ROOT_HID_STRING, PCI_EXPRESS_ROOT_HID_STRING,
sizeof(PCI_EXPRESS_ROOT_HID_STRING))))) sizeof(PCI_EXPRESS_ROOT_HID_STRING)))) {
{
/* Install a handler for this PCI root bridge */ /* Install a handler for this PCI root bridge */

View File

@ -1702,13 +1702,11 @@ static struct ibm_struct ibms[] = {
.name = "brightness", .name = "brightness",
.read = brightness_read, .read = brightness_read,
.write = brightness_write, .write = brightness_write,
.experimental = 1,
}, },
{ {
.name = "volume", .name = "volume",
.read = volume_read, .read = volume_read,
.write = volume_write, .write = volume_write,
.experimental = 1,
}, },
{ {
.name = "fan", .name = "fan",

View File

@ -48,6 +48,12 @@ ACPI_MODULE_NAME("acpi_motherboard")
* the io ports if they really know they can use it, while * the io ports if they really know they can use it, while
* still preventing hotplug PCI devices from using it. * still preventing hotplug PCI devices from using it.
*/ */
/*
* When CONFIG_PNP is enabled, pnp/system.c binds to PNP0C01
* and PNP0C02, redundant with acpi_reserve_io_ranges().
* But acpi_reserve_io_ranges() is necessary for !CONFIG_PNP.
*/
static acpi_status acpi_reserve_io_ranges(struct acpi_resource *res, void *data) static acpi_status acpi_reserve_io_ranges(struct acpi_resource *res, void *data)
{ {
struct resource *requested_res = NULL; struct resource *requested_res = NULL;

View File

@ -73,6 +73,7 @@ static unsigned int acpi_irq_irq;
static acpi_osd_handler acpi_irq_handler; static acpi_osd_handler acpi_irq_handler;
static void *acpi_irq_context; static void *acpi_irq_context;
static struct workqueue_struct *kacpid_wq; static struct workqueue_struct *kacpid_wq;
static struct workqueue_struct *kacpi_notify_wq;
acpi_status acpi_os_initialize(void) acpi_status acpi_os_initialize(void)
{ {
@ -91,8 +92,9 @@ acpi_status acpi_os_initialize1(void)
return AE_NULL_ENTRY; return AE_NULL_ENTRY;
} }
kacpid_wq = create_singlethread_workqueue("kacpid"); kacpid_wq = create_singlethread_workqueue("kacpid");
kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify");
BUG_ON(!kacpid_wq); BUG_ON(!kacpid_wq);
BUG_ON(!kacpi_notify_wq);
return AE_OK; return AE_OK;
} }
@ -104,6 +106,7 @@ acpi_status acpi_os_terminate(void)
} }
destroy_workqueue(kacpid_wq); destroy_workqueue(kacpid_wq);
destroy_workqueue(kacpi_notify_wq);
return AE_OK; return AE_OK;
} }
@ -566,10 +569,7 @@ void acpi_os_derive_pci_id(acpi_handle rhandle, /* upper bound */
static void acpi_os_execute_deferred(void *context) static void acpi_os_execute_deferred(void *context)
{ {
struct acpi_os_dpc *dpc = NULL; struct acpi_os_dpc *dpc = (struct acpi_os_dpc *)context;
dpc = (struct acpi_os_dpc *)context;
if (!dpc) { if (!dpc) {
printk(KERN_ERR PREFIX "Invalid (NULL) context\n"); printk(KERN_ERR PREFIX "Invalid (NULL) context\n");
return; return;
@ -604,14 +604,12 @@ acpi_status acpi_os_execute(acpi_execute_type type,
struct acpi_os_dpc *dpc; struct acpi_os_dpc *dpc;
struct work_struct *task; struct work_struct *task;
ACPI_FUNCTION_TRACE("os_queue_for_execution");
ACPI_DEBUG_PRINT((ACPI_DB_EXEC, ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
"Scheduling function [%p(%p)] for deferred execution.\n", "Scheduling function [%p(%p)] for deferred execution.\n",
function, context)); function, context));
if (!function) if (!function)
return_ACPI_STATUS(AE_BAD_PARAMETER); return AE_BAD_PARAMETER;
/* /*
* Allocate/initialize DPC structure. Note that this memory will be * Allocate/initialize DPC structure. Note that this memory will be
@ -624,26 +622,20 @@ acpi_status acpi_os_execute(acpi_execute_type type,
* from the same memory. * from the same memory.
*/ */
dpc = dpc = kmalloc(sizeof(struct acpi_os_dpc) +
kmalloc(sizeof(struct acpi_os_dpc) + sizeof(struct work_struct), sizeof(struct work_struct), GFP_ATOMIC);
GFP_ATOMIC);
if (!dpc) if (!dpc)
return_ACPI_STATUS(AE_NO_MEMORY); return AE_NO_MEMORY;
dpc->function = function; dpc->function = function;
dpc->context = context; dpc->context = context;
task = (void *)(dpc + 1); task = (void *)(dpc + 1);
INIT_WORK(task, acpi_os_execute_deferred, (void *)dpc); INIT_WORK(task, acpi_os_execute_deferred, (void *)dpc);
if (!queue_work((type == OSL_NOTIFY_HANDLER)?
if (!queue_work(kacpid_wq, task)) { kacpi_notify_wq : kacpid_wq, task)) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Call to queue_work() failed.\n"));
kfree(dpc);
status = AE_ERROR; status = AE_ERROR;
kfree(dpc);
} }
return status;
return_ACPI_STATUS(status);
} }
EXPORT_SYMBOL(acpi_os_execute); EXPORT_SYMBOL(acpi_os_execute);

View File

@ -307,7 +307,7 @@ static int acpi_pci_link_set(struct acpi_pci_link *link, int irq)
if (!link || !irq) if (!link || !irq)
return -EINVAL; return -EINVAL;
resource = kmalloc(sizeof(*resource) + 1, GFP_ATOMIC); resource = kmalloc(sizeof(*resource) + 1, irqs_disabled() ? GFP_ATOMIC: GFP_KERNEL);
if (!resource) if (!resource)
return -ENOMEM; return -ENOMEM;

View File

@ -216,10 +216,8 @@ static int acpi_power_off_device(acpi_handle handle)
{ {
int result = 0; int result = 0;
acpi_status status = AE_OK; acpi_status status = AE_OK;
struct acpi_device *device = NULL;
struct acpi_power_resource *resource = NULL; struct acpi_power_resource *resource = NULL;
result = acpi_power_get_context(handle, &resource); result = acpi_power_get_context(handle, &resource);
if (result) if (result)
return result; return result;
@ -230,13 +228,13 @@ static int acpi_power_off_device(acpi_handle handle)
if (resource->references) { if (resource->references) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Resource [%s] is still in use, dereferencing\n", "Resource [%s] is still in use, dereferencing\n",
device->pnp.bus_id)); resource->device->pnp.bus_id));
return 0; return 0;
} }
if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) { if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n", ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n",
device->pnp.bus_id)); resource->device->pnp.bus_id));
return 0; return 0;
} }
@ -251,8 +249,7 @@ static int acpi_power_off_device(acpi_handle handle)
return -ENOEXEC; return -ENOEXEC;
/* Update the power resource's _device_ power state */ /* Update the power resource's _device_ power state */
device = resource->device; resource->device->power.state = ACPI_STATE_D3;
device->power.state = ACPI_STATE_D3;
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n", ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n",
resource->name)); resource->name));

View File

@ -519,7 +519,7 @@ static int acpi_processor_get_info(struct acpi_processor *pr)
static void *processor_device_array[NR_CPUS]; static void *processor_device_array[NR_CPUS];
static int acpi_processor_start(struct acpi_device *device) static int __cpuinit acpi_processor_start(struct acpi_device *device)
{ {
int result = 0; int result = 0;
acpi_status status = AE_OK; acpi_status status = AE_OK;

View File

@ -219,6 +219,23 @@ static void acpi_safe_halt(void)
static atomic_t c3_cpu_count; static atomic_t c3_cpu_count;
/* Common C-state entry for C2, C3, .. */
static void acpi_cstate_enter(struct acpi_processor_cx *cstate)
{
if (cstate->space_id == ACPI_CSTATE_FFH) {
/* Call into architectural FFH based C-state */
acpi_processor_ffh_cstate_enter(cstate);
} else {
int unused;
/* IO port based C-state */
inb(cstate->address);
/* Dummy wait op - must do something useless after P_LVL2 read
because chipsets cannot guarantee that STPCLK# signal
gets asserted in time to freeze execution properly. */
unused = inl(acpi_fadt.xpm_tmr_blk.address);
}
}
static void acpi_processor_idle(void) static void acpi_processor_idle(void)
{ {
struct acpi_processor *pr = NULL; struct acpi_processor *pr = NULL;
@ -361,11 +378,7 @@ static void acpi_processor_idle(void)
/* Get start time (ticks) */ /* Get start time (ticks) */
t1 = inl(acpi_fadt.xpm_tmr_blk.address); t1 = inl(acpi_fadt.xpm_tmr_blk.address);
/* Invoke C2 */ /* Invoke C2 */
inb(cx->address); acpi_cstate_enter(cx);
/* Dummy wait op - must do something useless after P_LVL2 read
because chipsets cannot guarantee that STPCLK# signal
gets asserted in time to freeze execution properly. */
t2 = inl(acpi_fadt.xpm_tmr_blk.address);
/* Get end time (ticks) */ /* Get end time (ticks) */
t2 = inl(acpi_fadt.xpm_tmr_blk.address); t2 = inl(acpi_fadt.xpm_tmr_blk.address);
@ -401,9 +414,7 @@ static void acpi_processor_idle(void)
/* Get start time (ticks) */ /* Get start time (ticks) */
t1 = inl(acpi_fadt.xpm_tmr_blk.address); t1 = inl(acpi_fadt.xpm_tmr_blk.address);
/* Invoke C3 */ /* Invoke C3 */
inb(cx->address); acpi_cstate_enter(cx);
/* Dummy wait op (see above) */
t2 = inl(acpi_fadt.xpm_tmr_blk.address);
/* Get end time (ticks) */ /* Get end time (ticks) */
t2 = inl(acpi_fadt.xpm_tmr_blk.address); t2 = inl(acpi_fadt.xpm_tmr_blk.address);
if (pr->flags.bm_check) { if (pr->flags.bm_check) {
@ -628,20 +639,16 @@ static int acpi_processor_get_power_info_fadt(struct acpi_processor *pr)
return 0; return 0;
} }
static int acpi_processor_get_power_info_default_c1(struct acpi_processor *pr) static int acpi_processor_get_power_info_default(struct acpi_processor *pr)
{ {
if (!pr->power.states[ACPI_STATE_C1].valid) {
/* Zero initialize all the C-states info. */ /* set the first C-State to C1 */
memset(pr->power.states, 0, sizeof(pr->power.states)); /* all processors need to support C1 */
pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1;
/* set the first C-State to C1 */ pr->power.states[ACPI_STATE_C1].valid = 1;
pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1; }
/* the C0 state only exists as a filler in our array */
/* the C0 state only exists as a filler in our array,
* and all processors need to support C1 */
pr->power.states[ACPI_STATE_C0].valid = 1; pr->power.states[ACPI_STATE_C0].valid = 1;
pr->power.states[ACPI_STATE_C1].valid = 1;
return 0; return 0;
} }
@ -658,12 +665,7 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr)
if (nocst) if (nocst)
return -ENODEV; return -ENODEV;
current_count = 1; current_count = 0;
/* Zero initialize C2 onwards and prepare for fresh CST lookup */
for (i = 2; i < ACPI_PROCESSOR_MAX_POWER; i++)
memset(&(pr->power.states[i]), 0,
sizeof(struct acpi_processor_cx));
status = acpi_evaluate_object(pr->handle, "_CST", NULL, &buffer); status = acpi_evaluate_object(pr->handle, "_CST", NULL, &buffer);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
@ -718,22 +720,39 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr)
(reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE))
continue; continue;
cx.address = (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) ?
0 : reg->address;
/* There should be an easy way to extract an integer... */ /* There should be an easy way to extract an integer... */
obj = (union acpi_object *)&(element->package.elements[1]); obj = (union acpi_object *)&(element->package.elements[1]);
if (obj->type != ACPI_TYPE_INTEGER) if (obj->type != ACPI_TYPE_INTEGER)
continue; continue;
cx.type = obj->integer.value; cx.type = obj->integer.value;
/*
* Some buggy BIOSes won't list C1 in _CST -
* Let acpi_processor_get_power_info_default() handle them later
*/
if (i == 1 && cx.type != ACPI_STATE_C1)
current_count++;
if ((cx.type != ACPI_STATE_C1) && cx.address = reg->address;
(reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO)) cx.index = current_count + 1;
continue;
if ((cx.type < ACPI_STATE_C2) || (cx.type > ACPI_STATE_C3)) cx.space_id = ACPI_CSTATE_SYSTEMIO;
continue; if (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) {
if (acpi_processor_ffh_cstate_probe
(pr->id, &cx, reg) == 0) {
cx.space_id = ACPI_CSTATE_FFH;
} else if (cx.type != ACPI_STATE_C1) {
/*
* C1 is a special case where FIXED_HARDWARE
* can be handled in non-MWAIT way as well.
* In that case, save this _CST entry info.
* That is, we retain space_id of SYSTEM_IO for
* halt based C1.
* Otherwise, ignore this info and continue.
*/
continue;
}
}
obj = (union acpi_object *)&(element->package.elements[2]); obj = (union acpi_object *)&(element->package.elements[2]);
if (obj->type != ACPI_TYPE_INTEGER) if (obj->type != ACPI_TYPE_INTEGER)
@ -938,12 +957,18 @@ static int acpi_processor_get_power_info(struct acpi_processor *pr)
/* NOTE: the idle thread may not be running while calling /* NOTE: the idle thread may not be running while calling
* this function */ * this function */
/* Adding C1 state */ /* Zero initialize all the C-states info. */
acpi_processor_get_power_info_default_c1(pr); memset(pr->power.states, 0, sizeof(pr->power.states));
result = acpi_processor_get_power_info_cst(pr); result = acpi_processor_get_power_info_cst(pr);
if (result == -ENODEV) if (result == -ENODEV)
acpi_processor_get_power_info_fadt(pr); acpi_processor_get_power_info_fadt(pr);
if (result)
return result;
acpi_processor_get_power_info_default(pr);
pr->power.count = acpi_processor_power_verify(pr); pr->power.count = acpi_processor_power_verify(pr);
/* /*
@ -1105,7 +1130,7 @@ static struct notifier_block acpi_processor_latency_notifier = {
.notifier_call = acpi_processor_latency_notify, .notifier_call = acpi_processor_latency_notify,
}; };
int acpi_processor_power_init(struct acpi_processor *pr, int __cpuinit acpi_processor_power_init(struct acpi_processor *pr,
struct acpi_device *device) struct acpi_device *device)
{ {
acpi_status status = 0; acpi_status status = 0;

View File

@ -98,11 +98,11 @@ static int update_info_mode = UPDATE_INFO_MODE;
static int update_time = UPDATE_TIME; static int update_time = UPDATE_TIME;
static int update_time2 = UPDATE_TIME2; static int update_time2 = UPDATE_TIME2;
module_param(capacity_mode, int, CAPACITY_UNIT); module_param(capacity_mode, int, 0);
module_param(update_mode, int, UPDATE_MODE); module_param(update_mode, int, 0);
module_param(update_info_mode, int, UPDATE_INFO_MODE); module_param(update_info_mode, int, 0);
module_param(update_time, int, UPDATE_TIME); module_param(update_time, int, 0);
module_param(update_time2, int, UPDATE_TIME2); module_param(update_time2, int, 0);
static int acpi_sbs_add(struct acpi_device *device); static int acpi_sbs_add(struct acpi_device *device);
static int acpi_sbs_remove(struct acpi_device *device, int type); static int acpi_sbs_remove(struct acpi_device *device, int type);
@ -1685,10 +1685,16 @@ static int acpi_sbs_add(struct acpi_device *device)
int acpi_sbs_remove(struct acpi_device *device, int type) int acpi_sbs_remove(struct acpi_device *device, int type)
{ {
struct acpi_sbs *sbs = (struct acpi_sbs *)acpi_driver_data(device); struct acpi_sbs *sbs = NULL;
int id; int id;
if (!device || !sbs) { if (!device) {
return -EINVAL;
}
sbs = (struct acpi_sbs *)acpi_driver_data(device);
if (!sbs) {
return -EINVAL; return -EINVAL;
} }

View File

@ -324,7 +324,7 @@ acpi_tb_get_this_table(struct acpi_pointer *address,
if (header->length < sizeof(struct acpi_table_header)) { if (header->length < sizeof(struct acpi_table_header)) {
ACPI_ERROR((AE_INFO, ACPI_ERROR((AE_INFO,
"Table length (%X) is smaller than minimum (%X)", "Table length (%X) is smaller than minimum (%zX)",
header->length, sizeof(struct acpi_table_header))); header->length, sizeof(struct acpi_table_header)));
return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH); return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH);

View File

@ -187,7 +187,7 @@ acpi_status acpi_tb_validate_rsdt(struct acpi_table_header *table_ptr)
if (table_ptr->length < sizeof(struct acpi_table_header)) { if (table_ptr->length < sizeof(struct acpi_table_header)) {
ACPI_ERROR((AE_INFO, ACPI_ERROR((AE_INFO,
"RSDT/XSDT length (%X) is smaller than minimum (%X)", "RSDT/XSDT length (%X) is smaller than minimum (%zX)",
table_ptr->length, table_ptr->length,
sizeof(struct acpi_table_header))); sizeof(struct acpi_table_header)));

View File

@ -57,4 +57,23 @@ config TIFM_7XX1
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called tifm_7xx1. be called tifm_7xx1.
config MSI_LAPTOP
tristate "MSI Laptop Extras"
depends on X86
depends on ACPI_EC
depends on BACKLIGHT_CLASS_DEVICE
---help---
This is a driver for laptops built by MSI (MICRO-STAR
INTERNATIONAL):
MSI MegaBook S270 (MS-1013)
Cytron/TCM/Medion/Tchibo MD96100/SAM2000
It adds support for Bluetooth, WLAN and LCD brightness control.
More information about this driver is available at
<http://0pointer.de/lennart/tchibo.html>.
If you have an MSI S270 laptop, say Y or M here.
endmenu endmenu

View File

@ -5,6 +5,7 @@ obj- := misc.o # Dummy rule to force built-in.o to be made
obj-$(CONFIG_IBM_ASM) += ibmasm/ obj-$(CONFIG_IBM_ASM) += ibmasm/
obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/ obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
obj-$(CONFIG_LKDTM) += lkdtm.o obj-$(CONFIG_LKDTM) += lkdtm.o
obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_CORE) += tifm_core.o
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o

395
drivers/misc/msi-laptop.c Normal file
View File

@ -0,0 +1,395 @@
/*-*-linux-c-*-*/
/*
Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) de>
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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
/*
* msi-laptop.c - MSI S270 laptop support. This laptop is sold under
* various brands, including "Cytron/TCM/Medion/Tchibo MD96100".
*
* This driver exports a few files in /sys/devices/platform/msi-laptop-pf/:
*
* lcd_level - Screen brightness: contains a single integer in the
* range 0..8. (rw)
*
* auto_brightness - Enable automatic brightness control: contains
* either 0 or 1. If set to 1 the hardware adjusts the screen
* brightness automatically when the power cord is
* plugged/unplugged. (rw)
*
* wlan - WLAN subsystem enabled: contains either 0 or 1. (ro)
*
* bluetooth - Bluetooth subsystem enabled: contains either 0 or 1
* Please note that this file is constantly 0 if no Bluetooth
* hardware is available. (ro)
*
* In addition to these platform device attributes the driver
* registers itself in the Linux backlight control subsystem and is
* available to userspace under /sys/class/backlight/msi-laptop-bl/.
*
* This driver might work on other laptops produced by MSI. If you
* want to try it you can pass force=1 as argument to the module which
* will force it to load even when the DMI data doesn't identify the
* laptop as MSI S270. YMMV.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/backlight.h>
#include <linux/platform_device.h>
#include <linux/autoconf.h>
#define MSI_DRIVER_VERSION "0.5"
#define MSI_LCD_LEVEL_MAX 9
#define MSI_EC_COMMAND_WIRELESS 0x10
#define MSI_EC_COMMAND_LCD_LEVEL 0x11
static int force;
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
static int auto_brightness;
module_param(auto_brightness, int, 0);
MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
/* Hardware access */
static int set_lcd_level(int level)
{
u8 buf[2];
if (level < 0 || level >= MSI_LCD_LEVEL_MAX)
return -EINVAL;
buf[0] = 0x80;
buf[1] = (u8) (level*31);
return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, buf, sizeof(buf), NULL, 0);
}
static int get_lcd_level(void)
{
u8 wdata = 0, rdata;
int result;
result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1);
if (result < 0)
return result;
return (int) rdata / 31;
}
static int get_auto_brightness(void)
{
u8 wdata = 4, rdata;
int result;
result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, &wdata, 1, &rdata, 1);
if (result < 0)
return result;
return !!(rdata & 8);
}
static int set_auto_brightness(int enable)
{
u8 wdata[2], rdata;
int result;
wdata[0] = 4;
result = ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 1, &rdata, 1);
if (result < 0)
return result;
wdata[0] = 0x84;
wdata[1] = (rdata & 0xF7) | (enable ? 8 : 0);
return ec_transaction(MSI_EC_COMMAND_LCD_LEVEL, wdata, 2, NULL, 0);
}
static int get_wireless_state(int *wlan, int *bluetooth)
{
u8 wdata = 0, rdata;
int result;
result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1);
if (result < 0)
return -1;
if (wlan)
*wlan = !!(rdata & 8);
if (bluetooth)
*bluetooth = !!(rdata & 128);
return 0;
}
/* Backlight device stuff */
static int bl_get_brightness(struct backlight_device *b)
{
return get_lcd_level();
}
static int bl_update_status(struct backlight_device *b)
{
return set_lcd_level(b->props->brightness);
}
static struct backlight_properties msibl_props = {
.owner = THIS_MODULE,
.get_brightness = bl_get_brightness,
.update_status = bl_update_status,
.max_brightness = MSI_LCD_LEVEL_MAX-1,
};
static struct backlight_device *msibl_device;
/* Platform device */
static ssize_t show_wlan(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret, enabled;
ret = get_wireless_state(&enabled, NULL);
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", enabled);
}
static ssize_t show_bluetooth(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret, enabled;
ret = get_wireless_state(NULL, &enabled);
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", enabled);
}
static ssize_t show_lcd_level(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
ret = get_lcd_level();
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", ret);
}
static ssize_t store_lcd_level(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int level, ret;
if (sscanf(buf, "%i", &level) != 1 || (level < 0 || level >= MSI_LCD_LEVEL_MAX))
return -EINVAL;
ret = set_lcd_level(level);
if (ret < 0)
return ret;
return count;
}
static ssize_t show_auto_brightness(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
ret = get_auto_brightness();
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", ret);
}
static ssize_t store_auto_brightness(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
int enable, ret;
if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
return -EINVAL;
ret = set_auto_brightness(enable);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness, store_auto_brightness);
static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
static struct attribute *msipf_attributes[] = {
&dev_attr_lcd_level.attr,
&dev_attr_auto_brightness.attr,
&dev_attr_bluetooth.attr,
&dev_attr_wlan.attr,
NULL
};
static struct attribute_group msipf_attribute_group = {
.attrs = msipf_attributes
};
static struct platform_driver msipf_driver = {
.driver = {
.name = "msi-laptop-pf",
.owner = THIS_MODULE,
}
};
static struct platform_device *msipf_device;
/* Initialization */
static struct dmi_system_id __initdata msi_dmi_table[] = {
{
.ident = "MSI S270",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"),
DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"),
}
},
{
.ident = "Medion MD96100",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"),
DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"),
}
},
{ }
};
static int __init msi_init(void)
{
int ret;
if (acpi_disabled)
return -ENODEV;
if (!force && !dmi_check_system(msi_dmi_table))
return -ENODEV;
if (auto_brightness < 0 || auto_brightness > 2)
return -EINVAL;
/* Register backlight stuff */
msibl_device = backlight_device_register("msi-laptop-bl", NULL, &msibl_props);
if (IS_ERR(msibl_device))
return PTR_ERR(msibl_device);
ret = platform_driver_register(&msipf_driver);
if (ret)
goto fail_backlight;
/* Register platform stuff */
msipf_device = platform_device_alloc("msi-laptop-pf", -1);
if (!msipf_device) {
ret = -ENOMEM;
goto fail_platform_driver;
}
ret = platform_device_add(msipf_device);
if (ret)
goto fail_platform_device1;
ret = sysfs_create_group(&msipf_device->dev.kobj, &msipf_attribute_group);
if (ret)
goto fail_platform_device2;
/* Disable automatic brightness control by default because
* this module was probably loaded to do brightness control in
* software. */
if (auto_brightness != 2)
set_auto_brightness(auto_brightness);
printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n");
return 0;
fail_platform_device2:
platform_device_del(msipf_device);
fail_platform_device1:
platform_device_put(msipf_device);
fail_platform_driver:
platform_driver_unregister(&msipf_driver);
fail_backlight:
backlight_device_unregister(msibl_device);
return ret;
}
static void __exit msi_cleanup(void)
{
sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
platform_device_unregister(msipf_device);
platform_driver_unregister(&msipf_driver);
backlight_device_unregister(msibl_device);
/* Enable automatic brightness control again */
if (auto_brightness != 2)
set_auto_brightness(1);
printk(KERN_INFO "msi-laptop: driver unloaded.\n");
}
module_init(msi_init);
module_exit(msi_cleanup);
MODULE_AUTHOR("Lennart Poettering");
MODULE_DESCRIPTION("MSI Laptop Support");
MODULE_VERSION(MSI_DRIVER_VERSION);
MODULE_LICENSE("GPL");

View File

@ -13,6 +13,7 @@
#define ACPI_PDC_SMP_C_SWCOORD (0x0040) #define ACPI_PDC_SMP_C_SWCOORD (0x0040)
#define ACPI_PDC_SMP_T_SWCOORD (0x0080) #define ACPI_PDC_SMP_T_SWCOORD (0x0080)
#define ACPI_PDC_C_C1_FFH (0x0100) #define ACPI_PDC_C_C1_FFH (0x0100)
#define ACPI_PDC_C_C2C3_FFH (0x0200)
#define ACPI_PDC_EST_CAPABILITY_SMP (ACPI_PDC_SMP_C1PT | \ #define ACPI_PDC_EST_CAPABILITY_SMP (ACPI_PDC_SMP_C1PT | \
ACPI_PDC_C_C1_HALT | \ ACPI_PDC_C_C1_HALT | \
@ -23,8 +24,10 @@
ACPI_PDC_SMP_P_SWCOORD | \ ACPI_PDC_SMP_P_SWCOORD | \
ACPI_PDC_P_FFH) ACPI_PDC_P_FFH)
#define ACPI_PDC_C_CAPABILITY_SMP (ACPI_PDC_SMP_C2C3 | \ #define ACPI_PDC_C_CAPABILITY_SMP (ACPI_PDC_SMP_C2C3 | \
ACPI_PDC_SMP_C1PT | \ ACPI_PDC_SMP_C1PT | \
ACPI_PDC_C_C1_HALT) ACPI_PDC_C_C1_HALT | \
ACPI_PDC_C_C1_FFH | \
ACPI_PDC_C_C2C3_FFH)
#endif /* __PDC_INTEL_H__ */ #endif /* __PDC_INTEL_H__ */

View File

@ -29,6 +29,9 @@
#define DOMAIN_COORD_TYPE_SW_ANY 0xfd #define DOMAIN_COORD_TYPE_SW_ANY 0xfd
#define DOMAIN_COORD_TYPE_HW_ALL 0xfe #define DOMAIN_COORD_TYPE_HW_ALL 0xfe
#define ACPI_CSTATE_SYSTEMIO (0)
#define ACPI_CSTATE_FFH (1)
/* Power Management */ /* Power Management */
struct acpi_processor_cx; struct acpi_processor_cx;
@ -58,6 +61,8 @@ struct acpi_processor_cx {
u8 valid; u8 valid;
u8 type; u8 type;
u32 address; u32 address;
u8 space_id;
u8 index;
u32 latency; u32 latency;
u32 latency_ticks; u32 latency_ticks;
u32 power; u32 power;
@ -206,6 +211,9 @@ void arch_acpi_processor_init_pdc(struct acpi_processor *pr);
#ifdef ARCH_HAS_POWER_INIT #ifdef ARCH_HAS_POWER_INIT
void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags, void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags,
unsigned int cpu); unsigned int cpu);
int acpi_processor_ffh_cstate_probe(unsigned int cpu,
struct acpi_processor_cx *cx, struct acpi_power_register *reg);
void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cstate);
#else #else
static inline void acpi_processor_power_init_bm_check(struct static inline void acpi_processor_power_init_bm_check(struct
acpi_processor_flags acpi_processor_flags
@ -214,6 +222,16 @@ static inline void acpi_processor_power_init_bm_check(struct
flags->bm_check = 1; flags->bm_check = 1;
return; return;
} }
static inline int acpi_processor_ffh_cstate_probe(unsigned int cpu,
struct acpi_processor_cx *cx, struct acpi_power_register *reg)
{
return -1;
}
static inline void acpi_processor_ffh_cstate_enter(
struct acpi_processor_cx *cstate)
{
return;
}
#endif #endif
/* in processor_perflib.c */ /* in processor_perflib.c */

View File

@ -306,6 +306,8 @@ static inline void __mwait(unsigned long eax, unsigned long ecx)
: :"a" (eax), "c" (ecx)); : :"a" (eax), "c" (ecx));
} }
extern void mwait_idle_with_hints(unsigned long eax, unsigned long ecx);
/* from system description table in BIOS. Mostly for MCA use, but /* from system description table in BIOS. Mostly for MCA use, but
others may find it useful. */ others may find it useful. */
extern unsigned int machine_id; extern unsigned int machine_id;

View File

@ -475,6 +475,8 @@ static inline void __mwait(unsigned long eax, unsigned long ecx)
: :"a" (eax), "c" (ecx)); : :"a" (eax), "c" (ecx));
} }
extern void mwait_idle_with_hints(unsigned long eax, unsigned long ecx);
#define stack_current() \ #define stack_current() \
({ \ ({ \
struct thread_info *ti; \ struct thread_info *ti; \

View File

@ -494,6 +494,9 @@ void acpi_pci_unregister_driver(struct acpi_pci_driver *driver);
extern int ec_read(u8 addr, u8 *val); extern int ec_read(u8 addr, u8 *val);
extern int ec_write(u8 addr, u8 val); extern int ec_write(u8 addr, u8 val);
extern int ec_transaction(u8 command,
const u8 *wdata, unsigned wdata_len,
u8 *rdata, unsigned rdata_len);
#endif /*CONFIG_ACPI_EC*/ #endif /*CONFIG_ACPI_EC*/