diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2009-01-16 12:56:15 +1100 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2009-01-16 12:56:15 +1100 |
commit | 1aa0144b7b0efa0fc1ba45171a07647793e69984 (patch) | |
tree | 0b8c1225cd3d886e902949fe3f33606381ba0e61 | |
parent | 7ecd661e06385157141a0e8edaa2839637e5f72d (diff) | |
parent | 073ce4be3d0550c93664f18845831a86388b9f55 (diff) |
Merge commit 'acpi/test'
-rw-r--r-- | MAINTAINERS | 5 | ||||
-rw-r--r-- | arch/ia64/kernel/kprobes.c | 2 | ||||
-rw-r--r-- | arch/x86/kernel/kprobes.c | 2 | ||||
-rw-r--r-- | drivers/acpi/pci_link.c | 39 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 25 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 2 | ||||
-rw-r--r-- | drivers/platform/x86/dell-laptop.c (renamed from drivers/misc/dell-laptop.c) | 2 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 4 | ||||
-rw-r--r-- | drivers/platform/x86/oqo-wmi.c | 941 | ||||
-rw-r--r-- | include/acpi/actbl.h | 2 | ||||
-rw-r--r-- | kernel/kprobes.c | 2 | ||||
-rw-r--r-- | kernel/power/Makefile | 3 |
13 files changed, 1000 insertions, 30 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index c9eb6b5a6c7b..a5975c5f52d9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3245,6 +3245,11 @@ M: robert.richter@amd.com L: oprofile-list@lists.sf.net S: Maintained +OQO WMI EXTRAS DRIVER +P: Matthew Garrett +M: mjg59@srcf.ucam.org +S: Maintained + ORACLE CLUSTER FILESYSTEM 2 (OCFS2) P: Mark Fasheh M: mfasheh@suse.com diff --git a/arch/ia64/kernel/kprobes.c b/arch/ia64/kernel/kprobes.c index f90be51b1123..9adac441ac9b 100644 --- a/arch/ia64/kernel/kprobes.c +++ b/arch/ia64/kernel/kprobes.c @@ -870,7 +870,7 @@ static int __kprobes pre_kprobes_handler(struct die_args *args) return 1; ss_probe: -#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM) +#if !defined(CONFIG_PREEMPT) || defined(CONFIG_FREEZER) if (p->ainsn.inst_flag == INST_FLAG_BOOSTABLE && !p->post_handler) { /* Boost up -- we can execute copied instructions directly */ ia64_psr(regs)->ri = p->ainsn.slot; diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index 884d985b8b82..e948b28a5a9a 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c @@ -446,7 +446,7 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, static void __kprobes setup_singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb) { -#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM) +#if !defined(CONFIG_PREEMPT) || defined(CONFIG_FREEZER) if (p->ainsn.boostable == 1 && !p->post_handler) { /* Boost up -- we can execute copied instructions directly */ reset_current_kprobe(); diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 1c6e73c7865e..4b084d8dc85b 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -414,7 +414,8 @@ static int acpi_pci_link_set(struct acpi_pci_link *link, int irq) link->irq.active = irq; } - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Set IRQ %d\n", link->irq.active)); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link %s set to IRQ %d\n", + acpi_device_bid(link->device), link->irq.active)); end: kfree(resource); @@ -649,6 +650,9 @@ acpi_pci_link_allocate_irq(acpi_handle handle, return -1; } link->refcnt++; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Link %s is referenced %d\n", + acpi_device_bid(link->device), link->refcnt)); mutex_unlock(&acpi_link_lock); if (triggering) @@ -657,9 +661,6 @@ acpi_pci_link_allocate_irq(acpi_handle handle, *polarity = link->irq.polarity; if (name) *name = acpi_device_bid(link->device); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Link %s is referenced\n", - acpi_device_bid(link->device))); return (link->irq.active); } @@ -692,23 +693,15 @@ int acpi_pci_link_free_irq(acpi_handle handle) printk(KERN_ERR PREFIX "Link isn't initialized\n"); return -1; } -#ifdef FUTURE_USE - /* - * The Link reference count allows us to _DISable an unused link - * and suspend time, and set it again on resume. - * However, 2.6.12 still has irq_router.resume - * which blindly restores the link state. - * So we disable the reference count method - * to prevent duplicate acpi_pci_link_set() - * which would harm some systems - */ link->refcnt--; -#endif ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Link %s is dereferenced\n", - acpi_device_bid(link->device))); + "Link %s is dereferenced %d\n", + acpi_device_bid(link->device), link->refcnt)); if (link->refcnt == 0) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Link %s is disabled\n", + acpi_device_bid(link->device))); acpi_evaluate_object(link->device->handle, "_DIS", NULL, NULL); } mutex_unlock(&acpi_link_lock); @@ -782,13 +775,17 @@ static int acpi_pci_link_add(struct acpi_device *device) return result; } -static int acpi_pci_link_resume(struct acpi_pci_link *link) +static void acpi_pci_link_resume(struct acpi_pci_link *link) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link %s resume ref %d act %d ini %d\n", + acpi_device_bid(link->device), link->refcnt, + link->irq.active, link->irq.initialized)); + if (link->refcnt && link->irq.active && link->irq.initialized) - return (acpi_pci_link_set(link, link->irq.active)); - else - return 0; + acpi_pci_link_set(link, link->irq.active); + + return; } static int irqrouter_resume(struct sys_device *dev) diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 9cf8ae6e4b39..d5749a7bc777 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -10,7 +10,6 @@ obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o obj-$(CONFIG_ICS932S401) += ics932s401.o obj-$(CONFIG_LKDTM) += lkdtm.o obj-$(CONFIG_TIFM_CORE) += tifm_core.o -obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_PHANTOM) += phantom.o obj-$(CONFIG_SGI_IOC4) += ioc4.o diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index e65448e99b48..017fb8173425 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -54,6 +54,18 @@ config ASUS_LAPTOP If you have an ACPI-compatible ASUS laptop, say Y or M here. +config DELL_LAPTOP + tristate "Dell Laptop Extras (EXPERIMENTAL)" + depends on X86 + depends on DCDBAS + depends on EXPERIMENTAL + depends on BACKLIGHT_CLASS_DEVICE + depends on RFKILL + default n + ---help--- + This driver adds support for rfkill and backlight control to Dell + laptops. + config FUJITSU_LAPTOP tristate "Fujitsu Laptop Extras" depends on ACPI @@ -120,6 +132,19 @@ config MSI_LAPTOP If you have an MSI S270 laptop, say Y or M here. +config OQO_WMI + tristate "OQO WMI extras" + depends on ACPI_WMI + depends on INPUT + depends on RFKILL + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to support rfkill and backlight control on + series 2 OQO handheld devices. + + To compile this driver as a module, choose M here: the module will + be called oqo-wmi. + config PANASONIC_LAPTOP tristate "Panasonic Laptop Extras" depends on INPUT && ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 1e9de2ae0de5..37ac07f2362d 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -6,12 +6,14 @@ obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o +obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o obj-$(CONFIG_ACER_WMI) += acer-wmi.o obj-$(CONFIG_HP_WMI) += hp-wmi.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o +obj-$(CONFIG_OQO_WMI) += oqo-wmi.o obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_ACPI_WMI) += wmi.o diff --git a/drivers/misc/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 4d33a2068b7a..16e11c2ee19a 100644 --- a/drivers/misc/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -22,7 +22,7 @@ #include <linux/rfkill.h> #include <linux/power_supply.h> #include <linux/acpi.h> -#include "../firmware/dcdbas.h" +#include "../../firmware/dcdbas.h" #define BRIGHTNESS_TOKEN 0x7d diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 02fe2b8b8939..9d93cb971e59 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -90,7 +90,7 @@ enum { }; static const char *cm_getv[] = { - "WLDG", NULL, NULL, NULL, + "WLDG", "BTHG", NULL, NULL, "CAMG", NULL, NULL, NULL, NULL, "PBLG", NULL, NULL, "CFVG", NULL, NULL, NULL, @@ -99,7 +99,7 @@ static const char *cm_getv[] = { }; static const char *cm_setv[] = { - "WLDS", NULL, NULL, NULL, + "WLDS", "BTHS", NULL, NULL, "CAMS", NULL, NULL, NULL, "SDSP", "PBLS", "HDPS", NULL, "CFVS", NULL, NULL, NULL, diff --git a/drivers/platform/x86/oqo-wmi.c b/drivers/platform/x86/oqo-wmi.c new file mode 100644 index 000000000000..940e605cb460 --- /dev/null +++ b/drivers/platform/x86/oqo-wmi.c @@ -0,0 +1,941 @@ +/* + * OQO WMI UPMC Extras + * + * Copyright (C) 2008 Brian S. Julin <bri@abrij.org> + * + * Based on acer-wmi: + * Copyright (C) 2007-2008 Carlos Corbacho <cathectic@gmail.com> + * + * 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 + */ + +/* + * NOTE: You need to turn SMI on in BIOS (if dmidecode works, you already have) + * NOTE: acpi-wmi support mandatory + * NOTE: backlight and inputdev support a must, ifdefs will come later + */ + +/* + * + * 0.3: added WLAN enable switch, restore settings on unload, + * resume/suspend handling + * 0.2: Still not production-ready, but added ambient light sensor, + * backlight, and it prints the unit serial number to dmesg (do + * not know where to make that available to userspace yet.) + * 0.1: This is a first cut. Plan to reboot after playing with this. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/dmi.h> +#include <linux/backlight.h> +#include <linux/leds.h> +#include <linux/platform_device.h> +#include <linux/acpi.h> +#include <linux/i8042.h> +#include <linux/input-polldev.h> +#include <linux/rfkill.h> + +#include <acpi/acpi_drivers.h> + +MODULE_AUTHOR("Brian Julin"); +MODULE_DESCRIPTION("OQO UPMC WMI Extras Driver"); +MODULE_LICENSE("GPL"); + +#define OQO_LOGPREFIX "oqo-wmi: " +#define OQO_ERR KERN_ERR OQO_LOGPREFIX +#define OQO_NOTICE KERN_NOTICE OQO_LOGPREFIX +#define OQO_INFO KERN_INFO OQO_LOGPREFIX + +#define OQO_KINE_MAXTRY 3 + +/* Store defined devices globally since we only have one instance. */ +static struct platform_device *oqo_platform_device; +static struct backlight_device *oqo_backlight_device; +static struct rfkill *oqo_rfkill; +static struct input_dev *oqo_kine; +static struct input_polled_dev *oqo_kine_polled; + +/* Likewise store current and original settings globally. */ +struct oqo_settings { + int lid_wakes; /* not sure if ACPI handles/needs help here */ + int kine_itvl; + int bl_bright; +}; + +static struct oqo_settings orig, curr; + +/* Some of this code is left like in acer-wmi so we can add the older + Model 01 and any future models more easily, but we should not expect + it to be as complicated as Acer given each model is a leap rather than + a subtle variant on the last, so we aren't using "quirks" perse. Not + sure if there is any real difference for our purposes between the o2 + and e2. +*/ +struct oqo_model { + const char *model; + u16 model_subs; +}; +#define MODEL_SUB_OQO_O2_SMB0 3 + +static struct oqo_model oqo_models[] = { + { + .model = "Model 2", + .model_subs = MODEL_SUB_OQO_O2_SMB0, + }, + {} +}; + +static struct oqo_model *model; + +static int force; +module_param(force, bool, 0644); +MODULE_PARM_DESC(force, "Force WMI detection even if DMI detection failed"); + +/* + * OQO Model 2 SMBUS registers + * We are just using WMI to read the Cx700 smbus, to share the + * ACPI mutex (what may also eventually work in VMs/win32) + * Using i2c-viapro directly could interfere with PM. + */ + +#define OQO_O2_SMB0_WWAN_DSBL_ADDR 0x19 +#define OQO_O2_SMB0_WWAN_DSBL_MASK 0x02 +#define OQO_O2_SMB0_LUMIN_LO 0x20 +#define OQO_O2_SMB0_LUMIN_HI 0x21 +#define OQO_O2_SMB0_BL_LO 0x26 +#define OQO_O2_SMB0_BL_HI 0x27 +#define OQO_O2_SMB0_ACCEL_POLL_ITVL 0x45 +#define OQO_O2_SMB0_ACCEL_XLO 0x50 +#define OQO_O2_SMB0_ACCEL_XHI 0x51 +#define OQO_O2_SMB0_ACCEL_YLO 0x52 +#define OQO_O2_SMB0_ACCEL_YHI 0x53 +#define OQO_O2_SMB0_ACCEL_ZLO 0x54 +#define OQO_O2_SMB0_ACCEL_ZHI 0x55 +/* These may be handled by ACPI not sure yet. */ +#define OQO_O2_SMB0_LID_WAKES_ADDR 0x58 +#define OQO_O2_SMB0_LID_WAKES_MASK 0x08 + +#define OQO_O2_SMB0_SERIAL_START 0x70 +#define OQO_O2_SMB0_SERIAL_LEN 11 + +static char oqo_sn[OQO_O2_SMB0_SERIAL_LEN + 1]; + +/* Other addresses I have noticed used on the 02 SMBUS (from DSDT and whatnot) + * + * These are not used because the linux ACPI drivers work fine on them + * + * 0x0A -- processor sleep mode? + * 0x0C -- ACPI events, probably clears when read. + * 0x30 -- thermal zone + * There is something going on at 0x31 through 0x34 which is likely + * also thermal. The values change over time. Have not figured that + * out yet. + * 0x41 -- AC detect + * 0x42 -- LID button ACTUALLY THIS DOES NOT WORK AND NEEDS TO BE FIXED + * 0xa0 and 0xa1 -- battery something (presence? state?) + * 0xa4 to 0xcf -- battery info (0xc8-0xca contains "OQO") + * 0xd4 to 0xef -- other battery stats + */ + +/* + * OQO method GUIDs + */ +#define OQO_O2_AMW0_GUID "ABBC0F6D-8EA1-11D1-00A0-C90629100000" +MODULE_ALIAS("wmi:ABBC0F6D-8EA1-11D1-00A0-C90629100000"); + +/* + * Interface type flags + */ +enum interface_type { + OQO_O2_AMW0, +}; + +/* Each low-level interface must define at least some of the following */ +struct wmi_interface { + /* The WMI device type */ + u32 type; +}; + +static struct wmi_interface AMW0_interface = { + .type = OQO_O2_AMW0, +}; + +/* The detected/chosen interface */ +static struct wmi_interface *interface; + +static int dmi_matched(const struct dmi_system_id *dmi) +{ + model = dmi->driver_data; + /* + * Detect which ACPI-WMI interface we're using. + */ + if (wmi_has_guid(OQO_O2_AMW0_GUID)) + interface = &AMW0_interface; + + return 0; +} + +static struct dmi_system_id oqo_dmis[] = { + { + .callback = dmi_matched, + .ident = "OQO 02", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "OQO Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OQO Model 2"), + }, + .driver_data = oqo_models + 0, + }, + {} +}; + +/* + * AMW0 (V1) interface on OQO Model 2 + * + * wmba: has four functions selected by int arg 1. arg2 is 3 byte buffer. + * 1: performs GETB method on the SMBUS using bytes 0, 1 of Arg2 + * returns a buffer object containing a single byte + * 2: performs SETB on SMBUS using bytes 0, 1, 2 of Arg2 + * returns 0 as int. + * 3: dumps 256 values into a given SMBUS register (not used here) + * returns 0 as int. + * 4: puts byte 0 of arg2 into some sort of busy flag. Some ACPI + * funcs check this (==0) to decide if SMBUS operations are safe. + * returns 0 as int. + * wmbb: simply returns the busy flag set by wmba #4 + */ +static acpi_status oqo_smbus_getb(u8 addr, u8 *result) +{ + struct acpi_buffer input, res; + acpi_status status; + union acpi_object *obj; + u32 arg2; + + input.length = 4; + input.pointer = &arg2; + res.length = ACPI_ALLOCATE_BUFFER; + res.pointer = NULL; + + arg2 = addr; + arg2 <<= 8; + arg2 |= 0x12; /* HOSTCMD */ + + status = wmi_evaluate_method(OQO_O2_AMW0_GUID, 1, 1, &input, &res); + + if (status != AE_OK) + return status; + + obj = (union acpi_object *)res.pointer; + if (!obj) + return AE_NULL_OBJECT; + + if (obj->type != ACPI_TYPE_BUFFER + || obj->buffer.length != 1 || obj->buffer.pointer == NULL) { + kfree(obj); + return AE_TYPE; + } + *result = ((u8 *) (obj->buffer.pointer))[0]; + kfree(obj); + return status; +} + +static acpi_status oqo_smbus_setb(u8 addr, u8 val) +{ + struct acpi_buffer input, res; + acpi_status status; + union acpi_object *obj; + u32 arg2; + + input.length = 4; + input.pointer = &arg2; + res.length = ACPI_ALLOCATE_BUFFER; + res.pointer = NULL; + + arg2 = val; + arg2 <<= 8; + arg2 |= addr; + arg2 <<= 8; + arg2 |= 0x12; /* HOSTCMD */ + + status = wmi_evaluate_method(OQO_O2_AMW0_GUID, 1, 2, &input, &res); + + if (status != AE_OK) + return status; + + obj = (union acpi_object *)res.pointer; + if (!obj) + return AE_NULL_OBJECT; + + if (obj->type != ACPI_TYPE_INTEGER) { + kfree(obj); + return AE_TYPE; + } + kfree(obj); + return status; +} + +/* + * We assume we are the only one using this ...ahem... "lock" on + * the SMBUS because it would be pathetically noneffective otherwise. + * + * Nonzero silly_lock will keep certain ACPI routines away from the + * SMBUS (if they aren't already on it when you call it.) Zero + * silly_lock will let them back on + * + * This is probably useful before sleeping the system, and one + * waits until any ACPI funcs would have long finished before + * proceeding. It seems harmless enough and will work to wrap + * more accesses with it. + */ +static acpi_status oqo_lock_smbus(int silly_lock) +{ + struct acpi_buffer input, res; + acpi_status status; + union acpi_object *obj; + u32 arg2; + + input.length = 4; + input.pointer = &arg2; + res.length = ACPI_ALLOCATE_BUFFER; + res.pointer = NULL; + + arg2 = !!silly_lock; + + status = wmi_evaluate_method(OQO_O2_AMW0_GUID, 1, 4, &input, &res); + + if (status != AE_OK) + return status; + + obj = (union acpi_object *)res.pointer; + if (!obj) + return AE_NULL_OBJECT; + + if (obj->type != ACPI_TYPE_INTEGER) { + kfree(obj); + return AE_TYPE; + } + kfree(obj); + return status; +} + +static int smread_s16(u8 hi_addr, u8 lo_addr) +{ + s16 ret = -1; + acpi_status status; + u8 r; + + /* Keep some ACPI routines off the SMBUS */ + status = oqo_lock_smbus(1); + if (ACPI_FAILURE(status)) + goto skip; + + status = oqo_smbus_getb(hi_addr, &r); + if (ACPI_FAILURE(status)) + goto skip; + + ret = r; + ret <<= 8; + + status = oqo_smbus_getb(lo_addr, &r); + if (ACPI_FAILURE(status)) { + ret = -1; + goto skip; + } + + ret |= r; + ret &= 0x7fff; +skip: + /* Let ACPI routines back on the SMBUS */ + status = oqo_lock_smbus(0); + if (ACPI_FAILURE(status)) + return -1; + return (int)ret; +} + +static int smwrite_s16(u8 hi_addr, u8 lo_addr, s16 val) +{ + acpi_status status; + u8 r; + int ret = -1; + + status = oqo_lock_smbus(1); + if (ACPI_FAILURE(status)) + goto skip; + + r = (val >> 8) & 0x7f; + status = oqo_smbus_setb(hi_addr, r); + if (ACPI_FAILURE(status)) + goto skip; + + r = val & 0xff; + status = oqo_smbus_setb(lo_addr, r); + if (ACPI_FAILURE(status)) + goto skip; + + ret = 0; +skip: + status = oqo_lock_smbus(0); + if (ACPI_FAILURE(status)) + return -1; + return ret; +} + +static int smread_u8(u8 addr) +{ + int ret = -1; + acpi_status status; + u8 r; + + status = oqo_lock_smbus(1); + if (ACPI_FAILURE(status)) + goto skip; + + status = oqo_smbus_getb(addr, &r); + if (ACPI_FAILURE(status)) + goto skip; + + ret = r; +skip: + status = oqo_lock_smbus(0); + if (ACPI_FAILURE(status)) + return -1; + return (int)ret; +} + +static int smwrite_u8(u8 addr, u8 val) +{ + acpi_status status; + int ret = -1; + + status = oqo_lock_smbus(1); + if (ACPI_FAILURE(status)) + goto skip; + + status = oqo_smbus_setb(addr, val); + if (ACPI_FAILURE(status)) + goto skip; + + ret = 0; +skip: + status = oqo_lock_smbus(0); + if (ACPI_FAILURE(status)) + return -1; + return ret; +} + +/* + * Accelerometer inputdev + */ + +/* + * Get a reading of the accelerometer from the firwmware and push + * it to an inputdev. + * + * Also the ambient light detector hitch-hikes on the inputdev, since + * it could be useful in some of the same applications for accelerometers. + * + * Available information and a bit of poking have not found a + * way to freeze a snapshot of the accelerometer data, so we have + * to do consistency checks to reduce the odds that we mix low + * and high bytes from different updates. + * + * Unfortunately SMBUS access is very slow (11ms) and the firmware API + * does not provide 2-byte transfers, so mixed readings happen and + * have to be corrected a lot. (Do not know why; it should be a + * multi-kHz.. bus and the reads take only a hundred-ish cycles/byte. + * It is not the ACPI function -- it is slow on i2c-viapro as well.) + * + * Since there is such a big time lag between readings, the axis + * are decoupled and reported separately on different timelines as + * different events rather than as a set. + */ +static acpi_status oqo_read_kine(int *good, s16 *x, s16 *y, s16 *z, + u16 *lumin) +{ + u8 hiregs[4] = { OQO_O2_SMB0_ACCEL_XHI, + OQO_O2_SMB0_ACCEL_YHI, + OQO_O2_SMB0_ACCEL_ZHI, + OQO_O2_SMB0_LUMIN_HI + }; + u8 loregs[4] = { OQO_O2_SMB0_ACCEL_XLO, + OQO_O2_SMB0_ACCEL_YLO, + OQO_O2_SMB0_ACCEL_ZLO, + OQO_O2_SMB0_LUMIN_LO + }; + + short ax[4] = { ABS_X, ABS_Y, ABS_Z, ABS_MISC }; + u8 realgood = 0; + u16 res[4]; + acpi_status status; + int i; + + *good = 0; + + /* Routine: Starting with the lo byte, read lo/hi bytes + alternately until two lo byte readings, match. Then + take that reading and combine it with the hi reading + sandwiched between. Errors can still happen when + jittering at wrap boundaries, but should be rare. + + Don't use this for missile guidance. + + Userspace post-processing error detection encouraged. + */ + for (i = 0; i < 4; i++) { + int maxtry; + u32 log; + u8 r, lo, hi; + + lo = loregs[i]; + hi = hiregs[i]; + log = 0; + +#define LOGRES(reg) do { \ + status = oqo_smbus_getb(reg, &r); \ + log <<= 8; log |= r; log &= 0xffffff; \ + if (ACPI_FAILURE(status)) \ + goto leave; \ + } while (0) + + maxtry = OQO_KINE_MAXTRY + 1; + while (maxtry) { + LOGRES(lo); + if (maxtry <= OQO_KINE_MAXTRY && + (log >> 16) == (log & 0xff)) { + *(res + i) = log & 0xffff; + break; + } + LOGRES(hi); + maxtry--; + } + + if (maxtry == OQO_KINE_MAXTRY) + realgood |= 1 << i; + + if (maxtry) { + *good |= 1 << i; + /* JIC CYA: this bit may be reserved */ + res[3] &= 0x7fff; + input_report_abs(oqo_kine, ax[i], (s16) res[i]); + } + /* else we had trouble getting the reading to lock + and we skip reporting this axis. + */ + } + + *x = (u16) res[0]; + *y = (u16) res[1]; + *z = (u16) res[2]; + *lumin = (u16) res[3]; + return status; +leave: + return status; +} + +/* + * Generic Device (interface-independent) + */ + +static void oqo_kine_poll(struct input_polled_dev *dev) +{ + s16 x, y, z; + u16 lumin; + int good; + /* struct timeval tv1, tv2; */ + + if (dev != oqo_kine_polled) + return; + if (orig.kine_itvl < 0) + return; + + x = y = z = 0; + oqo_read_kine(&good, &x, &y, &z, &lumin); +} + +static int __devinit oqo_kine_init(void) +{ + int err; + + oqo_kine = input_allocate_device(); + if (!oqo_kine) + return -ENOMEM; + + oqo_kine->name = "OQO embedded accelerometer"; + oqo_kine->phys = "platform:oqo-wmi:kine"; + oqo_kine->id.bustype = 0; + oqo_kine->id.vendor = 0; + oqo_kine->id.product = 2; + oqo_kine->id.version = 0; + oqo_kine->evbit[0] = BIT_MASK(EV_ABS); + set_bit(ABS_X, oqo_kine->absbit); + set_bit(ABS_Y, oqo_kine->absbit); + set_bit(ABS_Z, oqo_kine->absbit); + set_bit(ABS_MISC, oqo_kine->absbit); + oqo_kine->absmin[ABS_X] = + oqo_kine->absmin[ABS_Y] = + oqo_kine->absmin[ABS_Z] = oqo_kine->absmin[ABS_MISC] = -32768; + oqo_kine->absmax[ABS_X] = + oqo_kine->absmax[ABS_Y] = + oqo_kine->absmax[ABS_Z] = oqo_kine->absmax[ABS_MISC] = 32767; + + memcpy(oqo_kine->dev.bus_id, "kine", 4); + + oqo_kine_polled = input_allocate_polled_device(); + if (!oqo_kine_polled) { + err = -ENOMEM; + goto bail0; + } + + oqo_kine_polled->poll = oqo_kine_poll; + oqo_kine_polled->poll_interval = 250; + oqo_kine_polled->input = oqo_kine; + + orig.kine_itvl = -1; /* prevent callback from running */ + err = input_register_polled_device(oqo_kine_polled); + if (err) { + printk(OQO_ERR "Failed to register OQO kine input\n"); + goto bail1; + } + + /* This will allow the callback to run now if successful. */ + orig.kine_itvl = smread_u8(OQO_O2_SMB0_ACCEL_POLL_ITVL); + smwrite_u8(OQO_O2_SMB0_ACCEL_POLL_ITVL, 250); + curr.kine_itvl = smread_u8(OQO_O2_SMB0_ACCEL_POLL_ITVL); + if (orig.kine_itvl < 0 || curr.kine_itvl != 250) { + printk(OQO_ERR "Test communication with kine sensor failed\n"); + err = -ENODEV; + goto bail2; + } + + printk(OQO_INFO "Created OQO kine input.\n"); + printk(OQO_INFO "Firmware interval %ims, driver interval %ims\n", + curr.kine_itvl, oqo_kine_polled->poll_interval); + return 0; +bail2: + input_unregister_polled_device(oqo_kine_polled); +bail1: + input_free_polled_device(oqo_kine_polled); /* frees oqo_kine */ + return err; +bail0: + input_free_device(oqo_kine); + return err; +} + +static void __devexit oqo_kine_fini(void) +{ + smwrite_u8(OQO_O2_SMB0_ACCEL_POLL_ITVL, orig.kine_itvl); + input_unregister_polled_device(oqo_kine_polled); + input_free_polled_device(oqo_kine_polled); +} + +/* + * Backlight device + */ +static int read_brightness(struct backlight_device *bd) +{ + return (int)smread_s16(OQO_O2_SMB0_BL_HI, OQO_O2_SMB0_BL_LO); +} + +static int update_bl_status(struct backlight_device *bd) +{ + return smwrite_s16(OQO_O2_SMB0_BL_HI, + OQO_O2_SMB0_BL_LO, (s16) bd->props.brightness); +} + +static struct backlight_ops oqo_bl_ops = { + .get_brightness = read_brightness, + .update_status = update_bl_status, +}; + +static int __devinit oqo_backlight_init(struct device *dev) +{ + struct backlight_device *bd; + + /* + * It would be nice if someone would figure out how backlights + * like these, which are not driven through the video hardware, + * are supposed to find their associated fb and bind to it (and + * rebind when fb drivers change. + * + * Most extras backlights just shove a junk name in like we do here, + * and don't end up integrated with fbcon sysfs as a result. + */ + bd = backlight_device_register("oqo-bl", dev, NULL, &oqo_bl_ops); + + if (IS_ERR(bd)) { + printk(OQO_ERR "Could not register OQO backlight device\n"); + oqo_backlight_device = NULL; + return PTR_ERR(bd); + } + + oqo_backlight_device = bd; + bd->props.max_brightness = 0x7fff; + curr.bl_bright = orig.bl_bright = bd->props.brightness = + read_brightness(NULL); + + if (bd->props.brightness < 0) + goto fail; + + backlight_update_status(bd); + printk(OQO_INFO "Found backlight set at %i\n", bd->props.brightness); + return 0; + +fail: + backlight_device_unregister(oqo_backlight_device); + oqo_backlight_device = NULL; + return -ENODEV; +} + +static void __devexit oqo_backlight_fini(void) +{ + if (!oqo_backlight_device) + return; + oqo_backlight_device->props.brightness = orig.bl_bright; + backlight_update_status(oqo_backlight_device); + backlight_device_unregister(oqo_backlight_device); +} + +/* + * RFKill device + */ + +static int oqo_rfkill_get(void *data, enum rfkill_state *state) +{ + int res; + + res = smread_u8(OQO_O2_SMB0_WWAN_DSBL_ADDR); + if (res < 0) + return res; + + res &= OQO_O2_SMB0_WWAN_DSBL_MASK; + + if (res) + *state = RFKILL_STATE_SOFT_BLOCKED; + else + *state = RFKILL_STATE_UNBLOCKED; + + return 0; +} + +static int oqo_rfkill_toggle(void *data, enum rfkill_state state) +{ + int res; + + res = smread_u8(OQO_O2_SMB0_WWAN_DSBL_ADDR); + + if (state == RFKILL_STATE_UNBLOCKED) + res &= ~OQO_O2_SMB0_WWAN_DSBL_MASK; + else + res |= OQO_O2_SMB0_WWAN_DSBL_MASK; + + return smwrite_u8(OQO_O2_SMB0_WWAN_DSBL_ADDR, res); +} + +static int __devinit oqo_rfkill_init(struct device *dev) +{ + int res; + + oqo_rfkill = rfkill_allocate(dev, RFKILL_TYPE_WWAN); + if (!oqo_rfkill) + return -ENODEV; + + res = smread_u8(OQO_O2_SMB0_WWAN_DSBL_ADDR); + res &= OQO_O2_SMB0_WWAN_DSBL_MASK; + + oqo_rfkill->name = "oqo-wwan"; + if (res) + oqo_rfkill->state = RFKILL_STATE_SOFT_BLOCKED; + else + oqo_rfkill->state = RFKILL_STATE_UNBLOCKED; + + oqo_rfkill->get_state = oqo_rfkill_get; + oqo_rfkill->toggle_radio = oqo_rfkill_toggle; + oqo_rfkill->user_claim_unsupported = 1; + + res = rfkill_register(oqo_rfkill); + + if (res) + rfkill_free(oqo_rfkill); + + return res; +} + +static void __devexit oqo_rfkill_fini(void) +{ + if (!oqo_rfkill) + return; + rfkill_unregister(oqo_rfkill); +} + +/* + * Platform device + */ + +static int __devinit oqo_platform_probe(struct platform_device *device) +{ + int err; + int i; + char *troubleok = "trouble, but continuing.\n"; + + memset(oqo_sn, 0, OQO_O2_SMB0_SERIAL_LEN + 1); + for (i = 0; i < OQO_O2_SMB0_SERIAL_LEN; i++) { + err = oqo_smbus_getb(OQO_O2_SMB0_SERIAL_START + i, oqo_sn + i); + if (err) { + printk(OQO_ERR "Serial number check failed.\n"); + return err; + } + } + printk(OQO_INFO "Found OQO with serial number %s.\n", oqo_sn); + + err = oqo_backlight_init(&device->dev); + if (err) + printk(OQO_ERR "Backlight init %s", troubleok); + + err = oqo_rfkill_init(&device->dev); + if (err) + printk(OQO_ERR "RFKill init %s", troubleok); + + /* LID does not work at all yet, and this may be taken + care of by ACPI. + */ + orig.lid_wakes = smread_u8(OQO_O2_SMB0_LID_WAKES_ADDR); + orig.lid_wakes &= OQO_O2_SMB0_LID_WAKES_MASK; + orig.lid_wakes = curr.lid_wakes = !!orig.lid_wakes; + if (orig.lid_wakes < 0) { + printk(OQO_ERR "Wake on LID event %s", troubleok); + } else { + printk(OQO_INFO "Wake on LID is %s.\n", + (orig.lid_wakes ? "on" : "off")); + } + + err = oqo_kine_init(); + return err; +} + +static int oqo_platform_remove(struct platform_device *device) +{ + oqo_backlight_fini(); + oqo_rfkill_fini(); + oqo_kine_fini(); + + return 0; +} + +#ifdef CONFIG_PM + +static int oqo_platform_suspend(struct platform_device *dev, pm_message_t state) +{ + if (!interface) + return -ENOMEM; + + /* This sticks during boot so do not turn it entirely off */ + if (oqo_backlight_device) { + curr.bl_bright = read_brightness(oqo_backlight_device); + smwrite_s16(OQO_O2_SMB0_BL_HI, OQO_O2_SMB0_BL_LO, 256); + } + return 0; +} + +static int oqo_platform_resume(struct platform_device *device) +{ + if (!interface) + return -ENOMEM; + + if (oqo_backlight_device) { + smwrite_s16(OQO_O2_SMB0_BL_HI, + OQO_O2_SMB0_BL_LO, curr.bl_bright); + } + + return 0; +} + +#else +#define oqo_platform_suspend NULL +#define oqo_platform_resume NULL +#endif + +static struct platform_driver oqo_platform_driver = { + .driver = { + .name = "oqo-wmi", + .owner = THIS_MODULE, + }, + .probe = oqo_platform_probe, + .remove = oqo_platform_remove, + .suspend = oqo_platform_suspend, + .resume = oqo_platform_resume, +}; + +static int __init oqo_wmi_init(void) +{ + int err; + + dmi_check_system(oqo_dmis); + + if (!interface && force) { + model = oqo_models; + if (wmi_has_guid(OQO_O2_AMW0_GUID)) + interface = &AMW0_interface; + } + + if (!interface) { + printk(OQO_ERR "No or unsupported WMI interface. Aborting.\n"); + printk(OQO_ERR "Hint: Get dmidecode working and try again.\n"); + printk(OQO_ERR "(Check \"System Management BIOS\" in BIOS)\n"); + if (!force) + printk(OQO_ERR "Use the force option to skip DMI" + " checking\n"); + return -ENODEV; + } + + err = platform_driver_register(&oqo_platform_driver); + if (err) { + printk(OQO_ERR "platform_driver_register gave %d.\n", err); + goto bail0; + } + + oqo_platform_device = platform_device_alloc("oqo-wmi", -1); + if (!oqo_platform_device) { + printk(OQO_ERR "Could not allocate platform device.\n"); + err = -ENOMEM; + goto bail1; + } + + err = platform_device_add(oqo_platform_device); + if (err) { + printk(OQO_ERR "platform_device_add gave %d.\n", err); + platform_device_put(oqo_platform_device); + goto bail1; + } + + return 0; + +bail1: + platform_driver_unregister(&oqo_platform_driver); +bail0: + return err; +} + +static void __exit oqo_wmi_fini(void) +{ + platform_device_del(oqo_platform_device); + platform_driver_unregister(&oqo_platform_driver); + + return; +} + +module_init(oqo_wmi_init); +module_exit(oqo_wmi_fini); diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index 813e4b6c2c0d..bf8d4cfd8cf5 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -245,7 +245,7 @@ struct acpi_table_fadt { #define ACPI_FADT_POWER_BUTTON (1<<4) /* 04: Power button is handled as a generic feature */ #define ACPI_FADT_SLEEP_BUTTON (1<<5) /* 05: Sleep button is handled as a generic feature, or not present */ #define ACPI_FADT_FIXED_RTC (1<<6) /* 06: RTC wakeup stat not in fixed register space */ -#define ACPI_FADT_S4_RTC_WAKE (1<<7) /* 07: RTC wakeup stat not possible from S4 */ +#define ACPI_FADT_S4_RTC_WAKE (1<<7) /* 07: RTC wakeup possible from S4 */ #define ACPI_FADT_32BIT_TIMER (1<<8) /* 08: tmr_val is 32 bits 0=24-bits */ #define ACPI_FADT_DOCKING_SUPPORTED (1<<9) /* 09: Docking supported */ #define ACPI_FADT_RESET_REGISTER (1<<10) /* 10: System reset via the FADT RESET_REG supported */ diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 1b9cbdc0127a..7ba8cd9845cb 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -123,7 +123,7 @@ static int collect_garbage_slots(void); static int __kprobes check_safety(void) { int ret = 0; -#if defined(CONFIG_PREEMPT) && defined(CONFIG_PM) +#if defined(CONFIG_PREEMPT) && defined(CONFIG_FREEZER) ret = freeze_processes(); if (ret == 0) { struct task_struct *p, *q; diff --git a/kernel/power/Makefile b/kernel/power/Makefile index 597823b5b700..d7a10167a25b 100644 --- a/kernel/power/Makefile +++ b/kernel/power/Makefile @@ -4,7 +4,8 @@ EXTRA_CFLAGS += -DDEBUG endif obj-y := main.o -obj-$(CONFIG_PM_SLEEP) += process.o console.o +obj-$(CONFIG_PM_SLEEP) += console.o +obj-$(CONFIG_FREEZER) += process.o obj-$(CONFIG_HIBERNATION) += swsusp.o disk.o snapshot.o swap.o user.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o |