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: (72 commits) asus-laptop: remove EXPERIMENTAL dependency asus-laptop: use pr_fmt and pr_<level> eeepc-laptop: cpufv updates eeepc-laptop: sync eeepc-laptop with asus_acpi asus_acpi: Deprecate in favor of asus-laptop acpi4asus: update MAINTAINER and KConfig links asus-laptop: platform dev as parent for led and backlight eeepc-laptop: enable camera by default ACPI: Rename ACPI processor device bus ID acerhdf: Acer Aspire One fan control ACPI: video: DMI workaround broken Acer 7720 BIOS enabling display brightness ACPI: run ACPI device hot removal in kacpi_hotplug_wq ACPI: Add the reference count to avoid unloading ACPI video bus twice ACPI: DMI to disable Vista compatibility on some Sony laptops ACPI: fix a deadlock in hotplug case Show the physical device node of backlight class device. ACPI: pdc init related memory leak with physical CPU hotplug ACPI: pci_root: remove unused dev/fn information ACPI: pci_root: simplify list traversals ACPI: pci_root: use driver data rather than list lookup ...
This commit is contained in:
commit
0c26d7cc31
45 changed files with 1934 additions and 1065 deletions
|
@ -229,14 +229,6 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||||
to assume that this machine's pmtimer latches its value
|
to assume that this machine's pmtimer latches its value
|
||||||
and always returns good values.
|
and always returns good values.
|
||||||
|
|
||||||
acpi.power_nocheck= [HW,ACPI]
|
|
||||||
Format: 1/0 enable/disable the check of power state.
|
|
||||||
On some bogus BIOS the _PSC object/_STA object of
|
|
||||||
power resource can't return the correct device power
|
|
||||||
state. In such case it is unneccessary to check its
|
|
||||||
power state again in power transition.
|
|
||||||
1 : disable the power state check
|
|
||||||
|
|
||||||
acpi_sci= [HW,ACPI] ACPI System Control Interrupt trigger mode
|
acpi_sci= [HW,ACPI] ACPI System Control Interrupt trigger mode
|
||||||
Format: { level | edge | high | low }
|
Format: { level | edge | high | low }
|
||||||
|
|
||||||
|
|
|
@ -920,7 +920,7 @@ The available commands are:
|
||||||
echo '<LED number> off' >/proc/acpi/ibm/led
|
echo '<LED number> off' >/proc/acpi/ibm/led
|
||||||
echo '<LED number> blink' >/proc/acpi/ibm/led
|
echo '<LED number> blink' >/proc/acpi/ibm/led
|
||||||
|
|
||||||
The <LED number> range is 0 to 7. The set of LEDs that can be
|
The <LED number> range is 0 to 15. The set of LEDs that can be
|
||||||
controlled varies from model to model. Here is the common ThinkPad
|
controlled varies from model to model. Here is the common ThinkPad
|
||||||
mapping:
|
mapping:
|
||||||
|
|
||||||
|
@ -932,6 +932,11 @@ mapping:
|
||||||
5 - UltraBase battery slot
|
5 - UltraBase battery slot
|
||||||
6 - (unknown)
|
6 - (unknown)
|
||||||
7 - standby
|
7 - standby
|
||||||
|
8 - dock status 1
|
||||||
|
9 - dock status 2
|
||||||
|
10, 11 - (unknown)
|
||||||
|
12 - thinkvantage
|
||||||
|
13, 14, 15 - (unknown)
|
||||||
|
|
||||||
All of the above can be turned on and off and can be made to blink.
|
All of the above can be turned on and off and can be made to blink.
|
||||||
|
|
||||||
|
@ -940,10 +945,12 @@ sysfs notes:
|
||||||
The ThinkPad LED sysfs interface is described in detail by the LED class
|
The ThinkPad LED sysfs interface is described in detail by the LED class
|
||||||
documentation, in Documentation/leds-class.txt.
|
documentation, in Documentation/leds-class.txt.
|
||||||
|
|
||||||
The leds are named (in LED ID order, from 0 to 7):
|
The LEDs are named (in LED ID order, from 0 to 12):
|
||||||
"tpacpi::power", "tpacpi:orange:batt", "tpacpi:green:batt",
|
"tpacpi::power", "tpacpi:orange:batt", "tpacpi:green:batt",
|
||||||
"tpacpi::dock_active", "tpacpi::bay_active", "tpacpi::dock_batt",
|
"tpacpi::dock_active", "tpacpi::bay_active", "tpacpi::dock_batt",
|
||||||
"tpacpi::unknown_led", "tpacpi::standby".
|
"tpacpi::unknown_led", "tpacpi::standby", "tpacpi::dock_status1",
|
||||||
|
"tpacpi::dock_status2", "tpacpi::unknown_led2", "tpacpi::unknown_led3",
|
||||||
|
"tpacpi::thinkvantage".
|
||||||
|
|
||||||
Due to limitations in the sysfs LED class, if the status of the LED
|
Due to limitations in the sysfs LED class, if the status of the LED
|
||||||
indicators cannot be read due to an error, thinkpad-acpi will report it as
|
indicators cannot be read due to an error, thinkpad-acpi will report it as
|
||||||
|
@ -958,6 +965,12 @@ ThinkPad indicator LED should blink in hardware accelerated mode, use the
|
||||||
"timer" trigger, and leave the delay_on and delay_off parameters set to
|
"timer" trigger, and leave the delay_on and delay_off parameters set to
|
||||||
zero (to request hardware acceleration autodetection).
|
zero (to request hardware acceleration autodetection).
|
||||||
|
|
||||||
|
LEDs that are known not to exist in a given ThinkPad model are not
|
||||||
|
made available through the sysfs interface. If you have a dock and you
|
||||||
|
notice there are LEDs listed for your ThinkPad that do not exist (and
|
||||||
|
are not in the dock), or if you notice that there are missing LEDs,
|
||||||
|
a report to ibm-acpi-devel@lists.sourceforge.net is appreciated.
|
||||||
|
|
||||||
|
|
||||||
ACPI sounds -- /proc/acpi/ibm/beep
|
ACPI sounds -- /proc/acpi/ibm/beep
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
@ -1156,17 +1169,19 @@ may not be distinct. Later Lenovo models that implement the ACPI
|
||||||
display backlight brightness control methods have 16 levels, ranging
|
display backlight brightness control methods have 16 levels, ranging
|
||||||
from 0 to 15.
|
from 0 to 15.
|
||||||
|
|
||||||
There are two interfaces to the firmware for direct brightness control,
|
For IBM ThinkPads, there are two interfaces to the firmware for direct
|
||||||
EC and UCMS (or CMOS). To select which one should be used, use the
|
brightness control, EC and UCMS (or CMOS). To select which one should be
|
||||||
brightness_mode module parameter: brightness_mode=1 selects EC mode,
|
used, use the brightness_mode module parameter: brightness_mode=1 selects
|
||||||
brightness_mode=2 selects UCMS mode, brightness_mode=3 selects EC
|
EC mode, brightness_mode=2 selects UCMS mode, brightness_mode=3 selects EC
|
||||||
mode with NVRAM backing (so that brightness changes are remembered
|
mode with NVRAM backing (so that brightness changes are remembered across
|
||||||
across shutdown/reboot).
|
shutdown/reboot).
|
||||||
|
|
||||||
The driver tries to select which interface to use from a table of
|
The driver tries to select which interface to use from a table of
|
||||||
defaults for each ThinkPad model. If it makes a wrong choice, please
|
defaults for each ThinkPad model. If it makes a wrong choice, please
|
||||||
report this as a bug, so that we can fix it.
|
report this as a bug, so that we can fix it.
|
||||||
|
|
||||||
|
Lenovo ThinkPads only support brightness_mode=2 (UCMS).
|
||||||
|
|
||||||
When display backlight brightness controls are available through the
|
When display backlight brightness controls are available through the
|
||||||
standard ACPI interface, it is best to use it instead of this direct
|
standard ACPI interface, it is best to use it instead of this direct
|
||||||
ThinkPad-specific interface. The driver will disable its native
|
ThinkPad-specific interface. The driver will disable its native
|
||||||
|
@ -1254,7 +1269,7 @@ Fan control and monitoring: fan speed, fan enable/disable
|
||||||
|
|
||||||
procfs: /proc/acpi/ibm/fan
|
procfs: /proc/acpi/ibm/fan
|
||||||
sysfs device attributes: (hwmon "thinkpad") fan1_input, pwm1,
|
sysfs device attributes: (hwmon "thinkpad") fan1_input, pwm1,
|
||||||
pwm1_enable
|
pwm1_enable, fan2_input
|
||||||
sysfs hwmon driver attributes: fan_watchdog
|
sysfs hwmon driver attributes: fan_watchdog
|
||||||
|
|
||||||
NOTE NOTE NOTE: fan control operations are disabled by default for
|
NOTE NOTE NOTE: fan control operations are disabled by default for
|
||||||
|
@ -1267,6 +1282,9 @@ from the hardware registers of the embedded controller. This is known
|
||||||
to work on later R, T, X and Z series ThinkPads but may show a bogus
|
to work on later R, T, X and Z series ThinkPads but may show a bogus
|
||||||
value on other models.
|
value on other models.
|
||||||
|
|
||||||
|
Some Lenovo ThinkPads support a secondary fan. This fan cannot be
|
||||||
|
controlled separately, it shares the main fan control.
|
||||||
|
|
||||||
Fan levels:
|
Fan levels:
|
||||||
|
|
||||||
Most ThinkPad fans work in "levels" at the firmware interface. Level 0
|
Most ThinkPad fans work in "levels" at the firmware interface. Level 0
|
||||||
|
@ -1397,6 +1415,11 @@ hwmon device attribute fan1_input:
|
||||||
which can take up to two minutes. May return rubbish on older
|
which can take up to two minutes. May return rubbish on older
|
||||||
ThinkPads.
|
ThinkPads.
|
||||||
|
|
||||||
|
hwmon device attribute fan2_input:
|
||||||
|
Fan tachometer reading, in RPM, for the secondary fan.
|
||||||
|
Available only on some ThinkPads. If the secondary fan is
|
||||||
|
not installed, will always read 0.
|
||||||
|
|
||||||
hwmon driver attribute fan_watchdog:
|
hwmon driver attribute fan_watchdog:
|
||||||
Fan safety watchdog timer interval, in seconds. Minimum is
|
Fan safety watchdog timer interval, in seconds. Minimum is
|
||||||
1 second, maximum is 120 seconds. 0 disables the watchdog.
|
1 second, maximum is 120 seconds. 0 disables the watchdog.
|
||||||
|
@ -1555,3 +1578,7 @@ Sysfs interface changelog:
|
||||||
0x020300: hotkey enable/disable support removed, attributes
|
0x020300: hotkey enable/disable support removed, attributes
|
||||||
hotkey_bios_enabled and hotkey_enable deprecated and
|
hotkey_bios_enabled and hotkey_enable deprecated and
|
||||||
marked for removal.
|
marked for removal.
|
||||||
|
|
||||||
|
0x020400: Marker for 16 LEDs support. Also, LEDs that are known
|
||||||
|
to not exist in a given model are not registered with
|
||||||
|
the LED sysfs class anymore.
|
||||||
|
|
15
MAINTAINERS
15
MAINTAINERS
|
@ -230,6 +230,13 @@ L: linux-acenic@sunsite.dk
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/net/acenic*
|
F: drivers/net/acenic*
|
||||||
|
|
||||||
|
ACER ASPIRE ONE TEMPERATURE AND FAN DRIVER
|
||||||
|
P: Peter Feuerer
|
||||||
|
M: peter@piie.net
|
||||||
|
W: http://piie.net/?section=acerhdf
|
||||||
|
S: Maintained
|
||||||
|
F: drivers/platform/x86/acerhdf.c
|
||||||
|
|
||||||
ACER WMI LAPTOP EXTRAS
|
ACER WMI LAPTOP EXTRAS
|
||||||
P: Carlos Corbacho
|
P: Carlos Corbacho
|
||||||
M: carlos@strangeworlds.co.uk
|
M: carlos@strangeworlds.co.uk
|
||||||
|
@ -913,8 +920,7 @@ M: corentincj@iksaif.net
|
||||||
P: Karol Kozimor
|
P: Karol Kozimor
|
||||||
M: sziwan@users.sourceforge.net
|
M: sziwan@users.sourceforge.net
|
||||||
L: acpi4asus-user@lists.sourceforge.net
|
L: acpi4asus-user@lists.sourceforge.net
|
||||||
W: http://sourceforge.net/projects/acpi4asus
|
W: http://acpi4asus.sf.net
|
||||||
W: http://xf.iksaif.net/acpi4asus
|
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: arch/x86/kernel/acpi/boot.c
|
F: arch/x86/kernel/acpi/boot.c
|
||||||
F: drivers/platform/x86/asus_acpi.c
|
F: drivers/platform/x86/asus_acpi.c
|
||||||
|
@ -930,8 +936,7 @@ ASUS LAPTOP EXTRAS DRIVER
|
||||||
P: Corentin Chary
|
P: Corentin Chary
|
||||||
M: corentincj@iksaif.net
|
M: corentincj@iksaif.net
|
||||||
L: acpi4asus-user@lists.sourceforge.net
|
L: acpi4asus-user@lists.sourceforge.net
|
||||||
W: http://sourceforge.net/projects/acpi4asus
|
W: http://acpi4asus.sf.net
|
||||||
W: http://xf.iksaif.net/acpi4asus
|
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/platform/x86/asus-laptop.c
|
F: drivers/platform/x86/asus-laptop.c
|
||||||
|
|
||||||
|
@ -2110,7 +2115,7 @@ EEEPC LAPTOP EXTRAS DRIVER
|
||||||
P: Corentin Chary
|
P: Corentin Chary
|
||||||
M: corentincj@iksaif.net
|
M: corentincj@iksaif.net
|
||||||
L: acpi4asus-user@lists.sourceforge.net
|
L: acpi4asus-user@lists.sourceforge.net
|
||||||
W: http://sourceforge.net/projects/acpi4asus
|
W: http://acpi4asus.sf.net
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/platform/x86/eeepc-laptop.c
|
F: drivers/platform/x86/eeepc-laptop.c
|
||||||
|
|
||||||
|
|
|
@ -71,3 +71,15 @@ void arch_acpi_processor_init_pdc(struct acpi_processor *pr)
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(arch_acpi_processor_init_pdc);
|
EXPORT_SYMBOL(arch_acpi_processor_init_pdc);
|
||||||
|
|
||||||
|
void arch_acpi_processor_cleanup_pdc(struct acpi_processor *pr)
|
||||||
|
{
|
||||||
|
if (pr->pdc) {
|
||||||
|
kfree(pr->pdc->pointer->buffer.pointer);
|
||||||
|
kfree(pr->pdc->pointer);
|
||||||
|
kfree(pr->pdc);
|
||||||
|
pr->pdc = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL(arch_acpi_processor_cleanup_pdc);
|
||||||
|
|
|
@ -144,6 +144,7 @@ static inline unsigned int acpi_processor_cstate_check(unsigned int max_cstate)
|
||||||
|
|
||||||
#else /* !CONFIG_ACPI */
|
#else /* !CONFIG_ACPI */
|
||||||
|
|
||||||
|
#define acpi_disabled 1
|
||||||
#define acpi_lapic 0
|
#define acpi_lapic 0
|
||||||
#define acpi_ioapic 0
|
#define acpi_ioapic 0
|
||||||
static inline void acpi_noirq_set(void) { }
|
static inline void acpi_noirq_set(void) { }
|
||||||
|
|
|
@ -121,6 +121,9 @@ extern int __init pcibios_init(void);
|
||||||
extern int __init pci_mmcfg_arch_init(void);
|
extern int __init pci_mmcfg_arch_init(void);
|
||||||
extern void __init pci_mmcfg_arch_free(void);
|
extern void __init pci_mmcfg_arch_free(void);
|
||||||
|
|
||||||
|
extern struct acpi_mcfg_allocation *pci_mmcfg_config;
|
||||||
|
extern int pci_mmcfg_config_num;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AMD Fam10h CPUs are buggy, and cannot access MMIO config space
|
* AMD Fam10h CPUs are buggy, and cannot access MMIO config space
|
||||||
* on their northbrige except through the * %eax register. As such, you MUST
|
* on their northbrige except through the * %eax register. As such, you MUST
|
||||||
|
|
|
@ -44,11 +44,7 @@
|
||||||
|
|
||||||
static int __initdata acpi_force = 0;
|
static int __initdata acpi_force = 0;
|
||||||
u32 acpi_rsdt_forced;
|
u32 acpi_rsdt_forced;
|
||||||
#ifdef CONFIG_ACPI
|
int acpi_disabled;
|
||||||
int acpi_disabled = 0;
|
|
||||||
#else
|
|
||||||
int acpi_disabled = 1;
|
|
||||||
#endif
|
|
||||||
EXPORT_SYMBOL(acpi_disabled);
|
EXPORT_SYMBOL(acpi_disabled);
|
||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
|
@ -122,72 +118,6 @@ void __init __acpi_unmap_table(char *map, unsigned long size)
|
||||||
early_iounmap(map, size);
|
early_iounmap(map, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PCI_MMCONFIG
|
|
||||||
|
|
||||||
static int acpi_mcfg_64bit_base_addr __initdata = FALSE;
|
|
||||||
|
|
||||||
/* The physical address of the MMCONFIG aperture. Set from ACPI tables. */
|
|
||||||
struct acpi_mcfg_allocation *pci_mmcfg_config;
|
|
||||||
int pci_mmcfg_config_num;
|
|
||||||
|
|
||||||
static int __init acpi_mcfg_oem_check(struct acpi_table_mcfg *mcfg)
|
|
||||||
{
|
|
||||||
if (!strcmp(mcfg->header.oem_id, "SGI"))
|
|
||||||
acpi_mcfg_64bit_base_addr = TRUE;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int __init acpi_parse_mcfg(struct acpi_table_header *header)
|
|
||||||
{
|
|
||||||
struct acpi_table_mcfg *mcfg;
|
|
||||||
unsigned long i;
|
|
||||||
int config_size;
|
|
||||||
|
|
||||||
if (!header)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
mcfg = (struct acpi_table_mcfg *)header;
|
|
||||||
|
|
||||||
/* how many config structures do we have */
|
|
||||||
pci_mmcfg_config_num = 0;
|
|
||||||
i = header->length - sizeof(struct acpi_table_mcfg);
|
|
||||||
while (i >= sizeof(struct acpi_mcfg_allocation)) {
|
|
||||||
++pci_mmcfg_config_num;
|
|
||||||
i -= sizeof(struct acpi_mcfg_allocation);
|
|
||||||
};
|
|
||||||
if (pci_mmcfg_config_num == 0) {
|
|
||||||
printk(KERN_ERR PREFIX "MMCONFIG has no entries\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
config_size = pci_mmcfg_config_num * sizeof(*pci_mmcfg_config);
|
|
||||||
pci_mmcfg_config = kmalloc(config_size, GFP_KERNEL);
|
|
||||||
if (!pci_mmcfg_config) {
|
|
||||||
printk(KERN_WARNING PREFIX
|
|
||||||
"No memory for MCFG config tables\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(pci_mmcfg_config, &mcfg[1], config_size);
|
|
||||||
|
|
||||||
acpi_mcfg_oem_check(mcfg);
|
|
||||||
|
|
||||||
for (i = 0; i < pci_mmcfg_config_num; ++i) {
|
|
||||||
if ((pci_mmcfg_config[i].address > 0xFFFFFFFF) &&
|
|
||||||
!acpi_mcfg_64bit_base_addr) {
|
|
||||||
printk(KERN_ERR PREFIX
|
|
||||||
"MMCONFIG not in low 4GB of memory\n");
|
|
||||||
kfree(pci_mmcfg_config);
|
|
||||||
pci_mmcfg_config_num = 0;
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_PCI_MMCONFIG */
|
|
||||||
|
|
||||||
#ifdef CONFIG_X86_LOCAL_APIC
|
#ifdef CONFIG_X86_LOCAL_APIC
|
||||||
static int __init acpi_parse_madt(struct acpi_table_header *table)
|
static int __init acpi_parse_madt(struct acpi_table_header *table)
|
||||||
{
|
{
|
||||||
|
@ -1517,14 +1447,6 @@ static struct dmi_system_id __initdata acpi_dmi_table[] = {
|
||||||
DMI_MATCH(DMI_PRODUCT_NAME, "Workstation W8000"),
|
DMI_MATCH(DMI_PRODUCT_NAME, "Workstation W8000"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
.callback = force_acpi_ht,
|
|
||||||
.ident = "ASUS P4B266",
|
|
||||||
.matches = {
|
|
||||||
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
|
|
||||||
DMI_MATCH(DMI_BOARD_NAME, "P4B266"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
.callback = force_acpi_ht,
|
.callback = force_acpi_ht,
|
||||||
.ident = "ASUS P2B-DS",
|
.ident = "ASUS P2B-DS",
|
||||||
|
|
|
@ -34,12 +34,22 @@ void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags,
|
||||||
flags->bm_check = 1;
|
flags->bm_check = 1;
|
||||||
else if (c->x86_vendor == X86_VENDOR_INTEL) {
|
else if (c->x86_vendor == X86_VENDOR_INTEL) {
|
||||||
/*
|
/*
|
||||||
* Today all CPUs that support C3 share cache.
|
* Today all MP CPUs that support C3 share cache.
|
||||||
* TBD: This needs to look at cache shared map, once
|
* And caches should not be flushed by software while
|
||||||
* multi-core detection patch makes to the base.
|
* entering C3 type state.
|
||||||
*/
|
*/
|
||||||
flags->bm_check = 1;
|
flags->bm_check = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* On all recent Intel platforms, ARB_DISABLE is a nop.
|
||||||
|
* So, set bm_control to zero to indicate that ARB_DISABLE
|
||||||
|
* is not required while entering C3 type state on
|
||||||
|
* P4, Core and beyond CPUs
|
||||||
|
*/
|
||||||
|
if (c->x86_vendor == X86_VENDOR_INTEL &&
|
||||||
|
(c->x86 > 0x6 || (c->x86 == 6 && c->x86_model >= 14)))
|
||||||
|
flags->bm_control = 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(acpi_processor_power_init_bm_check);
|
EXPORT_SYMBOL(acpi_processor_power_init_bm_check);
|
||||||
|
|
||||||
|
|
|
@ -72,6 +72,7 @@ static void init_intel_pdc(struct acpi_processor *pr, struct cpuinfo_x86 *c)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Initialize _PDC data based on the CPU vendor */
|
/* Initialize _PDC data based on the CPU vendor */
|
||||||
void arch_acpi_processor_init_pdc(struct acpi_processor *pr)
|
void arch_acpi_processor_init_pdc(struct acpi_processor *pr)
|
||||||
{
|
{
|
||||||
|
@ -85,3 +86,15 @@ void arch_acpi_processor_init_pdc(struct acpi_processor *pr)
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(arch_acpi_processor_init_pdc);
|
EXPORT_SYMBOL(arch_acpi_processor_init_pdc);
|
||||||
|
|
||||||
|
void arch_acpi_processor_cleanup_pdc(struct acpi_processor *pr)
|
||||||
|
{
|
||||||
|
if (pr->pdc) {
|
||||||
|
kfree(pr->pdc->pointer->buffer.pointer);
|
||||||
|
kfree(pr->pdc->pointer);
|
||||||
|
kfree(pr->pdc);
|
||||||
|
pr->pdc = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL(arch_acpi_processor_cleanup_pdc);
|
||||||
|
|
|
@ -523,6 +523,69 @@ reject:
|
||||||
|
|
||||||
static int __initdata known_bridge;
|
static int __initdata known_bridge;
|
||||||
|
|
||||||
|
static int acpi_mcfg_64bit_base_addr __initdata = FALSE;
|
||||||
|
|
||||||
|
/* The physical address of the MMCONFIG aperture. Set from ACPI tables. */
|
||||||
|
struct acpi_mcfg_allocation *pci_mmcfg_config;
|
||||||
|
int pci_mmcfg_config_num;
|
||||||
|
|
||||||
|
static int __init acpi_mcfg_oem_check(struct acpi_table_mcfg *mcfg)
|
||||||
|
{
|
||||||
|
if (!strcmp(mcfg->header.oem_id, "SGI"))
|
||||||
|
acpi_mcfg_64bit_base_addr = TRUE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init pci_parse_mcfg(struct acpi_table_header *header)
|
||||||
|
{
|
||||||
|
struct acpi_table_mcfg *mcfg;
|
||||||
|
unsigned long i;
|
||||||
|
int config_size;
|
||||||
|
|
||||||
|
if (!header)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mcfg = (struct acpi_table_mcfg *)header;
|
||||||
|
|
||||||
|
/* how many config structures do we have */
|
||||||
|
pci_mmcfg_config_num = 0;
|
||||||
|
i = header->length - sizeof(struct acpi_table_mcfg);
|
||||||
|
while (i >= sizeof(struct acpi_mcfg_allocation)) {
|
||||||
|
++pci_mmcfg_config_num;
|
||||||
|
i -= sizeof(struct acpi_mcfg_allocation);
|
||||||
|
};
|
||||||
|
if (pci_mmcfg_config_num == 0) {
|
||||||
|
printk(KERN_ERR PREFIX "MMCONFIG has no entries\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
config_size = pci_mmcfg_config_num * sizeof(*pci_mmcfg_config);
|
||||||
|
pci_mmcfg_config = kmalloc(config_size, GFP_KERNEL);
|
||||||
|
if (!pci_mmcfg_config) {
|
||||||
|
printk(KERN_WARNING PREFIX
|
||||||
|
"No memory for MCFG config tables\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(pci_mmcfg_config, &mcfg[1], config_size);
|
||||||
|
|
||||||
|
acpi_mcfg_oem_check(mcfg);
|
||||||
|
|
||||||
|
for (i = 0; i < pci_mmcfg_config_num; ++i) {
|
||||||
|
if ((pci_mmcfg_config[i].address > 0xFFFFFFFF) &&
|
||||||
|
!acpi_mcfg_64bit_base_addr) {
|
||||||
|
printk(KERN_ERR PREFIX
|
||||||
|
"MMCONFIG not in low 4GB of memory\n");
|
||||||
|
kfree(pci_mmcfg_config);
|
||||||
|
pci_mmcfg_config_num = 0;
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void __init __pci_mmcfg_init(int early)
|
static void __init __pci_mmcfg_init(int early)
|
||||||
{
|
{
|
||||||
/* MMCONFIG disabled */
|
/* MMCONFIG disabled */
|
||||||
|
@ -543,7 +606,7 @@ static void __init __pci_mmcfg_init(int early)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!known_bridge)
|
if (!known_bridge)
|
||||||
acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
|
acpi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
|
||||||
|
|
||||||
pci_mmcfg_reject_broken(early);
|
pci_mmcfg_reject_broken(early);
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ static int acpi_ac_open_fs(struct inode *inode, struct file *file);
|
||||||
static int acpi_ac_add(struct acpi_device *device);
|
static int acpi_ac_add(struct acpi_device *device);
|
||||||
static int acpi_ac_remove(struct acpi_device *device, int type);
|
static int acpi_ac_remove(struct acpi_device *device, int type);
|
||||||
static int acpi_ac_resume(struct acpi_device *device);
|
static int acpi_ac_resume(struct acpi_device *device);
|
||||||
|
static void acpi_ac_notify(struct acpi_device *device, u32 event);
|
||||||
|
|
||||||
static const struct acpi_device_id ac_device_ids[] = {
|
static const struct acpi_device_id ac_device_ids[] = {
|
||||||
{"ACPI0003", 0},
|
{"ACPI0003", 0},
|
||||||
|
@ -72,10 +73,12 @@ static struct acpi_driver acpi_ac_driver = {
|
||||||
.name = "ac",
|
.name = "ac",
|
||||||
.class = ACPI_AC_CLASS,
|
.class = ACPI_AC_CLASS,
|
||||||
.ids = ac_device_ids,
|
.ids = ac_device_ids,
|
||||||
|
.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
|
||||||
.ops = {
|
.ops = {
|
||||||
.add = acpi_ac_add,
|
.add = acpi_ac_add,
|
||||||
.remove = acpi_ac_remove,
|
.remove = acpi_ac_remove,
|
||||||
.resume = acpi_ac_resume,
|
.resume = acpi_ac_resume,
|
||||||
|
.notify = acpi_ac_notify,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -220,16 +223,14 @@ static int acpi_ac_remove_fs(struct acpi_device *device)
|
||||||
Driver Model
|
Driver Model
|
||||||
-------------------------------------------------------------------------- */
|
-------------------------------------------------------------------------- */
|
||||||
|
|
||||||
static void acpi_ac_notify(acpi_handle handle, u32 event, void *data)
|
static void acpi_ac_notify(struct acpi_device *device, u32 event)
|
||||||
{
|
{
|
||||||
struct acpi_ac *ac = data;
|
struct acpi_ac *ac = acpi_driver_data(device);
|
||||||
struct acpi_device *device = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
if (!ac)
|
if (!ac)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
device = ac->device;
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
default:
|
default:
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||||
|
@ -253,7 +254,6 @@ static void acpi_ac_notify(acpi_handle handle, u32 event, void *data)
|
||||||
static int acpi_ac_add(struct acpi_device *device)
|
static int acpi_ac_add(struct acpi_device *device)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
acpi_status status = AE_OK;
|
|
||||||
struct acpi_ac *ac = NULL;
|
struct acpi_ac *ac = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
@ -286,13 +286,6 @@ static int acpi_ac_add(struct acpi_device *device)
|
||||||
ac->charger.get_property = get_ac_property;
|
ac->charger.get_property = get_ac_property;
|
||||||
power_supply_register(&ac->device->dev, &ac->charger);
|
power_supply_register(&ac->device->dev, &ac->charger);
|
||||||
#endif
|
#endif
|
||||||
status = acpi_install_notify_handler(device->handle,
|
|
||||||
ACPI_ALL_NOTIFY, acpi_ac_notify,
|
|
||||||
ac);
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
result = -ENODEV;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
|
printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
|
||||||
acpi_device_name(device), acpi_device_bid(device),
|
acpi_device_name(device), acpi_device_bid(device),
|
||||||
|
@ -328,7 +321,6 @@ static int acpi_ac_resume(struct acpi_device *device)
|
||||||
|
|
||||||
static int acpi_ac_remove(struct acpi_device *device, int type)
|
static int acpi_ac_remove(struct acpi_device *device, int type)
|
||||||
{
|
{
|
||||||
acpi_status status = AE_OK;
|
|
||||||
struct acpi_ac *ac = NULL;
|
struct acpi_ac *ac = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
@ -337,8 +329,6 @@ static int acpi_ac_remove(struct acpi_device *device, int type)
|
||||||
|
|
||||||
ac = acpi_driver_data(device);
|
ac = acpi_driver_data(device);
|
||||||
|
|
||||||
status = acpi_remove_notify_handler(device->handle,
|
|
||||||
ACPI_ALL_NOTIFY, acpi_ac_notify);
|
|
||||||
#ifdef CONFIG_ACPI_SYSFS_POWER
|
#ifdef CONFIG_ACPI_SYSFS_POWER
|
||||||
if (ac->charger.dev)
|
if (ac->charger.dev)
|
||||||
power_supply_unregister(&ac->charger);
|
power_supply_unregister(&ac->charger);
|
||||||
|
|
|
@ -796,13 +796,12 @@ static void acpi_battery_remove_fs(struct acpi_device *device)
|
||||||
Driver Interface
|
Driver Interface
|
||||||
-------------------------------------------------------------------------- */
|
-------------------------------------------------------------------------- */
|
||||||
|
|
||||||
static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
|
static void acpi_battery_notify(struct acpi_device *device, u32 event)
|
||||||
{
|
{
|
||||||
struct acpi_battery *battery = data;
|
struct acpi_battery *battery = acpi_driver_data(device);
|
||||||
struct acpi_device *device;
|
|
||||||
if (!battery)
|
if (!battery)
|
||||||
return;
|
return;
|
||||||
device = battery->device;
|
|
||||||
acpi_battery_update(battery);
|
acpi_battery_update(battery);
|
||||||
acpi_bus_generate_proc_event(device, event,
|
acpi_bus_generate_proc_event(device, event,
|
||||||
acpi_battery_present(battery));
|
acpi_battery_present(battery));
|
||||||
|
@ -819,7 +818,6 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
|
||||||
static int acpi_battery_add(struct acpi_device *device)
|
static int acpi_battery_add(struct acpi_device *device)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
acpi_status status = 0;
|
|
||||||
struct acpi_battery *battery = NULL;
|
struct acpi_battery *battery = NULL;
|
||||||
if (!device)
|
if (!device)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -834,22 +832,12 @@ static int acpi_battery_add(struct acpi_device *device)
|
||||||
acpi_battery_update(battery);
|
acpi_battery_update(battery);
|
||||||
#ifdef CONFIG_ACPI_PROCFS_POWER
|
#ifdef CONFIG_ACPI_PROCFS_POWER
|
||||||
result = acpi_battery_add_fs(device);
|
result = acpi_battery_add_fs(device);
|
||||||
if (result)
|
|
||||||
goto end;
|
|
||||||
#endif
|
#endif
|
||||||
status = acpi_install_notify_handler(device->handle,
|
if (!result) {
|
||||||
ACPI_ALL_NOTIFY,
|
|
||||||
acpi_battery_notify, battery);
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
ACPI_EXCEPTION((AE_INFO, status, "Installing notify handler"));
|
|
||||||
result = -ENODEV;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
|
printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
|
||||||
ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
|
ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
|
||||||
device->status.battery_present ? "present" : "absent");
|
device->status.battery_present ? "present" : "absent");
|
||||||
end:
|
} else {
|
||||||
if (result) {
|
|
||||||
#ifdef CONFIG_ACPI_PROCFS_POWER
|
#ifdef CONFIG_ACPI_PROCFS_POWER
|
||||||
acpi_battery_remove_fs(device);
|
acpi_battery_remove_fs(device);
|
||||||
#endif
|
#endif
|
||||||
|
@ -860,15 +848,11 @@ 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)
|
||||||
{
|
{
|
||||||
acpi_status status = 0;
|
|
||||||
struct acpi_battery *battery = NULL;
|
struct acpi_battery *battery = NULL;
|
||||||
|
|
||||||
if (!device || !acpi_driver_data(device))
|
if (!device || !acpi_driver_data(device))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
battery = acpi_driver_data(device);
|
battery = acpi_driver_data(device);
|
||||||
status = acpi_remove_notify_handler(device->handle,
|
|
||||||
ACPI_ALL_NOTIFY,
|
|
||||||
acpi_battery_notify);
|
|
||||||
#ifdef CONFIG_ACPI_PROCFS_POWER
|
#ifdef CONFIG_ACPI_PROCFS_POWER
|
||||||
acpi_battery_remove_fs(device);
|
acpi_battery_remove_fs(device);
|
||||||
#endif
|
#endif
|
||||||
|
@ -896,10 +880,12 @@ static struct acpi_driver acpi_battery_driver = {
|
||||||
.name = "battery",
|
.name = "battery",
|
||||||
.class = ACPI_BATTERY_CLASS,
|
.class = ACPI_BATTERY_CLASS,
|
||||||
.ids = battery_device_ids,
|
.ids = battery_device_ids,
|
||||||
|
.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
|
||||||
.ops = {
|
.ops = {
|
||||||
.add = acpi_battery_add,
|
.add = acpi_battery_add,
|
||||||
.resume = acpi_battery_resume,
|
.resume = acpi_battery_resume,
|
||||||
.remove = acpi_battery_remove,
|
.remove = acpi_battery_remove,
|
||||||
|
.notify = acpi_battery_notify,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -192,6 +192,22 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = {
|
||||||
DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"),
|
DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.callback = dmi_disable_osi_vista,
|
||||||
|
.ident = "Sony VGN-NS10J_S",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.callback = dmi_disable_osi_vista,
|
||||||
|
.ident = "Sony VGN-SR290J",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "Sony VGN-SR290J"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
|
* BIOS invocation of _OSI(Linux) is almost always a BIOS bug.
|
||||||
|
|
|
@ -450,18 +450,16 @@ int acpi_bus_receive_event(struct acpi_bus_event *event)
|
||||||
Notification Handling
|
Notification Handling
|
||||||
-------------------------------------------------------------------------- */
|
-------------------------------------------------------------------------- */
|
||||||
|
|
||||||
static int
|
static void acpi_bus_check_device(acpi_handle handle)
|
||||||
acpi_bus_check_device(struct acpi_device *device, int *status_changed)
|
|
||||||
{
|
{
|
||||||
acpi_status status = 0;
|
struct acpi_device *device;
|
||||||
|
acpi_status status;
|
||||||
struct acpi_device_status old_status;
|
struct acpi_device_status old_status;
|
||||||
|
|
||||||
|
if (acpi_bus_get_device(handle, &device))
|
||||||
|
return;
|
||||||
if (!device)
|
if (!device)
|
||||||
return -EINVAL;
|
return;
|
||||||
|
|
||||||
if (status_changed)
|
|
||||||
*status_changed = 0;
|
|
||||||
|
|
||||||
old_status = device->status;
|
old_status = device->status;
|
||||||
|
|
||||||
|
@ -471,22 +469,15 @@ acpi_bus_check_device(struct acpi_device *device, int *status_changed)
|
||||||
*/
|
*/
|
||||||
if (device->parent && !device->parent->status.present) {
|
if (device->parent && !device->parent->status.present) {
|
||||||
device->status = device->parent->status;
|
device->status = device->parent->status;
|
||||||
if (STRUCT_TO_INT(old_status) != STRUCT_TO_INT(device->status)) {
|
return;
|
||||||
if (status_changed)
|
|
||||||
*status_changed = 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status = acpi_bus_get_status(device);
|
status = acpi_bus_get_status(device);
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
return -ENODEV;
|
return;
|
||||||
|
|
||||||
if (STRUCT_TO_INT(old_status) == STRUCT_TO_INT(device->status))
|
if (STRUCT_TO_INT(old_status) == STRUCT_TO_INT(device->status))
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
if (status_changed)
|
|
||||||
*status_changed = 1;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Device Insertion/Removal
|
* Device Insertion/Removal
|
||||||
|
@ -498,33 +489,17 @@ acpi_bus_check_device(struct acpi_device *device, int *status_changed)
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device removal detected\n"));
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device removal detected\n"));
|
||||||
/* TBD: Handle device removal */
|
/* TBD: Handle device removal */
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int acpi_bus_check_scope(struct acpi_device *device)
|
static void acpi_bus_check_scope(acpi_handle handle)
|
||||||
{
|
{
|
||||||
int result = 0;
|
|
||||||
int status_changed = 0;
|
|
||||||
|
|
||||||
|
|
||||||
if (!device)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/* Status Change? */
|
/* Status Change? */
|
||||||
result = acpi_bus_check_device(device, &status_changed);
|
acpi_bus_check_device(handle);
|
||||||
if (result)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
if (!status_changed)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TBD: Enumerate child devices within this device's scope and
|
* TBD: Enumerate child devices within this device's scope and
|
||||||
* run acpi_bus_check_device()'s on them.
|
* run acpi_bus_check_device()'s on them.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static BLOCKING_NOTIFIER_HEAD(acpi_bus_notify_list);
|
static BLOCKING_NOTIFIER_HEAD(acpi_bus_notify_list);
|
||||||
|
@ -547,22 +522,19 @@ EXPORT_SYMBOL_GPL(unregister_acpi_bus_notifier);
|
||||||
*/
|
*/
|
||||||
static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
|
static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
|
||||||
{
|
{
|
||||||
int result = 0;
|
|
||||||
struct acpi_device *device = NULL;
|
struct acpi_device *device = NULL;
|
||||||
|
struct acpi_driver *driver;
|
||||||
|
|
||||||
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n",
|
||||||
|
type, handle));
|
||||||
|
|
||||||
blocking_notifier_call_chain(&acpi_bus_notify_list,
|
blocking_notifier_call_chain(&acpi_bus_notify_list,
|
||||||
type, (void *)handle);
|
type, (void *)handle);
|
||||||
|
|
||||||
if (acpi_bus_get_device(handle, &device))
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
||||||
case ACPI_NOTIFY_BUS_CHECK:
|
case ACPI_NOTIFY_BUS_CHECK:
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
acpi_bus_check_scope(handle);
|
||||||
"Received BUS CHECK notification for device [%s]\n",
|
|
||||||
device->pnp.bus_id));
|
|
||||||
result = acpi_bus_check_scope(device);
|
|
||||||
/*
|
/*
|
||||||
* TBD: We'll need to outsource certain events to non-ACPI
|
* TBD: We'll need to outsource certain events to non-ACPI
|
||||||
* drivers via the device manager (device.c).
|
* drivers via the device manager (device.c).
|
||||||
|
@ -570,10 +542,7 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACPI_NOTIFY_DEVICE_CHECK:
|
case ACPI_NOTIFY_DEVICE_CHECK:
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
acpi_bus_check_device(handle);
|
||||||
"Received DEVICE CHECK notification for device [%s]\n",
|
|
||||||
device->pnp.bus_id));
|
|
||||||
result = acpi_bus_check_device(device, NULL);
|
|
||||||
/*
|
/*
|
||||||
* TBD: We'll need to outsource certain events to non-ACPI
|
* TBD: We'll need to outsource certain events to non-ACPI
|
||||||
* drivers via the device manager (device.c).
|
* drivers via the device manager (device.c).
|
||||||
|
@ -581,44 +550,26 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACPI_NOTIFY_DEVICE_WAKE:
|
case ACPI_NOTIFY_DEVICE_WAKE:
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
||||||
"Received DEVICE WAKE notification for device [%s]\n",
|
|
||||||
device->pnp.bus_id));
|
|
||||||
/* TBD */
|
/* TBD */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACPI_NOTIFY_EJECT_REQUEST:
|
case ACPI_NOTIFY_EJECT_REQUEST:
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
||||||
"Received EJECT REQUEST notification for device [%s]\n",
|
|
||||||
device->pnp.bus_id));
|
|
||||||
/* TBD */
|
/* TBD */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
|
case ACPI_NOTIFY_DEVICE_CHECK_LIGHT:
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
||||||
"Received DEVICE CHECK LIGHT notification for device [%s]\n",
|
|
||||||
device->pnp.bus_id));
|
|
||||||
/* TBD: Exactly what does 'light' mean? */
|
/* TBD: Exactly what does 'light' mean? */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACPI_NOTIFY_FREQUENCY_MISMATCH:
|
case ACPI_NOTIFY_FREQUENCY_MISMATCH:
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
||||||
"Received FREQUENCY MISMATCH notification for device [%s]\n",
|
|
||||||
device->pnp.bus_id));
|
|
||||||
/* TBD */
|
/* TBD */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACPI_NOTIFY_BUS_MODE_MISMATCH:
|
case ACPI_NOTIFY_BUS_MODE_MISMATCH:
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
||||||
"Received BUS MODE MISMATCH notification for device [%s]\n",
|
|
||||||
device->pnp.bus_id));
|
|
||||||
/* TBD */
|
/* TBD */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACPI_NOTIFY_POWER_FAULT:
|
case ACPI_NOTIFY_POWER_FAULT:
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
||||||
"Received POWER FAULT notification for device [%s]\n",
|
|
||||||
device->pnp.bus_id));
|
|
||||||
/* TBD */
|
/* TBD */
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -629,7 +580,13 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
acpi_bus_get_device(handle, &device);
|
||||||
|
if (device) {
|
||||||
|
driver = device->driver;
|
||||||
|
if (driver && driver->ops.notify &&
|
||||||
|
(driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS))
|
||||||
|
driver->ops.notify(device, type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
|
|
|
@ -140,46 +140,6 @@ struct device *acpi_get_physical_device(acpi_handle handle)
|
||||||
|
|
||||||
EXPORT_SYMBOL(acpi_get_physical_device);
|
EXPORT_SYMBOL(acpi_get_physical_device);
|
||||||
|
|
||||||
/* ToDo: When a PCI bridge is found, return the PCI device behind the bridge
|
|
||||||
* This should work in general, but did not on a Lenovo T61 for the
|
|
||||||
* graphics card. But this must be fixed when the PCI device is
|
|
||||||
* bound and the kernel device struct is attached to the acpi device
|
|
||||||
* Note: A success call will increase reference count by one
|
|
||||||
* Do call put_device(dev) on the returned device then
|
|
||||||
*/
|
|
||||||
struct device *acpi_get_physical_pci_device(acpi_handle handle)
|
|
||||||
{
|
|
||||||
struct device *dev;
|
|
||||||
long long device_id;
|
|
||||||
acpi_status status;
|
|
||||||
|
|
||||||
status =
|
|
||||||
acpi_evaluate_integer(handle, "_ADR", NULL, &device_id);
|
|
||||||
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* We need to attempt to determine whether the _ADR refers to a
|
|
||||||
PCI device or not. There's no terribly good way to do this,
|
|
||||||
so the best we can hope for is to assume that there'll never
|
|
||||||
be a device in the host bridge */
|
|
||||||
if (device_id >= 0x10000) {
|
|
||||||
/* It looks like a PCI device. Does it exist? */
|
|
||||||
dev = acpi_get_physical_device(handle);
|
|
||||||
} else {
|
|
||||||
/* It doesn't look like a PCI device. Does its parent
|
|
||||||
exist? */
|
|
||||||
acpi_handle phandle;
|
|
||||||
if (acpi_get_parent(handle, &phandle))
|
|
||||||
return NULL;
|
|
||||||
dev = acpi_get_physical_device(phandle);
|
|
||||||
}
|
|
||||||
if (!dev)
|
|
||||||
return NULL;
|
|
||||||
return dev;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(acpi_get_physical_pci_device);
|
|
||||||
|
|
||||||
static int acpi_bind_one(struct device *dev, acpi_handle handle)
|
static int acpi_bind_one(struct device *dev, acpi_handle handle)
|
||||||
{
|
{
|
||||||
struct acpi_device *acpi_dev;
|
struct acpi_device *acpi_dev;
|
||||||
|
|
|
@ -79,6 +79,7 @@ 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;
|
static struct workqueue_struct *kacpi_notify_wq;
|
||||||
|
static struct workqueue_struct *kacpi_hotplug_wq;
|
||||||
|
|
||||||
struct acpi_res_list {
|
struct acpi_res_list {
|
||||||
resource_size_t start;
|
resource_size_t start;
|
||||||
|
@ -192,8 +193,10 @@ acpi_status acpi_os_initialize1(void)
|
||||||
{
|
{
|
||||||
kacpid_wq = create_singlethread_workqueue("kacpid");
|
kacpid_wq = create_singlethread_workqueue("kacpid");
|
||||||
kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify");
|
kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify");
|
||||||
|
kacpi_hotplug_wq = create_singlethread_workqueue("kacpi_hotplug");
|
||||||
BUG_ON(!kacpid_wq);
|
BUG_ON(!kacpid_wq);
|
||||||
BUG_ON(!kacpi_notify_wq);
|
BUG_ON(!kacpi_notify_wq);
|
||||||
|
BUG_ON(!kacpi_hotplug_wq);
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,6 +209,7 @@ acpi_status acpi_os_terminate(void)
|
||||||
|
|
||||||
destroy_workqueue(kacpid_wq);
|
destroy_workqueue(kacpid_wq);
|
||||||
destroy_workqueue(kacpi_notify_wq);
|
destroy_workqueue(kacpi_notify_wq);
|
||||||
|
destroy_workqueue(kacpi_hotplug_wq);
|
||||||
|
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
}
|
}
|
||||||
|
@ -716,6 +720,7 @@ static acpi_status __acpi_os_execute(acpi_execute_type type,
|
||||||
acpi_status status = AE_OK;
|
acpi_status status = AE_OK;
|
||||||
struct acpi_os_dpc *dpc;
|
struct acpi_os_dpc *dpc;
|
||||||
struct workqueue_struct *queue;
|
struct workqueue_struct *queue;
|
||||||
|
work_func_t func;
|
||||||
int ret;
|
int ret;
|
||||||
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",
|
||||||
|
@ -740,15 +745,17 @@ static acpi_status __acpi_os_execute(acpi_execute_type type,
|
||||||
dpc->function = function;
|
dpc->function = function;
|
||||||
dpc->context = context;
|
dpc->context = context;
|
||||||
|
|
||||||
if (!hp) {
|
/*
|
||||||
INIT_WORK(&dpc->work, acpi_os_execute_deferred);
|
* We can't run hotplug code in keventd_wq/kacpid_wq/kacpid_notify_wq
|
||||||
queue = (type == OSL_NOTIFY_HANDLER) ?
|
* because the hotplug code may call driver .remove() functions,
|
||||||
kacpi_notify_wq : kacpid_wq;
|
* which invoke flush_scheduled_work/acpi_os_wait_events_complete
|
||||||
|
* to flush these workqueues.
|
||||||
|
*/
|
||||||
|
queue = hp ? kacpi_hotplug_wq :
|
||||||
|
(type == OSL_NOTIFY_HANDLER ? kacpi_notify_wq : kacpid_wq);
|
||||||
|
func = hp ? acpi_os_execute_hp_deferred : acpi_os_execute_deferred;
|
||||||
|
INIT_WORK(&dpc->work, func);
|
||||||
ret = queue_work(queue, &dpc->work);
|
ret = queue_work(queue, &dpc->work);
|
||||||
} else {
|
|
||||||
INIT_WORK(&dpc->work, acpi_os_execute_hp_deferred);
|
|
||||||
ret = schedule_work(&dpc->work);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
printk(KERN_ERR PREFIX
|
printk(KERN_ERR PREFIX
|
||||||
|
|
|
@ -24,12 +24,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/proc_fs.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <linux/pm.h>
|
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <acpi/acpi_bus.h>
|
#include <acpi/acpi_bus.h>
|
||||||
|
@ -38,310 +33,76 @@
|
||||||
#define _COMPONENT ACPI_PCI_COMPONENT
|
#define _COMPONENT ACPI_PCI_COMPONENT
|
||||||
ACPI_MODULE_NAME("pci_bind");
|
ACPI_MODULE_NAME("pci_bind");
|
||||||
|
|
||||||
struct acpi_pci_data {
|
static int acpi_pci_unbind(struct acpi_device *device)
|
||||||
struct acpi_pci_id id;
|
{
|
||||||
|
struct pci_dev *dev;
|
||||||
|
|
||||||
|
dev = acpi_get_pci_dev(device->handle);
|
||||||
|
if (!dev || !dev->subordinate)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
acpi_pci_irq_del_prt(dev->subordinate);
|
||||||
|
|
||||||
|
device->ops.bind = NULL;
|
||||||
|
device->ops.unbind = NULL;
|
||||||
|
|
||||||
|
out:
|
||||||
|
pci_dev_put(dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acpi_pci_bind(struct acpi_device *device)
|
||||||
|
{
|
||||||
|
acpi_status status;
|
||||||
|
acpi_handle handle;
|
||||||
struct pci_bus *bus;
|
struct pci_bus *bus;
|
||||||
struct pci_dev *dev;
|
struct pci_dev *dev;
|
||||||
};
|
|
||||||
|
|
||||||
static int acpi_pci_unbind(struct acpi_device *device);
|
dev = acpi_get_pci_dev(device->handle);
|
||||||
|
if (!dev)
|
||||||
static void acpi_pci_data_handler(acpi_handle handle, u32 function,
|
return 0;
|
||||||
void *context)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* TBD: Anything we need to do here? */
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* acpi_get_pci_id
|
|
||||||
* ------------------
|
|
||||||
* This function is used by the ACPI Interpreter (a.k.a. Core Subsystem)
|
|
||||||
* to resolve PCI information for ACPI-PCI devices defined in the namespace.
|
|
||||||
* This typically occurs when resolving PCI operation region information.
|
|
||||||
*/
|
|
||||||
acpi_status acpi_get_pci_id(acpi_handle handle, struct acpi_pci_id *id)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
acpi_status status = AE_OK;
|
|
||||||
struct acpi_device *device = NULL;
|
|
||||||
struct acpi_pci_data *data = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
if (!id)
|
|
||||||
return AE_BAD_PARAMETER;
|
|
||||||
|
|
||||||
result = acpi_bus_get_device(handle, &device);
|
|
||||||
if (result) {
|
|
||||||
printk(KERN_ERR PREFIX
|
|
||||||
"Invalid ACPI Bus context for device %s\n",
|
|
||||||
acpi_device_bid(device));
|
|
||||||
return AE_NOT_EXIST;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = acpi_get_data(handle, acpi_pci_data_handler, (void **)&data);
|
|
||||||
if (ACPI_FAILURE(status) || !data) {
|
|
||||||
ACPI_EXCEPTION((AE_INFO, status,
|
|
||||||
"Invalid ACPI-PCI context for device %s",
|
|
||||||
acpi_device_bid(device)));
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
*id = data->id;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
id->segment = data->id.segment;
|
* Install the 'bind' function to facilitate callbacks for
|
||||||
id->bus = data->id.bus;
|
* children of the P2P bridge.
|
||||||
id->device = data->id.device;
|
|
||||||
id->function = data->id.function;
|
|
||||||
*/
|
*/
|
||||||
|
if (dev->subordinate) {
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
||||||
"Device %s has PCI address %04x:%02x:%02x.%d\n",
|
|
||||||
acpi_device_bid(device), id->segment, id->bus,
|
|
||||||
id->device, id->function));
|
|
||||||
|
|
||||||
return AE_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(acpi_get_pci_id);
|
|
||||||
|
|
||||||
int acpi_pci_bind(struct acpi_device *device)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
acpi_status status;
|
|
||||||
struct acpi_pci_data *data;
|
|
||||||
struct acpi_pci_data *pdata;
|
|
||||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
||||||
acpi_handle handle;
|
|
||||||
|
|
||||||
if (!device || !device->parent)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
data = kzalloc(sizeof(struct acpi_pci_data), GFP_KERNEL);
|
|
||||||
if (!data)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer);
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
kfree(data);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI device [%s]...\n",
|
|
||||||
(char *)buffer.pointer));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Segment & Bus
|
|
||||||
* -------------
|
|
||||||
* These are obtained via the parent device's ACPI-PCI context.
|
|
||||||
*/
|
|
||||||
status = acpi_get_data(device->parent->handle, acpi_pci_data_handler,
|
|
||||||
(void **)&pdata);
|
|
||||||
if (ACPI_FAILURE(status) || !pdata || !pdata->bus) {
|
|
||||||
ACPI_EXCEPTION((AE_INFO, status,
|
|
||||||
"Invalid ACPI-PCI context for parent device %s",
|
|
||||||
acpi_device_bid(device->parent)));
|
|
||||||
result = -ENODEV;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
data->id.segment = pdata->id.segment;
|
|
||||||
data->id.bus = pdata->bus->number;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Device & Function
|
|
||||||
* -----------------
|
|
||||||
* These are simply obtained from the device's _ADR method. Note
|
|
||||||
* that a value of zero is valid.
|
|
||||||
*/
|
|
||||||
data->id.device = device->pnp.bus_address >> 16;
|
|
||||||
data->id.function = device->pnp.bus_address & 0xFFFF;
|
|
||||||
|
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "...to %04x:%02x:%02x.%d\n",
|
|
||||||
data->id.segment, data->id.bus, data->id.device,
|
|
||||||
data->id.function));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TBD: Support slot devices (e.g. function=0xFFFF).
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Locate PCI Device
|
|
||||||
* -----------------
|
|
||||||
* Locate matching device in PCI namespace. If it doesn't exist
|
|
||||||
* this typically means that the device isn't currently inserted
|
|
||||||
* (e.g. docking station, port replicator, etc.).
|
|
||||||
*/
|
|
||||||
data->dev = pci_get_slot(pdata->bus,
|
|
||||||
PCI_DEVFN(data->id.device, data->id.function));
|
|
||||||
if (!data->dev) {
|
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
||||||
"Device %04x:%02x:%02x.%d not present in PCI namespace\n",
|
|
||||||
data->id.segment, data->id.bus,
|
|
||||||
data->id.device, data->id.function));
|
|
||||||
result = -ENODEV;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
if (!data->dev->bus) {
|
|
||||||
printk(KERN_ERR PREFIX
|
|
||||||
"Device %04x:%02x:%02x.%d has invalid 'bus' field\n",
|
|
||||||
data->id.segment, data->id.bus,
|
|
||||||
data->id.device, data->id.function);
|
|
||||||
result = -ENODEV;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* PCI Bridge?
|
|
||||||
* -----------
|
|
||||||
* If so, set the 'bus' field and install the 'bind' function to
|
|
||||||
* facilitate callbacks for all of its children.
|
|
||||||
*/
|
|
||||||
if (data->dev->subordinate) {
|
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||||
"Device %04x:%02x:%02x.%d is a PCI bridge\n",
|
"Device %04x:%02x:%02x.%d is a PCI bridge\n",
|
||||||
data->id.segment, data->id.bus,
|
pci_domain_nr(dev->bus), dev->bus->number,
|
||||||
data->id.device, data->id.function));
|
PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)));
|
||||||
data->bus = data->dev->subordinate;
|
|
||||||
device->ops.bind = acpi_pci_bind;
|
device->ops.bind = acpi_pci_bind;
|
||||||
device->ops.unbind = acpi_pci_unbind;
|
device->ops.unbind = acpi_pci_unbind;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Attach ACPI-PCI Context
|
* Evaluate and parse _PRT, if exists. This code allows parsing of
|
||||||
* -----------------------
|
* _PRT objects within the scope of non-bridge devices. Note that
|
||||||
* Thus binding the ACPI and PCI devices.
|
* _PRTs within the scope of a PCI bridge assume the bridge's
|
||||||
*/
|
* subordinate bus number.
|
||||||
status = acpi_attach_data(device->handle, acpi_pci_data_handler, data);
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
ACPI_EXCEPTION((AE_INFO, status,
|
|
||||||
"Unable to attach ACPI-PCI context to device %s",
|
|
||||||
acpi_device_bid(device)));
|
|
||||||
result = -ENODEV;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* PCI Routing Table
|
|
||||||
* -----------------
|
|
||||||
* Evaluate and parse _PRT, if exists. This code is independent of
|
|
||||||
* PCI bridges (above) to allow parsing of _PRT objects within the
|
|
||||||
* scope of non-bridge devices. Note that _PRTs within the scope of
|
|
||||||
* a PCI bridge assume the bridge's subordinate bus number.
|
|
||||||
*
|
*
|
||||||
* TBD: Can _PRTs exist within the scope of non-bridge PCI devices?
|
* TBD: Can _PRTs exist within the scope of non-bridge PCI devices?
|
||||||
*/
|
*/
|
||||||
status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle);
|
status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle);
|
||||||
if (ACPI_SUCCESS(status)) {
|
|
||||||
if (data->bus) /* PCI-PCI bridge */
|
|
||||||
acpi_pci_irq_add_prt(device->handle, data->id.segment,
|
|
||||||
data->bus->number);
|
|
||||||
else /* non-bridge PCI device */
|
|
||||||
acpi_pci_irq_add_prt(device->handle, data->id.segment,
|
|
||||||
data->id.bus);
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
kfree(buffer.pointer);
|
|
||||||
if (result) {
|
|
||||||
pci_dev_put(data->dev);
|
|
||||||
kfree(data);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int acpi_pci_unbind(struct acpi_device *device)
|
|
||||||
{
|
|
||||||
int result = 0;
|
|
||||||
acpi_status status;
|
|
||||||
struct acpi_pci_data *data;
|
|
||||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
||||||
|
|
||||||
|
|
||||||
if (!device || !device->parent)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer);
|
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
return -ENODEV;
|
goto out;
|
||||||
|
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unbinding PCI device [%s]...\n",
|
if (dev->subordinate)
|
||||||
(char *) buffer.pointer));
|
bus = dev->subordinate;
|
||||||
kfree(buffer.pointer);
|
else
|
||||||
|
bus = dev->bus;
|
||||||
|
|
||||||
status =
|
acpi_pci_irq_add_prt(device->handle, bus);
|
||||||
acpi_get_data(device->handle, acpi_pci_data_handler,
|
|
||||||
(void **)&data);
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
result = -ENODEV;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = acpi_detach_data(device->handle, acpi_pci_data_handler);
|
out:
|
||||||
if (ACPI_FAILURE(status)) {
|
pci_dev_put(dev);
|
||||||
ACPI_EXCEPTION((AE_INFO, status,
|
return 0;
|
||||||
"Unable to detach data from device %s",
|
|
||||||
acpi_device_bid(device)));
|
|
||||||
result = -ENODEV;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
if (data->dev->subordinate) {
|
|
||||||
acpi_pci_irq_del_prt(data->id.segment, data->bus->number);
|
|
||||||
}
|
|
||||||
pci_dev_put(data->dev);
|
|
||||||
kfree(data);
|
|
||||||
|
|
||||||
end:
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int acpi_pci_bind_root(struct acpi_device *device)
|
||||||
acpi_pci_bind_root(struct acpi_device *device,
|
|
||||||
struct acpi_pci_id *id, struct pci_bus *bus)
|
|
||||||
{
|
{
|
||||||
int result = 0;
|
|
||||||
acpi_status status;
|
|
||||||
struct acpi_pci_data *data = NULL;
|
|
||||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
|
||||||
|
|
||||||
if (!device || !id || !bus) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
data = kzalloc(sizeof(struct acpi_pci_data), GFP_KERNEL);
|
|
||||||
if (!data)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
data->id = *id;
|
|
||||||
data->bus = bus;
|
|
||||||
device->ops.bind = acpi_pci_bind;
|
device->ops.bind = acpi_pci_bind;
|
||||||
device->ops.unbind = acpi_pci_unbind;
|
device->ops.unbind = acpi_pci_unbind;
|
||||||
|
|
||||||
status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer);
|
return 0;
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
kfree (data);
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI root bridge [%s] to "
|
|
||||||
"%04x:%02x\n", (char *)buffer.pointer,
|
|
||||||
id->segment, id->bus));
|
|
||||||
|
|
||||||
status = acpi_attach_data(device->handle, acpi_pci_data_handler, data);
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
ACPI_EXCEPTION((AE_INFO, status,
|
|
||||||
"Unable to attach ACPI-PCI context to device %s",
|
|
||||||
(char *)buffer.pointer));
|
|
||||||
result = -ENODEV;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
kfree(buffer.pointer);
|
|
||||||
if (result != 0)
|
|
||||||
kfree(data);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,7 +182,7 @@ static void do_prt_fixups(struct acpi_prt_entry *entry,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus,
|
static int acpi_pci_irq_add_entry(acpi_handle handle, struct pci_bus *bus,
|
||||||
struct acpi_pci_routing_table *prt)
|
struct acpi_pci_routing_table *prt)
|
||||||
{
|
{
|
||||||
struct acpi_prt_entry *entry;
|
struct acpi_prt_entry *entry;
|
||||||
|
@ -196,8 +196,8 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus,
|
||||||
* 1=INTA, 2=INTB. We use the PCI encoding throughout, so convert
|
* 1=INTA, 2=INTB. We use the PCI encoding throughout, so convert
|
||||||
* it here.
|
* it here.
|
||||||
*/
|
*/
|
||||||
entry->id.segment = segment;
|
entry->id.segment = pci_domain_nr(bus);
|
||||||
entry->id.bus = bus;
|
entry->id.bus = bus->number;
|
||||||
entry->id.device = (prt->address >> 16) & 0xFFFF;
|
entry->id.device = (prt->address >> 16) & 0xFFFF;
|
||||||
entry->pin = prt->pin + 1;
|
entry->pin = prt->pin + 1;
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus)
|
int acpi_pci_irq_add_prt(acpi_handle handle, struct pci_bus *bus)
|
||||||
{
|
{
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||||
|
@ -271,7 +271,7 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus)
|
||||||
|
|
||||||
entry = buffer.pointer;
|
entry = buffer.pointer;
|
||||||
while (entry && (entry->length > 0)) {
|
while (entry && (entry->length > 0)) {
|
||||||
acpi_pci_irq_add_entry(handle, segment, bus, entry);
|
acpi_pci_irq_add_entry(handle, bus, entry);
|
||||||
entry = (struct acpi_pci_routing_table *)
|
entry = (struct acpi_pci_routing_table *)
|
||||||
((unsigned long)entry + entry->length);
|
((unsigned long)entry + entry->length);
|
||||||
}
|
}
|
||||||
|
@ -280,16 +280,17 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void acpi_pci_irq_del_prt(int segment, int bus)
|
void acpi_pci_irq_del_prt(struct pci_bus *bus)
|
||||||
{
|
{
|
||||||
struct acpi_prt_entry *entry, *tmp;
|
struct acpi_prt_entry *entry, *tmp;
|
||||||
|
|
||||||
printk(KERN_DEBUG
|
printk(KERN_DEBUG
|
||||||
"ACPI: Delete PCI Interrupt Routing Table for %04x:%02x\n",
|
"ACPI: Delete PCI Interrupt Routing Table for %04x:%02x\n",
|
||||||
segment, bus);
|
pci_domain_nr(bus), bus->number);
|
||||||
spin_lock(&acpi_prt_lock);
|
spin_lock(&acpi_prt_lock);
|
||||||
list_for_each_entry_safe(entry, tmp, &acpi_prt_list, list) {
|
list_for_each_entry_safe(entry, tmp, &acpi_prt_list, list) {
|
||||||
if (segment == entry->id.segment && bus == entry->id.bus) {
|
if (pci_domain_nr(bus) == entry->id.segment
|
||||||
|
&& bus->number == entry->id.bus) {
|
||||||
list_del(&entry->list);
|
list_del(&entry->list);
|
||||||
kfree(entry);
|
kfree(entry);
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,9 +63,10 @@ static struct acpi_driver acpi_pci_root_driver = {
|
||||||
|
|
||||||
struct acpi_pci_root {
|
struct acpi_pci_root {
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
struct acpi_device * device;
|
struct acpi_device *device;
|
||||||
struct acpi_pci_id id;
|
|
||||||
struct pci_bus *bus;
|
struct pci_bus *bus;
|
||||||
|
u16 segment;
|
||||||
|
u8 bus_nr;
|
||||||
|
|
||||||
u32 osc_support_set; /* _OSC state of support bits */
|
u32 osc_support_set; /* _OSC state of support bits */
|
||||||
u32 osc_control_set; /* _OSC state of control bits */
|
u32 osc_control_set; /* _OSC state of control bits */
|
||||||
|
@ -82,7 +83,7 @@ static DEFINE_MUTEX(osc_lock);
|
||||||
int acpi_pci_register_driver(struct acpi_pci_driver *driver)
|
int acpi_pci_register_driver(struct acpi_pci_driver *driver)
|
||||||
{
|
{
|
||||||
int n = 0;
|
int n = 0;
|
||||||
struct list_head *entry;
|
struct acpi_pci_root *root;
|
||||||
|
|
||||||
struct acpi_pci_driver **pptr = &sub_driver;
|
struct acpi_pci_driver **pptr = &sub_driver;
|
||||||
while (*pptr)
|
while (*pptr)
|
||||||
|
@ -92,9 +93,7 @@ int acpi_pci_register_driver(struct acpi_pci_driver *driver)
|
||||||
if (!driver->add)
|
if (!driver->add)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
list_for_each(entry, &acpi_pci_roots) {
|
list_for_each_entry(root, &acpi_pci_roots, node) {
|
||||||
struct acpi_pci_root *root;
|
|
||||||
root = list_entry(entry, struct acpi_pci_root, node);
|
|
||||||
driver->add(root->device->handle);
|
driver->add(root->device->handle);
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
@ -106,7 +105,7 @@ EXPORT_SYMBOL(acpi_pci_register_driver);
|
||||||
|
|
||||||
void acpi_pci_unregister_driver(struct acpi_pci_driver *driver)
|
void acpi_pci_unregister_driver(struct acpi_pci_driver *driver)
|
||||||
{
|
{
|
||||||
struct list_head *entry;
|
struct acpi_pci_root *root;
|
||||||
|
|
||||||
struct acpi_pci_driver **pptr = &sub_driver;
|
struct acpi_pci_driver **pptr = &sub_driver;
|
||||||
while (*pptr) {
|
while (*pptr) {
|
||||||
|
@ -120,28 +119,48 @@ void acpi_pci_unregister_driver(struct acpi_pci_driver *driver)
|
||||||
if (!driver->remove)
|
if (!driver->remove)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
list_for_each(entry, &acpi_pci_roots) {
|
list_for_each_entry(root, &acpi_pci_roots, node)
|
||||||
struct acpi_pci_root *root;
|
|
||||||
root = list_entry(entry, struct acpi_pci_root, node);
|
|
||||||
driver->remove(root->device->handle);
|
driver->remove(root->device->handle);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(acpi_pci_unregister_driver);
|
EXPORT_SYMBOL(acpi_pci_unregister_driver);
|
||||||
|
|
||||||
acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus)
|
acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus)
|
||||||
{
|
{
|
||||||
struct acpi_pci_root *tmp;
|
struct acpi_pci_root *root;
|
||||||
|
|
||||||
list_for_each_entry(tmp, &acpi_pci_roots, node) {
|
list_for_each_entry(root, &acpi_pci_roots, node)
|
||||||
if ((tmp->id.segment == (u16) seg) && (tmp->id.bus == (u16) bus))
|
if ((root->segment == (u16) seg) && (root->bus_nr == (u16) bus))
|
||||||
return tmp->device->handle;
|
return root->device->handle;
|
||||||
}
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle);
|
EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge
|
||||||
|
* @handle - the ACPI CA node in question.
|
||||||
|
*
|
||||||
|
* Note: we could make this API take a struct acpi_device * instead, but
|
||||||
|
* for now, it's more convenient to operate on an acpi_handle.
|
||||||
|
*/
|
||||||
|
int acpi_is_root_bridge(acpi_handle handle)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct acpi_device *device;
|
||||||
|
|
||||||
|
ret = acpi_bus_get_device(handle, &device);
|
||||||
|
if (ret)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = acpi_match_device_ids(device, root_device_ids);
|
||||||
|
if (ret)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(acpi_is_root_bridge);
|
||||||
|
|
||||||
static acpi_status
|
static acpi_status
|
||||||
get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data)
|
get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data)
|
||||||
{
|
{
|
||||||
|
@ -161,19 +180,22 @@ get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data)
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static acpi_status try_get_root_bridge_busnr(acpi_handle handle, int *busnum)
|
static acpi_status try_get_root_bridge_busnr(acpi_handle handle,
|
||||||
|
unsigned long long *bus)
|
||||||
{
|
{
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
|
int busnum;
|
||||||
|
|
||||||
*busnum = -1;
|
busnum = -1;
|
||||||
status =
|
status =
|
||||||
acpi_walk_resources(handle, METHOD_NAME__CRS,
|
acpi_walk_resources(handle, METHOD_NAME__CRS,
|
||||||
get_root_bridge_busnr_callback, busnum);
|
get_root_bridge_busnr_callback, &busnum);
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
return status;
|
return status;
|
||||||
/* Check if we really get a bus number from _CRS */
|
/* Check if we really get a bus number from _CRS */
|
||||||
if (*busnum == -1)
|
if (busnum == -1)
|
||||||
return AE_ERROR;
|
return AE_ERROR;
|
||||||
|
*bus = busnum;
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,6 +320,7 @@ static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags)
|
||||||
static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
|
static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
|
||||||
{
|
{
|
||||||
struct acpi_pci_root *root;
|
struct acpi_pci_root *root;
|
||||||
|
|
||||||
list_for_each_entry(root, &acpi_pci_roots, node) {
|
list_for_each_entry(root, &acpi_pci_roots, node) {
|
||||||
if (root->device->handle == handle)
|
if (root->device->handle == handle)
|
||||||
return root;
|
return root;
|
||||||
|
@ -305,6 +328,87 @@ static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct acpi_handle_node {
|
||||||
|
struct list_head node;
|
||||||
|
acpi_handle handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* acpi_get_pci_dev - convert ACPI CA handle to struct pci_dev
|
||||||
|
* @handle: the handle in question
|
||||||
|
*
|
||||||
|
* Given an ACPI CA handle, the desired PCI device is located in the
|
||||||
|
* list of PCI devices.
|
||||||
|
*
|
||||||
|
* If the device is found, its reference count is increased and this
|
||||||
|
* function returns a pointer to its data structure. The caller must
|
||||||
|
* decrement the reference count by calling pci_dev_put().
|
||||||
|
* If no device is found, %NULL is returned.
|
||||||
|
*/
|
||||||
|
struct pci_dev *acpi_get_pci_dev(acpi_handle handle)
|
||||||
|
{
|
||||||
|
int dev, fn;
|
||||||
|
unsigned long long adr;
|
||||||
|
acpi_status status;
|
||||||
|
acpi_handle phandle;
|
||||||
|
struct pci_bus *pbus;
|
||||||
|
struct pci_dev *pdev = NULL;
|
||||||
|
struct acpi_handle_node *node, *tmp;
|
||||||
|
struct acpi_pci_root *root;
|
||||||
|
LIST_HEAD(device_list);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walk up the ACPI CA namespace until we reach a PCI root bridge.
|
||||||
|
*/
|
||||||
|
phandle = handle;
|
||||||
|
while (!acpi_is_root_bridge(phandle)) {
|
||||||
|
node = kzalloc(sizeof(struct acpi_handle_node), GFP_KERNEL);
|
||||||
|
if (!node)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&node->node);
|
||||||
|
node->handle = phandle;
|
||||||
|
list_add(&node->node, &device_list);
|
||||||
|
|
||||||
|
status = acpi_get_parent(phandle, &phandle);
|
||||||
|
if (ACPI_FAILURE(status))
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
root = acpi_pci_find_root(phandle);
|
||||||
|
if (!root)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
pbus = root->bus;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now, walk back down the PCI device tree until we return to our
|
||||||
|
* original handle. Assumes that everything between the PCI root
|
||||||
|
* bridge and the device we're looking for must be a P2P bridge.
|
||||||
|
*/
|
||||||
|
list_for_each_entry(node, &device_list, node) {
|
||||||
|
acpi_handle hnd = node->handle;
|
||||||
|
status = acpi_evaluate_integer(hnd, "_ADR", NULL, &adr);
|
||||||
|
if (ACPI_FAILURE(status))
|
||||||
|
goto out;
|
||||||
|
dev = (adr >> 16) & 0xffff;
|
||||||
|
fn = adr & 0xffff;
|
||||||
|
|
||||||
|
pdev = pci_get_slot(pbus, PCI_DEVFN(dev, fn));
|
||||||
|
if (hnd == handle)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pbus = pdev->subordinate;
|
||||||
|
pci_dev_put(pdev);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
list_for_each_entry_safe(node, tmp, &device_list, node)
|
||||||
|
kfree(node);
|
||||||
|
|
||||||
|
return pdev;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(acpi_get_pci_dev);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* acpi_pci_osc_control_set - commit requested control to Firmware
|
* acpi_pci_osc_control_set - commit requested control to Firmware
|
||||||
* @handle: acpi_handle for the target ACPI object
|
* @handle: acpi_handle for the target ACPI object
|
||||||
|
@ -363,31 +467,46 @@ EXPORT_SYMBOL(acpi_pci_osc_control_set);
|
||||||
|
|
||||||
static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
||||||
{
|
{
|
||||||
int result = 0;
|
unsigned long long segment, bus;
|
||||||
struct acpi_pci_root *root = NULL;
|
acpi_status status;
|
||||||
struct acpi_pci_root *tmp;
|
int result;
|
||||||
acpi_status status = AE_OK;
|
struct acpi_pci_root *root;
|
||||||
unsigned long long value = 0;
|
acpi_handle handle;
|
||||||
acpi_handle handle = NULL;
|
|
||||||
struct acpi_device *child;
|
struct acpi_device *child;
|
||||||
u32 flags, base_flags;
|
u32 flags, base_flags;
|
||||||
|
|
||||||
|
segment = 0;
|
||||||
|
status = acpi_evaluate_integer(device->handle, METHOD_NAME__SEG, NULL,
|
||||||
|
&segment);
|
||||||
|
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
|
||||||
|
printk(KERN_ERR PREFIX "can't evaluate _SEG\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
if (!device)
|
/* Check _CRS first, then _BBN. If no _BBN, default to zero. */
|
||||||
return -EINVAL;
|
bus = 0;
|
||||||
|
status = try_get_root_bridge_busnr(device->handle, &bus);
|
||||||
|
if (ACPI_FAILURE(status)) {
|
||||||
|
status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN, NULL, &bus);
|
||||||
|
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
|
||||||
|
printk(KERN_ERR PREFIX
|
||||||
|
"no bus number in _CRS and can't evaluate _BBN\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL);
|
root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL);
|
||||||
if (!root)
|
if (!root)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
INIT_LIST_HEAD(&root->node);
|
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&root->node);
|
||||||
root->device = device;
|
root->device = device;
|
||||||
|
root->segment = segment & 0xFFFF;
|
||||||
|
root->bus_nr = bus & 0xFF;
|
||||||
strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME);
|
strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME);
|
||||||
strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS);
|
strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS);
|
||||||
device->driver_data = root;
|
device->driver_data = root;
|
||||||
|
|
||||||
device->ops.bind = acpi_pci_bind;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All supported architectures that use ACPI have support for
|
* All supported architectures that use ACPI have support for
|
||||||
* PCI domains, so we indicate this in _OSC support capabilities.
|
* PCI domains, so we indicate this in _OSC support capabilities.
|
||||||
|
@ -395,79 +514,6 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
||||||
flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT;
|
flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT;
|
||||||
acpi_pci_osc_support(root, flags);
|
acpi_pci_osc_support(root, flags);
|
||||||
|
|
||||||
/*
|
|
||||||
* Segment
|
|
||||||
* -------
|
|
||||||
* Obtained via _SEG, if exists, otherwise assumed to be zero (0).
|
|
||||||
*/
|
|
||||||
status = acpi_evaluate_integer(device->handle, METHOD_NAME__SEG, NULL,
|
|
||||||
&value);
|
|
||||||
switch (status) {
|
|
||||||
case AE_OK:
|
|
||||||
root->id.segment = (u16) value;
|
|
||||||
break;
|
|
||||||
case AE_NOT_FOUND:
|
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
|
||||||
"Assuming segment 0 (no _SEG)\n"));
|
|
||||||
root->id.segment = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SEG"));
|
|
||||||
result = -ENODEV;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Bus
|
|
||||||
* ---
|
|
||||||
* Obtained via _BBN, if exists, otherwise assumed to be zero (0).
|
|
||||||
*/
|
|
||||||
status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN, NULL,
|
|
||||||
&value);
|
|
||||||
switch (status) {
|
|
||||||
case AE_OK:
|
|
||||||
root->id.bus = (u16) value;
|
|
||||||
break;
|
|
||||||
case AE_NOT_FOUND:
|
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Assuming bus 0 (no _BBN)\n"));
|
|
||||||
root->id.bus = 0;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BBN"));
|
|
||||||
result = -ENODEV;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Some systems have wrong _BBN */
|
|
||||||
list_for_each_entry(tmp, &acpi_pci_roots, node) {
|
|
||||||
if ((tmp->id.segment == root->id.segment)
|
|
||||||
&& (tmp->id.bus == root->id.bus)) {
|
|
||||||
int bus = 0;
|
|
||||||
acpi_status status;
|
|
||||||
|
|
||||||
printk(KERN_ERR PREFIX
|
|
||||||
"Wrong _BBN value, reboot"
|
|
||||||
" and use option 'pci=noacpi'\n");
|
|
||||||
|
|
||||||
status = try_get_root_bridge_busnr(device->handle, &bus);
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
break;
|
|
||||||
if (bus != root->id.bus) {
|
|
||||||
printk(KERN_INFO PREFIX
|
|
||||||
"PCI _CRS %d overrides _BBN 0\n", bus);
|
|
||||||
root->id.bus = bus;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Device & Function
|
|
||||||
* -----------------
|
|
||||||
* Obtained from _ADR (which has already been evaluated for us).
|
|
||||||
*/
|
|
||||||
root->id.device = device->pnp.bus_address >> 16;
|
|
||||||
root->id.function = device->pnp.bus_address & 0xFFFF;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TBD: Need PCI interface for enumeration/configuration of roots.
|
* TBD: Need PCI interface for enumeration/configuration of roots.
|
||||||
*/
|
*/
|
||||||
|
@ -477,7 +523,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
||||||
|
|
||||||
printk(KERN_INFO PREFIX "%s [%s] (%04x:%02x)\n",
|
printk(KERN_INFO PREFIX "%s [%s] (%04x:%02x)\n",
|
||||||
acpi_device_name(device), acpi_device_bid(device),
|
acpi_device_name(device), acpi_device_bid(device),
|
||||||
root->id.segment, root->id.bus);
|
root->segment, root->bus_nr);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Scan the Root Bridge
|
* Scan the Root Bridge
|
||||||
|
@ -486,11 +532,11 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
||||||
* PCI namespace does not get created until this call is made (and
|
* PCI namespace does not get created until this call is made (and
|
||||||
* thus the root bridge's pci_dev does not exist).
|
* thus the root bridge's pci_dev does not exist).
|
||||||
*/
|
*/
|
||||||
root->bus = pci_acpi_scan_root(device, root->id.segment, root->id.bus);
|
root->bus = pci_acpi_scan_root(device, segment, bus);
|
||||||
if (!root->bus) {
|
if (!root->bus) {
|
||||||
printk(KERN_ERR PREFIX
|
printk(KERN_ERR PREFIX
|
||||||
"Bus %04x:%02x not present in PCI namespace\n",
|
"Bus %04x:%02x not present in PCI namespace\n",
|
||||||
root->id.segment, root->id.bus);
|
root->segment, root->bus_nr);
|
||||||
result = -ENODEV;
|
result = -ENODEV;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -500,7 +546,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
||||||
* -----------------------
|
* -----------------------
|
||||||
* Thus binding the ACPI and PCI devices.
|
* Thus binding the ACPI and PCI devices.
|
||||||
*/
|
*/
|
||||||
result = acpi_pci_bind_root(device, &root->id, root->bus);
|
result = acpi_pci_bind_root(device);
|
||||||
if (result)
|
if (result)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
|
@ -511,8 +557,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
||||||
*/
|
*/
|
||||||
status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle);
|
status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle);
|
||||||
if (ACPI_SUCCESS(status))
|
if (ACPI_SUCCESS(status))
|
||||||
result = acpi_pci_irq_add_prt(device->handle, root->id.segment,
|
result = acpi_pci_irq_add_prt(device->handle, root->bus);
|
||||||
root->id.bus);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Scan and bind all _ADR-Based Devices
|
* Scan and bind all _ADR-Based Devices
|
||||||
|
@ -531,42 +576,28 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
|
||||||
if (flags != base_flags)
|
if (flags != base_flags)
|
||||||
acpi_pci_osc_support(root, flags);
|
acpi_pci_osc_support(root, flags);
|
||||||
|
|
||||||
end:
|
return 0;
|
||||||
if (result) {
|
|
||||||
|
end:
|
||||||
if (!list_empty(&root->node))
|
if (!list_empty(&root->node))
|
||||||
list_del(&root->node);
|
list_del(&root->node);
|
||||||
kfree(root);
|
kfree(root);
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int acpi_pci_root_start(struct acpi_device *device)
|
static int acpi_pci_root_start(struct acpi_device *device)
|
||||||
{
|
{
|
||||||
struct acpi_pci_root *root;
|
struct acpi_pci_root *root = acpi_driver_data(device);
|
||||||
|
|
||||||
|
|
||||||
list_for_each_entry(root, &acpi_pci_roots, node) {
|
|
||||||
if (root->device == device) {
|
|
||||||
pci_bus_add_devices(root->bus);
|
pci_bus_add_devices(root->bus);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int acpi_pci_root_remove(struct acpi_device *device, int type)
|
static int acpi_pci_root_remove(struct acpi_device *device, int type)
|
||||||
{
|
{
|
||||||
struct acpi_pci_root *root = NULL;
|
struct acpi_pci_root *root = acpi_driver_data(device);
|
||||||
|
|
||||||
|
|
||||||
if (!device || !acpi_driver_data(device))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
root = acpi_driver_data(device);
|
|
||||||
|
|
||||||
kfree(root);
|
kfree(root);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -194,7 +194,7 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
|
||||||
|
|
||||||
static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
|
static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
|
||||||
{
|
{
|
||||||
int result = 0, state;
|
int result = 0;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
acpi_status status = AE_OK;
|
acpi_status status = AE_OK;
|
||||||
struct acpi_power_resource *resource = NULL;
|
struct acpi_power_resource *resource = NULL;
|
||||||
|
@ -236,18 +236,6 @@ static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
if (!acpi_power_nocheck) {
|
|
||||||
/*
|
|
||||||
* If acpi_power_nocheck is set, it is unnecessary to check
|
|
||||||
* the power state after power transition.
|
|
||||||
*/
|
|
||||||
result = acpi_power_get_state(resource->device->handle,
|
|
||||||
&state);
|
|
||||||
if (result)
|
|
||||||
return result;
|
|
||||||
if (state != ACPI_POWER_RESOURCE_STATE_ON)
|
|
||||||
return -ENOEXEC;
|
|
||||||
}
|
|
||||||
/* Update the power resource's _device_ power state */
|
/* Update the power resource's _device_ power state */
|
||||||
resource->device->power.state = ACPI_STATE_D0;
|
resource->device->power.state = ACPI_STATE_D0;
|
||||||
|
|
||||||
|
@ -258,7 +246,7 @@ static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
|
||||||
|
|
||||||
static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
|
static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
|
||||||
{
|
{
|
||||||
int result = 0, state;
|
int result = 0;
|
||||||
acpi_status status = AE_OK;
|
acpi_status status = AE_OK;
|
||||||
struct acpi_power_resource *resource = NULL;
|
struct acpi_power_resource *resource = NULL;
|
||||||
struct list_head *node, *next;
|
struct list_head *node, *next;
|
||||||
|
@ -293,18 +281,6 @@ static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
if (!acpi_power_nocheck) {
|
|
||||||
/*
|
|
||||||
* If acpi_power_nocheck is set, it is unnecessary to check
|
|
||||||
* the power state after power transition.
|
|
||||||
*/
|
|
||||||
result = acpi_power_get_state(handle, &state);
|
|
||||||
if (result)
|
|
||||||
return result;
|
|
||||||
if (state != ACPI_POWER_RESOURCE_STATE_OFF)
|
|
||||||
return -ENOEXEC;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update the power resource's _device_ power state */
|
/* Update the power resource's _device_ power state */
|
||||||
resource->device->power.state = ACPI_STATE_D3;
|
resource->device->power.state = ACPI_STATE_D3;
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ static int acpi_processor_handle_eject(struct acpi_processor *pr);
|
||||||
|
|
||||||
static const struct acpi_device_id processor_device_ids[] = {
|
static const struct acpi_device_id processor_device_ids[] = {
|
||||||
{ACPI_PROCESSOR_OBJECT_HID, 0},
|
{ACPI_PROCESSOR_OBJECT_HID, 0},
|
||||||
{ACPI_PROCESSOR_HID, 0},
|
{"ACPI0007", 0},
|
||||||
{"", 0},
|
{"", 0},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(acpi, processor_device_ids);
|
MODULE_DEVICE_TABLE(acpi, processor_device_ids);
|
||||||
|
@ -596,7 +596,21 @@ static int acpi_processor_get_info(struct acpi_device *device)
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||||
"No bus mastering arbitration control\n"));
|
"No bus mastering arbitration control\n"));
|
||||||
|
|
||||||
if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_HID)) {
|
if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) {
|
||||||
|
/* Declared with "Processor" statement; match ProcessorID */
|
||||||
|
status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer);
|
||||||
|
if (ACPI_FAILURE(status)) {
|
||||||
|
printk(KERN_ERR PREFIX "Evaluating processor object\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP.
|
||||||
|
* >>> 'acpi_get_processor_id(acpi_id, &id)' in
|
||||||
|
* arch/xxx/acpi.c
|
||||||
|
*/
|
||||||
|
pr->acpi_id = object.processor.proc_id;
|
||||||
|
} else {
|
||||||
/*
|
/*
|
||||||
* Declared with "Device" statement; match _UID.
|
* Declared with "Device" statement; match _UID.
|
||||||
* Note that we don't handle string _UIDs yet.
|
* Note that we don't handle string _UIDs yet.
|
||||||
|
@ -611,20 +625,6 @@ static int acpi_processor_get_info(struct acpi_device *device)
|
||||||
}
|
}
|
||||||
device_declaration = 1;
|
device_declaration = 1;
|
||||||
pr->acpi_id = value;
|
pr->acpi_id = value;
|
||||||
} else {
|
|
||||||
/* Declared with "Processor" statement; match ProcessorID */
|
|
||||||
status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer);
|
|
||||||
if (ACPI_FAILURE(status)) {
|
|
||||||
printk(KERN_ERR PREFIX "Evaluating processor object\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP.
|
|
||||||
* >>> 'acpi_get_processor_id(acpi_id, &id)' in
|
|
||||||
* arch/xxx/acpi.c
|
|
||||||
*/
|
|
||||||
pr->acpi_id = object.processor.proc_id;
|
|
||||||
}
|
}
|
||||||
cpu_index = get_cpu_id(pr->handle, device_declaration, pr->acpi_id);
|
cpu_index = get_cpu_id(pr->handle, device_declaration, pr->acpi_id);
|
||||||
|
|
||||||
|
@ -649,7 +649,16 @@ static int acpi_processor_get_info(struct acpi_device *device)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* On some boxes several processors use the same processor bus id.
|
||||||
|
* But they are located in different scope. For example:
|
||||||
|
* \_SB.SCK0.CPU0
|
||||||
|
* \_SB.SCK1.CPU0
|
||||||
|
* Rename the processor device bus id. And the new bus id will be
|
||||||
|
* generated as the following format:
|
||||||
|
* CPU+CPU ID.
|
||||||
|
*/
|
||||||
|
sprintf(acpi_device_bid(device), "CPU%X", pr->id);
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id,
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id,
|
||||||
pr->acpi_id));
|
pr->acpi_id));
|
||||||
|
|
||||||
|
@ -731,6 +740,8 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device)
|
||||||
/* _PDC call should be done before doing anything else (if reqd.). */
|
/* _PDC call should be done before doing anything else (if reqd.). */
|
||||||
arch_acpi_processor_init_pdc(pr);
|
arch_acpi_processor_init_pdc(pr);
|
||||||
acpi_processor_set_pdc(pr);
|
acpi_processor_set_pdc(pr);
|
||||||
|
arch_acpi_processor_cleanup_pdc(pr);
|
||||||
|
|
||||||
#ifdef CONFIG_CPU_FREQ
|
#ifdef CONFIG_CPU_FREQ
|
||||||
acpi_processor_ppc_has_changed(pr);
|
acpi_processor_ppc_has_changed(pr);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -139,7 +139,7 @@ static void acpi_safe_halt(void)
|
||||||
* are affected too. We pick the most conservative approach: we assume
|
* are affected too. We pick the most conservative approach: we assume
|
||||||
* that the local APIC stops in both C2 and C3.
|
* that the local APIC stops in both C2 and C3.
|
||||||
*/
|
*/
|
||||||
static void acpi_timer_check_state(int state, struct acpi_processor *pr,
|
static void lapic_timer_check_state(int state, struct acpi_processor *pr,
|
||||||
struct acpi_processor_cx *cx)
|
struct acpi_processor_cx *cx)
|
||||||
{
|
{
|
||||||
struct acpi_processor_power *pwr = &pr->power;
|
struct acpi_processor_power *pwr = &pr->power;
|
||||||
|
@ -162,7 +162,7 @@ static void acpi_timer_check_state(int state, struct acpi_processor *pr,
|
||||||
pr->power.timer_broadcast_on_state = state;
|
pr->power.timer_broadcast_on_state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void acpi_propagate_timer_broadcast(struct acpi_processor *pr)
|
static void lapic_timer_propagate_broadcast(struct acpi_processor *pr)
|
||||||
{
|
{
|
||||||
unsigned long reason;
|
unsigned long reason;
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ static void acpi_propagate_timer_broadcast(struct acpi_processor *pr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Power(C) State timer broadcast control */
|
/* Power(C) State timer broadcast control */
|
||||||
static void acpi_state_timer_broadcast(struct acpi_processor *pr,
|
static void lapic_timer_state_broadcast(struct acpi_processor *pr,
|
||||||
struct acpi_processor_cx *cx,
|
struct acpi_processor_cx *cx,
|
||||||
int broadcast)
|
int broadcast)
|
||||||
{
|
{
|
||||||
|
@ -190,10 +190,10 @@ static void acpi_state_timer_broadcast(struct acpi_processor *pr,
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static void acpi_timer_check_state(int state, struct acpi_processor *pr,
|
static void lapic_timer_check_state(int state, struct acpi_processor *pr,
|
||||||
struct acpi_processor_cx *cstate) { }
|
struct acpi_processor_cx *cstate) { }
|
||||||
static void acpi_propagate_timer_broadcast(struct acpi_processor *pr) { }
|
static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) { }
|
||||||
static void acpi_state_timer_broadcast(struct acpi_processor *pr,
|
static void lapic_timer_state_broadcast(struct acpi_processor *pr,
|
||||||
struct acpi_processor_cx *cx,
|
struct acpi_processor_cx *cx,
|
||||||
int broadcast)
|
int broadcast)
|
||||||
{
|
{
|
||||||
|
@ -515,7 +515,8 @@ static void acpi_processor_power_verify_c2(struct acpi_processor_cx *cx)
|
||||||
static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
|
static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
|
||||||
struct acpi_processor_cx *cx)
|
struct acpi_processor_cx *cx)
|
||||||
{
|
{
|
||||||
static int bm_check_flag;
|
static int bm_check_flag = -1;
|
||||||
|
static int bm_control_flag = -1;
|
||||||
|
|
||||||
|
|
||||||
if (!cx->address)
|
if (!cx->address)
|
||||||
|
@ -545,12 +546,14 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* All the logic here assumes flags.bm_check is same across all CPUs */
|
/* All the logic here assumes flags.bm_check is same across all CPUs */
|
||||||
if (!bm_check_flag) {
|
if (bm_check_flag == -1) {
|
||||||
/* Determine whether bm_check is needed based on CPU */
|
/* Determine whether bm_check is needed based on CPU */
|
||||||
acpi_processor_power_init_bm_check(&(pr->flags), pr->id);
|
acpi_processor_power_init_bm_check(&(pr->flags), pr->id);
|
||||||
bm_check_flag = pr->flags.bm_check;
|
bm_check_flag = pr->flags.bm_check;
|
||||||
|
bm_control_flag = pr->flags.bm_control;
|
||||||
} else {
|
} else {
|
||||||
pr->flags.bm_check = bm_check_flag;
|
pr->flags.bm_check = bm_check_flag;
|
||||||
|
pr->flags.bm_control = bm_control_flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pr->flags.bm_check) {
|
if (pr->flags.bm_check) {
|
||||||
|
@ -614,29 +617,25 @@ static int acpi_processor_power_verify(struct acpi_processor *pr)
|
||||||
switch (cx->type) {
|
switch (cx->type) {
|
||||||
case ACPI_STATE_C1:
|
case ACPI_STATE_C1:
|
||||||
cx->valid = 1;
|
cx->valid = 1;
|
||||||
acpi_timer_check_state(i, pr, cx);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACPI_STATE_C2:
|
case ACPI_STATE_C2:
|
||||||
acpi_processor_power_verify_c2(cx);
|
acpi_processor_power_verify_c2(cx);
|
||||||
if (cx->valid)
|
|
||||||
acpi_timer_check_state(i, pr, cx);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACPI_STATE_C3:
|
case ACPI_STATE_C3:
|
||||||
acpi_processor_power_verify_c3(pr, cx);
|
acpi_processor_power_verify_c3(pr, cx);
|
||||||
if (cx->valid)
|
|
||||||
acpi_timer_check_state(i, pr, cx);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (cx->valid)
|
if (!cx->valid)
|
||||||
tsc_check_state(cx->type);
|
continue;
|
||||||
|
|
||||||
if (cx->valid)
|
lapic_timer_check_state(i, pr, cx);
|
||||||
|
tsc_check_state(cx->type);
|
||||||
working++;
|
working++;
|
||||||
}
|
}
|
||||||
|
|
||||||
acpi_propagate_timer_broadcast(pr);
|
lapic_timer_propagate_broadcast(pr);
|
||||||
|
|
||||||
return (working);
|
return (working);
|
||||||
}
|
}
|
||||||
|
@ -839,7 +838,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
acpi_state_timer_broadcast(pr, cx, 1);
|
lapic_timer_state_broadcast(pr, cx, 1);
|
||||||
kt1 = ktime_get_real();
|
kt1 = ktime_get_real();
|
||||||
acpi_idle_do_entry(cx);
|
acpi_idle_do_entry(cx);
|
||||||
kt2 = ktime_get_real();
|
kt2 = ktime_get_real();
|
||||||
|
@ -847,7 +846,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
|
||||||
|
|
||||||
local_irq_enable();
|
local_irq_enable();
|
||||||
cx->usage++;
|
cx->usage++;
|
||||||
acpi_state_timer_broadcast(pr, cx, 0);
|
lapic_timer_state_broadcast(pr, cx, 0);
|
||||||
|
|
||||||
return idle_time;
|
return idle_time;
|
||||||
}
|
}
|
||||||
|
@ -892,7 +891,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
|
||||||
* Must be done before busmaster disable as we might need to
|
* Must be done before busmaster disable as we might need to
|
||||||
* access HPET !
|
* access HPET !
|
||||||
*/
|
*/
|
||||||
acpi_state_timer_broadcast(pr, cx, 1);
|
lapic_timer_state_broadcast(pr, cx, 1);
|
||||||
|
|
||||||
if (cx->type == ACPI_STATE_C3)
|
if (cx->type == ACPI_STATE_C3)
|
||||||
ACPI_FLUSH_CPU_CACHE();
|
ACPI_FLUSH_CPU_CACHE();
|
||||||
|
@ -914,7 +913,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
|
||||||
|
|
||||||
cx->usage++;
|
cx->usage++;
|
||||||
|
|
||||||
acpi_state_timer_broadcast(pr, cx, 0);
|
lapic_timer_state_broadcast(pr, cx, 0);
|
||||||
cx->time += sleep_ticks;
|
cx->time += sleep_ticks;
|
||||||
return idle_time;
|
return idle_time;
|
||||||
}
|
}
|
||||||
|
@ -981,7 +980,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
|
||||||
* Must be done before busmaster disable as we might need to
|
* Must be done before busmaster disable as we might need to
|
||||||
* access HPET !
|
* access HPET !
|
||||||
*/
|
*/
|
||||||
acpi_state_timer_broadcast(pr, cx, 1);
|
lapic_timer_state_broadcast(pr, cx, 1);
|
||||||
|
|
||||||
kt1 = ktime_get_real();
|
kt1 = ktime_get_real();
|
||||||
/*
|
/*
|
||||||
|
@ -1026,7 +1025,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
|
||||||
|
|
||||||
cx->usage++;
|
cx->usage++;
|
||||||
|
|
||||||
acpi_state_timer_broadcast(pr, cx, 0);
|
lapic_timer_state_broadcast(pr, cx, 0);
|
||||||
cx->time += sleep_ticks;
|
cx->time += sleep_ticks;
|
||||||
return idle_time;
|
return idle_time;
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,7 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
|
static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
|
||||||
|
|
||||||
static int acpi_bus_hot_remove_device(void *context)
|
static void acpi_bus_hot_remove_device(void *context)
|
||||||
{
|
{
|
||||||
struct acpi_device *device;
|
struct acpi_device *device;
|
||||||
acpi_handle handle = context;
|
acpi_handle handle = context;
|
||||||
|
@ -104,10 +104,10 @@ static int acpi_bus_hot_remove_device(void *context)
|
||||||
acpi_status status = AE_OK;
|
acpi_status status = AE_OK;
|
||||||
|
|
||||||
if (acpi_bus_get_device(handle, &device))
|
if (acpi_bus_get_device(handle, &device))
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
if (!device)
|
if (!device)
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||||
"Hot-removing device %s...\n", dev_name(&device->dev)));
|
"Hot-removing device %s...\n", dev_name(&device->dev)));
|
||||||
|
@ -115,7 +115,7 @@ static int acpi_bus_hot_remove_device(void *context)
|
||||||
if (acpi_bus_trim(device, 1)) {
|
if (acpi_bus_trim(device, 1)) {
|
||||||
printk(KERN_ERR PREFIX
|
printk(KERN_ERR PREFIX
|
||||||
"Removing device failed\n");
|
"Removing device failed\n");
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* power off device */
|
/* power off device */
|
||||||
|
@ -142,9 +142,10 @@ static int acpi_bus_hot_remove_device(void *context)
|
||||||
*/
|
*/
|
||||||
status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL);
|
status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL);
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
return -ENODEV;
|
printk(KERN_WARNING PREFIX
|
||||||
|
"Eject device failed\n");
|
||||||
|
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
|
@ -155,7 +156,6 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
acpi_object_type type = 0;
|
acpi_object_type type = 0;
|
||||||
struct acpi_device *acpi_device = to_acpi_device(d);
|
struct acpi_device *acpi_device = to_acpi_device(d);
|
||||||
struct task_struct *task;
|
|
||||||
|
|
||||||
if ((!count) || (buf[0] != '1')) {
|
if ((!count) || (buf[0] != '1')) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
@ -172,11 +172,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remove the device in another thread to fix the deadlock issue */
|
acpi_os_hotplug_execute(acpi_bus_hot_remove_device, acpi_device->handle);
|
||||||
task = kthread_run(acpi_bus_hot_remove_device,
|
|
||||||
acpi_device->handle, "acpi_hot_remove_device");
|
|
||||||
if (IS_ERR(task))
|
|
||||||
ret = PTR_ERR(task);
|
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -198,12 +194,12 @@ acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *b
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path);
|
result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path);
|
||||||
if(result)
|
if (result)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
result = sprintf(buf, "%s\n", (char*)path.pointer);
|
result = sprintf(buf, "%s\n", (char*)path.pointer);
|
||||||
kfree(path.pointer);
|
kfree(path.pointer);
|
||||||
end:
|
end:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL);
|
static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL);
|
||||||
|
@ -217,21 +213,21 @@ static int acpi_device_setup_files(struct acpi_device *dev)
|
||||||
/*
|
/*
|
||||||
* Devices gotten from FADT don't have a "path" attribute
|
* Devices gotten from FADT don't have a "path" attribute
|
||||||
*/
|
*/
|
||||||
if(dev->handle) {
|
if (dev->handle) {
|
||||||
result = device_create_file(&dev->dev, &dev_attr_path);
|
result = device_create_file(&dev->dev, &dev_attr_path);
|
||||||
if(result)
|
if (result)
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(dev->flags.hardware_id) {
|
if (dev->flags.hardware_id) {
|
||||||
result = device_create_file(&dev->dev, &dev_attr_hid);
|
result = device_create_file(&dev->dev, &dev_attr_hid);
|
||||||
if(result)
|
if (result)
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev->flags.hardware_id || dev->flags.compatible_ids){
|
if (dev->flags.hardware_id || dev->flags.compatible_ids) {
|
||||||
result = device_create_file(&dev->dev, &dev_attr_modalias);
|
result = device_create_file(&dev->dev, &dev_attr_modalias);
|
||||||
if(result)
|
if (result)
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +238,7 @@ static int acpi_device_setup_files(struct acpi_device *dev)
|
||||||
status = acpi_get_handle(dev->handle, "_EJ0", &temp);
|
status = acpi_get_handle(dev->handle, "_EJ0", &temp);
|
||||||
if (ACPI_SUCCESS(status))
|
if (ACPI_SUCCESS(status))
|
||||||
result = device_create_file(&dev->dev, &dev_attr_eject);
|
result = device_create_file(&dev->dev, &dev_attr_eject);
|
||||||
end:
|
end:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,9 +258,9 @@ static void acpi_device_remove_files(struct acpi_device *dev)
|
||||||
if (dev->flags.hardware_id || dev->flags.compatible_ids)
|
if (dev->flags.hardware_id || dev->flags.compatible_ids)
|
||||||
device_remove_file(&dev->dev, &dev_attr_modalias);
|
device_remove_file(&dev->dev, &dev_attr_modalias);
|
||||||
|
|
||||||
if(dev->flags.hardware_id)
|
if (dev->flags.hardware_id)
|
||||||
device_remove_file(&dev->dev, &dev_attr_hid);
|
device_remove_file(&dev->dev, &dev_attr_hid);
|
||||||
if(dev->handle)
|
if (dev->handle)
|
||||||
device_remove_file(&dev->dev, &dev_attr_path);
|
device_remove_file(&dev->dev, &dev_attr_path);
|
||||||
}
|
}
|
||||||
/* --------------------------------------------------------------------------
|
/* --------------------------------------------------------------------------
|
||||||
|
@ -512,7 +508,7 @@ static int acpi_device_register(struct acpi_device *device,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!found) {
|
if (!found) {
|
||||||
acpi_device_bus_id = new_bus_id;
|
acpi_device_bus_id = new_bus_id;
|
||||||
strcpy(acpi_device_bus_id->bus_id, device->flags.hardware_id ? device->pnp.hardware_id : "device");
|
strcpy(acpi_device_bus_id->bus_id, device->flags.hardware_id ? device->pnp.hardware_id : "device");
|
||||||
acpi_device_bus_id->instance_no = 0;
|
acpi_device_bus_id->instance_no = 0;
|
||||||
|
@ -530,22 +526,21 @@ static int acpi_device_register(struct acpi_device *device,
|
||||||
if (device->parent)
|
if (device->parent)
|
||||||
device->dev.parent = &parent->dev;
|
device->dev.parent = &parent->dev;
|
||||||
device->dev.bus = &acpi_bus_type;
|
device->dev.bus = &acpi_bus_type;
|
||||||
device_initialize(&device->dev);
|
|
||||||
device->dev.release = &acpi_device_release;
|
device->dev.release = &acpi_device_release;
|
||||||
result = device_add(&device->dev);
|
result = device_register(&device->dev);
|
||||||
if(result) {
|
if (result) {
|
||||||
dev_err(&device->dev, "Error adding device\n");
|
dev_err(&device->dev, "Error registering device\n");
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = acpi_device_setup_files(device);
|
result = acpi_device_setup_files(device);
|
||||||
if(result)
|
if (result)
|
||||||
printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n",
|
printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n",
|
||||||
dev_name(&device->dev));
|
dev_name(&device->dev));
|
||||||
|
|
||||||
device->removal_type = ACPI_BUS_REMOVAL_NORMAL;
|
device->removal_type = ACPI_BUS_REMOVAL_NORMAL;
|
||||||
return 0;
|
return 0;
|
||||||
end:
|
end:
|
||||||
mutex_lock(&acpi_device_lock);
|
mutex_lock(&acpi_device_lock);
|
||||||
if (device->parent)
|
if (device->parent)
|
||||||
list_del(&device->node);
|
list_del(&device->node);
|
||||||
|
@ -585,7 +580,6 @@ acpi_bus_driver_init(struct acpi_device *device, struct acpi_driver *driver)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
|
|
||||||
if (!device || !driver)
|
if (!device || !driver)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -802,7 +796,7 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device)
|
||||||
if (!acpi_match_device_ids(device, button_device_ids))
|
if (!acpi_match_device_ids(device, button_device_ids))
|
||||||
device->wakeup.flags.run_wake = 1;
|
device->wakeup.flags.run_wake = 1;
|
||||||
|
|
||||||
end:
|
end:
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
device->flags.wake_capable = 0;
|
device->flags.wake_capable = 0;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1320,7 +1314,7 @@ acpi_add_single_object(struct acpi_device **child,
|
||||||
device->parent->ops.bind(device);
|
device->parent->ops.bind(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
end:
|
end:
|
||||||
if (!result)
|
if (!result)
|
||||||
*child = device;
|
*child = device;
|
||||||
else {
|
else {
|
||||||
|
@ -1464,7 +1458,6 @@ acpi_bus_add(struct acpi_device **child,
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(acpi_bus_add);
|
EXPORT_SYMBOL(acpi_bus_add);
|
||||||
|
|
||||||
int acpi_bus_start(struct acpi_device *device)
|
int acpi_bus_start(struct acpi_device *device)
|
||||||
|
@ -1484,7 +1477,6 @@ int acpi_bus_start(struct acpi_device *device)
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(acpi_bus_start);
|
EXPORT_SYMBOL(acpi_bus_start);
|
||||||
|
|
||||||
int acpi_bus_trim(struct acpi_device *start, int rmdevice)
|
int acpi_bus_trim(struct acpi_device *start, int rmdevice)
|
||||||
|
@ -1542,7 +1534,6 @@ int acpi_bus_trim(struct acpi_device *start, int rmdevice)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(acpi_bus_trim);
|
EXPORT_SYMBOL_GPL(acpi_bus_trim);
|
||||||
|
|
||||||
|
|
||||||
static int acpi_bus_scan_fixed(struct acpi_device *root)
|
static int acpi_bus_scan_fixed(struct acpi_device *root)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
@ -1610,6 +1601,6 @@ int __init acpi_scan_init(void)
|
||||||
if (result)
|
if (result)
|
||||||
acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL);
|
acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL);
|
||||||
|
|
||||||
Done:
|
Done:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,7 @@ MODULE_LICENSE("GPL");
|
||||||
static int brightness_switch_enabled = 1;
|
static int brightness_switch_enabled = 1;
|
||||||
module_param(brightness_switch_enabled, bool, 0644);
|
module_param(brightness_switch_enabled, bool, 0644);
|
||||||
|
|
||||||
|
static int register_count = 0;
|
||||||
static int acpi_video_bus_add(struct acpi_device *device);
|
static int acpi_video_bus_add(struct acpi_device *device);
|
||||||
static int acpi_video_bus_remove(struct acpi_device *device, int type);
|
static int acpi_video_bus_remove(struct acpi_device *device, int type);
|
||||||
static int acpi_video_resume(struct acpi_device *device);
|
static int acpi_video_resume(struct acpi_device *device);
|
||||||
|
@ -586,6 +587,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
|
||||||
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"),
|
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.callback = video_set_bqc_offset,
|
||||||
|
.ident = "Acer Aspire 7720",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"),
|
||||||
|
},
|
||||||
|
},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -976,6 +985,11 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
|
||||||
device->backlight->props.max_brightness = device->brightness->count-3;
|
device->backlight->props.max_brightness = device->brightness->count-3;
|
||||||
kfree(name);
|
kfree(name);
|
||||||
|
|
||||||
|
result = sysfs_create_link(&device->backlight->dev.kobj,
|
||||||
|
&device->dev->dev.kobj, "device");
|
||||||
|
if (result)
|
||||||
|
printk(KERN_ERR PREFIX "Create sysfs link\n");
|
||||||
|
|
||||||
device->cdev = thermal_cooling_device_register("LCD",
|
device->cdev = thermal_cooling_device_register("LCD",
|
||||||
device->dev, &video_cooling_ops);
|
device->dev, &video_cooling_ops);
|
||||||
if (IS_ERR(device->cdev))
|
if (IS_ERR(device->cdev))
|
||||||
|
@ -1054,15 +1068,15 @@ static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
|
||||||
static int acpi_video_bus_check(struct acpi_video_bus *video)
|
static int acpi_video_bus_check(struct acpi_video_bus *video)
|
||||||
{
|
{
|
||||||
acpi_status status = -ENOENT;
|
acpi_status status = -ENOENT;
|
||||||
struct device *dev;
|
struct pci_dev *dev;
|
||||||
|
|
||||||
if (!video)
|
if (!video)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
dev = acpi_get_physical_pci_device(video->device->handle);
|
dev = acpi_get_pci_dev(video->device->handle);
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
put_device(dev);
|
pci_dev_put(dev);
|
||||||
|
|
||||||
/* Since there is no HID, CID and so on for VGA driver, we have
|
/* Since there is no HID, CID and so on for VGA driver, we have
|
||||||
* to check well known required nodes.
|
* to check well known required nodes.
|
||||||
|
@ -1990,6 +2004,7 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
|
||||||
status = acpi_remove_notify_handler(device->dev->handle,
|
status = acpi_remove_notify_handler(device->dev->handle,
|
||||||
ACPI_DEVICE_NOTIFY,
|
ACPI_DEVICE_NOTIFY,
|
||||||
acpi_video_device_notify);
|
acpi_video_device_notify);
|
||||||
|
sysfs_remove_link(&device->backlight->dev.kobj, "device");
|
||||||
backlight_device_unregister(device->backlight);
|
backlight_device_unregister(device->backlight);
|
||||||
if (device->cdev) {
|
if (device->cdev) {
|
||||||
sysfs_remove_link(&device->dev->dev.kobj,
|
sysfs_remove_link(&device->dev->dev.kobj,
|
||||||
|
@ -2318,6 +2333,13 @@ static int __init intel_opregion_present(void)
|
||||||
int acpi_video_register(void)
|
int acpi_video_register(void)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
if (register_count) {
|
||||||
|
/*
|
||||||
|
* if the function of acpi_video_register is already called,
|
||||||
|
* don't register the acpi_vide_bus again and return no error.
|
||||||
|
*/
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
|
acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
|
||||||
if (!acpi_video_dir)
|
if (!acpi_video_dir)
|
||||||
|
@ -2329,10 +2351,35 @@ int acpi_video_register(void)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the acpi_video_bus is loaded successfully, increase
|
||||||
|
* the counter reference.
|
||||||
|
*/
|
||||||
|
register_count = 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(acpi_video_register);
|
EXPORT_SYMBOL(acpi_video_register);
|
||||||
|
|
||||||
|
void acpi_video_unregister(void)
|
||||||
|
{
|
||||||
|
if (!register_count) {
|
||||||
|
/*
|
||||||
|
* If the acpi video bus is already unloaded, don't
|
||||||
|
* unload it again and return directly.
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
acpi_bus_unregister_driver(&acpi_video_bus);
|
||||||
|
|
||||||
|
remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
|
||||||
|
|
||||||
|
register_count = 0;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(acpi_video_unregister);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is kind of nasty. Hardware using Intel chipsets may require
|
* This is kind of nasty. Hardware using Intel chipsets may require
|
||||||
* the video opregion code to be run first in order to initialise
|
* the video opregion code to be run first in order to initialise
|
||||||
|
@ -2350,16 +2397,12 @@ static int __init acpi_video_init(void)
|
||||||
return acpi_video_register();
|
return acpi_video_register();
|
||||||
}
|
}
|
||||||
|
|
||||||
void acpi_video_exit(void)
|
static void __exit acpi_video_exit(void)
|
||||||
{
|
{
|
||||||
|
acpi_video_unregister();
|
||||||
acpi_bus_unregister_driver(&acpi_video_bus);
|
|
||||||
|
|
||||||
remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(acpi_video_exit);
|
|
||||||
|
|
||||||
module_init(acpi_video_init);
|
module_init(acpi_video_init);
|
||||||
module_exit(acpi_video_exit);
|
module_exit(acpi_video_exit);
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* assinged
|
* assinged
|
||||||
*
|
*
|
||||||
* After PCI devices are glued with ACPI devices
|
* After PCI devices are glued with ACPI devices
|
||||||
* acpi_get_physical_pci_device() can be called to identify ACPI graphics
|
* acpi_get_pci_dev() can be called to identify ACPI graphics
|
||||||
* devices for which a real graphics card is plugged in
|
* devices for which a real graphics card is plugged in
|
||||||
*
|
*
|
||||||
* Now acpi_video_get_capabilities() can be called to check which
|
* Now acpi_video_get_capabilities() can be called to check which
|
||||||
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/dmi.h>
|
#include <linux/dmi.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
|
||||||
ACPI_MODULE_NAME("video");
|
ACPI_MODULE_NAME("video");
|
||||||
#define _COMPONENT ACPI_VIDEO_COMPONENT
|
#define _COMPONENT ACPI_VIDEO_COMPONENT
|
||||||
|
@ -109,7 +110,7 @@ static acpi_status
|
||||||
find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
|
find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
|
||||||
{
|
{
|
||||||
long *cap = context;
|
long *cap = context;
|
||||||
struct device *dev;
|
struct pci_dev *dev;
|
||||||
struct acpi_device *acpi_dev;
|
struct acpi_device *acpi_dev;
|
||||||
|
|
||||||
const struct acpi_device_id video_ids[] = {
|
const struct acpi_device_id video_ids[] = {
|
||||||
|
@ -120,10 +121,10 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv)
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
|
|
||||||
if (!acpi_match_device_ids(acpi_dev, video_ids)) {
|
if (!acpi_match_device_ids(acpi_dev, video_ids)) {
|
||||||
dev = acpi_get_physical_pci_device(handle);
|
dev = acpi_get_pci_dev(handle);
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
put_device(dev);
|
pci_dev_put(dev);
|
||||||
*cap |= acpi_is_video_device(acpi_dev);
|
*cap |= acpi_is_video_device(acpi_dev);
|
||||||
}
|
}
|
||||||
return AE_OK;
|
return AE_OK;
|
||||||
|
|
|
@ -419,7 +419,7 @@ void intel_opregion_free(struct drm_device *dev, int suspend)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!suspend)
|
if (!suspend)
|
||||||
acpi_video_exit();
|
acpi_video_unregister();
|
||||||
|
|
||||||
opregion->acpi->drdy = 0;
|
opregion->acpi->drdy = 0;
|
||||||
|
|
||||||
|
|
|
@ -354,7 +354,7 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
|
||||||
status = acpi_run_hpp(handle, hpp);
|
status = acpi_run_hpp(handle, hpp);
|
||||||
if (ACPI_SUCCESS(status))
|
if (ACPI_SUCCESS(status))
|
||||||
break;
|
break;
|
||||||
if (acpi_root_bridge(handle))
|
if (acpi_is_root_bridge(handle))
|
||||||
break;
|
break;
|
||||||
status = acpi_get_parent(handle, &phandle);
|
status = acpi_get_parent(handle, &phandle);
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
|
@ -428,7 +428,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags)
|
||||||
status = acpi_run_oshp(handle);
|
status = acpi_run_oshp(handle);
|
||||||
if (ACPI_SUCCESS(status))
|
if (ACPI_SUCCESS(status))
|
||||||
goto got_one;
|
goto got_one;
|
||||||
if (acpi_root_bridge(handle))
|
if (acpi_is_root_bridge(handle))
|
||||||
break;
|
break;
|
||||||
chandle = handle;
|
chandle = handle;
|
||||||
status = acpi_get_parent(chandle, &handle);
|
status = acpi_get_parent(chandle, &handle);
|
||||||
|
@ -449,42 +449,6 @@ got_one:
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware);
|
EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware);
|
||||||
|
|
||||||
/* acpi_root_bridge - check to see if this acpi object is a root bridge
|
|
||||||
*
|
|
||||||
* @handle - the acpi object in question.
|
|
||||||
*/
|
|
||||||
int acpi_root_bridge(acpi_handle handle)
|
|
||||||
{
|
|
||||||
acpi_status status;
|
|
||||||
struct acpi_device_info *info;
|
|
||||||
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
|
|
||||||
int i;
|
|
||||||
|
|
||||||
status = acpi_get_object_info(handle, &buffer);
|
|
||||||
if (ACPI_SUCCESS(status)) {
|
|
||||||
info = buffer.pointer;
|
|
||||||
if ((info->valid & ACPI_VALID_HID) &&
|
|
||||||
!strcmp(PCI_ROOT_HID_STRING,
|
|
||||||
info->hardware_id.value)) {
|
|
||||||
kfree(buffer.pointer);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (info->valid & ACPI_VALID_CID) {
|
|
||||||
for (i=0; i < info->compatibility_id.count; i++) {
|
|
||||||
if (!strcmp(PCI_ROOT_HID_STRING,
|
|
||||||
info->compatibility_id.id[i].value)) {
|
|
||||||
kfree(buffer.pointer);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kfree(buffer.pointer);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(acpi_root_bridge);
|
|
||||||
|
|
||||||
|
|
||||||
static int is_ejectable(acpi_handle handle)
|
static int is_ejectable(acpi_handle handle)
|
||||||
{
|
{
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
|
|
|
@ -678,18 +678,9 @@ static void remove_bridge(acpi_handle handle)
|
||||||
|
|
||||||
static struct pci_dev * get_apic_pci_info(acpi_handle handle)
|
static struct pci_dev * get_apic_pci_info(acpi_handle handle)
|
||||||
{
|
{
|
||||||
struct acpi_pci_id id;
|
|
||||||
struct pci_bus *bus;
|
|
||||||
struct pci_dev *dev;
|
struct pci_dev *dev;
|
||||||
|
|
||||||
if (ACPI_FAILURE(acpi_get_pci_id(handle, &id)))
|
dev = acpi_get_pci_dev(handle);
|
||||||
return NULL;
|
|
||||||
|
|
||||||
bus = pci_find_bus(id.segment, id.bus);
|
|
||||||
if (!bus)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
dev = pci_get_slot(bus, PCI_DEVFN(id.device, id.function));
|
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -1396,19 +1387,16 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus)
|
||||||
/* Program resources in newly inserted bridge */
|
/* Program resources in newly inserted bridge */
|
||||||
static int acpiphp_configure_bridge (acpi_handle handle)
|
static int acpiphp_configure_bridge (acpi_handle handle)
|
||||||
{
|
{
|
||||||
struct acpi_pci_id pci_id;
|
struct pci_dev *dev;
|
||||||
struct pci_bus *bus;
|
struct pci_bus *bus;
|
||||||
|
|
||||||
if (ACPI_FAILURE(acpi_get_pci_id(handle, &pci_id))) {
|
dev = acpi_get_pci_dev(handle);
|
||||||
|
if (!dev) {
|
||||||
err("cannot get PCI domain and bus number for bridge\n");
|
err("cannot get PCI domain and bus number for bridge\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
bus = pci_find_bus(pci_id.segment, pci_id.bus);
|
|
||||||
if (!bus) {
|
bus = dev->bus;
|
||||||
err("cannot find bus %d:%d\n",
|
|
||||||
pci_id.segment, pci_id.bus);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
pci_bus_size_bridges(bus);
|
pci_bus_size_bridges(bus);
|
||||||
pci_bus_assign_resources(bus);
|
pci_bus_assign_resources(bus);
|
||||||
|
@ -1416,6 +1404,7 @@ static int acpiphp_configure_bridge (acpi_handle handle)
|
||||||
acpiphp_set_hpp_values(handle, bus);
|
acpiphp_set_hpp_values(handle, bus);
|
||||||
pci_enable_bridges(bus);
|
pci_enable_bridges(bus);
|
||||||
acpiphp_configure_ioapics(handle);
|
acpiphp_configure_ioapics(handle);
|
||||||
|
pci_dev_put(dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1631,7 +1620,7 @@ find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
|
||||||
{
|
{
|
||||||
int *count = (int *)context;
|
int *count = (int *)context;
|
||||||
|
|
||||||
if (acpi_root_bridge(handle)) {
|
if (acpi_is_root_bridge(handle)) {
|
||||||
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
|
acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
|
||||||
handle_hotplug_event_bridge, NULL);
|
handle_hotplug_event_bridge, NULL);
|
||||||
(*count)++;
|
(*count)++;
|
||||||
|
|
|
@ -34,10 +34,27 @@ config ACER_WMI
|
||||||
If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
|
If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M
|
||||||
here.
|
here.
|
||||||
|
|
||||||
|
config ACERHDF
|
||||||
|
tristate "Acer Aspire One temperature and fan driver"
|
||||||
|
depends on THERMAL && THERMAL_HWMON && ACPI
|
||||||
|
---help---
|
||||||
|
This is a driver for Acer Aspire One netbooks. It allows to access
|
||||||
|
the temperature sensor and to control the fan.
|
||||||
|
|
||||||
|
After loading this driver the BIOS is still in control of the fan.
|
||||||
|
To let the kernel handle the fan, do:
|
||||||
|
echo -n enabled > /sys/class/thermal/thermal_zone0/mode
|
||||||
|
|
||||||
|
For more information about this driver see
|
||||||
|
<http://piie.net/files/acerhdf_README.txt>
|
||||||
|
|
||||||
|
If you have an Acer Aspire One netbook, say Y or M
|
||||||
|
here.
|
||||||
|
|
||||||
config ASUS_LAPTOP
|
config ASUS_LAPTOP
|
||||||
tristate "Asus Laptop Extras (EXPERIMENTAL)"
|
tristate "Asus Laptop Extras"
|
||||||
depends on ACPI
|
depends on ACPI
|
||||||
depends on EXPERIMENTAL && !ACPI_ASUS
|
depends on !ACPI_ASUS
|
||||||
select LEDS_CLASS
|
select LEDS_CLASS
|
||||||
select NEW_LEDS
|
select NEW_LEDS
|
||||||
select BACKLIGHT_CLASS_DEVICE
|
select BACKLIGHT_CLASS_DEVICE
|
||||||
|
@ -45,12 +62,12 @@ config ASUS_LAPTOP
|
||||||
---help---
|
---help---
|
||||||
This is the new Linux driver for Asus laptops. It may also support some
|
This is the new Linux driver for Asus laptops. It may also support some
|
||||||
MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate
|
MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate
|
||||||
standard ACPI events that go through /proc/acpi/events. It also adds
|
standard ACPI events and input events. It also adds
|
||||||
support for video output switching, LCD backlight control, Bluetooth and
|
support for video output switching, LCD backlight control, Bluetooth and
|
||||||
Wlan control, and most importantly, allows you to blink those fancy LEDs.
|
Wlan control, and most importantly, allows you to blink those fancy LEDs.
|
||||||
|
|
||||||
For more information and a userspace daemon for handling the extra
|
For more information and a userspace daemon for handling the extra
|
||||||
buttons see <http://acpi4asus.sf.net/>.
|
buttons see <http://acpi4asus.sf.net>.
|
||||||
|
|
||||||
If you have an ACPI-compatible ASUS laptop, say Y or M here.
|
If you have an ACPI-compatible ASUS laptop, say Y or M here.
|
||||||
|
|
||||||
|
@ -342,7 +359,10 @@ config EEEPC_LAPTOP
|
||||||
select HWMON
|
select HWMON
|
||||||
---help---
|
---help---
|
||||||
This driver supports the Fn-Fx keys on Eee PC laptops.
|
This driver supports the Fn-Fx keys on Eee PC laptops.
|
||||||
It also adds the ability to switch camera/wlan on/off.
|
|
||||||
|
It also gives access to some extra laptop functionalities like
|
||||||
|
Bluetooth, backlight and allows powering on/off some other
|
||||||
|
devices.
|
||||||
|
|
||||||
If you have an Eee PC laptop, say Y or M here.
|
If you have an Eee PC laptop, say Y or M here.
|
||||||
|
|
||||||
|
@ -369,7 +389,7 @@ config ACPI_WMI
|
||||||
any ACPI-WMI devices.
|
any ACPI-WMI devices.
|
||||||
|
|
||||||
config ACPI_ASUS
|
config ACPI_ASUS
|
||||||
tristate "ASUS/Medion Laptop Extras"
|
tristate "ASUS/Medion Laptop Extras (DEPRECATED)"
|
||||||
depends on ACPI
|
depends on ACPI
|
||||||
select BACKLIGHT_CLASS_DEVICE
|
select BACKLIGHT_CLASS_DEVICE
|
||||||
---help---
|
---help---
|
||||||
|
@ -390,7 +410,7 @@ config ACPI_ASUS
|
||||||
parameters.
|
parameters.
|
||||||
|
|
||||||
More information and a userspace daemon for handling the extra buttons
|
More information and a userspace daemon for handling the extra buttons
|
||||||
at <http://sourceforge.net/projects/acpi4asus/>.
|
at <http://acpi4asus.sf.net>.
|
||||||
|
|
||||||
If you have an ACPI-compatible ASUS laptop, say Y or M here. This
|
If you have an ACPI-compatible ASUS laptop, say Y or M here. This
|
||||||
driver is still under development, so if your laptop is unsupported or
|
driver is still under development, so if your laptop is unsupported or
|
||||||
|
|
|
@ -9,6 +9,7 @@ obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
|
||||||
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
|
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
|
||||||
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
|
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
|
||||||
obj-$(CONFIG_ACER_WMI) += acer-wmi.o
|
obj-$(CONFIG_ACER_WMI) += acer-wmi.o
|
||||||
|
obj-$(CONFIG_ACERHDF) += acerhdf.o
|
||||||
obj-$(CONFIG_HP_WMI) += hp-wmi.o
|
obj-$(CONFIG_HP_WMI) += hp-wmi.o
|
||||||
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
|
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
|
||||||
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
|
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
|
||||||
|
|
602
drivers/platform/x86/acerhdf.c
Normal file
602
drivers/platform/x86/acerhdf.c
Normal file
|
@ -0,0 +1,602 @@
|
||||||
|
/*
|
||||||
|
* acerhdf - A driver which monitors the temperature
|
||||||
|
* of the aspire one netbook, turns on/off the fan
|
||||||
|
* as soon as the upper/lower threshold is reached.
|
||||||
|
*
|
||||||
|
* (C) 2009 - Peter Feuerer peter (a) piie.net
|
||||||
|
* http://piie.net
|
||||||
|
* 2009 Borislav Petkov <petkovbb@gmail.com>
|
||||||
|
*
|
||||||
|
* Inspired by and many thanks to:
|
||||||
|
* o acerfand - Rachel Greenham
|
||||||
|
* o acer_ec.pl - Michael Kurz michi.kurz (at) googlemail.com
|
||||||
|
* - Petr Tomasek tomasek (#) etf,cuni,cz
|
||||||
|
* - Carlos Corbacho cathectic (at) gmail.com
|
||||||
|
* o lkml - Matthew Garrett
|
||||||
|
* - Borislav Petkov
|
||||||
|
* - Andreas Mohr
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "acerhdf: " fmt
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/dmi.h>
|
||||||
|
#include <acpi/acpi_drivers.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/thermal.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The driver is started with "kernel mode off" by default. That means, the BIOS
|
||||||
|
* is still in control of the fan. In this mode the driver allows to read the
|
||||||
|
* temperature of the cpu and a userspace tool may take over control of the fan.
|
||||||
|
* If the driver is switched to "kernel mode" (e.g. via module parameter) the
|
||||||
|
* driver is in full control of the fan. If you want the module to be started in
|
||||||
|
* kernel mode by default, define the following:
|
||||||
|
*/
|
||||||
|
#undef START_IN_KERNEL_MODE
|
||||||
|
|
||||||
|
#define DRV_VER "0.5.13"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* According to the Atom N270 datasheet,
|
||||||
|
* (http://download.intel.com/design/processor/datashts/320032.pdf) the
|
||||||
|
* CPU's optimal operating limits denoted in junction temperature as
|
||||||
|
* measured by the on-die thermal monitor are within 0 <= Tj <= 90. So,
|
||||||
|
* assume 89°C is critical temperature.
|
||||||
|
*/
|
||||||
|
#define ACERHDF_TEMP_CRIT 89
|
||||||
|
#define ACERHDF_FAN_OFF 0
|
||||||
|
#define ACERHDF_FAN_AUTO 1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* No matter what value the user puts into the fanon variable, turn on the fan
|
||||||
|
* at 80 degree Celsius to prevent hardware damage
|
||||||
|
*/
|
||||||
|
#define ACERHDF_MAX_FANON 80
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Maximum interval between two temperature checks is 15 seconds, as the die
|
||||||
|
* can get hot really fast under heavy load (plus we shouldn't forget about
|
||||||
|
* possible impact of _external_ aggressive sources such as heaters, sun etc.)
|
||||||
|
*/
|
||||||
|
#define ACERHDF_MAX_INTERVAL 15
|
||||||
|
|
||||||
|
#ifdef START_IN_KERNEL_MODE
|
||||||
|
static int kernelmode = 1;
|
||||||
|
#else
|
||||||
|
static int kernelmode;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static unsigned int interval = 10;
|
||||||
|
static unsigned int fanon = 63;
|
||||||
|
static unsigned int fanoff = 58;
|
||||||
|
static unsigned int verbose;
|
||||||
|
static unsigned int fanstate = ACERHDF_FAN_AUTO;
|
||||||
|
static char force_bios[16];
|
||||||
|
static unsigned int prev_interval;
|
||||||
|
struct thermal_zone_device *thz_dev;
|
||||||
|
struct thermal_cooling_device *cl_dev;
|
||||||
|
struct platform_device *acerhdf_dev;
|
||||||
|
|
||||||
|
module_param(kernelmode, uint, 0);
|
||||||
|
MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off");
|
||||||
|
module_param(interval, uint, 0600);
|
||||||
|
MODULE_PARM_DESC(interval, "Polling interval of temperature check");
|
||||||
|
module_param(fanon, uint, 0600);
|
||||||
|
MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature");
|
||||||
|
module_param(fanoff, uint, 0600);
|
||||||
|
MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature");
|
||||||
|
module_param(verbose, uint, 0600);
|
||||||
|
MODULE_PARM_DESC(verbose, "Enable verbose dmesg output");
|
||||||
|
module_param_string(force_bios, force_bios, 16, 0);
|
||||||
|
MODULE_PARM_DESC(force_bios, "Force BIOS version and omit BIOS check");
|
||||||
|
|
||||||
|
/* BIOS settings */
|
||||||
|
struct bios_settings_t {
|
||||||
|
const char *vendor;
|
||||||
|
const char *version;
|
||||||
|
unsigned char fanreg;
|
||||||
|
unsigned char tempreg;
|
||||||
|
unsigned char fancmd[2]; /* fan off and auto commands */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Register addresses and values for different BIOS versions */
|
||||||
|
static const struct bios_settings_t bios_tbl[] = {
|
||||||
|
{"Acer", "v0.3109", 0x55, 0x58, {0x1f, 0x00} },
|
||||||
|
{"Acer", "v0.3114", 0x55, 0x58, {0x1f, 0x00} },
|
||||||
|
{"Acer", "v0.3301", 0x55, 0x58, {0xaf, 0x00} },
|
||||||
|
{"Acer", "v0.3304", 0x55, 0x58, {0xaf, 0x00} },
|
||||||
|
{"Acer", "v0.3305", 0x55, 0x58, {0xaf, 0x00} },
|
||||||
|
{"Acer", "v0.3308", 0x55, 0x58, {0x21, 0x00} },
|
||||||
|
{"Acer", "v0.3309", 0x55, 0x58, {0x21, 0x00} },
|
||||||
|
{"Acer", "v0.3310", 0x55, 0x58, {0x21, 0x00} },
|
||||||
|
{"Gateway", "v0.3103", 0x55, 0x58, {0x21, 0x00} },
|
||||||
|
{"Packard Bell", "v0.3105", 0x55, 0x58, {0x21, 0x00} },
|
||||||
|
{"", "", 0, 0, {0, 0} }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct bios_settings_t *bios_cfg __read_mostly;
|
||||||
|
|
||||||
|
|
||||||
|
static int acerhdf_get_temp(int *temp)
|
||||||
|
{
|
||||||
|
u8 read_temp;
|
||||||
|
|
||||||
|
if (ec_read(bios_cfg->tempreg, &read_temp))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
*temp = read_temp;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acerhdf_get_fanstate(int *state)
|
||||||
|
{
|
||||||
|
u8 fan;
|
||||||
|
bool tmp;
|
||||||
|
|
||||||
|
if (ec_read(bios_cfg->fanreg, &fan))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
tmp = (fan == bios_cfg->fancmd[ACERHDF_FAN_OFF]);
|
||||||
|
*state = tmp ? ACERHDF_FAN_OFF : ACERHDF_FAN_AUTO;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void acerhdf_change_fanstate(int state)
|
||||||
|
{
|
||||||
|
unsigned char cmd;
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
pr_notice("fan %s\n", (state == ACERHDF_FAN_OFF) ?
|
||||||
|
"OFF" : "ON");
|
||||||
|
|
||||||
|
if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) {
|
||||||
|
pr_err("invalid fan state %d requested, setting to auto!\n",
|
||||||
|
state);
|
||||||
|
state = ACERHDF_FAN_AUTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd = bios_cfg->fancmd[state];
|
||||||
|
fanstate = state;
|
||||||
|
|
||||||
|
ec_write(bios_cfg->fanreg, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void acerhdf_check_param(struct thermal_zone_device *thermal)
|
||||||
|
{
|
||||||
|
if (fanon > ACERHDF_MAX_FANON) {
|
||||||
|
pr_err("fanon temperature too high, set to %d\n",
|
||||||
|
ACERHDF_MAX_FANON);
|
||||||
|
fanon = ACERHDF_MAX_FANON;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kernelmode && prev_interval != interval) {
|
||||||
|
if (interval > ACERHDF_MAX_INTERVAL) {
|
||||||
|
pr_err("interval too high, set to %d\n",
|
||||||
|
ACERHDF_MAX_INTERVAL);
|
||||||
|
interval = ACERHDF_MAX_INTERVAL;
|
||||||
|
}
|
||||||
|
if (verbose)
|
||||||
|
pr_notice("interval changed to: %d\n",
|
||||||
|
interval);
|
||||||
|
thermal->polling_delay = interval*1000;
|
||||||
|
prev_interval = interval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the thermal zone callback which does the delayed polling of the fan
|
||||||
|
* state. We do check /sysfs-originating settings here in acerhdf_check_param()
|
||||||
|
* as late as the polling interval is since we can't do that in the respective
|
||||||
|
* accessors of the module parameters.
|
||||||
|
*/
|
||||||
|
static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal,
|
||||||
|
unsigned long *t)
|
||||||
|
{
|
||||||
|
int temp, err = 0;
|
||||||
|
|
||||||
|
acerhdf_check_param(thermal);
|
||||||
|
|
||||||
|
err = acerhdf_get_temp(&temp);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
pr_notice("temp %d\n", temp);
|
||||||
|
|
||||||
|
*t = temp;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acerhdf_bind(struct thermal_zone_device *thermal,
|
||||||
|
struct thermal_cooling_device *cdev)
|
||||||
|
{
|
||||||
|
/* if the cooling device is the one from acerhdf bind it */
|
||||||
|
if (cdev != cl_dev)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
|
||||||
|
pr_err("error binding cooling dev\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acerhdf_unbind(struct thermal_zone_device *thermal,
|
||||||
|
struct thermal_cooling_device *cdev)
|
||||||
|
{
|
||||||
|
if (cdev != cl_dev)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) {
|
||||||
|
pr_err("error unbinding cooling dev\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void acerhdf_revert_to_bios_mode(void)
|
||||||
|
{
|
||||||
|
acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
|
||||||
|
kernelmode = 0;
|
||||||
|
if (thz_dev)
|
||||||
|
thz_dev->polling_delay = 0;
|
||||||
|
pr_notice("kernel mode fan control OFF\n");
|
||||||
|
}
|
||||||
|
static inline void acerhdf_enable_kernelmode(void)
|
||||||
|
{
|
||||||
|
kernelmode = 1;
|
||||||
|
|
||||||
|
thz_dev->polling_delay = interval*1000;
|
||||||
|
thermal_zone_device_update(thz_dev);
|
||||||
|
pr_notice("kernel mode fan control ON\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acerhdf_get_mode(struct thermal_zone_device *thermal,
|
||||||
|
enum thermal_device_mode *mode)
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
pr_notice("kernel mode fan control %d\n", kernelmode);
|
||||||
|
|
||||||
|
*mode = (kernelmode) ? THERMAL_DEVICE_ENABLED
|
||||||
|
: THERMAL_DEVICE_DISABLED;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set operation mode;
|
||||||
|
* enabled: the thermal layer of the kernel takes care about
|
||||||
|
* the temperature and the fan.
|
||||||
|
* disabled: the BIOS takes control of the fan.
|
||||||
|
*/
|
||||||
|
static int acerhdf_set_mode(struct thermal_zone_device *thermal,
|
||||||
|
enum thermal_device_mode mode)
|
||||||
|
{
|
||||||
|
if (mode == THERMAL_DEVICE_DISABLED && kernelmode)
|
||||||
|
acerhdf_revert_to_bios_mode();
|
||||||
|
else if (mode == THERMAL_DEVICE_ENABLED && !kernelmode)
|
||||||
|
acerhdf_enable_kernelmode();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip,
|
||||||
|
enum thermal_trip_type *type)
|
||||||
|
{
|
||||||
|
if (trip == 0)
|
||||||
|
*type = THERMAL_TRIP_ACTIVE;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip,
|
||||||
|
unsigned long *temp)
|
||||||
|
{
|
||||||
|
if (trip == 0)
|
||||||
|
*temp = fanon;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal,
|
||||||
|
unsigned long *temperature)
|
||||||
|
{
|
||||||
|
*temperature = ACERHDF_TEMP_CRIT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bind callback functions to thermalzone */
|
||||||
|
struct thermal_zone_device_ops acerhdf_dev_ops = {
|
||||||
|
.bind = acerhdf_bind,
|
||||||
|
.unbind = acerhdf_unbind,
|
||||||
|
.get_temp = acerhdf_get_ec_temp,
|
||||||
|
.get_mode = acerhdf_get_mode,
|
||||||
|
.set_mode = acerhdf_set_mode,
|
||||||
|
.get_trip_type = acerhdf_get_trip_type,
|
||||||
|
.get_trip_temp = acerhdf_get_trip_temp,
|
||||||
|
.get_crit_temp = acerhdf_get_crit_temp,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cooling device callback functions
|
||||||
|
* get maximal fan cooling state
|
||||||
|
*/
|
||||||
|
static int acerhdf_get_max_state(struct thermal_cooling_device *cdev,
|
||||||
|
unsigned long *state)
|
||||||
|
{
|
||||||
|
*state = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acerhdf_get_cur_state(struct thermal_cooling_device *cdev,
|
||||||
|
unsigned long *state)
|
||||||
|
{
|
||||||
|
int err = 0, tmp;
|
||||||
|
|
||||||
|
err = acerhdf_get_fanstate(&tmp);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
*state = (tmp == ACERHDF_FAN_AUTO) ? 1 : 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* change current fan state - is overwritten when running in kernel mode */
|
||||||
|
static int acerhdf_set_cur_state(struct thermal_cooling_device *cdev,
|
||||||
|
unsigned long state)
|
||||||
|
{
|
||||||
|
int cur_temp, cur_state, err = 0;
|
||||||
|
|
||||||
|
if (!kernelmode)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err = acerhdf_get_temp(&cur_temp);
|
||||||
|
if (err) {
|
||||||
|
pr_err("error reading temperature, hand off control to BIOS\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = acerhdf_get_fanstate(&cur_state);
|
||||||
|
if (err) {
|
||||||
|
pr_err("error reading fan state, hand off control to BIOS\n");
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == 0) {
|
||||||
|
/* turn fan off only if below fanoff temperature */
|
||||||
|
if ((cur_state == ACERHDF_FAN_AUTO) &&
|
||||||
|
(cur_temp < fanoff))
|
||||||
|
acerhdf_change_fanstate(ACERHDF_FAN_OFF);
|
||||||
|
} else {
|
||||||
|
if (cur_state == ACERHDF_FAN_OFF)
|
||||||
|
acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_out:
|
||||||
|
acerhdf_revert_to_bios_mode();
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bind fan callbacks to fan device */
|
||||||
|
struct thermal_cooling_device_ops acerhdf_cooling_ops = {
|
||||||
|
.get_max_state = acerhdf_get_max_state,
|
||||||
|
.get_cur_state = acerhdf_get_cur_state,
|
||||||
|
.set_cur_state = acerhdf_set_cur_state,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* suspend / resume functionality */
|
||||||
|
static int acerhdf_suspend(struct platform_device *dev, pm_message_t state)
|
||||||
|
{
|
||||||
|
if (kernelmode)
|
||||||
|
acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
pr_notice("going suspend\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acerhdf_resume(struct platform_device *device)
|
||||||
|
{
|
||||||
|
if (verbose)
|
||||||
|
pr_notice("resuming\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit acerhdf_probe(struct platform_device *device)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acerhdf_remove(struct platform_device *device)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct platform_driver acerhdf_drv = {
|
||||||
|
.driver = {
|
||||||
|
.name = "acerhdf",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = acerhdf_probe,
|
||||||
|
.remove = acerhdf_remove,
|
||||||
|
.suspend = acerhdf_suspend,
|
||||||
|
.resume = acerhdf_resume,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* check hardware */
|
||||||
|
static int acerhdf_check_hardware(void)
|
||||||
|
{
|
||||||
|
char const *vendor, *version, *product;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* get BIOS data */
|
||||||
|
vendor = dmi_get_system_info(DMI_SYS_VENDOR);
|
||||||
|
version = dmi_get_system_info(DMI_BIOS_VERSION);
|
||||||
|
product = dmi_get_system_info(DMI_PRODUCT_NAME);
|
||||||
|
|
||||||
|
pr_info("Acer Aspire One Fan driver, v.%s\n", DRV_VER);
|
||||||
|
|
||||||
|
if (!force_bios[0]) {
|
||||||
|
if (strncmp(product, "AO", 2)) {
|
||||||
|
pr_err("no Aspire One hardware found\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pr_info("forcing BIOS version: %s\n", version);
|
||||||
|
version = force_bios;
|
||||||
|
kernelmode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
pr_info("BIOS info: %s %s, product: %s\n",
|
||||||
|
vendor, version, product);
|
||||||
|
|
||||||
|
/* search BIOS version and vendor in BIOS settings table */
|
||||||
|
for (i = 0; bios_tbl[i].version[0]; i++) {
|
||||||
|
if (!strcmp(bios_tbl[i].vendor, vendor) &&
|
||||||
|
!strcmp(bios_tbl[i].version, version)) {
|
||||||
|
bios_cfg = &bios_tbl[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bios_cfg) {
|
||||||
|
pr_err("unknown (unsupported) BIOS version %s/%s, "
|
||||||
|
"please report, aborting!\n", vendor, version);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if started with kernel mode off, prevent the kernel from switching
|
||||||
|
* off the fan
|
||||||
|
*/
|
||||||
|
if (!kernelmode) {
|
||||||
|
pr_notice("Fan control off, to enable do:\n");
|
||||||
|
pr_notice("echo -n \"enabled\" > "
|
||||||
|
"/sys/class/thermal/thermal_zone0/mode\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acerhdf_register_platform(void)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
err = platform_driver_register(&acerhdf_drv);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
acerhdf_dev = platform_device_alloc("acerhdf", -1);
|
||||||
|
platform_device_add(acerhdf_dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void acerhdf_unregister_platform(void)
|
||||||
|
{
|
||||||
|
if (!acerhdf_dev)
|
||||||
|
return;
|
||||||
|
|
||||||
|
platform_device_del(acerhdf_dev);
|
||||||
|
platform_driver_unregister(&acerhdf_drv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int acerhdf_register_thermal(void)
|
||||||
|
{
|
||||||
|
cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL,
|
||||||
|
&acerhdf_cooling_ops);
|
||||||
|
|
||||||
|
if (IS_ERR(cl_dev))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
thz_dev = thermal_zone_device_register("acerhdf", 1, NULL,
|
||||||
|
&acerhdf_dev_ops, 0, 0, 0,
|
||||||
|
(kernelmode) ? interval*1000 : 0);
|
||||||
|
if (IS_ERR(thz_dev))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void acerhdf_unregister_thermal(void)
|
||||||
|
{
|
||||||
|
if (cl_dev) {
|
||||||
|
thermal_cooling_device_unregister(cl_dev);
|
||||||
|
cl_dev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thz_dev) {
|
||||||
|
thermal_zone_device_unregister(thz_dev);
|
||||||
|
thz_dev = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init acerhdf_init(void)
|
||||||
|
{
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
err = acerhdf_check_hardware();
|
||||||
|
if (err)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
|
err = acerhdf_register_platform();
|
||||||
|
if (err)
|
||||||
|
goto err_unreg;
|
||||||
|
|
||||||
|
err = acerhdf_register_thermal();
|
||||||
|
if (err)
|
||||||
|
goto err_unreg;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_unreg:
|
||||||
|
acerhdf_unregister_thermal();
|
||||||
|
acerhdf_unregister_platform();
|
||||||
|
|
||||||
|
out_err:
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit acerhdf_exit(void)
|
||||||
|
{
|
||||||
|
acerhdf_change_fanstate(ACERHDF_FAN_AUTO);
|
||||||
|
acerhdf_unregister_thermal();
|
||||||
|
acerhdf_unregister_platform();
|
||||||
|
}
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("Peter Feuerer");
|
||||||
|
MODULE_DESCRIPTION("Aspire One temperature and fan driver");
|
||||||
|
MODULE_ALIAS("dmi:*:*Acer*:*:");
|
||||||
|
MODULE_ALIAS("dmi:*:*Gateway*:*:");
|
||||||
|
MODULE_ALIAS("dmi:*:*Packard Bell*:*:");
|
||||||
|
|
||||||
|
module_init(acerhdf_init);
|
||||||
|
module_exit(acerhdf_exit);
|
|
@ -33,6 +33,8 @@
|
||||||
* Sam Lin - GPS support
|
* Sam Lin - GPS support
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
@ -53,9 +55,10 @@
|
||||||
#define ASUS_HOTK_NAME "Asus Laptop Support"
|
#define ASUS_HOTK_NAME "Asus Laptop Support"
|
||||||
#define ASUS_HOTK_CLASS "hotkey"
|
#define ASUS_HOTK_CLASS "hotkey"
|
||||||
#define ASUS_HOTK_DEVICE_NAME "Hotkey"
|
#define ASUS_HOTK_DEVICE_NAME "Hotkey"
|
||||||
#define ASUS_HOTK_FILE "asus-laptop"
|
#define ASUS_HOTK_FILE KBUILD_MODNAME
|
||||||
#define ASUS_HOTK_PREFIX "\\_SB.ATKD."
|
#define ASUS_HOTK_PREFIX "\\_SB.ATKD."
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Some events we use, same for all Asus
|
* Some events we use, same for all Asus
|
||||||
*/
|
*/
|
||||||
|
@ -207,13 +210,17 @@ MODULE_DEVICE_TABLE(acpi, asus_device_ids);
|
||||||
|
|
||||||
static int asus_hotk_add(struct acpi_device *device);
|
static int asus_hotk_add(struct acpi_device *device);
|
||||||
static int asus_hotk_remove(struct acpi_device *device, int type);
|
static int asus_hotk_remove(struct acpi_device *device, int type);
|
||||||
|
static void asus_hotk_notify(struct acpi_device *device, u32 event);
|
||||||
|
|
||||||
static struct acpi_driver asus_hotk_driver = {
|
static struct acpi_driver asus_hotk_driver = {
|
||||||
.name = ASUS_HOTK_NAME,
|
.name = ASUS_HOTK_NAME,
|
||||||
.class = ASUS_HOTK_CLASS,
|
.class = ASUS_HOTK_CLASS,
|
||||||
.ids = asus_device_ids,
|
.ids = asus_device_ids,
|
||||||
|
.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
|
||||||
.ops = {
|
.ops = {
|
||||||
.add = asus_hotk_add,
|
.add = asus_hotk_add,
|
||||||
.remove = asus_hotk_remove,
|
.remove = asus_hotk_remove,
|
||||||
|
.notify = asus_hotk_notify,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -323,7 +330,7 @@ static int read_wireless_status(int mask)
|
||||||
|
|
||||||
rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status);
|
rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status);
|
||||||
if (ACPI_FAILURE(rv))
|
if (ACPI_FAILURE(rv))
|
||||||
printk(ASUS_WARNING "Error reading Wireless status\n");
|
pr_warning("Error reading Wireless status\n");
|
||||||
else
|
else
|
||||||
return (status & mask) ? 1 : 0;
|
return (status & mask) ? 1 : 0;
|
||||||
|
|
||||||
|
@ -337,7 +344,7 @@ static int read_gps_status(void)
|
||||||
|
|
||||||
rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status);
|
rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status);
|
||||||
if (ACPI_FAILURE(rv))
|
if (ACPI_FAILURE(rv))
|
||||||
printk(ASUS_WARNING "Error reading GPS status\n");
|
pr_warning("Error reading GPS status\n");
|
||||||
else
|
else
|
||||||
return status ? 1 : 0;
|
return status ? 1 : 0;
|
||||||
|
|
||||||
|
@ -377,7 +384,7 @@ static void write_status(acpi_handle handle, int out, int mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (write_acpi_int(handle, NULL, out, NULL))
|
if (write_acpi_int(handle, NULL, out, NULL))
|
||||||
printk(ASUS_WARNING " write failed %x\n", mask);
|
pr_warning(" write failed %x\n", mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* /sys/class/led handlers */
|
/* /sys/class/led handlers */
|
||||||
|
@ -420,7 +427,7 @@ static int set_lcd_state(int value)
|
||||||
NULL, NULL, NULL);
|
NULL, NULL, NULL);
|
||||||
|
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
printk(ASUS_WARNING "Error switching LCD\n");
|
pr_warning("Error switching LCD\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
write_status(NULL, lcd, LCD_ON);
|
write_status(NULL, lcd, LCD_ON);
|
||||||
|
@ -444,7 +451,7 @@ static int read_brightness(struct backlight_device *bd)
|
||||||
|
|
||||||
rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value);
|
rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value);
|
||||||
if (ACPI_FAILURE(rv))
|
if (ACPI_FAILURE(rv))
|
||||||
printk(ASUS_WARNING "Error reading brightness\n");
|
pr_warning("Error reading brightness\n");
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -457,7 +464,7 @@ static int set_brightness(struct backlight_device *bd, int value)
|
||||||
/* 0 <= value <= 15 */
|
/* 0 <= value <= 15 */
|
||||||
|
|
||||||
if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) {
|
if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) {
|
||||||
printk(ASUS_WARNING "Error changing brightness\n");
|
pr_warning("Error changing brightness\n");
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,7 +594,7 @@ static ssize_t store_ledd(struct device *dev, struct device_attribute *attr,
|
||||||
rv = parse_arg(buf, count, &value);
|
rv = parse_arg(buf, count, &value);
|
||||||
if (rv > 0) {
|
if (rv > 0) {
|
||||||
if (write_acpi_int(ledd_set_handle, NULL, value, NULL))
|
if (write_acpi_int(ledd_set_handle, NULL, value, NULL))
|
||||||
printk(ASUS_WARNING "LED display write failed\n");
|
pr_warning("LED display write failed\n");
|
||||||
else
|
else
|
||||||
hotk->ledd_status = (u32) value;
|
hotk->ledd_status = (u32) value;
|
||||||
}
|
}
|
||||||
|
@ -632,7 +639,7 @@ static void set_display(int value)
|
||||||
{
|
{
|
||||||
/* no sanity check needed for now */
|
/* no sanity check needed for now */
|
||||||
if (write_acpi_int(display_set_handle, NULL, value, NULL))
|
if (write_acpi_int(display_set_handle, NULL, value, NULL))
|
||||||
printk(ASUS_WARNING "Error setting display\n");
|
pr_warning("Error setting display\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,7 +654,7 @@ static int read_display(void)
|
||||||
rv = acpi_evaluate_integer(display_get_handle, NULL,
|
rv = acpi_evaluate_integer(display_get_handle, NULL,
|
||||||
NULL, &value);
|
NULL, &value);
|
||||||
if (ACPI_FAILURE(rv))
|
if (ACPI_FAILURE(rv))
|
||||||
printk(ASUS_WARNING "Error reading display status\n");
|
pr_warning("Error reading display status\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
value &= 0x0F; /* needed for some models, shouldn't hurt others */
|
value &= 0x0F; /* needed for some models, shouldn't hurt others */
|
||||||
|
@ -689,7 +696,7 @@ static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
|
||||||
static void set_light_sens_switch(int value)
|
static void set_light_sens_switch(int value)
|
||||||
{
|
{
|
||||||
if (write_acpi_int(ls_switch_handle, NULL, value, NULL))
|
if (write_acpi_int(ls_switch_handle, NULL, value, NULL))
|
||||||
printk(ASUS_WARNING "Error setting light sensor switch\n");
|
pr_warning("Error setting light sensor switch\n");
|
||||||
hotk->light_switch = value;
|
hotk->light_switch = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -714,7 +721,7 @@ static ssize_t store_lssw(struct device *dev, struct device_attribute *attr,
|
||||||
static void set_light_sens_level(int value)
|
static void set_light_sens_level(int value)
|
||||||
{
|
{
|
||||||
if (write_acpi_int(ls_level_handle, NULL, value, NULL))
|
if (write_acpi_int(ls_level_handle, NULL, value, NULL))
|
||||||
printk(ASUS_WARNING "Error setting light sensor level\n");
|
pr_warning("Error setting light sensor level\n");
|
||||||
hotk->light_level = value;
|
hotk->light_level = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -812,7 +819,7 @@ static int asus_setkeycode(struct input_dev *dev, int scancode, int keycode)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
|
static void asus_hotk_notify(struct acpi_device *device, u32 event)
|
||||||
{
|
{
|
||||||
static struct key_entry *key;
|
static struct key_entry *key;
|
||||||
u16 count;
|
u16 count;
|
||||||
|
@ -975,11 +982,11 @@ static int asus_hotk_get_info(void)
|
||||||
*/
|
*/
|
||||||
status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
|
status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
printk(ASUS_WARNING "Couldn't get the DSDT table header\n");
|
pr_warning("Couldn't get the DSDT table header\n");
|
||||||
|
|
||||||
/* We have to write 0 on init this far for all ASUS models */
|
/* We have to write 0 on init this far for all ASUS models */
|
||||||
if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
|
if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
|
||||||
printk(ASUS_ERR "Hotkey initialization failed\n");
|
pr_err("Hotkey initialization failed\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -987,9 +994,9 @@ static int asus_hotk_get_info(void)
|
||||||
status =
|
status =
|
||||||
acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result);
|
acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result);
|
||||||
if (ACPI_FAILURE(status))
|
if (ACPI_FAILURE(status))
|
||||||
printk(ASUS_WARNING "Error calling BSTS\n");
|
pr_warning("Error calling BSTS\n");
|
||||||
else if (bsts_result)
|
else if (bsts_result)
|
||||||
printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n",
|
pr_notice("BSTS called, 0x%02x returned\n",
|
||||||
(uint) bsts_result);
|
(uint) bsts_result);
|
||||||
|
|
||||||
/* This too ... */
|
/* This too ... */
|
||||||
|
@ -1020,7 +1027,7 @@ static int asus_hotk_get_info(void)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (*string)
|
if (*string)
|
||||||
printk(ASUS_NOTICE " %s model detected\n", string);
|
pr_notice(" %s model detected\n", string);
|
||||||
|
|
||||||
ASUS_HANDLE_INIT(mled_set);
|
ASUS_HANDLE_INIT(mled_set);
|
||||||
ASUS_HANDLE_INIT(tled_set);
|
ASUS_HANDLE_INIT(tled_set);
|
||||||
|
@ -1077,7 +1084,7 @@ static int asus_input_init(void)
|
||||||
|
|
||||||
hotk->inputdev = input_allocate_device();
|
hotk->inputdev = input_allocate_device();
|
||||||
if (!hotk->inputdev) {
|
if (!hotk->inputdev) {
|
||||||
printk(ASUS_INFO "Unable to allocate input device\n");
|
pr_info("Unable to allocate input device\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
hotk->inputdev->name = "Asus Laptop extra buttons";
|
hotk->inputdev->name = "Asus Laptop extra buttons";
|
||||||
|
@ -1096,7 +1103,7 @@ static int asus_input_init(void)
|
||||||
}
|
}
|
||||||
result = input_register_device(hotk->inputdev);
|
result = input_register_device(hotk->inputdev);
|
||||||
if (result) {
|
if (result) {
|
||||||
printk(ASUS_INFO "Unable to register input device\n");
|
pr_info("Unable to register input device\n");
|
||||||
input_free_device(hotk->inputdev);
|
input_free_device(hotk->inputdev);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -1113,7 +1120,7 @@ static int asus_hotk_check(void)
|
||||||
if (hotk->device->status.present) {
|
if (hotk->device->status.present) {
|
||||||
result = asus_hotk_get_info();
|
result = asus_hotk_get_info();
|
||||||
} else {
|
} else {
|
||||||
printk(ASUS_ERR "Hotkey device not present, aborting\n");
|
pr_err("Hotkey device not present, aborting\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1124,13 +1131,12 @@ static int asus_hotk_found;
|
||||||
|
|
||||||
static int asus_hotk_add(struct acpi_device *device)
|
static int asus_hotk_add(struct acpi_device *device)
|
||||||
{
|
{
|
||||||
acpi_status status = AE_OK;
|
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
if (!device)
|
if (!device)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
printk(ASUS_NOTICE "Asus Laptop Support version %s\n",
|
pr_notice("Asus Laptop Support version %s\n",
|
||||||
ASUS_LAPTOP_VERSION);
|
ASUS_LAPTOP_VERSION);
|
||||||
|
|
||||||
hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
|
hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
|
||||||
|
@ -1149,15 +1155,6 @@ static int asus_hotk_add(struct acpi_device *device)
|
||||||
|
|
||||||
asus_hotk_add_fs();
|
asus_hotk_add_fs();
|
||||||
|
|
||||||
/*
|
|
||||||
* We install the handler, it will receive the hotk in parameter, so, we
|
|
||||||
* could add other data to the hotk struct
|
|
||||||
*/
|
|
||||||
status = acpi_install_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
|
|
||||||
asus_hotk_notify, hotk);
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
printk(ASUS_ERR "Error installing notify handler\n");
|
|
||||||
|
|
||||||
asus_hotk_found = 1;
|
asus_hotk_found = 1;
|
||||||
|
|
||||||
/* WLED and BLED are on by default */
|
/* WLED and BLED are on by default */
|
||||||
|
@ -1198,16 +1195,9 @@ end:
|
||||||
|
|
||||||
static int asus_hotk_remove(struct acpi_device *device, int type)
|
static int asus_hotk_remove(struct acpi_device *device, int type)
|
||||||
{
|
{
|
||||||
acpi_status status = 0;
|
|
||||||
|
|
||||||
if (!device || !acpi_driver_data(device))
|
if (!device || !acpi_driver_data(device))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
status = acpi_remove_notify_handler(hotk->handle, ACPI_ALL_NOTIFY,
|
|
||||||
asus_hotk_notify);
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
printk(ASUS_ERR "Error removing notify handler\n");
|
|
||||||
|
|
||||||
kfree(hotk->name);
|
kfree(hotk->name);
|
||||||
kfree(hotk);
|
kfree(hotk);
|
||||||
|
|
||||||
|
@ -1260,8 +1250,7 @@ static int asus_backlight_init(struct device *dev)
|
||||||
bd = backlight_device_register(ASUS_HOTK_FILE, dev,
|
bd = backlight_device_register(ASUS_HOTK_FILE, dev,
|
||||||
NULL, &asusbl_ops);
|
NULL, &asusbl_ops);
|
||||||
if (IS_ERR(bd)) {
|
if (IS_ERR(bd)) {
|
||||||
printk(ASUS_ERR
|
pr_err("Could not register asus backlight device\n");
|
||||||
"Could not register asus backlight device\n");
|
|
||||||
asus_backlight_device = NULL;
|
asus_backlight_device = NULL;
|
||||||
return PTR_ERR(bd);
|
return PTR_ERR(bd);
|
||||||
}
|
}
|
||||||
|
@ -1334,7 +1323,6 @@ out:
|
||||||
|
|
||||||
static int __init asus_laptop_init(void)
|
static int __init asus_laptop_init(void)
|
||||||
{
|
{
|
||||||
struct device *dev;
|
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
if (acpi_disabled)
|
if (acpi_disabled)
|
||||||
|
@ -1356,24 +1344,10 @@ static int __init asus_laptop_init(void)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev = acpi_get_physical_device(hotk->device->handle);
|
|
||||||
|
|
||||||
if (!acpi_video_backlight_support()) {
|
|
||||||
result = asus_backlight_init(dev);
|
|
||||||
if (result)
|
|
||||||
goto fail_backlight;
|
|
||||||
} else
|
|
||||||
printk(ASUS_INFO "Brightness ignored, must be controlled by "
|
|
||||||
"ACPI video driver\n");
|
|
||||||
|
|
||||||
result = asus_input_init();
|
result = asus_input_init();
|
||||||
if (result)
|
if (result)
|
||||||
goto fail_input;
|
goto fail_input;
|
||||||
|
|
||||||
result = asus_led_init(dev);
|
|
||||||
if (result)
|
|
||||||
goto fail_led;
|
|
||||||
|
|
||||||
/* Register platform stuff */
|
/* Register platform stuff */
|
||||||
result = platform_driver_register(&asuspf_driver);
|
result = platform_driver_register(&asuspf_driver);
|
||||||
if (result)
|
if (result)
|
||||||
|
@ -1394,8 +1368,27 @@ static int __init asus_laptop_init(void)
|
||||||
if (result)
|
if (result)
|
||||||
goto fail_sysfs;
|
goto fail_sysfs;
|
||||||
|
|
||||||
|
result = asus_led_init(&asuspf_device->dev);
|
||||||
|
if (result)
|
||||||
|
goto fail_led;
|
||||||
|
|
||||||
|
if (!acpi_video_backlight_support()) {
|
||||||
|
result = asus_backlight_init(&asuspf_device->dev);
|
||||||
|
if (result)
|
||||||
|
goto fail_backlight;
|
||||||
|
} else
|
||||||
|
pr_info("Brightness ignored, must be controlled by "
|
||||||
|
"ACPI video driver\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
fail_backlight:
|
||||||
|
asus_led_exit();
|
||||||
|
|
||||||
|
fail_led:
|
||||||
|
sysfs_remove_group(&asuspf_device->dev.kobj,
|
||||||
|
&asuspf_attribute_group);
|
||||||
|
|
||||||
fail_sysfs:
|
fail_sysfs:
|
||||||
platform_device_del(asuspf_device);
|
platform_device_del(asuspf_device);
|
||||||
|
|
||||||
|
@ -1406,15 +1399,9 @@ fail_platform_device1:
|
||||||
platform_driver_unregister(&asuspf_driver);
|
platform_driver_unregister(&asuspf_driver);
|
||||||
|
|
||||||
fail_platform_driver:
|
fail_platform_driver:
|
||||||
asus_led_exit();
|
|
||||||
|
|
||||||
fail_led:
|
|
||||||
asus_input_exit();
|
asus_input_exit();
|
||||||
|
|
||||||
fail_input:
|
fail_input:
|
||||||
asus_backlight_exit();
|
|
||||||
|
|
||||||
fail_backlight:
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -455,6 +455,8 @@ static struct asus_hotk *hotk;
|
||||||
*/
|
*/
|
||||||
static int asus_hotk_add(struct acpi_device *device);
|
static int asus_hotk_add(struct acpi_device *device);
|
||||||
static int asus_hotk_remove(struct acpi_device *device, int type);
|
static int asus_hotk_remove(struct acpi_device *device, int type);
|
||||||
|
static void asus_hotk_notify(struct acpi_device *device, u32 event);
|
||||||
|
|
||||||
static const struct acpi_device_id asus_device_ids[] = {
|
static const struct acpi_device_id asus_device_ids[] = {
|
||||||
{"ATK0100", 0},
|
{"ATK0100", 0},
|
||||||
{"", 0},
|
{"", 0},
|
||||||
|
@ -465,9 +467,11 @@ static struct acpi_driver asus_hotk_driver = {
|
||||||
.name = "asus_acpi",
|
.name = "asus_acpi",
|
||||||
.class = ACPI_HOTK_CLASS,
|
.class = ACPI_HOTK_CLASS,
|
||||||
.ids = asus_device_ids,
|
.ids = asus_device_ids,
|
||||||
|
.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
|
||||||
.ops = {
|
.ops = {
|
||||||
.add = asus_hotk_add,
|
.add = asus_hotk_add,
|
||||||
.remove = asus_hotk_remove,
|
.remove = asus_hotk_remove,
|
||||||
|
.notify = asus_hotk_notify,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1101,12 +1105,20 @@ static int asus_hotk_remove_fs(struct acpi_device *device)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void asus_hotk_notify(acpi_handle handle, u32 event, void *data)
|
static void asus_hotk_notify(struct acpi_device *device, u32 event)
|
||||||
{
|
{
|
||||||
/* TODO Find a better way to handle events count. */
|
/* TODO Find a better way to handle events count. */
|
||||||
if (!hotk)
|
if (!hotk)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The BIOS *should* be sending us device events, but apparently
|
||||||
|
* Asus uses system events instead, so just ignore any device
|
||||||
|
* events we get.
|
||||||
|
*/
|
||||||
|
if (event > ACPI_MAX_SYS_NOTIFY)
|
||||||
|
return;
|
||||||
|
|
||||||
if ((event & ~((u32) BR_UP)) < 16)
|
if ((event & ~((u32) BR_UP)) < 16)
|
||||||
hotk->brightness = (event & ~((u32) BR_UP));
|
hotk->brightness = (event & ~((u32) BR_UP));
|
||||||
else if ((event & ~((u32) BR_DOWN)) < 16)
|
else if ((event & ~((u32) BR_DOWN)) < 16)
|
||||||
|
@ -1346,15 +1358,6 @@ static int asus_hotk_add(struct acpi_device *device)
|
||||||
if (result)
|
if (result)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
/*
|
|
||||||
* We install the handler, it will receive the hotk in parameter, so, we
|
|
||||||
* could add other data to the hotk struct
|
|
||||||
*/
|
|
||||||
status = acpi_install_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
|
|
||||||
asus_hotk_notify, hotk);
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
printk(KERN_ERR " Error installing notify handler\n");
|
|
||||||
|
|
||||||
/* For laptops without GPLV: init the hotk->brightness value */
|
/* For laptops without GPLV: init the hotk->brightness value */
|
||||||
if ((!hotk->methods->brightness_get)
|
if ((!hotk->methods->brightness_get)
|
||||||
&& (!hotk->methods->brightness_status)
|
&& (!hotk->methods->brightness_status)
|
||||||
|
@ -1389,16 +1392,9 @@ end:
|
||||||
|
|
||||||
static int asus_hotk_remove(struct acpi_device *device, int type)
|
static int asus_hotk_remove(struct acpi_device *device, int type)
|
||||||
{
|
{
|
||||||
acpi_status status = 0;
|
|
||||||
|
|
||||||
if (!device || !acpi_driver_data(device))
|
if (!device || !acpi_driver_data(device))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
status = acpi_remove_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY,
|
|
||||||
asus_hotk_notify);
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
printk(KERN_ERR "Asus ACPI: Error removing notify handler\n");
|
|
||||||
|
|
||||||
asus_hotk_remove_fs(device);
|
asus_hotk_remove_fs(device);
|
||||||
|
|
||||||
kfree(hotk);
|
kfree(hotk);
|
||||||
|
|
|
@ -46,10 +46,53 @@ struct key_entry {
|
||||||
u16 keycode;
|
u16 keycode;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum { KE_KEY, KE_SW, KE_END };
|
enum { KE_KEY, KE_SW, KE_IGNORE, KE_END };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Certain keys are flagged as KE_IGNORE. All of these are either
|
||||||
|
* notifications (rather than requests for change) or are also sent
|
||||||
|
* via the keyboard controller so should not be sent again.
|
||||||
|
*/
|
||||||
|
|
||||||
static struct key_entry dell_wmi_keymap[] = {
|
static struct key_entry dell_wmi_keymap[] = {
|
||||||
{KE_KEY, 0xe045, KEY_PROG1},
|
{KE_KEY, 0xe045, KEY_PROG1},
|
||||||
|
{KE_KEY, 0xe009, KEY_EJECTCD},
|
||||||
|
|
||||||
|
/* These also contain the brightness level at offset 6 */
|
||||||
|
{KE_KEY, 0xe006, KEY_BRIGHTNESSUP},
|
||||||
|
{KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN},
|
||||||
|
|
||||||
|
/* Battery health status button */
|
||||||
|
{KE_KEY, 0xe007, KEY_BATTERY},
|
||||||
|
|
||||||
|
/* This is actually for all radios. Although physically a
|
||||||
|
* switch, the notification does not provide an indication of
|
||||||
|
* state and so it should be reported as a key */
|
||||||
|
{KE_KEY, 0xe008, KEY_WLAN},
|
||||||
|
|
||||||
|
/* The next device is at offset 6, the active devices are at
|
||||||
|
offset 8 and the attached devices at offset 10 */
|
||||||
|
{KE_KEY, 0xe00b, KEY_DISPLAYTOGGLE},
|
||||||
|
|
||||||
|
{KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE},
|
||||||
|
|
||||||
|
/* BIOS error detected */
|
||||||
|
{KE_IGNORE, 0xe00d, KEY_RESERVED},
|
||||||
|
|
||||||
|
/* Wifi Catcher */
|
||||||
|
{KE_KEY, 0xe011, KEY_PROG2},
|
||||||
|
|
||||||
|
/* Ambient light sensor toggle */
|
||||||
|
{KE_IGNORE, 0xe013, KEY_RESERVED},
|
||||||
|
|
||||||
|
{KE_IGNORE, 0xe020, KEY_MUTE},
|
||||||
|
{KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN},
|
||||||
|
{KE_IGNORE, 0xe030, KEY_VOLUMEUP},
|
||||||
|
{KE_IGNORE, 0xe033, KEY_KBDILLUMUP},
|
||||||
|
{KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN},
|
||||||
|
{KE_IGNORE, 0xe03a, KEY_CAPSLOCK},
|
||||||
|
{KE_IGNORE, 0xe045, KEY_NUMLOCK},
|
||||||
|
{KE_IGNORE, 0xe046, KEY_SCROLLLOCK},
|
||||||
{KE_END, 0}
|
{KE_END, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -122,15 +165,20 @@ static void dell_wmi_notify(u32 value, void *context)
|
||||||
|
|
||||||
if (obj && obj->type == ACPI_TYPE_BUFFER) {
|
if (obj && obj->type == ACPI_TYPE_BUFFER) {
|
||||||
int *buffer = (int *)obj->buffer.pointer;
|
int *buffer = (int *)obj->buffer.pointer;
|
||||||
key = dell_wmi_get_entry_by_scancode(buffer[1]);
|
/*
|
||||||
|
* The upper bytes of the event may contain
|
||||||
|
* additional information, so mask them off for the
|
||||||
|
* scancode lookup
|
||||||
|
*/
|
||||||
|
key = dell_wmi_get_entry_by_scancode(buffer[1] & 0xFFFF);
|
||||||
if (key) {
|
if (key) {
|
||||||
input_report_key(dell_wmi_input_dev, key->keycode, 1);
|
input_report_key(dell_wmi_input_dev, key->keycode, 1);
|
||||||
input_sync(dell_wmi_input_dev);
|
input_sync(dell_wmi_input_dev);
|
||||||
input_report_key(dell_wmi_input_dev, key->keycode, 0);
|
input_report_key(dell_wmi_input_dev, key->keycode, 0);
|
||||||
input_sync(dell_wmi_input_dev);
|
input_sync(dell_wmi_input_dev);
|
||||||
} else
|
} else if (buffer[1] & 0xFFFF)
|
||||||
printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n",
|
printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n",
|
||||||
buffer[1]);
|
buffer[1] & 0xFFFF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,10 @@ enum {
|
||||||
DISABLE_ASL_GPS = 0x0020,
|
DISABLE_ASL_GPS = 0x0020,
|
||||||
DISABLE_ASL_DISPLAYSWITCH = 0x0040,
|
DISABLE_ASL_DISPLAYSWITCH = 0x0040,
|
||||||
DISABLE_ASL_MODEM = 0x0080,
|
DISABLE_ASL_MODEM = 0x0080,
|
||||||
DISABLE_ASL_CARDREADER = 0x0100
|
DISABLE_ASL_CARDREADER = 0x0100,
|
||||||
|
DISABLE_ASL_3G = 0x0200,
|
||||||
|
DISABLE_ASL_WIMAX = 0x0400,
|
||||||
|
DISABLE_ASL_HWCF = 0x0800
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -87,7 +90,13 @@ enum {
|
||||||
CM_ASL_USBPORT3,
|
CM_ASL_USBPORT3,
|
||||||
CM_ASL_MODEM,
|
CM_ASL_MODEM,
|
||||||
CM_ASL_CARDREADER,
|
CM_ASL_CARDREADER,
|
||||||
CM_ASL_LID
|
CM_ASL_3G,
|
||||||
|
CM_ASL_WIMAX,
|
||||||
|
CM_ASL_HWCF,
|
||||||
|
CM_ASL_LID,
|
||||||
|
CM_ASL_TYPE,
|
||||||
|
CM_ASL_PANELPOWER, /*P901*/
|
||||||
|
CM_ASL_TPD
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *cm_getv[] = {
|
static const char *cm_getv[] = {
|
||||||
|
@ -96,7 +105,8 @@ static const char *cm_getv[] = {
|
||||||
NULL, "PBLG", NULL, NULL,
|
NULL, "PBLG", NULL, NULL,
|
||||||
"CFVG", NULL, NULL, NULL,
|
"CFVG", NULL, NULL, NULL,
|
||||||
"USBG", NULL, NULL, "MODG",
|
"USBG", NULL, NULL, "MODG",
|
||||||
"CRDG", "LIDG"
|
"CRDG", "M3GG", "WIMG", "HWCF",
|
||||||
|
"LIDG", "TYPE", "PBPG", "TPDG"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *cm_setv[] = {
|
static const char *cm_setv[] = {
|
||||||
|
@ -105,7 +115,8 @@ static const char *cm_setv[] = {
|
||||||
"SDSP", "PBLS", "HDPS", NULL,
|
"SDSP", "PBLS", "HDPS", NULL,
|
||||||
"CFVS", NULL, NULL, NULL,
|
"CFVS", NULL, NULL, NULL,
|
||||||
"USBG", NULL, NULL, "MODS",
|
"USBG", NULL, NULL, "MODS",
|
||||||
"CRDS", NULL
|
"CRDS", "M3GS", "WIMS", NULL,
|
||||||
|
NULL, NULL, "PBPS", "TPDS"
|
||||||
};
|
};
|
||||||
|
|
||||||
#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
|
#define EEEPC_EC "\\_SB.PCI0.SBRG.EC0."
|
||||||
|
@ -181,6 +192,7 @@ static struct key_entry eeepc_keymap[] = {
|
||||||
static int eeepc_hotk_add(struct acpi_device *device);
|
static int eeepc_hotk_add(struct acpi_device *device);
|
||||||
static int eeepc_hotk_remove(struct acpi_device *device, int type);
|
static int eeepc_hotk_remove(struct acpi_device *device, int type);
|
||||||
static int eeepc_hotk_resume(struct acpi_device *device);
|
static int eeepc_hotk_resume(struct acpi_device *device);
|
||||||
|
static void eeepc_hotk_notify(struct acpi_device *device, u32 event);
|
||||||
|
|
||||||
static const struct acpi_device_id eeepc_device_ids[] = {
|
static const struct acpi_device_id eeepc_device_ids[] = {
|
||||||
{EEEPC_HOTK_HID, 0},
|
{EEEPC_HOTK_HID, 0},
|
||||||
|
@ -192,10 +204,12 @@ static struct acpi_driver eeepc_hotk_driver = {
|
||||||
.name = EEEPC_HOTK_NAME,
|
.name = EEEPC_HOTK_NAME,
|
||||||
.class = EEEPC_HOTK_CLASS,
|
.class = EEEPC_HOTK_CLASS,
|
||||||
.ids = eeepc_device_ids,
|
.ids = eeepc_device_ids,
|
||||||
|
.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
|
||||||
.ops = {
|
.ops = {
|
||||||
.add = eeepc_hotk_add,
|
.add = eeepc_hotk_add,
|
||||||
.remove = eeepc_hotk_remove,
|
.remove = eeepc_hotk_remove,
|
||||||
.resume = eeepc_hotk_resume,
|
.resume = eeepc_hotk_resume,
|
||||||
|
.notify = eeepc_hotk_notify,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -318,6 +332,15 @@ static const struct rfkill_ops eeepc_rfkill_ops = {
|
||||||
.set_block = eeepc_rfkill_set,
|
.set_block = eeepc_rfkill_set,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void __init eeepc_enable_camera(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If the following call to set_acpi() fails, it's because there's no
|
||||||
|
* camera so we can ignore the error.
|
||||||
|
*/
|
||||||
|
set_acpi(CM_ASL_CAMERA, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sys helpers
|
* Sys helpers
|
||||||
*/
|
*/
|
||||||
|
@ -369,13 +392,88 @@ static ssize_t show_sys_acpi(int cm, char *buf)
|
||||||
EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
|
EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA);
|
||||||
EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
|
EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER);
|
||||||
EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
|
EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH);
|
||||||
EEEPC_CREATE_DEVICE_ATTR(cpufv, CM_ASL_CPUFV);
|
|
||||||
|
struct eeepc_cpufv {
|
||||||
|
int num;
|
||||||
|
int cur;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int get_cpufv(struct eeepc_cpufv *c)
|
||||||
|
{
|
||||||
|
c->cur = get_acpi(CM_ASL_CPUFV);
|
||||||
|
c->num = (c->cur >> 8) & 0xff;
|
||||||
|
c->cur &= 0xff;
|
||||||
|
if (c->cur < 0 || c->num <= 0 || c->num > 12)
|
||||||
|
return -ENODEV;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t show_available_cpufv(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct eeepc_cpufv c;
|
||||||
|
int i;
|
||||||
|
ssize_t len = 0;
|
||||||
|
|
||||||
|
if (get_cpufv(&c))
|
||||||
|
return -ENODEV;
|
||||||
|
for (i = 0; i < c.num; i++)
|
||||||
|
len += sprintf(buf + len, "%d ", i);
|
||||||
|
len += sprintf(buf + len, "\n");
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t show_cpufv(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct eeepc_cpufv c;
|
||||||
|
|
||||||
|
if (get_cpufv(&c))
|
||||||
|
return -ENODEV;
|
||||||
|
return sprintf(buf, "%#x\n", (c.num << 8) | c.cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t store_cpufv(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct eeepc_cpufv c;
|
||||||
|
int rv, value;
|
||||||
|
|
||||||
|
if (get_cpufv(&c))
|
||||||
|
return -ENODEV;
|
||||||
|
rv = parse_arg(buf, count, &value);
|
||||||
|
if (rv < 0)
|
||||||
|
return rv;
|
||||||
|
if (!rv || value < 0 || value >= c.num)
|
||||||
|
return -EINVAL;
|
||||||
|
set_acpi(CM_ASL_CPUFV, value);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct device_attribute dev_attr_cpufv = {
|
||||||
|
.attr = {
|
||||||
|
.name = "cpufv",
|
||||||
|
.mode = 0644 },
|
||||||
|
.show = show_cpufv,
|
||||||
|
.store = store_cpufv
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device_attribute dev_attr_available_cpufv = {
|
||||||
|
.attr = {
|
||||||
|
.name = "available_cpufv",
|
||||||
|
.mode = 0444 },
|
||||||
|
.show = show_available_cpufv
|
||||||
|
};
|
||||||
|
|
||||||
static struct attribute *platform_attributes[] = {
|
static struct attribute *platform_attributes[] = {
|
||||||
&dev_attr_camera.attr,
|
&dev_attr_camera.attr,
|
||||||
&dev_attr_cardr.attr,
|
&dev_attr_cardr.attr,
|
||||||
&dev_attr_disp.attr,
|
&dev_attr_disp.attr,
|
||||||
&dev_attr_cpufv.attr,
|
&dev_attr_cpufv.attr,
|
||||||
|
&dev_attr_available_cpufv.attr,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -558,7 +656,7 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data)
|
||||||
eeepc_rfkill_hotplug();
|
eeepc_rfkill_hotplug();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
|
static void eeepc_hotk_notify(struct acpi_device *device, u32 event)
|
||||||
{
|
{
|
||||||
static struct key_entry *key;
|
static struct key_entry *key;
|
||||||
u16 count;
|
u16 count;
|
||||||
|
@ -566,6 +664,8 @@ static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data)
|
||||||
|
|
||||||
if (!ehotk)
|
if (!ehotk)
|
||||||
return;
|
return;
|
||||||
|
if (event > ACPI_MAX_SYS_NOTIFY)
|
||||||
|
return;
|
||||||
if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
|
if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX)
|
||||||
brn = notify_brn();
|
brn = notify_brn();
|
||||||
count = ehotk->event_count[event % 128]++;
|
count = ehotk->event_count[event % 128]++;
|
||||||
|
@ -646,7 +746,6 @@ static void eeepc_unregister_rfkill_notifier(char *node)
|
||||||
|
|
||||||
static int eeepc_hotk_add(struct acpi_device *device)
|
static int eeepc_hotk_add(struct acpi_device *device)
|
||||||
{
|
{
|
||||||
acpi_status status = AE_OK;
|
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
if (!device)
|
if (!device)
|
||||||
|
@ -664,10 +763,6 @@ static int eeepc_hotk_add(struct acpi_device *device)
|
||||||
result = eeepc_hotk_check();
|
result = eeepc_hotk_check();
|
||||||
if (result)
|
if (result)
|
||||||
goto ehotk_fail;
|
goto ehotk_fail;
|
||||||
status = acpi_install_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
|
|
||||||
eeepc_hotk_notify, ehotk);
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
printk(EEEPC_ERR "Error installing notify handler\n");
|
|
||||||
|
|
||||||
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
|
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6");
|
||||||
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
|
eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7");
|
||||||
|
@ -725,14 +820,8 @@ static int eeepc_hotk_add(struct acpi_device *device)
|
||||||
|
|
||||||
static int eeepc_hotk_remove(struct acpi_device *device, int type)
|
static int eeepc_hotk_remove(struct acpi_device *device, int type)
|
||||||
{
|
{
|
||||||
acpi_status status = 0;
|
|
||||||
|
|
||||||
if (!device || !acpi_driver_data(device))
|
if (!device || !acpi_driver_data(device))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
status = acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY,
|
|
||||||
eeepc_hotk_notify);
|
|
||||||
if (ACPI_FAILURE(status))
|
|
||||||
printk(EEEPC_ERR "Error removing notify handler\n");
|
|
||||||
|
|
||||||
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
|
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6");
|
||||||
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
|
eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7");
|
||||||
|
@ -989,6 +1078,9 @@ static int __init eeepc_laptop_init(void)
|
||||||
result = eeepc_hwmon_init(dev);
|
result = eeepc_hwmon_init(dev);
|
||||||
if (result)
|
if (result)
|
||||||
goto fail_hwmon;
|
goto fail_hwmon;
|
||||||
|
|
||||||
|
eeepc_enable_camera();
|
||||||
|
|
||||||
/* Register platform stuff */
|
/* Register platform stuff */
|
||||||
result = platform_driver_register(&platform_driver);
|
result = platform_driver_register(&platform_driver);
|
||||||
if (result)
|
if (result)
|
||||||
|
|
|
@ -47,7 +47,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
|
||||||
#define HPWMI_DISPLAY_QUERY 0x1
|
#define HPWMI_DISPLAY_QUERY 0x1
|
||||||
#define HPWMI_HDDTEMP_QUERY 0x2
|
#define HPWMI_HDDTEMP_QUERY 0x2
|
||||||
#define HPWMI_ALS_QUERY 0x3
|
#define HPWMI_ALS_QUERY 0x3
|
||||||
#define HPWMI_DOCK_QUERY 0x4
|
#define HPWMI_HARDWARE_QUERY 0x4
|
||||||
#define HPWMI_WIRELESS_QUERY 0x5
|
#define HPWMI_WIRELESS_QUERY 0x5
|
||||||
#define HPWMI_HOTKEY_QUERY 0xc
|
#define HPWMI_HOTKEY_QUERY 0xc
|
||||||
|
|
||||||
|
@ -75,10 +75,9 @@ struct key_entry {
|
||||||
u16 keycode;
|
u16 keycode;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum { KE_KEY, KE_SW, KE_END };
|
enum { KE_KEY, KE_END };
|
||||||
|
|
||||||
static struct key_entry hp_wmi_keymap[] = {
|
static struct key_entry hp_wmi_keymap[] = {
|
||||||
{KE_SW, 0x01, SW_DOCK},
|
|
||||||
{KE_KEY, 0x02, KEY_BRIGHTNESSUP},
|
{KE_KEY, 0x02, KEY_BRIGHTNESSUP},
|
||||||
{KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
|
{KE_KEY, 0x03, KEY_BRIGHTNESSDOWN},
|
||||||
{KE_KEY, 0x20e6, KEY_PROG1},
|
{KE_KEY, 0x20e6, KEY_PROG1},
|
||||||
|
@ -151,7 +150,22 @@ static int hp_wmi_als_state(void)
|
||||||
|
|
||||||
static int hp_wmi_dock_state(void)
|
static int hp_wmi_dock_state(void)
|
||||||
{
|
{
|
||||||
return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0);
|
int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return ret & 0x1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hp_wmi_tablet_state(void)
|
||||||
|
{
|
||||||
|
int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return (ret & 0x4) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hp_wmi_set_block(void *data, bool blocked)
|
static int hp_wmi_set_block(void *data, bool blocked)
|
||||||
|
@ -232,6 +246,15 @@ static ssize_t show_dock(struct device *dev, struct device_attribute *attr,
|
||||||
return sprintf(buf, "%d\n", value);
|
return sprintf(buf, "%d\n", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t show_tablet(struct device *dev, struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
int value = hp_wmi_tablet_state();
|
||||||
|
if (value < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
return sprintf(buf, "%d\n", value);
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t set_als(struct device *dev, struct device_attribute *attr,
|
static ssize_t set_als(struct device *dev, struct device_attribute *attr,
|
||||||
const char *buf, size_t count)
|
const char *buf, size_t count)
|
||||||
{
|
{
|
||||||
|
@ -244,6 +267,7 @@ static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
|
||||||
static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
|
static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
|
||||||
static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
|
static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
|
||||||
static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
|
static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
|
||||||
|
static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL);
|
||||||
|
|
||||||
static struct key_entry *hp_wmi_get_entry_by_scancode(int code)
|
static struct key_entry *hp_wmi_get_entry_by_scancode(int code)
|
||||||
{
|
{
|
||||||
|
@ -326,13 +350,13 @@ static void hp_wmi_notify(u32 value, void *context)
|
||||||
key->keycode, 0);
|
key->keycode, 0);
|
||||||
input_sync(hp_wmi_input_dev);
|
input_sync(hp_wmi_input_dev);
|
||||||
break;
|
break;
|
||||||
case KE_SW:
|
|
||||||
input_report_switch(hp_wmi_input_dev,
|
|
||||||
key->keycode,
|
|
||||||
hp_wmi_dock_state());
|
|
||||||
input_sync(hp_wmi_input_dev);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
} else if (eventcode == 0x1) {
|
||||||
|
input_report_switch(hp_wmi_input_dev, SW_DOCK,
|
||||||
|
hp_wmi_dock_state());
|
||||||
|
input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
|
||||||
|
hp_wmi_tablet_state());
|
||||||
|
input_sync(hp_wmi_input_dev);
|
||||||
} else if (eventcode == 0x5) {
|
} else if (eventcode == 0x5) {
|
||||||
if (wifi_rfkill)
|
if (wifi_rfkill)
|
||||||
rfkill_set_sw_state(wifi_rfkill,
|
rfkill_set_sw_state(wifi_rfkill,
|
||||||
|
@ -369,17 +393,18 @@ static int __init hp_wmi_input_setup(void)
|
||||||
set_bit(EV_KEY, hp_wmi_input_dev->evbit);
|
set_bit(EV_KEY, hp_wmi_input_dev->evbit);
|
||||||
set_bit(key->keycode, hp_wmi_input_dev->keybit);
|
set_bit(key->keycode, hp_wmi_input_dev->keybit);
|
||||||
break;
|
break;
|
||||||
case KE_SW:
|
}
|
||||||
set_bit(EV_SW, hp_wmi_input_dev->evbit);
|
}
|
||||||
set_bit(key->keycode, hp_wmi_input_dev->swbit);
|
|
||||||
|
|
||||||
/* Set initial dock state */
|
set_bit(EV_SW, hp_wmi_input_dev->evbit);
|
||||||
input_report_switch(hp_wmi_input_dev, key->keycode,
|
set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
|
||||||
hp_wmi_dock_state());
|
set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
|
||||||
|
|
||||||
|
/* Set initial hardware state */
|
||||||
|
input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
|
||||||
|
input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
|
||||||
|
hp_wmi_tablet_state());
|
||||||
input_sync(hp_wmi_input_dev);
|
input_sync(hp_wmi_input_dev);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = input_register_device(hp_wmi_input_dev);
|
err = input_register_device(hp_wmi_input_dev);
|
||||||
|
|
||||||
|
@ -397,6 +422,7 @@ static void cleanup_sysfs(struct platform_device *device)
|
||||||
device_remove_file(&device->dev, &dev_attr_hddtemp);
|
device_remove_file(&device->dev, &dev_attr_hddtemp);
|
||||||
device_remove_file(&device->dev, &dev_attr_als);
|
device_remove_file(&device->dev, &dev_attr_als);
|
||||||
device_remove_file(&device->dev, &dev_attr_dock);
|
device_remove_file(&device->dev, &dev_attr_dock);
|
||||||
|
device_remove_file(&device->dev, &dev_attr_tablet);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init hp_wmi_bios_setup(struct platform_device *device)
|
static int __init hp_wmi_bios_setup(struct platform_device *device)
|
||||||
|
@ -414,6 +440,9 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
|
||||||
if (err)
|
if (err)
|
||||||
goto add_sysfs_error;
|
goto add_sysfs_error;
|
||||||
err = device_create_file(&device->dev, &dev_attr_dock);
|
err = device_create_file(&device->dev, &dev_attr_dock);
|
||||||
|
if (err)
|
||||||
|
goto add_sysfs_error;
|
||||||
|
err = device_create_file(&device->dev, &dev_attr_tablet);
|
||||||
if (err)
|
if (err)
|
||||||
goto add_sysfs_error;
|
goto add_sysfs_error;
|
||||||
|
|
||||||
|
@ -485,23 +514,17 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
|
||||||
|
|
||||||
static int hp_wmi_resume_handler(struct platform_device *device)
|
static int hp_wmi_resume_handler(struct platform_device *device)
|
||||||
{
|
{
|
||||||
struct key_entry *key;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Docking state may have changed while suspended, so trigger
|
* Hardware state may have changed while suspended, so trigger
|
||||||
* an input event for the current state. As this is a switch,
|
* input events for the current state. As this is a switch,
|
||||||
* the input layer will only actually pass it on if the state
|
* the input layer will only actually pass it on if the state
|
||||||
* changed.
|
* changed.
|
||||||
*/
|
*/
|
||||||
for (key = hp_wmi_keymap; key->type != KE_END; key++) {
|
|
||||||
switch (key->type) {
|
input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
|
||||||
case KE_SW:
|
input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
|
||||||
input_report_switch(hp_wmi_input_dev, key->keycode,
|
hp_wmi_tablet_state());
|
||||||
hp_wmi_dock_state());
|
|
||||||
input_sync(hp_wmi_input_dev);
|
input_sync(hp_wmi_input_dev);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define TPACPI_VERSION "0.23"
|
#define TPACPI_VERSION "0.23"
|
||||||
#define TPACPI_SYSFS_VERSION 0x020300
|
#define TPACPI_SYSFS_VERSION 0x020400
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Changelog:
|
* Changelog:
|
||||||
|
@ -257,6 +257,8 @@ static struct {
|
||||||
u32 wan:1;
|
u32 wan:1;
|
||||||
u32 uwb:1;
|
u32 uwb:1;
|
||||||
u32 fan_ctrl_status_undef:1;
|
u32 fan_ctrl_status_undef:1;
|
||||||
|
u32 second_fan:1;
|
||||||
|
u32 beep_needs_two_args:1;
|
||||||
u32 input_device_registered:1;
|
u32 input_device_registered:1;
|
||||||
u32 platform_drv_registered:1;
|
u32 platform_drv_registered:1;
|
||||||
u32 platform_drv_attrs_registered:1;
|
u32 platform_drv_attrs_registered:1;
|
||||||
|
@ -277,8 +279,10 @@ struct thinkpad_id_data {
|
||||||
char *bios_version_str; /* Something like 1ZET51WW (1.03z) */
|
char *bios_version_str; /* Something like 1ZET51WW (1.03z) */
|
||||||
char *ec_version_str; /* Something like 1ZHT51WW-1.04a */
|
char *ec_version_str; /* Something like 1ZHT51WW-1.04a */
|
||||||
|
|
||||||
u16 bios_model; /* Big Endian, TP-1Y = 0x5931, 0 = unknown */
|
u16 bios_model; /* 1Y = 0x5931, 0 = unknown */
|
||||||
u16 ec_model;
|
u16 ec_model;
|
||||||
|
u16 bios_release; /* 1ZETK1WW = 0x314b, 0 = unknown */
|
||||||
|
u16 ec_release;
|
||||||
|
|
||||||
char *model_str; /* ThinkPad T43 */
|
char *model_str; /* ThinkPad T43 */
|
||||||
char *nummodel_str; /* 9384A9C for a 9384-A9C model */
|
char *nummodel_str; /* 9384A9C for a 9384-A9C model */
|
||||||
|
@ -355,6 +359,73 @@ static void tpacpi_log_usertask(const char * const what)
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Quirk handling helpers
|
||||||
|
*
|
||||||
|
* ThinkPad IDs and versions seen in the field so far
|
||||||
|
* are two-characters from the set [0-9A-Z], i.e. base 36.
|
||||||
|
*
|
||||||
|
* We use values well outside that range as specials.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define TPACPI_MATCH_ANY 0xffffU
|
||||||
|
#define TPACPI_MATCH_UNKNOWN 0U
|
||||||
|
|
||||||
|
/* TPID('1', 'Y') == 0x5931 */
|
||||||
|
#define TPID(__c1, __c2) (((__c2) << 8) | (__c1))
|
||||||
|
|
||||||
|
#define TPACPI_Q_IBM(__id1, __id2, __quirk) \
|
||||||
|
{ .vendor = PCI_VENDOR_ID_IBM, \
|
||||||
|
.bios = TPID(__id1, __id2), \
|
||||||
|
.ec = TPACPI_MATCH_ANY, \
|
||||||
|
.quirks = (__quirk) }
|
||||||
|
|
||||||
|
#define TPACPI_Q_LNV(__id1, __id2, __quirk) \
|
||||||
|
{ .vendor = PCI_VENDOR_ID_LENOVO, \
|
||||||
|
.bios = TPID(__id1, __id2), \
|
||||||
|
.ec = TPACPI_MATCH_ANY, \
|
||||||
|
.quirks = (__quirk) }
|
||||||
|
|
||||||
|
struct tpacpi_quirk {
|
||||||
|
unsigned int vendor;
|
||||||
|
u16 bios;
|
||||||
|
u16 ec;
|
||||||
|
unsigned long quirks;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tpacpi_check_quirks() - search BIOS/EC version on a list
|
||||||
|
* @qlist: array of &struct tpacpi_quirk
|
||||||
|
* @qlist_size: number of elements in @qlist
|
||||||
|
*
|
||||||
|
* Iterates over a quirks list until one is found that matches the
|
||||||
|
* ThinkPad's vendor, BIOS and EC model.
|
||||||
|
*
|
||||||
|
* Returns 0 if nothing matches, otherwise returns the quirks field of
|
||||||
|
* the matching &struct tpacpi_quirk entry.
|
||||||
|
*
|
||||||
|
* The match criteria is: vendor, ec and bios much match.
|
||||||
|
*/
|
||||||
|
static unsigned long __init tpacpi_check_quirks(
|
||||||
|
const struct tpacpi_quirk *qlist,
|
||||||
|
unsigned int qlist_size)
|
||||||
|
{
|
||||||
|
while (qlist_size) {
|
||||||
|
if ((qlist->vendor == thinkpad_id.vendor ||
|
||||||
|
qlist->vendor == TPACPI_MATCH_ANY) &&
|
||||||
|
(qlist->bios == thinkpad_id.bios_model ||
|
||||||
|
qlist->bios == TPACPI_MATCH_ANY) &&
|
||||||
|
(qlist->ec == thinkpad_id.ec_model ||
|
||||||
|
qlist->ec == TPACPI_MATCH_ANY))
|
||||||
|
return qlist->quirks;
|
||||||
|
|
||||||
|
qlist_size--;
|
||||||
|
qlist++;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
****************************************************************************
|
****************************************************************************
|
||||||
*
|
*
|
||||||
|
@ -2880,7 +2951,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
|
||||||
/* update bright_acpimode... */
|
/* update bright_acpimode... */
|
||||||
tpacpi_check_std_acpi_brightness_support();
|
tpacpi_check_std_acpi_brightness_support();
|
||||||
|
|
||||||
if (tp_features.bright_acpimode) {
|
if (tp_features.bright_acpimode && acpi_video_backlight_support()) {
|
||||||
printk(TPACPI_INFO
|
printk(TPACPI_INFO
|
||||||
"This ThinkPad has standard ACPI backlight "
|
"This ThinkPad has standard ACPI backlight "
|
||||||
"brightness control, supported by the ACPI "
|
"brightness control, supported by the ACPI "
|
||||||
|
@ -4773,7 +4844,7 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */
|
||||||
"LED", /* all others */
|
"LED", /* all others */
|
||||||
); /* R30, R31 */
|
); /* R30, R31 */
|
||||||
|
|
||||||
#define TPACPI_LED_NUMLEDS 8
|
#define TPACPI_LED_NUMLEDS 16
|
||||||
static struct tpacpi_led_classdev *tpacpi_leds;
|
static struct tpacpi_led_classdev *tpacpi_leds;
|
||||||
static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS];
|
static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS];
|
||||||
static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
|
static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
|
||||||
|
@ -4786,15 +4857,20 @@ static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
|
||||||
"tpacpi::dock_batt",
|
"tpacpi::dock_batt",
|
||||||
"tpacpi::unknown_led",
|
"tpacpi::unknown_led",
|
||||||
"tpacpi::standby",
|
"tpacpi::standby",
|
||||||
|
"tpacpi::dock_status1",
|
||||||
|
"tpacpi::dock_status2",
|
||||||
|
"tpacpi::unknown_led2",
|
||||||
|
"tpacpi::unknown_led3",
|
||||||
|
"tpacpi::thinkvantage",
|
||||||
};
|
};
|
||||||
#define TPACPI_SAFE_LEDS 0x0081U
|
#define TPACPI_SAFE_LEDS 0x1081U
|
||||||
|
|
||||||
static inline bool tpacpi_is_led_restricted(const unsigned int led)
|
static inline bool tpacpi_is_led_restricted(const unsigned int led)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS
|
#ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS
|
||||||
return false;
|
return false;
|
||||||
#else
|
#else
|
||||||
return (TPACPI_SAFE_LEDS & (1 << led)) == 0;
|
return (1U & (TPACPI_SAFE_LEDS >> led)) == 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4956,6 +5032,10 @@ static int __init tpacpi_init_led(unsigned int led)
|
||||||
|
|
||||||
tpacpi_leds[led].led = led;
|
tpacpi_leds[led].led = led;
|
||||||
|
|
||||||
|
/* LEDs with no name don't get registered */
|
||||||
|
if (!tpacpi_led_names[led])
|
||||||
|
return 0;
|
||||||
|
|
||||||
tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set;
|
tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set;
|
||||||
tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set;
|
tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set;
|
||||||
if (led_supported == TPACPI_LED_570)
|
if (led_supported == TPACPI_LED_570)
|
||||||
|
@ -4974,10 +5054,59 @@ static int __init tpacpi_init_led(unsigned int led)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct tpacpi_quirk led_useful_qtable[] __initconst = {
|
||||||
|
TPACPI_Q_IBM('1', 'E', 0x009f), /* A30 */
|
||||||
|
TPACPI_Q_IBM('1', 'N', 0x009f), /* A31 */
|
||||||
|
TPACPI_Q_IBM('1', 'G', 0x009f), /* A31 */
|
||||||
|
|
||||||
|
TPACPI_Q_IBM('1', 'I', 0x0097), /* T30 */
|
||||||
|
TPACPI_Q_IBM('1', 'R', 0x0097), /* T40, T41, T42, R50, R51 */
|
||||||
|
TPACPI_Q_IBM('7', '0', 0x0097), /* T43, R52 */
|
||||||
|
TPACPI_Q_IBM('1', 'Y', 0x0097), /* T43 */
|
||||||
|
TPACPI_Q_IBM('1', 'W', 0x0097), /* R50e */
|
||||||
|
TPACPI_Q_IBM('1', 'V', 0x0097), /* R51 */
|
||||||
|
TPACPI_Q_IBM('7', '8', 0x0097), /* R51e */
|
||||||
|
TPACPI_Q_IBM('7', '6', 0x0097), /* R52 */
|
||||||
|
|
||||||
|
TPACPI_Q_IBM('1', 'K', 0x00bf), /* X30 */
|
||||||
|
TPACPI_Q_IBM('1', 'Q', 0x00bf), /* X31, X32 */
|
||||||
|
TPACPI_Q_IBM('1', 'U', 0x00bf), /* X40 */
|
||||||
|
TPACPI_Q_IBM('7', '4', 0x00bf), /* X41 */
|
||||||
|
TPACPI_Q_IBM('7', '5', 0x00bf), /* X41t */
|
||||||
|
|
||||||
|
TPACPI_Q_IBM('7', '9', 0x1f97), /* T60 (1) */
|
||||||
|
TPACPI_Q_IBM('7', '7', 0x1f97), /* Z60* (1) */
|
||||||
|
TPACPI_Q_IBM('7', 'F', 0x1f97), /* Z61* (1) */
|
||||||
|
TPACPI_Q_IBM('7', 'B', 0x1fb7), /* X60 (1) */
|
||||||
|
|
||||||
|
/* (1) - may have excess leds enabled on MSB */
|
||||||
|
|
||||||
|
/* Defaults (order matters, keep last, don't reorder!) */
|
||||||
|
{ /* Lenovo */
|
||||||
|
.vendor = PCI_VENDOR_ID_LENOVO,
|
||||||
|
.bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY,
|
||||||
|
.quirks = 0x1fffU,
|
||||||
|
},
|
||||||
|
{ /* IBM ThinkPads with no EC version string */
|
||||||
|
.vendor = PCI_VENDOR_ID_IBM,
|
||||||
|
.bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_UNKNOWN,
|
||||||
|
.quirks = 0x00ffU,
|
||||||
|
},
|
||||||
|
{ /* IBM ThinkPads with EC version string */
|
||||||
|
.vendor = PCI_VENDOR_ID_IBM,
|
||||||
|
.bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY,
|
||||||
|
.quirks = 0x00bfU,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef TPACPI_LEDQ_IBM
|
||||||
|
#undef TPACPI_LEDQ_LNV
|
||||||
|
|
||||||
static int __init led_init(struct ibm_init_struct *iibm)
|
static int __init led_init(struct ibm_init_struct *iibm)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int rc;
|
int rc;
|
||||||
|
unsigned long useful_leds;
|
||||||
|
|
||||||
vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
|
vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
|
||||||
|
|
||||||
|
@ -4999,6 +5128,9 @@ static int __init led_init(struct ibm_init_struct *iibm)
|
||||||
vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
|
vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
|
||||||
str_supported(led_supported), led_supported);
|
str_supported(led_supported), led_supported);
|
||||||
|
|
||||||
|
if (led_supported == TPACPI_LED_NONE)
|
||||||
|
return 1;
|
||||||
|
|
||||||
tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS,
|
tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!tpacpi_leds) {
|
if (!tpacpi_leds) {
|
||||||
|
@ -5006,8 +5138,12 @@ static int __init led_init(struct ibm_init_struct *iibm)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useful_leds = tpacpi_check_quirks(led_useful_qtable,
|
||||||
|
ARRAY_SIZE(led_useful_qtable));
|
||||||
|
|
||||||
for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
|
for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
|
||||||
if (!tpacpi_is_led_restricted(i)) {
|
if (!tpacpi_is_led_restricted(i) &&
|
||||||
|
test_bit(i, &useful_leds)) {
|
||||||
rc = tpacpi_init_led(i);
|
rc = tpacpi_init_led(i);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
led_exit();
|
led_exit();
|
||||||
|
@ -5017,12 +5153,11 @@ static int __init led_init(struct ibm_init_struct *iibm)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS
|
#ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS
|
||||||
if (led_supported != TPACPI_LED_NONE)
|
|
||||||
printk(TPACPI_NOTICE
|
printk(TPACPI_NOTICE
|
||||||
"warning: userspace override of important "
|
"warning: userspace override of important "
|
||||||
"firmware LEDs is enabled\n");
|
"firmware LEDs is enabled\n");
|
||||||
#endif
|
#endif
|
||||||
return (led_supported != TPACPI_LED_NONE)? 0 : 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define str_led_status(s) \
|
#define str_led_status(s) \
|
||||||
|
@ -5052,7 +5187,7 @@ static int led_read(char *p)
|
||||||
}
|
}
|
||||||
|
|
||||||
len += sprintf(p + len, "commands:\t"
|
len += sprintf(p + len, "commands:\t"
|
||||||
"<led> on, <led> off, <led> blink (<led> is 0-7)\n");
|
"<led> on, <led> off, <led> blink (<led> is 0-15)\n");
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
@ -5067,7 +5202,7 @@ static int led_write(char *buf)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
while ((cmd = next_cmd(&buf))) {
|
while ((cmd = next_cmd(&buf))) {
|
||||||
if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
|
if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 15)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (strstr(cmd, "off")) {
|
if (strstr(cmd, "off")) {
|
||||||
|
@ -5101,8 +5236,17 @@ static struct ibm_struct led_driver_data = {
|
||||||
|
|
||||||
TPACPI_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */
|
TPACPI_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */
|
||||||
|
|
||||||
|
#define TPACPI_BEEP_Q1 0x0001
|
||||||
|
|
||||||
|
static const struct tpacpi_quirk beep_quirk_table[] __initconst = {
|
||||||
|
TPACPI_Q_IBM('I', 'M', TPACPI_BEEP_Q1), /* 570 */
|
||||||
|
TPACPI_Q_IBM('I', 'U', TPACPI_BEEP_Q1), /* 570E - unverified */
|
||||||
|
};
|
||||||
|
|
||||||
static int __init beep_init(struct ibm_init_struct *iibm)
|
static int __init beep_init(struct ibm_init_struct *iibm)
|
||||||
{
|
{
|
||||||
|
unsigned long quirks;
|
||||||
|
|
||||||
vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n");
|
vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n");
|
||||||
|
|
||||||
TPACPI_ACPIHANDLE_INIT(beep);
|
TPACPI_ACPIHANDLE_INIT(beep);
|
||||||
|
@ -5110,6 +5254,11 @@ static int __init beep_init(struct ibm_init_struct *iibm)
|
||||||
vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n",
|
vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n",
|
||||||
str_supported(beep_handle != NULL));
|
str_supported(beep_handle != NULL));
|
||||||
|
|
||||||
|
quirks = tpacpi_check_quirks(beep_quirk_table,
|
||||||
|
ARRAY_SIZE(beep_quirk_table));
|
||||||
|
|
||||||
|
tp_features.beep_needs_two_args = !!(quirks & TPACPI_BEEP_Q1);
|
||||||
|
|
||||||
return (beep_handle)? 0 : 1;
|
return (beep_handle)? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5141,8 +5290,15 @@ static int beep_write(char *buf)
|
||||||
/* beep_cmd set */
|
/* beep_cmd set */
|
||||||
} else
|
} else
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0))
|
if (tp_features.beep_needs_two_args) {
|
||||||
|
if (!acpi_evalf(beep_handle, NULL, NULL, "vdd",
|
||||||
|
beep_cmd, 0))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
} else {
|
||||||
|
if (!acpi_evalf(beep_handle, NULL, NULL, "vd",
|
||||||
|
beep_cmd))
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -5569,6 +5725,10 @@ static struct ibm_struct ecdump_driver_data = {
|
||||||
* Bit 3-0: backlight brightness level
|
* Bit 3-0: backlight brightness level
|
||||||
*
|
*
|
||||||
* brightness_get_raw returns status data in the HBRV layout
|
* brightness_get_raw returns status data in the HBRV layout
|
||||||
|
*
|
||||||
|
* WARNING: The X61 has been verified to use HBRV for something else, so
|
||||||
|
* this should be used _only_ on IBM ThinkPads, and maybe with some careful
|
||||||
|
* testing on the very early *60 Lenovo models...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -5869,6 +6029,12 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
|
||||||
brightness_mode);
|
brightness_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Safety */
|
||||||
|
if (thinkpad_id.vendor != PCI_VENDOR_ID_IBM &&
|
||||||
|
(brightness_mode == TPACPI_BRGHT_MODE_ECNVRAM ||
|
||||||
|
brightness_mode == TPACPI_BRGHT_MODE_EC))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
if (tpacpi_brightness_get_raw(&b) < 0)
|
if (tpacpi_brightness_get_raw(&b) < 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
@ -6161,6 +6327,21 @@ static struct ibm_struct volume_driver_data = {
|
||||||
* For firmware bugs, refer to:
|
* For firmware bugs, refer to:
|
||||||
* http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
|
* http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
|
||||||
*
|
*
|
||||||
|
* ----
|
||||||
|
*
|
||||||
|
* ThinkPad EC register 0x31 bit 0 (only on select models)
|
||||||
|
*
|
||||||
|
* When bit 0 of EC register 0x31 is zero, the tachometer registers
|
||||||
|
* show the speed of the main fan. When bit 0 of EC register 0x31
|
||||||
|
* is one, the tachometer registers show the speed of the auxiliary
|
||||||
|
* fan.
|
||||||
|
*
|
||||||
|
* Fan control seems to affect both fans, regardless of the state
|
||||||
|
* of this bit.
|
||||||
|
*
|
||||||
|
* So far, only the firmware for the X60/X61 non-tablet versions
|
||||||
|
* seem to support this (firmware TP-7M).
|
||||||
|
*
|
||||||
* TPACPI_FAN_WR_ACPI_FANS:
|
* TPACPI_FAN_WR_ACPI_FANS:
|
||||||
* ThinkPad X31, X40, X41. Not available in the X60.
|
* ThinkPad X31, X40, X41. Not available in the X60.
|
||||||
*
|
*
|
||||||
|
@ -6187,6 +6368,8 @@ enum { /* Fan control constants */
|
||||||
fan_status_offset = 0x2f, /* EC register 0x2f */
|
fan_status_offset = 0x2f, /* EC register 0x2f */
|
||||||
fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM)
|
fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM)
|
||||||
* 0x84 must be read before 0x85 */
|
* 0x84 must be read before 0x85 */
|
||||||
|
fan_select_offset = 0x31, /* EC register 0x31 (Firmware 7M)
|
||||||
|
bit 0 selects which fan is active */
|
||||||
|
|
||||||
TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */
|
TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */
|
||||||
TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */
|
TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */
|
||||||
|
@ -6249,30 +6432,18 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */
|
||||||
* We assume 0x07 really means auto mode while this quirk is active,
|
* We assume 0x07 really means auto mode while this quirk is active,
|
||||||
* as this is far more likely than the ThinkPad being in level 7,
|
* as this is far more likely than the ThinkPad being in level 7,
|
||||||
* which is only used by the firmware during thermal emergencies.
|
* which is only used by the firmware during thermal emergencies.
|
||||||
|
*
|
||||||
|
* Enable for TP-1Y (T43), TP-78 (R51e), TP-76 (R52),
|
||||||
|
* TP-70 (T43, R52), which are known to be buggy.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void fan_quirk1_detect(void)
|
static void fan_quirk1_setup(void)
|
||||||
{
|
{
|
||||||
/* In some ThinkPads, neither the EC nor the ACPI
|
|
||||||
* DSDT initialize the HFSP register, and it ends up
|
|
||||||
* being initially set to 0x07 when it *could* be
|
|
||||||
* either 0x07 or 0x80.
|
|
||||||
*
|
|
||||||
* Enable for TP-1Y (T43), TP-78 (R51e),
|
|
||||||
* TP-76 (R52), TP-70 (T43, R52), which are known
|
|
||||||
* to be buggy. */
|
|
||||||
if (fan_control_initial_status == 0x07) {
|
if (fan_control_initial_status == 0x07) {
|
||||||
switch (thinkpad_id.ec_model) {
|
|
||||||
case 0x5931: /* TP-1Y */
|
|
||||||
case 0x3837: /* TP-78 */
|
|
||||||
case 0x3637: /* TP-76 */
|
|
||||||
case 0x3037: /* TP-70 */
|
|
||||||
printk(TPACPI_NOTICE
|
printk(TPACPI_NOTICE
|
||||||
"fan_init: initial fan status is unknown, "
|
"fan_init: initial fan status is unknown, "
|
||||||
"assuming it is in auto mode\n");
|
"assuming it is in auto mode\n");
|
||||||
tp_features.fan_ctrl_status_undef = 1;
|
tp_features.fan_ctrl_status_undef = 1;
|
||||||
;;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6292,6 +6463,38 @@ static void fan_quirk1_handle(u8 *fan_status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Select main fan on X60/X61, NOOP on others */
|
||||||
|
static bool fan_select_fan1(void)
|
||||||
|
{
|
||||||
|
if (tp_features.second_fan) {
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
if (ec_read(fan_select_offset, &val) < 0)
|
||||||
|
return false;
|
||||||
|
val &= 0xFEU;
|
||||||
|
if (ec_write(fan_select_offset, val) < 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select secondary fan on X60/X61 */
|
||||||
|
static bool fan_select_fan2(void)
|
||||||
|
{
|
||||||
|
u8 val;
|
||||||
|
|
||||||
|
if (!tp_features.second_fan)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ec_read(fan_select_offset, &val) < 0)
|
||||||
|
return false;
|
||||||
|
val |= 0x01U;
|
||||||
|
if (ec_write(fan_select_offset, val) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call with fan_mutex held
|
* Call with fan_mutex held
|
||||||
*/
|
*/
|
||||||
|
@ -6369,6 +6572,8 @@ static int fan_get_speed(unsigned int *speed)
|
||||||
switch (fan_status_access_mode) {
|
switch (fan_status_access_mode) {
|
||||||
case TPACPI_FAN_RD_TPEC:
|
case TPACPI_FAN_RD_TPEC:
|
||||||
/* all except 570, 600e/x, 770e, 770x */
|
/* all except 570, 600e/x, 770e, 770x */
|
||||||
|
if (unlikely(!fan_select_fan1()))
|
||||||
|
return -EIO;
|
||||||
if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
|
if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
|
||||||
!acpi_ec_read(fan_rpm_offset + 1, &hi)))
|
!acpi_ec_read(fan_rpm_offset + 1, &hi)))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
@ -6385,6 +6590,34 @@ static int fan_get_speed(unsigned int *speed)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int fan2_get_speed(unsigned int *speed)
|
||||||
|
{
|
||||||
|
u8 hi, lo;
|
||||||
|
bool rc;
|
||||||
|
|
||||||
|
switch (fan_status_access_mode) {
|
||||||
|
case TPACPI_FAN_RD_TPEC:
|
||||||
|
/* all except 570, 600e/x, 770e, 770x */
|
||||||
|
if (unlikely(!fan_select_fan2()))
|
||||||
|
return -EIO;
|
||||||
|
rc = !acpi_ec_read(fan_rpm_offset, &lo) ||
|
||||||
|
!acpi_ec_read(fan_rpm_offset + 1, &hi);
|
||||||
|
fan_select_fan1(); /* play it safe */
|
||||||
|
if (rc)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
|
if (likely(speed))
|
||||||
|
*speed = (hi << 8) | lo;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int fan_set_level(int level)
|
static int fan_set_level(int level)
|
||||||
{
|
{
|
||||||
if (!fan_control_allowed)
|
if (!fan_control_allowed)
|
||||||
|
@ -6790,6 +7023,25 @@ static struct device_attribute dev_attr_fan_fan1_input =
|
||||||
__ATTR(fan1_input, S_IRUGO,
|
__ATTR(fan1_input, S_IRUGO,
|
||||||
fan_fan1_input_show, NULL);
|
fan_fan1_input_show, NULL);
|
||||||
|
|
||||||
|
/* sysfs fan fan2_input ------------------------------------------------ */
|
||||||
|
static ssize_t fan_fan2_input_show(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
unsigned int speed;
|
||||||
|
|
||||||
|
res = fan2_get_speed(&speed);
|
||||||
|
if (res < 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%u\n", speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct device_attribute dev_attr_fan_fan2_input =
|
||||||
|
__ATTR(fan2_input, S_IRUGO,
|
||||||
|
fan_fan2_input_show, NULL);
|
||||||
|
|
||||||
/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */
|
/* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */
|
||||||
static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
|
static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
|
||||||
char *buf)
|
char *buf)
|
||||||
|
@ -6823,6 +7075,7 @@ static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO,
|
||||||
static struct attribute *fan_attributes[] = {
|
static struct attribute *fan_attributes[] = {
|
||||||
&dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr,
|
&dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr,
|
||||||
&dev_attr_fan_fan1_input.attr,
|
&dev_attr_fan_fan1_input.attr,
|
||||||
|
NULL, /* for fan2_input */
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6830,9 +7083,36 @@ static const struct attribute_group fan_attr_group = {
|
||||||
.attrs = fan_attributes,
|
.attrs = fan_attributes,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */
|
||||||
|
#define TPACPI_FAN_2FAN 0x0002 /* EC 0x31 bit 0 selects fan2 */
|
||||||
|
|
||||||
|
#define TPACPI_FAN_QI(__id1, __id2, __quirks) \
|
||||||
|
{ .vendor = PCI_VENDOR_ID_IBM, \
|
||||||
|
.bios = TPACPI_MATCH_ANY, \
|
||||||
|
.ec = TPID(__id1, __id2), \
|
||||||
|
.quirks = __quirks }
|
||||||
|
|
||||||
|
#define TPACPI_FAN_QL(__id1, __id2, __quirks) \
|
||||||
|
{ .vendor = PCI_VENDOR_ID_LENOVO, \
|
||||||
|
.bios = TPACPI_MATCH_ANY, \
|
||||||
|
.ec = TPID(__id1, __id2), \
|
||||||
|
.quirks = __quirks }
|
||||||
|
|
||||||
|
static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
|
||||||
|
TPACPI_FAN_QI('1', 'Y', TPACPI_FAN_Q1),
|
||||||
|
TPACPI_FAN_QI('7', '8', TPACPI_FAN_Q1),
|
||||||
|
TPACPI_FAN_QI('7', '6', TPACPI_FAN_Q1),
|
||||||
|
TPACPI_FAN_QI('7', '0', TPACPI_FAN_Q1),
|
||||||
|
TPACPI_FAN_QL('7', 'M', TPACPI_FAN_2FAN),
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef TPACPI_FAN_QL
|
||||||
|
#undef TPACPI_FAN_QI
|
||||||
|
|
||||||
static int __init fan_init(struct ibm_init_struct *iibm)
|
static int __init fan_init(struct ibm_init_struct *iibm)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
unsigned long quirks;
|
||||||
|
|
||||||
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN,
|
vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN,
|
||||||
"initializing fan subdriver\n");
|
"initializing fan subdriver\n");
|
||||||
|
@ -6843,12 +7123,16 @@ static int __init fan_init(struct ibm_init_struct *iibm)
|
||||||
fan_control_commands = 0;
|
fan_control_commands = 0;
|
||||||
fan_watchdog_maxinterval = 0;
|
fan_watchdog_maxinterval = 0;
|
||||||
tp_features.fan_ctrl_status_undef = 0;
|
tp_features.fan_ctrl_status_undef = 0;
|
||||||
|
tp_features.second_fan = 0;
|
||||||
fan_control_desired_level = 7;
|
fan_control_desired_level = 7;
|
||||||
|
|
||||||
TPACPI_ACPIHANDLE_INIT(fans);
|
TPACPI_ACPIHANDLE_INIT(fans);
|
||||||
TPACPI_ACPIHANDLE_INIT(gfan);
|
TPACPI_ACPIHANDLE_INIT(gfan);
|
||||||
TPACPI_ACPIHANDLE_INIT(sfan);
|
TPACPI_ACPIHANDLE_INIT(sfan);
|
||||||
|
|
||||||
|
quirks = tpacpi_check_quirks(fan_quirk_table,
|
||||||
|
ARRAY_SIZE(fan_quirk_table));
|
||||||
|
|
||||||
if (gfan_handle) {
|
if (gfan_handle) {
|
||||||
/* 570, 600e/x, 770e, 770x */
|
/* 570, 600e/x, 770e, 770x */
|
||||||
fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
|
fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
|
||||||
|
@ -6858,7 +7142,13 @@ static int __init fan_init(struct ibm_init_struct *iibm)
|
||||||
if (likely(acpi_ec_read(fan_status_offset,
|
if (likely(acpi_ec_read(fan_status_offset,
|
||||||
&fan_control_initial_status))) {
|
&fan_control_initial_status))) {
|
||||||
fan_status_access_mode = TPACPI_FAN_RD_TPEC;
|
fan_status_access_mode = TPACPI_FAN_RD_TPEC;
|
||||||
fan_quirk1_detect();
|
if (quirks & TPACPI_FAN_Q1)
|
||||||
|
fan_quirk1_setup();
|
||||||
|
if (quirks & TPACPI_FAN_2FAN) {
|
||||||
|
tp_features.second_fan = 1;
|
||||||
|
dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN,
|
||||||
|
"secondary fan support enabled\n");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
printk(TPACPI_ERR
|
printk(TPACPI_ERR
|
||||||
"ThinkPad ACPI EC access misbehaving, "
|
"ThinkPad ACPI EC access misbehaving, "
|
||||||
|
@ -6914,6 +7204,11 @@ static int __init fan_init(struct ibm_init_struct *iibm)
|
||||||
|
|
||||||
if (fan_status_access_mode != TPACPI_FAN_NONE ||
|
if (fan_status_access_mode != TPACPI_FAN_NONE ||
|
||||||
fan_control_access_mode != TPACPI_FAN_WR_NONE) {
|
fan_control_access_mode != TPACPI_FAN_WR_NONE) {
|
||||||
|
if (tp_features.second_fan) {
|
||||||
|
/* attach second fan tachometer */
|
||||||
|
fan_attributes[ARRAY_SIZE(fan_attributes)-2] =
|
||||||
|
&dev_attr_fan_fan2_input.attr;
|
||||||
|
}
|
||||||
rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
|
rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
|
||||||
&fan_attr_group);
|
&fan_attr_group);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
|
@ -7385,6 +7680,24 @@ err_out:
|
||||||
|
|
||||||
/* Probing */
|
/* Probing */
|
||||||
|
|
||||||
|
static bool __pure __init tpacpi_is_fw_digit(const char c)
|
||||||
|
{
|
||||||
|
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Most models: xxyTkkWW (#.##c); Ancient 570/600 and -SL lacks (#.##c) */
|
||||||
|
static bool __pure __init tpacpi_is_valid_fw_id(const char* const s,
|
||||||
|
const char t)
|
||||||
|
{
|
||||||
|
return s && strlen(s) >= 8 &&
|
||||||
|
tpacpi_is_fw_digit(s[0]) &&
|
||||||
|
tpacpi_is_fw_digit(s[1]) &&
|
||||||
|
s[2] == t && s[3] == 'T' &&
|
||||||
|
tpacpi_is_fw_digit(s[4]) &&
|
||||||
|
tpacpi_is_fw_digit(s[5]) &&
|
||||||
|
s[6] == 'W' && s[7] == 'W';
|
||||||
|
}
|
||||||
|
|
||||||
/* returns 0 - probe ok, or < 0 - probe error.
|
/* returns 0 - probe ok, or < 0 - probe error.
|
||||||
* Probe ok doesn't mean thinkpad found.
|
* Probe ok doesn't mean thinkpad found.
|
||||||
* On error, kfree() cleanup on tp->* is not performed, caller must do it */
|
* On error, kfree() cleanup on tp->* is not performed, caller must do it */
|
||||||
|
@ -7411,10 +7724,15 @@ static int __must_check __init get_thinkpad_model_data(
|
||||||
tp->bios_version_str = kstrdup(s, GFP_KERNEL);
|
tp->bios_version_str = kstrdup(s, GFP_KERNEL);
|
||||||
if (s && !tp->bios_version_str)
|
if (s && !tp->bios_version_str)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
if (!tp->bios_version_str)
|
|
||||||
|
/* Really ancient ThinkPad 240X will fail this, which is fine */
|
||||||
|
if (!tpacpi_is_valid_fw_id(tp->bios_version_str, 'E'))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
tp->bios_model = tp->bios_version_str[0]
|
tp->bios_model = tp->bios_version_str[0]
|
||||||
| (tp->bios_version_str[1] << 8);
|
| (tp->bios_version_str[1] << 8);
|
||||||
|
tp->bios_release = (tp->bios_version_str[4] << 8)
|
||||||
|
| tp->bios_version_str[5];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ThinkPad T23 or newer, A31 or newer, R50e or newer,
|
* ThinkPad T23 or newer, A31 or newer, R50e or newer,
|
||||||
|
@ -7433,8 +7751,21 @@ static int __must_check __init get_thinkpad_model_data(
|
||||||
tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
|
tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL);
|
||||||
if (!tp->ec_version_str)
|
if (!tp->ec_version_str)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (tpacpi_is_valid_fw_id(ec_fw_string, 'H')) {
|
||||||
tp->ec_model = ec_fw_string[0]
|
tp->ec_model = ec_fw_string[0]
|
||||||
| (ec_fw_string[1] << 8);
|
| (ec_fw_string[1] << 8);
|
||||||
|
tp->ec_release = (ec_fw_string[4] << 8)
|
||||||
|
| ec_fw_string[5];
|
||||||
|
} else {
|
||||||
|
printk(TPACPI_NOTICE
|
||||||
|
"ThinkPad firmware release %s "
|
||||||
|
"doesn't match the known patterns\n",
|
||||||
|
ec_fw_string);
|
||||||
|
printk(TPACPI_NOTICE
|
||||||
|
"please report this to %s\n",
|
||||||
|
TPACPI_MAIL);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,6 +287,25 @@ static void pnpacpi_parse_allocated_address_space(struct pnp_dev *dev,
|
||||||
ACPI_DECODE_16);
|
ACPI_DECODE_16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pnpacpi_parse_allocated_ext_address_space(struct pnp_dev *dev,
|
||||||
|
struct acpi_resource *res)
|
||||||
|
{
|
||||||
|
struct acpi_resource_extended_address64 *p = &res->data.ext_address64;
|
||||||
|
|
||||||
|
if (p->producer_consumer == ACPI_PRODUCER)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (p->resource_type == ACPI_MEMORY_RANGE)
|
||||||
|
pnpacpi_parse_allocated_memresource(dev,
|
||||||
|
p->minimum, p->address_length,
|
||||||
|
p->info.mem.write_protect);
|
||||||
|
else if (p->resource_type == ACPI_IO_RANGE)
|
||||||
|
pnpacpi_parse_allocated_ioresource(dev,
|
||||||
|
p->minimum, p->address_length,
|
||||||
|
p->granularity == 0xfff ? ACPI_DECODE_10 :
|
||||||
|
ACPI_DECODE_16);
|
||||||
|
}
|
||||||
|
|
||||||
static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,
|
static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
|
@ -400,8 +419,7 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
|
case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
|
||||||
if (res->data.ext_address64.producer_consumer == ACPI_PRODUCER)
|
pnpacpi_parse_allocated_ext_address_space(dev, res);
|
||||||
return AE_OK;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
||||||
|
@ -630,6 +648,28 @@ static __init void pnpacpi_parse_address_option(struct pnp_dev *dev,
|
||||||
IORESOURCE_IO_FIXED);
|
IORESOURCE_IO_FIXED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev,
|
||||||
|
unsigned int option_flags,
|
||||||
|
struct acpi_resource *r)
|
||||||
|
{
|
||||||
|
struct acpi_resource_extended_address64 *p = &r->data.ext_address64;
|
||||||
|
unsigned char flags = 0;
|
||||||
|
|
||||||
|
if (p->address_length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (p->resource_type == ACPI_MEMORY_RANGE) {
|
||||||
|
if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY)
|
||||||
|
flags = IORESOURCE_MEM_WRITEABLE;
|
||||||
|
pnp_register_mem_resource(dev, option_flags, p->minimum,
|
||||||
|
p->minimum, 0, p->address_length,
|
||||||
|
flags);
|
||||||
|
} else if (p->resource_type == ACPI_IO_RANGE)
|
||||||
|
pnp_register_port_resource(dev, option_flags, p->minimum,
|
||||||
|
p->minimum, 0, p->address_length,
|
||||||
|
IORESOURCE_IO_FIXED);
|
||||||
|
}
|
||||||
|
|
||||||
struct acpipnp_parse_option_s {
|
struct acpipnp_parse_option_s {
|
||||||
struct pnp_dev *dev;
|
struct pnp_dev *dev;
|
||||||
unsigned int option_flags;
|
unsigned int option_flags;
|
||||||
|
@ -711,6 +751,7 @@ static __init acpi_status pnpacpi_option_resource(struct acpi_resource *res,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
|
case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
|
||||||
|
pnpacpi_parse_ext_address_option(dev, option_flags, res);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
||||||
|
@ -765,6 +806,7 @@ static int pnpacpi_supported_resource(struct acpi_resource *res)
|
||||||
case ACPI_RESOURCE_TYPE_ADDRESS16:
|
case ACPI_RESOURCE_TYPE_ADDRESS16:
|
||||||
case ACPI_RESOURCE_TYPE_ADDRESS32:
|
case ACPI_RESOURCE_TYPE_ADDRESS32:
|
||||||
case ACPI_RESOURCE_TYPE_ADDRESS64:
|
case ACPI_RESOURCE_TYPE_ADDRESS64:
|
||||||
|
case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
|
||||||
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,10 +114,13 @@ struct acpi_device_ops {
|
||||||
acpi_op_notify notify;
|
acpi_op_notify notify;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define ACPI_DRIVER_ALL_NOTIFY_EVENTS 0x1 /* system AND device events */
|
||||||
|
|
||||||
struct acpi_driver {
|
struct acpi_driver {
|
||||||
char name[80];
|
char name[80];
|
||||||
char class[80];
|
char class[80];
|
||||||
const struct acpi_device_id *ids; /* Supported Hardware IDs */
|
const struct acpi_device_id *ids; /* Supported Hardware IDs */
|
||||||
|
unsigned int flags;
|
||||||
struct acpi_device_ops ops;
|
struct acpi_device_ops ops;
|
||||||
struct device_driver drv;
|
struct device_driver drv;
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
|
@ -168,7 +171,7 @@ struct acpi_device_dir {
|
||||||
|
|
||||||
/* Plug and Play */
|
/* Plug and Play */
|
||||||
|
|
||||||
typedef char acpi_bus_id[5];
|
typedef char acpi_bus_id[8];
|
||||||
typedef unsigned long acpi_bus_address;
|
typedef unsigned long acpi_bus_address;
|
||||||
typedef char acpi_hardware_id[15];
|
typedef char acpi_hardware_id[15];
|
||||||
typedef char acpi_unique_id[9];
|
typedef char acpi_unique_id[9];
|
||||||
|
@ -365,10 +368,10 @@ struct acpi_bus_type {
|
||||||
int register_acpi_bus_type(struct acpi_bus_type *);
|
int register_acpi_bus_type(struct acpi_bus_type *);
|
||||||
int unregister_acpi_bus_type(struct acpi_bus_type *);
|
int unregister_acpi_bus_type(struct acpi_bus_type *);
|
||||||
struct device *acpi_get_physical_device(acpi_handle);
|
struct device *acpi_get_physical_device(acpi_handle);
|
||||||
struct device *acpi_get_physical_pci_device(acpi_handle);
|
|
||||||
|
|
||||||
/* helper */
|
/* helper */
|
||||||
acpi_handle acpi_get_child(acpi_handle, acpi_integer);
|
acpi_handle acpi_get_child(acpi_handle, acpi_integer);
|
||||||
|
int acpi_is_root_bridge(acpi_handle);
|
||||||
acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int);
|
acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int);
|
||||||
#define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)((dev)->archdata.acpi_handle))
|
#define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)((dev)->archdata.acpi_handle))
|
||||||
|
|
||||||
|
|
|
@ -57,8 +57,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define ACPI_POWER_HID "LNXPOWER"
|
#define ACPI_POWER_HID "LNXPOWER"
|
||||||
#define ACPI_PROCESSOR_OBJECT_HID "ACPI_CPU"
|
#define ACPI_PROCESSOR_OBJECT_HID "LNXCPU"
|
||||||
#define ACPI_PROCESSOR_HID "ACPI0007"
|
|
||||||
#define ACPI_SYSTEM_HID "LNXSYSTM"
|
#define ACPI_SYSTEM_HID "LNXSYSTM"
|
||||||
#define ACPI_THERMAL_HID "LNXTHERM"
|
#define ACPI_THERMAL_HID "LNXTHERM"
|
||||||
#define ACPI_BUTTON_HID_POWERF "LNXPWRBN"
|
#define ACPI_BUTTON_HID_POWERF "LNXPWRBN"
|
||||||
|
@ -91,17 +90,15 @@ int acpi_pci_link_free_irq(acpi_handle handle);
|
||||||
|
|
||||||
/* ACPI PCI Interrupt Routing (pci_irq.c) */
|
/* ACPI PCI Interrupt Routing (pci_irq.c) */
|
||||||
|
|
||||||
int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus);
|
int acpi_pci_irq_add_prt(acpi_handle handle, struct pci_bus *bus);
|
||||||
void acpi_pci_irq_del_prt(int segment, int bus);
|
void acpi_pci_irq_del_prt(struct pci_bus *bus);
|
||||||
|
|
||||||
/* ACPI PCI Device Binding (pci_bind.c) */
|
/* ACPI PCI Device Binding (pci_bind.c) */
|
||||||
|
|
||||||
struct pci_bus;
|
struct pci_bus;
|
||||||
|
|
||||||
acpi_status acpi_get_pci_id(acpi_handle handle, struct acpi_pci_id *id);
|
struct pci_dev *acpi_get_pci_dev(acpi_handle);
|
||||||
int acpi_pci_bind(struct acpi_device *device);
|
int acpi_pci_bind_root(struct acpi_device *device);
|
||||||
int acpi_pci_bind_root(struct acpi_device *device, struct acpi_pci_id *id,
|
|
||||||
struct pci_bus *bus);
|
|
||||||
|
|
||||||
/* Arch-defined function to add a bus to the system */
|
/* Arch-defined function to add a bus to the system */
|
||||||
|
|
||||||
|
|
|
@ -258,6 +258,7 @@ DECLARE_PER_CPU(struct acpi_processor *, processors);
|
||||||
extern struct acpi_processor_errata errata;
|
extern struct acpi_processor_errata errata;
|
||||||
|
|
||||||
void arch_acpi_processor_init_pdc(struct acpi_processor *pr);
|
void arch_acpi_processor_init_pdc(struct acpi_processor *pr);
|
||||||
|
void arch_acpi_processor_cleanup_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,
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
|
|
||||||
#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
|
#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
|
||||||
extern int acpi_video_register(void);
|
extern int acpi_video_register(void);
|
||||||
extern int acpi_video_exit(void);
|
extern void acpi_video_unregister(void);
|
||||||
#else
|
#else
|
||||||
static inline int acpi_video_register(void) { return 0; }
|
static inline int acpi_video_register(void) { return 0; }
|
||||||
static inline void acpi_video_exit(void) { return; }
|
static inline void acpi_video_unregister(void) { return; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -113,9 +113,6 @@ void acpi_irq_stats_init(void);
|
||||||
extern u32 acpi_irq_handled;
|
extern u32 acpi_irq_handled;
|
||||||
extern u32 acpi_irq_not_handled;
|
extern u32 acpi_irq_not_handled;
|
||||||
|
|
||||||
extern struct acpi_mcfg_allocation *pci_mmcfg_config;
|
|
||||||
extern int pci_mmcfg_config_num;
|
|
||||||
|
|
||||||
extern int sbf_port;
|
extern int sbf_port;
|
||||||
extern unsigned long acpi_realmode_flags;
|
extern unsigned long acpi_realmode_flags;
|
||||||
|
|
||||||
|
@ -293,7 +290,10 @@ void __init acpi_s4_no_nvs(void);
|
||||||
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)
|
OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL)
|
||||||
|
|
||||||
extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags);
|
extern acpi_status acpi_pci_osc_control_set(acpi_handle handle, u32 flags);
|
||||||
|
extern void acpi_early_init(void);
|
||||||
|
|
||||||
#else /* CONFIG_ACPI */
|
#else /* CONFIG_ACPI */
|
||||||
|
static inline void acpi_early_init(void) { }
|
||||||
|
|
||||||
static inline int early_acpi_boot_init(void)
|
static inline int early_acpi_boot_init(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -229,7 +229,6 @@ struct hotplug_params {
|
||||||
extern acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
|
extern acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
|
||||||
struct hotplug_params *hpp);
|
struct hotplug_params *hpp);
|
||||||
int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags);
|
int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags);
|
||||||
int acpi_root_bridge(acpi_handle handle);
|
|
||||||
int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle);
|
int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle);
|
||||||
int acpi_pci_detect_ejectable(struct pci_bus *pbus);
|
int acpi_pci_detect_ejectable(struct pci_bus *pbus);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <linux/smp_lock.h>
|
#include <linux/smp_lock.h>
|
||||||
#include <linux/initrd.h>
|
#include <linux/initrd.h>
|
||||||
#include <linux/bootmem.h>
|
#include <linux/bootmem.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
#include <linux/tty.h>
|
#include <linux/tty.h>
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
#include <linux/percpu.h>
|
#include <linux/percpu.h>
|
||||||
|
@ -88,11 +89,6 @@ extern void sbus_init(void);
|
||||||
extern void prio_tree_init(void);
|
extern void prio_tree_init(void);
|
||||||
extern void radix_tree_init(void);
|
extern void radix_tree_init(void);
|
||||||
extern void free_initmem(void);
|
extern void free_initmem(void);
|
||||||
#ifdef CONFIG_ACPI
|
|
||||||
extern void acpi_early_init(void);
|
|
||||||
#else
|
|
||||||
static inline void acpi_early_init(void) { }
|
|
||||||
#endif
|
|
||||||
#ifndef CONFIG_DEBUG_RODATA
|
#ifndef CONFIG_DEBUG_RODATA
|
||||||
static inline void mark_rodata_ro(void) { }
|
static inline void mark_rodata_ro(void) { }
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Reference in a new issue