summaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2012-04-08 21:48:52 +0200
committerJiri Kosina <jkosina@suse.cz>2012-04-08 21:48:52 +0200
commite75d660672ddd11704b7f0fdb8ff21968587b266 (patch)
treeccb9c107744c10b553c0373e450bee3971d16c00 /drivers/platform
parent61282f37927143e45b03153f3e7b48d6b702147a (diff)
parent0034102808e0dbbf3a2394b82b1bb40b5778de9e (diff)
downloadlinux-e75d660672ddd11704b7f0fdb8ff21968587b266.tar.gz
linux-e75d660672ddd11704b7f0fdb8ff21968587b266.tar.xz
linux-e75d660672ddd11704b7f0fdb8ff21968587b266.zip
Merge branch 'master' into for-next
Merge with latest Linus' tree, as I have incoming patches that fix code that is newer than current HEAD of for-next. Conflicts: drivers/net/ethernet/realtek/r8169.c
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/Kconfig119
-rw-r--r--drivers/platform/x86/Makefile9
-rw-r--r--drivers/platform/x86/acer-wmi.c182
-rw-r--r--drivers/platform/x86/acerhdf.c19
-rw-r--r--drivers/platform/x86/amilo-rfkill.c176
-rw-r--r--drivers/platform/x86/apple-gmux.c244
-rw-r--r--drivers/platform/x86/asus-laptop.c273
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c12
-rw-r--r--drivers/platform/x86/asus-wmi.c70
-rw-r--r--drivers/platform/x86/asus-wmi.h14
-rw-r--r--drivers/platform/x86/asus_acpi.c1513
-rw-r--r--drivers/platform/x86/compal-laptop.c14
-rw-r--r--drivers/platform/x86/dell-laptop.c34
-rw-r--r--drivers/platform/x86/eeepc-laptop.c15
-rw-r--r--drivers/platform/x86/eeepc-wmi.c108
-rw-r--r--drivers/platform/x86/fujitsu-tablet.c478
-rw-r--r--drivers/platform/x86/hdaps.c4
-rw-r--r--drivers/platform/x86/ibm_rtl.c15
-rw-r--r--drivers/platform/x86/intel_ips.c28
-rw-r--r--drivers/platform/x86/intel_mid_powerbtn.c44
-rw-r--r--drivers/platform/x86/intel_mid_thermal.c61
-rw-r--r--drivers/platform/x86/intel_oaktrail.c2
-rw-r--r--drivers/platform/x86/intel_rar_register.c669
-rw-r--r--drivers/platform/x86/intel_scu_ipc.c208
-rw-r--r--drivers/platform/x86/intel_scu_ipcutil.c32
-rw-r--r--drivers/platform/x86/panasonic-laptop.c4
-rw-r--r--drivers/platform/x86/samsung-laptop.c1749
-rw-r--r--drivers/platform/x86/sony-laptop.c15
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c2
-rw-r--r--drivers/platform/x86/toshiba_acpi.c254
-rw-r--r--drivers/platform/x86/xo1-rfkill.c13
31 files changed, 3009 insertions, 3371 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index f995e6e2f78c..2a262f5c5c0c 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -26,6 +26,10 @@ config ACER_WMI
depends on RFKILL || RFKILL = n
depends on ACPI_WMI
select INPUT_SPARSEKMAP
+ # Acer WMI depends on ACPI_VIDEO when ACPI is enabled
+ # but for select to work, need to select ACPI_VIDEO's dependencies, ick
+ select VIDEO_OUTPUT_CONTROL if ACPI
+ select ACPI_VIDEO if ACPI
---help---
This is a driver for newer Acer (and Wistron) laptops. It adds
wireless radio and bluetooth control, and on some laptops,
@@ -54,7 +58,6 @@ config ACERHDF
config ASUS_LAPTOP
tristate "Asus Laptop Extras"
depends on ACPI
- depends on !ACPI_ASUS
select LEDS_CLASS
select NEW_LEDS
select BACKLIGHT_CLASS_DEVICE
@@ -143,6 +146,30 @@ config FUJITSU_LAPTOP_DEBUG
If you are not sure, say N here.
+config FUJITSU_TABLET
+ tristate "Fujitsu Tablet Extras"
+ depends on ACPI
+ depends on INPUT
+ ---help---
+ This is a driver for tablets built by Fujitsu:
+
+ * Lifebook P1510/P1610/P1620/Txxxx
+ * Stylistic ST5xxx
+ * Possibly other Fujitsu tablet models
+
+ It adds support for the panel buttons, docking station detection,
+ tablet/notebook mode detection for convertible and
+ orientation detection for docked slates.
+
+ If you have a Fujitsu convertible or slate, say Y or M here.
+
+config AMILO_RFKILL
+ tristate "Fujitsu-Siemens Amilo rfkill support"
+ depends on RFKILL
+ ---help---
+ This is a driver for enabling wifi on some Fujitsu-Siemens Amilo
+ laptops.
+
config TC1100_WMI
tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)"
depends on !X86_64
@@ -436,10 +463,9 @@ config INTEL_MENLOW
If unsure, say N.
config EEEPC_LAPTOP
- tristate "Eee PC Hotkey Driver (EXPERIMENTAL)"
+ tristate "Eee PC Hotkey Driver"
depends on ACPI
depends on INPUT
- depends on EXPERIMENTAL
depends on RFKILL || RFKILL = n
depends on HOTPLUG_PCI
select BACKLIGHT_CLASS_DEVICE
@@ -458,11 +484,10 @@ config EEEPC_LAPTOP
doesn't work on your Eee PC, try eeepc-wmi instead.
config ASUS_WMI
- tristate "ASUS WMI Driver (EXPERIMENTAL)"
+ tristate "ASUS WMI Driver"
depends on ACPI_WMI
depends on INPUT
depends on HWMON
- depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE
depends on RFKILL || RFKILL = n
depends on HOTPLUG_PCI
@@ -477,7 +502,7 @@ config ASUS_WMI
be called asus-wmi.
config ASUS_NB_WMI
- tristate "Asus Notebook WMI Driver (EXPERIMENTAL)"
+ tristate "Asus Notebook WMI Driver"
depends on ASUS_WMI
---help---
This is a driver for newer Asus notebooks. It adds extra features
@@ -490,7 +515,7 @@ config ASUS_NB_WMI
here.
config EEEPC_WMI
- tristate "Eee PC WMI Driver (EXPERIMENTAL)"
+ tristate "Eee PC WMI Driver"
depends on ASUS_WMI
---help---
This is a driver for newer Eee PC laptops. It adds extra features
@@ -535,38 +560,6 @@ config MSI_WMI
To compile this driver as a module, choose M here: the module will
be called msi-wmi.
-config ACPI_ASUS
- tristate "ASUS/Medion Laptop Extras (DEPRECATED)"
- depends on ACPI
- select BACKLIGHT_CLASS_DEVICE
- ---help---
- This driver provides support for extra features of ACPI-compatible
- ASUS laptops. As some of Medion laptops are made by ASUS, it may also
- support some Medion laptops (such as 9675 for example). It makes all
- the extra buttons generate standard ACPI events that go through
- /proc/acpi/events, and (on some models) adds support for changing the
- display brightness and output, switching the LCD backlight on and off,
- and most importantly, allows you to blink those fancy LEDs intended
- for reporting mail and wireless status.
-
- Note: display switching code is currently considered EXPERIMENTAL,
- toying with these values may even lock your machine.
-
- All settings are changed via /proc/acpi/asus directory entries. Owner
- and group for these entries can be set with asus_uid and asus_gid
- parameters.
-
- More information and a userspace daemon for handling the extra buttons
- at <http://acpi4asus.sf.net>.
-
- 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
- something works not quite as expected, please use the mailing list
- available on the above page (acpi4asus-user@lists.sourceforge.net).
-
- NOTE: This driver is deprecated and will probably be removed soon,
- use asus-laptop instead.
-
config TOPSTAR_LAPTOP
tristate "Topstar Laptop Extras"
depends on ACPI
@@ -580,6 +573,7 @@ config TOPSTAR_LAPTOP
config ACPI_TOSHIBA
tristate "Toshiba Laptop Extras"
depends on ACPI
+ depends on ACPI_WMI
select LEDS_CLASS
select NEW_LEDS
depends on BACKLIGHT_CLASS_DEVICE
@@ -672,33 +666,11 @@ config INTEL_MID_POWER_BUTTON
config INTEL_MFLD_THERMAL
tristate "Thermal driver for Intel Medfield platform"
- depends on INTEL_SCU_IPC && THERMAL
+ depends on MFD_INTEL_MSIC && THERMAL
help
Say Y here to enable thermal driver support for the Intel Medfield
platform.
-config RAR_REGISTER
- bool "Restricted Access Region Register Driver"
- depends on PCI && X86_MRST
- default n
- ---help---
- This driver allows other kernel drivers access to the
- contents of the restricted access region control registers.
-
- The restricted access region control registers
- (rar_registers) are used to pass address and
- locking information on restricted access regions
- to other drivers that use restricted access regions.
-
- The restricted access regions are regions of memory
- on the Intel MID Platform that are not accessible to
- the x86 processor, but are accessible to dedicated
- processors on board peripheral devices.
-
- The purpose of the restricted access regions is to
- protect sensitive data from compromise by unauthorized
- programs running on the x86 processor.
-
config INTEL_IPS
tristate "Intel Intelligent Power Sharing"
depends on ACPI
@@ -744,13 +716,18 @@ config XO15_EBOOK
config SAMSUNG_LAPTOP
tristate "Samsung Laptop driver"
- depends on RFKILL && BACKLIGHT_CLASS_DEVICE && X86
+ depends on X86
+ depends on RFKILL || RFKILL = n
+ depends on BACKLIGHT_CLASS_DEVICE
+ select LEDS_CLASS
+ select NEW_LEDS
---help---
This module implements a driver for a wide range of different
Samsung laptops. It offers control over the different
- function keys, wireless LED, LCD backlight level, and
- sometimes provides a "performance_control" sysfs file to allow
- the performance level of the laptop to be changed.
+ function keys, wireless LED, LCD backlight level.
+
+ It may also provide some sysfs files described in
+ <file:Documentation/ABI/testing/sysfs-platform-samsung-laptop>
To compile this driver as a module, choose M here: the module
will be called samsung-laptop.
@@ -779,4 +756,14 @@ config SAMSUNG_Q10
This driver provides support for backlight control on Samsung Q10
and related laptops, including Dell Latitude X200.
+config APPLE_GMUX
+ tristate "Apple Gmux Driver"
+ depends on PNP
+ select BACKLIGHT_CLASS_DEVICE
+ ---help---
+ This driver provides support for the gmux device found on many
+ Apple laptops, which controls the display mux for the hybrid
+ graphics as well as the backlight. Currently only backlight
+ control is supported by the driver.
+
endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 293a320d9faa..bf7e4f935b17 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -17,24 +17,28 @@ obj-$(CONFIG_ACER_WMI) += acer-wmi.o
obj-$(CONFIG_ACERHDF) += acerhdf.o
obj-$(CONFIG_HP_ACCEL) += hp_accel.o
obj-$(CONFIG_HP_WMI) += hp-wmi.o
+obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
+obj-$(CONFIG_FUJITSU_TABLET) += fujitsu-tablet.o
obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
obj-$(CONFIG_ACPI_WMI) += wmi.o
obj-$(CONFIG_MSI_WMI) += msi-wmi.o
-obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o
obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
+
+# toshiba_acpi must link after wmi to ensure that wmi devices are found
+# before toshiba_acpi initializes
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
+
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
-obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
@@ -45,3 +49,4 @@ obj-$(CONFIG_MXM_WMI) += mxm-wmi.o
obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
+obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index b848277171a4..c1a3fd8e1243 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -43,6 +43,7 @@
#include <linux/input/sparse-keymap.h>
#include <acpi/acpi_drivers.h>
+#include <acpi/video.h>
MODULE_AUTHOR("Carlos Corbacho");
MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
@@ -105,13 +106,19 @@ static const struct key_entry acer_wmi_keymap[] = {
{KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */
{KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */
{KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */
+ {KE_KEY, 0x29, {KEY_PROG3} }, /* P_Key for TM8372 */
{KE_IGNORE, 0x41, {KEY_MUTE} },
{KE_IGNORE, 0x42, {KEY_PREVIOUSSONG} },
+ {KE_IGNORE, 0x4d, {KEY_PREVIOUSSONG} },
{KE_IGNORE, 0x43, {KEY_NEXTSONG} },
+ {KE_IGNORE, 0x4e, {KEY_NEXTSONG} },
{KE_IGNORE, 0x44, {KEY_PLAYPAUSE} },
+ {KE_IGNORE, 0x4f, {KEY_PLAYPAUSE} },
{KE_IGNORE, 0x45, {KEY_STOP} },
+ {KE_IGNORE, 0x50, {KEY_STOP} },
{KE_IGNORE, 0x48, {KEY_VOLUMEUP} },
{KE_IGNORE, 0x49, {KEY_VOLUMEDOWN} },
+ {KE_IGNORE, 0x4a, {KEY_VOLUMEDOWN} },
{KE_IGNORE, 0x61, {KEY_SWITCHVIDEOMODE} },
{KE_IGNORE, 0x62, {KEY_BRIGHTNESSUP} },
{KE_IGNORE, 0x63, {KEY_BRIGHTNESSDOWN} },
@@ -153,7 +160,14 @@ struct lm_return_value {
u16 reserved;
} __attribute__((packed));
-struct wmid3_gds_input_param { /* Get Device Status input parameter */
+struct wmid3_gds_set_input_param { /* Set Device Status input parameter */
+ u8 function_num; /* Function Number */
+ u8 hotkey_number; /* Hotkey Number */
+ u16 devices; /* Set Device */
+ u8 volume_value; /* Volume Value */
+} __attribute__((packed));
+
+struct wmid3_gds_get_input_param { /* Get Device Status input parameter */
u8 function_num; /* Function Number */
u8 hotkey_number; /* Hotkey Number */
u16 devices; /* Get Device */
@@ -171,6 +185,11 @@ struct hotkey_function_type_aa {
u8 length;
u16 handle;
u16 commun_func_bitmap;
+ u16 application_func_bitmap;
+ u16 media_func_bitmap;
+ u16 display_func_bitmap;
+ u16 others_func_bitmap;
+ u8 commun_fn_key_number;
} __attribute__((packed));
/*
@@ -207,6 +226,7 @@ static int force_series;
static bool ec_raw_mode;
static bool has_type_aa;
static u16 commun_func_bitmap;
+static u8 commun_fn_key_number;
module_param(mailled, int, 0444);
module_param(brightness, int, 0444);
@@ -468,6 +488,15 @@ static struct dmi_system_id acer_quirks[] = {
},
{
.callback = dmi_matched,
+ .ident = "Lenovo Ideapad S205 (Brazos)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Brazos"),
+ },
+ .driver_data = &quirk_lenovo_ideapad_s205,
+ },
+ {
+ .callback = dmi_matched,
.ident = "Lenovo 3000 N200",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
@@ -478,6 +507,25 @@ static struct dmi_system_id acer_quirks[] = {
{}
};
+static int video_set_backlight_video_vendor(const struct dmi_system_id *d)
+{
+ interface->capability &= ~ACER_CAP_BRIGHTNESS;
+ pr_info("Brightness must be controlled by generic video driver\n");
+ return 0;
+}
+
+static const struct dmi_system_id video_vendor_dmi_table[] = {
+ {
+ .callback = video_set_backlight_video_vendor,
+ .ident = "Acer TravelMate 4750",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4750"),
+ },
+ },
+ {}
+};
+
/* Find which quirks are needed for a particular vendor/ model pair */
static void find_quirks(void)
{
@@ -536,8 +584,7 @@ struct acpi_buffer *result)
return status;
}
-static acpi_status AMW0_get_u32(u32 *value, u32 cap,
-struct wmi_interface *iface)
+static acpi_status AMW0_get_u32(u32 *value, u32 cap)
{
int err;
u8 result;
@@ -607,7 +654,7 @@ struct wmi_interface *iface)
return AE_OK;
}
-static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
+static acpi_status AMW0_set_u32(u32 value, u32 cap)
{
struct wmab_args args;
@@ -679,6 +726,33 @@ static acpi_status AMW0_find_mailled(void)
return AE_OK;
}
+static int AMW0_set_cap_acpi_check_device_found;
+
+static acpi_status AMW0_set_cap_acpi_check_device_cb(acpi_handle handle,
+ u32 level, void *context, void **retval)
+{
+ AMW0_set_cap_acpi_check_device_found = 1;
+ return AE_OK;
+}
+
+static const struct acpi_device_id norfkill_ids[] = {
+ { "VPC2004", 0},
+ { "IBM0068", 0},
+ { "LEN0068", 0},
+ { "SNY5001", 0}, /* sony-laptop in charge */
+ { "", 0},
+};
+
+static int AMW0_set_cap_acpi_check_device(void)
+{
+ const struct acpi_device_id *id;
+
+ for (id = norfkill_ids; id->id[0]; id++)
+ acpi_get_devices(id->id, AMW0_set_cap_acpi_check_device_cb,
+ NULL, NULL);
+ return AMW0_set_cap_acpi_check_device_found;
+}
+
static acpi_status AMW0_set_capabilities(void)
{
struct wmab_args args;
@@ -692,7 +766,9 @@ static acpi_status AMW0_set_capabilities(void)
* work.
*/
if (wmi_has_guid(AMW0_GUID2)) {
- interface->capability |= ACER_CAP_WIRELESS;
+ if ((quirks != &quirk_unknown) ||
+ !AMW0_set_cap_acpi_check_device())
+ interface->capability |= ACER_CAP_WIRELESS;
return AE_OK;
}
@@ -799,8 +875,7 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out)
return status;
}
-static acpi_status WMID_get_u32(u32 *value, u32 cap,
-struct wmi_interface *iface)
+static acpi_status WMID_get_u32(u32 *value, u32 cap)
{
acpi_status status;
u8 tmp;
@@ -836,7 +911,7 @@ struct wmi_interface *iface)
return status;
}
-static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
+static acpi_status WMID_set_u32(u32 value, u32 cap)
{
u32 method_id = 0;
char param;
@@ -884,13 +959,13 @@ static acpi_status wmid3_get_device_status(u32 *value, u16 device)
struct wmid3_gds_return_value return_value;
acpi_status status;
union acpi_object *obj;
- struct wmid3_gds_input_param params = {
+ struct wmid3_gds_get_input_param params = {
.function_num = 0x1,
- .hotkey_number = 0x01,
+ .hotkey_number = commun_fn_key_number,
.devices = device,
};
struct acpi_buffer input = {
- sizeof(struct wmid3_gds_input_param),
+ sizeof(struct wmid3_gds_get_input_param),
&params
};
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -953,19 +1028,28 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device)
acpi_status status;
union acpi_object *obj;
u16 devices;
- struct wmid3_gds_input_param params = {
+ struct wmid3_gds_get_input_param get_params = {
.function_num = 0x1,
- .hotkey_number = 0x01,
+ .hotkey_number = commun_fn_key_number,
.devices = commun_func_bitmap,
};
- struct acpi_buffer input = {
- sizeof(struct wmid3_gds_input_param),
- &params
+ struct acpi_buffer get_input = {
+ sizeof(struct wmid3_gds_get_input_param),
+ &get_params
+ };
+ struct wmid3_gds_set_input_param set_params = {
+ .function_num = 0x2,
+ .hotkey_number = commun_fn_key_number,
+ .devices = commun_func_bitmap,
+ };
+ struct acpi_buffer set_input = {
+ sizeof(struct wmid3_gds_set_input_param),
+ &set_params
};
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL };
- status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
+ status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &get_input, &output);
if (ACPI_FAILURE(status))
return status;
@@ -978,7 +1062,7 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device)
return AE_ERROR;
}
if (obj->buffer.length != 8) {
- pr_warning("Unknown buffer length %d\n", obj->buffer.length);
+ pr_warn("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return AE_ERROR;
}
@@ -987,18 +1071,16 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device)
kfree(obj);
if (return_value.error_code || return_value.ec_return_value) {
- pr_warning("Get Current Device Status failed: "
- "0x%x - 0x%x\n", return_value.error_code,
+ pr_warn("Get Current Device Status failed: 0x%x - 0x%x\n",
+ return_value.error_code,
return_value.ec_return_value);
return status;
}
devices = return_value.devices;
- params.function_num = 0x2;
- params.hotkey_number = 0x01;
- params.devices = (value) ? (devices | device) : (devices & ~device);
+ set_params.devices = (value) ? (devices | device) : (devices & ~device);
- status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2);
+ status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &set_input, &output2);
if (ACPI_FAILURE(status))
return status;
@@ -1011,7 +1093,7 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device)
return AE_ERROR;
}
if (obj->buffer.length != 4) {
- pr_warning("Unknown buffer length %d\n", obj->buffer.length);
+ pr_warn("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return AE_ERROR;
}
@@ -1020,8 +1102,8 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device)
kfree(obj);
if (return_value.error_code || return_value.ec_return_value)
- pr_warning("Set Device Status failed: "
- "0x%x - 0x%x\n", return_value.error_code,
+ pr_warn("Set Device Status failed: 0x%x - 0x%x\n",
+ return_value.error_code,
return_value.ec_return_value);
return status;
@@ -1068,6 +1150,8 @@ static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
interface->capability |= ACER_CAP_THREEG;
if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH)
interface->capability |= ACER_CAP_BLUETOOTH;
+
+ commun_fn_key_number = type_aa->commun_fn_key_number;
}
static acpi_status WMID_set_capabilities(void)
@@ -1126,15 +1210,15 @@ static acpi_status get_u32(u32 *value, u32 cap)
switch (interface->type) {
case ACER_AMW0:
- status = AMW0_get_u32(value, cap, interface);
+ status = AMW0_get_u32(value, cap);
break;
case ACER_AMW0_V2:
if (cap == ACER_CAP_MAILLED) {
- status = AMW0_get_u32(value, cap, interface);
+ status = AMW0_get_u32(value, cap);
break;
}
case ACER_WMID:
- status = WMID_get_u32(value, cap, interface);
+ status = WMID_get_u32(value, cap);
break;
case ACER_WMID_v2:
if (cap & (ACER_CAP_WIRELESS |
@@ -1142,7 +1226,7 @@ static acpi_status get_u32(u32 *value, u32 cap)
ACER_CAP_THREEG))
status = wmid_v2_get_u32(value, cap);
else if (wmi_has_guid(WMID_GUID2))
- status = WMID_get_u32(value, cap, interface);
+ status = WMID_get_u32(value, cap);
break;
}
@@ -1156,10 +1240,10 @@ static acpi_status set_u32(u32 value, u32 cap)
if (interface->capability & cap) {
switch (interface->type) {
case ACER_AMW0:
- return AMW0_set_u32(value, cap, interface);
+ return AMW0_set_u32(value, cap);
case ACER_AMW0_V2:
if (cap == ACER_CAP_MAILLED)
- return AMW0_set_u32(value, cap, interface);
+ return AMW0_set_u32(value, cap);
/*
* On some models, some WMID methods don't toggle
@@ -1169,21 +1253,21 @@ static acpi_status set_u32(u32 value, u32 cap)
*/
if (cap == ACER_CAP_WIRELESS ||
cap == ACER_CAP_BLUETOOTH) {
- status = WMID_set_u32(value, cap, interface);
+ status = WMID_set_u32(value, cap);
if (ACPI_FAILURE(status))
return status;
- return AMW0_set_u32(value, cap, interface);
+ return AMW0_set_u32(value, cap);
}
case ACER_WMID:
- return WMID_set_u32(value, cap, interface);
+ return WMID_set_u32(value, cap);
case ACER_WMID_v2:
if (cap & (ACER_CAP_WIRELESS |
ACER_CAP_BLUETOOTH |
ACER_CAP_THREEG))
return wmid_v2_set_u32(value, cap);
else if (wmi_has_guid(WMID_GUID2))
- return WMID_set_u32(value, cap, interface);
+ return WMID_set_u32(value, cap);
default:
return AE_BAD_PARAMETER;
}
@@ -1460,8 +1544,8 @@ static ssize_t show_bool_threeg(struct device *dev,
u32 result; \
acpi_status status;
- pr_info("This threeg sysfs will be removed in 2012"
- " - used by: %s\n", current->comm);
+ pr_info("This threeg sysfs will be removed in 2012 - used by: %s\n",
+ current->comm);
status = get_u32(&result, ACER_CAP_THREEG);
if (ACPI_SUCCESS(status))
return sprintf(buf, "%u\n", result);
@@ -1473,8 +1557,8 @@ static ssize_t set_bool_threeg(struct device *dev,
{
u32 tmp = simple_strtoul(buf, NULL, 10);
acpi_status status = set_u32(tmp, ACER_CAP_THREEG);
- pr_info("This threeg sysfs will be removed in 2012"
- " - used by: %s\n", current->comm);
+ pr_info("This threeg sysfs will be removed in 2012 - used by: %s\n",
+ current->comm);
if (ACPI_FAILURE(status))
return -EINVAL;
return count;
@@ -1485,8 +1569,8 @@ static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg,
static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
char *buf)
{
- pr_info("This interface sysfs will be removed in 2012"
- " - used by: %s\n", current->comm);
+ pr_info("This interface sysfs will be removed in 2012 - used by: %s\n",
+ current->comm);
switch (interface->type) {
case ACER_AMW0:
return sprintf(buf, "AMW0\n");
@@ -1953,9 +2037,13 @@ static int __init acer_wmi_init(void)
set_quirks();
if (acpi_video_backlight_support()) {
- interface->capability &= ~ACER_CAP_BRIGHTNESS;
- pr_info("Brightness must be controlled by "
- "generic video driver\n");
+ if (dmi_check_system(video_vendor_dmi_table)) {
+ acpi_video_unregister();
+ } else {
+ interface->capability &= ~ACER_CAP_BRIGHTNESS;
+ pr_info("Brightness must be controlled by "
+ "acpi video driver\n");
+ }
}
if (wmi_has_guid(WMID_GUID3)) {
@@ -1980,7 +2068,7 @@ static int __init acer_wmi_init(void)
err = platform_driver_register(&acer_platform_driver);
if (err) {
- pr_err("Unable to register platform driver.\n");
+ pr_err("Unable to register platform driver\n");
goto error_platform_register;
}
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index 760c6d7624fe..bc8384c6f3eb 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -244,12 +244,11 @@ static void acerhdf_change_fanstate(int state)
unsigned char cmd;
if (verbose)
- pr_notice("fan %s\n", (state == ACERHDF_FAN_OFF) ?
- "OFF" : "ON");
+ 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);
state = ACERHDF_FAN_AUTO;
}
@@ -264,19 +263,18 @@ 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);
+ 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);
+ ACERHDF_MAX_INTERVAL);
interval = ACERHDF_MAX_INTERVAL;
}
if (verbose)
- pr_notice("interval changed to: %d\n",
- interval);
+ pr_notice("interval changed to: %d\n", interval);
thermal->polling_delay = interval*1000;
prev_interval = interval;
}
@@ -587,8 +585,8 @@ static int acerhdf_check_hardware(void)
}
if (!bios_cfg) {
- pr_err("unknown (unsupported) BIOS version %s/%s/%s, "
- "please report, aborting!\n", vendor, product, version);
+ pr_err("unknown (unsupported) BIOS version %s/%s/%s, please report, aborting!\n",
+ vendor, product, version);
return -EINVAL;
}
@@ -598,8 +596,7 @@ static int acerhdf_check_hardware(void)
*/
if (!kernelmode) {
pr_notice("Fan control off, to enable do:\n");
- pr_notice("echo -n \"enabled\" > "
- "/sys/class/thermal/thermal_zone0/mode\n");
+ pr_notice("echo -n \"enabled\" > /sys/class/thermal/thermal_zone0/mode\n");
}
return 0;
diff --git a/drivers/platform/x86/amilo-rfkill.c b/drivers/platform/x86/amilo-rfkill.c
new file mode 100644
index 000000000000..a514bf66fdd7
--- /dev/null
+++ b/drivers/platform/x86/amilo-rfkill.c
@@ -0,0 +1,176 @@
+/*
+ * Support for rfkill on some Fujitsu-Siemens Amilo laptops.
+ * Copyright 2011 Ben Hutchings.
+ *
+ * Based in part on the fsam7440 driver, which is:
+ * Copyright 2005 Alejandro Vidal Mata & Javier Vidal Mata.
+ * and on the fsaa1655g driver, which is:
+ * Copyright 2006 Martin Večeřa.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/i8042.h>
+#include <linux/io.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/rfkill.h>
+
+/*
+ * These values were obtained from disassembling and debugging the
+ * PM.exe program installed in the Fujitsu-Siemens AMILO A1655G
+ */
+#define A1655_WIFI_COMMAND 0x10C5
+#define A1655_WIFI_ON 0x25
+#define A1655_WIFI_OFF 0x45
+
+static int amilo_a1655_rfkill_set_block(void *data, bool blocked)
+{
+ u8 param = blocked ? A1655_WIFI_OFF : A1655_WIFI_ON;
+ int rc;
+
+ i8042_lock_chip();
+ rc = i8042_command(&param, A1655_WIFI_COMMAND);
+ i8042_unlock_chip();
+ return rc;
+}
+
+static const struct rfkill_ops amilo_a1655_rfkill_ops = {
+ .set_block = amilo_a1655_rfkill_set_block
+};
+
+/*
+ * These values were obtained from disassembling the PM.exe program
+ * installed in the Fujitsu-Siemens AMILO M 7440
+ */
+#define M7440_PORT1 0x118f
+#define M7440_PORT2 0x118e
+#define M7440_RADIO_ON1 0x12
+#define M7440_RADIO_ON2 0x80
+#define M7440_RADIO_OFF1 0x10
+#define M7440_RADIO_OFF2 0x00
+
+static int amilo_m7440_rfkill_set_block(void *data, bool blocked)
+{
+ u8 val1 = blocked ? M7440_RADIO_OFF1 : M7440_RADIO_ON1;
+ u8 val2 = blocked ? M7440_RADIO_OFF2 : M7440_RADIO_ON2;
+
+ outb(val1, M7440_PORT1);
+ outb(val2, M7440_PORT2);
+
+ /* Check whether the state has changed correctly */
+ if (inb(M7440_PORT1) != val1 || inb(M7440_PORT2) != val2)
+ return -EIO;
+
+ return 0;
+}
+
+static const struct rfkill_ops amilo_m7440_rfkill_ops = {
+ .set_block = amilo_m7440_rfkill_set_block
+};
+
+static const struct dmi_system_id __devinitdata amilo_rfkill_id_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_BOARD_NAME, "AMILO A1655"),
+ },
+ .driver_data = (void *)&amilo_a1655_rfkill_ops
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_BOARD_NAME, "AMILO M7440"),
+ },
+ .driver_data = (void *)&amilo_m7440_rfkill_ops
+ },
+ {}
+};
+
+static struct platform_device *amilo_rfkill_pdev;
+static struct rfkill *amilo_rfkill_dev;
+
+static int __devinit amilo_rfkill_probe(struct platform_device *device)
+{
+ int rc;
+ const struct dmi_system_id *system_id =
+ dmi_first_match(amilo_rfkill_id_table);
+
+ if (!system_id)
+ return -ENXIO;
+
+ amilo_rfkill_dev = rfkill_alloc(KBUILD_MODNAME, &device->dev,
+ RFKILL_TYPE_WLAN,
+ system_id->driver_data, NULL);
+ if (!amilo_rfkill_dev)
+ return -ENOMEM;
+
+ rc = rfkill_register(amilo_rfkill_dev);
+ if (rc)
+ goto fail;
+
+ return 0;
+
+fail:
+ rfkill_destroy(amilo_rfkill_dev);
+ return rc;
+}
+
+static int amilo_rfkill_remove(struct platform_device *device)
+{
+ rfkill_unregister(amilo_rfkill_dev);
+ rfkill_destroy(amilo_rfkill_dev);
+ return 0;
+}
+
+static struct platform_driver amilo_rfkill_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = amilo_rfkill_probe,
+ .remove = amilo_rfkill_remove,
+};
+
+static int __init amilo_rfkill_init(void)
+{
+ int rc;
+
+ if (dmi_first_match(amilo_rfkill_id_table) == NULL)
+ return -ENODEV;
+
+ rc = platform_driver_register(&amilo_rfkill_driver);
+ if (rc)
+ return rc;
+
+ amilo_rfkill_pdev = platform_device_register_simple(KBUILD_MODNAME, -1,
+ NULL, 0);
+ if (IS_ERR(amilo_rfkill_pdev)) {
+ rc = PTR_ERR(amilo_rfkill_pdev);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ platform_driver_unregister(&amilo_rfkill_driver);
+ return rc;
+}
+
+static void __exit amilo_rfkill_exit(void)
+{
+ platform_device_unregister(amilo_rfkill_pdev);
+ platform_driver_unregister(&amilo_rfkill_driver);
+}
+
+MODULE_AUTHOR("Ben Hutchings <ben@decadent.org.uk>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(dmi, amilo_rfkill_id_table);
+
+module_init(amilo_rfkill_init);
+module_exit(amilo_rfkill_exit);
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
new file mode 100644
index 000000000000..8a582bdfdc76
--- /dev/null
+++ b/drivers/platform/x86/apple-gmux.c
@@ -0,0 +1,244 @@
+/*
+ * Gmux driver for Apple laptops
+ *
+ * Copyright (C) Canonical Ltd. <seth.forshee@canonical.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/backlight.h>
+#include <linux/acpi.h>
+#include <linux/pnp.h>
+#include <linux/apple_bl.h>
+#include <linux/slab.h>
+#include <acpi/video.h>
+#include <asm/io.h>
+
+struct apple_gmux_data {
+ unsigned long iostart;
+ unsigned long iolen;
+
+ struct backlight_device *bdev;
+};
+
+/*
+ * gmux port offsets. Many of these are not yet used, but may be in the
+ * future, and it's useful to have them documented here anyhow.
+ */
+#define GMUX_PORT_VERSION_MAJOR 0x04
+#define GMUX_PORT_VERSION_MINOR 0x05
+#define GMUX_PORT_VERSION_RELEASE 0x06
+#define GMUX_PORT_SWITCH_DISPLAY 0x10
+#define GMUX_PORT_SWITCH_GET_DISPLAY 0x11
+#define GMUX_PORT_INTERRUPT_ENABLE 0x14
+#define GMUX_PORT_INTERRUPT_STATUS 0x16
+#define GMUX_PORT_SWITCH_DDC 0x28
+#define GMUX_PORT_SWITCH_EXTERNAL 0x40
+#define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41
+#define GMUX_PORT_DISCRETE_POWER 0x50
+#define GMUX_PORT_MAX_BRIGHTNESS 0x70
+#define GMUX_PORT_BRIGHTNESS 0x74
+
+#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
+
+#define GMUX_INTERRUPT_ENABLE 0xff
+#define GMUX_INTERRUPT_DISABLE 0x00
+
+#define GMUX_INTERRUPT_STATUS_ACTIVE 0
+#define GMUX_INTERRUPT_STATUS_DISPLAY (1 << 0)
+#define GMUX_INTERRUPT_STATUS_POWER (1 << 2)
+#define GMUX_INTERRUPT_STATUS_HOTPLUG (1 << 3)
+
+#define GMUX_BRIGHTNESS_MASK 0x00ffffff
+#define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK
+
+static inline u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
+{
+ return inb(gmux_data->iostart + port);
+}
+
+static inline void gmux_write8(struct apple_gmux_data *gmux_data, int port,
+ u8 val)
+{
+ outb(val, gmux_data->iostart + port);
+}
+
+static inline u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
+{
+ return inl(gmux_data->iostart + port);
+}
+
+static int gmux_get_brightness(struct backlight_device *bd)
+{
+ struct apple_gmux_data *gmux_data = bl_get_data(bd);
+ return gmux_read32(gmux_data, GMUX_PORT_BRIGHTNESS) &
+ GMUX_BRIGHTNESS_MASK;
+}
+
+static int gmux_update_status(struct backlight_device *bd)
+{
+ struct apple_gmux_data *gmux_data = bl_get_data(bd);
+ u32 brightness = bd->props.brightness;
+
+ /*
+ * Older gmux versions require writing out lower bytes first then
+ * setting the upper byte to 0 to flush the values. Newer versions
+ * accept a single u32 write, but the old method also works, so we
+ * just use the old method for all gmux versions.
+ */
+ gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
+ gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 1, brightness >> 8);
+ gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 2, brightness >> 16);
+ gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 3, 0);
+
+ return 0;
+}
+
+static const struct backlight_ops gmux_bl_ops = {
+ .get_brightness = gmux_get_brightness,
+ .update_status = gmux_update_status,
+};
+
+static int __devinit gmux_probe(struct pnp_dev *pnp,
+ const struct pnp_device_id *id)
+{
+ struct apple_gmux_data *gmux_data;
+ struct resource *res;
+ struct backlight_properties props;
+ struct backlight_device *bdev;
+ u8 ver_major, ver_minor, ver_release;
+ int ret = -ENXIO;
+
+ gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL);
+ if (!gmux_data)
+ return -ENOMEM;
+ pnp_set_drvdata(pnp, gmux_data);
+
+ res = pnp_get_resource(pnp, IORESOURCE_IO, 0);
+ if (!res) {
+ pr_err("Failed to find gmux I/O resource\n");
+ goto err_free;
+ }
+
+ gmux_data->iostart = res->start;
+ gmux_data->iolen = res->end - res->start;
+
+ if (gmux_data->iolen < GMUX_MIN_IO_LEN) {
+ pr_err("gmux I/O region too small (%lu < %u)\n",
+ gmux_data->iolen, GMUX_MIN_IO_LEN);
+ goto err_free;
+ }
+
+ if (!request_region(gmux_data->iostart, gmux_data->iolen,
+ "Apple gmux")) {
+ pr_err("gmux I/O already in use\n");
+ goto err_free;
+ }
+
+ /*
+ * On some machines the gmux is in ACPI even thought the machine
+ * doesn't really have a gmux. Check for invalid version information
+ * to detect this.
+ */
+ ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
+ ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
+ ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
+ if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
+ pr_info("gmux device not present\n");
+ ret = -ENODEV;
+ goto err_release;
+ }
+
+ pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor,
+ ver_release);
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS);
+
+ /*
+ * Currently it's assumed that the maximum brightness is less than
+ * 2^24 for compatibility with old gmux versions. Cap the max
+ * brightness at this value, but print a warning if the hardware
+ * reports something higher so that it can be fixed.
+ */
+ if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS))
+ props.max_brightness = GMUX_MAX_BRIGHTNESS;
+
+ bdev = backlight_device_register("gmux_backlight", &pnp->dev,
+ gmux_data, &gmux_bl_ops, &props);
+ if (IS_ERR(bdev)) {
+ ret = PTR_ERR(bdev);
+ goto err_release;
+ }
+
+ gmux_data->bdev = bdev;
+ bdev->props.brightness = gmux_get_brightness(bdev);
+ backlight_update_status(bdev);
+
+ /*
+ * The backlight situation on Macs is complicated. If the gmux is
+ * present it's the best choice, because it always works for
+ * backlight control and supports more levels than other options.
+ * Disable the other backlight choices.
+ */
+ acpi_video_unregister();
+ apple_bl_unregister();
+
+ return 0;
+
+err_release:
+ release_region(gmux_data->iostart, gmux_data->iolen);
+err_free:
+ kfree(gmux_data);
+ return ret;
+}
+
+static void __devexit gmux_remove(struct pnp_dev *pnp)
+{
+ struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+
+ backlight_device_unregister(gmux_data->bdev);
+ release_region(gmux_data->iostart, gmux_data->iolen);
+ kfree(gmux_data);
+
+ acpi_video_register();
+ apple_bl_register();
+}
+
+static const struct pnp_device_id gmux_device_ids[] = {
+ {"APP000B", 0},
+ {"", 0}
+};
+
+static struct pnp_driver gmux_pnp_driver = {
+ .name = "apple-gmux",
+ .probe = gmux_probe,
+ .remove = __devexit_p(gmux_remove),
+ .id_table = gmux_device_ids,
+};
+
+static int __init apple_gmux_init(void)
+{
+ return pnp_register_driver(&gmux_pnp_driver);
+}
+
+static void __exit apple_gmux_exit(void)
+{
+ pnp_unregister_driver(&gmux_pnp_driver);
+}
+
+module_init(apple_gmux_init);
+module_exit(apple_gmux_exit);
+
+MODULE_AUTHOR("Seth Forshee <seth.forshee@canonical.com>");
+MODULE_DESCRIPTION("Apple Gmux Driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pnp, gmux_device_ids);
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index b7944f903886..e38f91be0b10 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -81,6 +81,19 @@ static uint wapf = 1;
module_param(wapf, uint, 0444);
MODULE_PARM_DESC(wapf, "WAPF value");
+static char *wled_type = "unknown";
+static char *bled_type = "unknown";
+
+module_param(wled_type, charp, 0444);
+MODULE_PARM_DESC(wlan_status, "Set the wled type on boot "
+ "(unknown, led or rfkill). "
+ "default is unknown");
+
+module_param(bled_type, charp, 0444);
+MODULE_PARM_DESC(bled_type, "Set the bled type on boot "
+ "(unknown, led or rfkill). "
+ "default is unknown");
+
static int wlan_status = 1;
static int bluetooth_status = 1;
static int wimax_status = -1;
@@ -137,6 +150,11 @@ MODULE_PARM_DESC(als_status, "Set the ALS status on boot "
#define WM_RSTS 0x08 /* internal wimax */
#define WW_RSTS 0x20 /* internal wwan */
+/* WLED and BLED type */
+#define TYPE_UNKNOWN 0
+#define TYPE_LED 1
+#define TYPE_RFKILL 2
+
/* LED */
#define METHOD_MLED "MLED"
#define METHOD_TLED "TLED"
@@ -218,8 +236,9 @@ struct asus_led {
/*
* Same thing for rfkill
*/
-struct asus_pega_rfkill {
- int control_id; /* type of control. Maps to PEGA_* values */
+struct asus_rfkill {
+ /* type of control. Maps to PEGA_* values or *_RSTS */
+ int control_id;
struct rfkill *rfkill;
struct asus_laptop *asus;
};
@@ -240,6 +259,8 @@ struct asus_laptop {
struct key_entry *keymap;
struct input_polled_dev *pega_accel_poll;
+ struct asus_led wled;
+ struct asus_led bled;
struct asus_led mled;
struct asus_led tled;
struct asus_led rled;
@@ -248,6 +269,8 @@ struct asus_laptop {
struct asus_led kled;
struct workqueue_struct *led_workqueue;
+ int wled_type;
+ int bled_type;
int wireless_status;
bool have_rsts;
bool is_pega_lucid;
@@ -256,11 +279,11 @@ struct asus_laptop {
int pega_acc_y;
int pega_acc_z;
- struct rfkill *gps_rfkill;
-
- struct asus_pega_rfkill wlanrfk;
- struct asus_pega_rfkill btrfk;
- struct asus_pega_rfkill wwanrfk;
+ struct asus_rfkill wlan;
+ struct asus_rfkill bluetooth;
+ struct asus_rfkill wwan;
+ struct asus_rfkill wimax;
+ struct asus_rfkill gps;
acpi_handle handle; /* the handle of the hotk device */
u32 ledd_status; /* status of the LED display */
@@ -274,6 +297,7 @@ static const struct key_entry asus_keymap[] = {
{KE_KEY, 0x02, { KEY_SCREENLOCK } },
{KE_KEY, 0x05, { KEY_WLAN } },
{KE_KEY, 0x08, { KEY_F13 } },
+ {KE_KEY, 0x09, { KEY_PROG2 } }, /* Dock */
{KE_KEY, 0x17, { KEY_ZOOM } },
{KE_KEY, 0x1f, { KEY_BATTERY } },
/* End of Lenovo SL Specific keycodes */
@@ -299,6 +323,8 @@ static const struct key_entry asus_keymap[] = {
{KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } },
{KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } },
{KE_KEY, 0x6B, { KEY_F13 } }, /* Lock Touchpad */
+ {KE_KEY, 0x6C, { KEY_SLEEP } }, /* Suspend */
+ {KE_KEY, 0x6D, { KEY_SLEEP } }, /* Hibernate */
{KE_KEY, 0x7E, { KEY_BLUETOOTH } },
{KE_KEY, 0x7D, { KEY_BLUETOOTH } },
{KE_KEY, 0x82, { KEY_CAMERA } },
@@ -601,6 +627,10 @@ static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev)
static void asus_led_exit(struct asus_laptop *asus)
{
+ if (!IS_ERR_OR_NULL(asus->wled.led.dev))
+ led_classdev_unregister(&asus->wled.led);
+ if (!IS_ERR_OR_NULL(asus->bled.led.dev))
+ led_classdev_unregister(&asus->bled.led);
if (!IS_ERR_OR_NULL(asus->mled.led.dev))
led_classdev_unregister(&asus->mled.led);
if (!IS_ERR_OR_NULL(asus->tled.led.dev))
@@ -642,7 +672,7 @@ static int asus_led_register(struct asus_laptop *asus,
static int asus_led_init(struct asus_laptop *asus)
{
- int r;
+ int r = 0;
/*
* The Pegatron Lucid has no physical leds, but all methods are
@@ -661,6 +691,16 @@ static int asus_led_init(struct asus_laptop *asus)
if (!asus->led_workqueue)
return -ENOMEM;
+ if (asus->wled_type == TYPE_LED)
+ r = asus_led_register(asus, &asus->wled, "asus::wlan",
+ METHOD_WLAN);
+ if (r)
+ goto error;
+ if (asus->bled_type == TYPE_LED)
+ r = asus_led_register(asus, &asus->bled, "asus::bluetooth",
+ METHOD_BLUETOOTH);
+ if (r)
+ goto error;
r = asus_led_register(asus, &asus->mled, "asus::mail", METHOD_MLED);
if (r)
goto error;
@@ -963,7 +1003,7 @@ static ssize_t store_wlan(struct device *dev, struct device_attribute *attr,
return sysfs_acpi_set(asus, buf, count, METHOD_WLAN);
}
-/*
+/*e
* Bluetooth
*/
static int asus_bluetooth_set(struct asus_laptop *asus, int status)
@@ -1228,7 +1268,7 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr,
ret = asus_gps_switch(asus, !!value);
if (ret)
return ret;
- rfkill_set_sw_state(asus->gps_rfkill, !value);
+ rfkill_set_sw_state(asus->gps.rfkill, !value);
return rv;
}
@@ -1246,90 +1286,139 @@ static const struct rfkill_ops asus_gps_rfkill_ops = {
.set_block = asus_gps_rfkill_set,
};
+static int asus_rfkill_set(void *data, bool blocked)
+{
+ struct asus_rfkill *rfk = data;
+ struct asus_laptop *asus = rfk->asus;
+
+ if (rfk->control_id == WL_RSTS)
+ return asus_wlan_set(asus, !blocked);
+ else if (rfk->control_id == BT_RSTS)
+ return asus_bluetooth_set(asus, !blocked);
+ else if (rfk->control_id == WM_RSTS)
+ return asus_wimax_set(asus, !blocked);
+ else if (rfk->control_id == WW_RSTS)
+ return asus_wwan_set(asus, !blocked);
+
+ return -EINVAL;
+}
+
+static const struct rfkill_ops asus_rfkill_ops = {
+ .set_block = asus_rfkill_set,
+};
+
+static void asus_rfkill_terminate(struct asus_rfkill *rfk)
+{
+ if (!rfk->rfkill)
+ return ;
+
+ rfkill_unregister(rfk->rfkill);
+ rfkill_destroy(rfk->rfkill);
+ rfk->rfkill = NULL;
+}
+
static void asus_rfkill_exit(struct asus_laptop *asus)
{
- if (asus->gps_rfkill) {
- rfkill_unregister(asus->gps_rfkill);
- rfkill_destroy(asus->gps_rfkill);
- asus->gps_rfkill = NULL;
- }
+ asus_rfkill_terminate(&asus->wwan);
+ asus_rfkill_terminate(&asus->bluetooth);
+ asus_rfkill_terminate(&asus->wlan);
+ asus_rfkill_terminate(&asus->gps);
}
-static int asus_rfkill_init(struct asus_laptop *asus)
+static int asus_rfkill_setup(struct asus_laptop *asus, struct asus_rfkill *rfk,
+ const char *name, int control_id, int type,
+ const struct rfkill_ops *ops)
{
int result;
- if (acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) ||
- acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) ||
- acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL))
- return 0;
-
- asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev,
- RFKILL_TYPE_GPS,
- &asus_gps_rfkill_ops, asus);
- if (!asus->gps_rfkill)
+ rfk->control_id = control_id;
+ rfk->asus = asus;
+ rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev,
+ type, ops, rfk);
+ if (!rfk->rfkill)
return -EINVAL;
- result = rfkill_register(asus->gps_rfkill);
+ result = rfkill_register(rfk->rfkill);
if (result) {
- rfkill_destroy(asus->gps_rfkill);
- asus->gps_rfkill = NULL;
+ rfkill_destroy(rfk->rfkill);
+ rfk->rfkill = NULL;
}
return result;
}
-static int pega_rfkill_set(void *data, bool blocked)
+static int asus_rfkill_init(struct asus_laptop *asus)
{
- struct asus_pega_rfkill *pega_rfk = data;
+ int result = 0;
- int ret = asus_pega_lucid_set(pega_rfk->asus, pega_rfk->control_id, !blocked);
- pr_warn("Setting rfkill %d, to %d; returned %d\n", pega_rfk->control_id, !blocked, ret);
+ if (asus->is_pega_lucid)
+ return -ENODEV;
- return ret;
-}
+ if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) &&
+ !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) &&
+ !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL))
+ result = asus_rfkill_setup(asus, &asus->gps, "asus-gps",
+ -1, RFKILL_TYPE_GPS,
+ &asus_gps_rfkill_ops);
+ if (result)
+ goto exit;
-static const struct rfkill_ops pega_rfkill_ops = {
- .set_block = pega_rfkill_set,
-};
-static void pega_rfkill_terminate(struct asus_pega_rfkill *pega_rfk)
-{
- pr_warn("Terminating %d\n", pega_rfk->control_id);
- if (pega_rfk->rfkill) {
- rfkill_unregister(pega_rfk->rfkill);
- rfkill_destroy(pega_rfk->rfkill);
- pega_rfk->rfkill = NULL;
- }
-}
+ if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL) &&
+ asus->wled_type == TYPE_RFKILL)
+ result = asus_rfkill_setup(asus, &asus->wlan, "asus-wlan",
+ WL_RSTS, RFKILL_TYPE_WLAN,
+ &asus_rfkill_ops);
+ if (result)
+ goto exit;
-static void pega_rfkill_exit(struct asus_laptop *asus)
-{
- pega_rfkill_terminate(&asus->wwanrfk);
- pega_rfkill_terminate(&asus->btrfk);
- pega_rfkill_terminate(&asus->wlanrfk);
+ if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL) &&
+ asus->bled_type == TYPE_RFKILL)
+ result = asus_rfkill_setup(asus, &asus->bluetooth,
+ "asus-bluetooth", BT_RSTS,
+ RFKILL_TYPE_BLUETOOTH,
+ &asus_rfkill_ops);
+ if (result)
+ goto exit;
+
+ if (!acpi_check_handle(asus->handle, METHOD_WWAN, NULL))
+ result = asus_rfkill_setup(asus, &asus->wwan, "asus-wwan",
+ WW_RSTS, RFKILL_TYPE_WWAN,
+ &asus_rfkill_ops);
+ if (result)
+ goto exit;
+
+ if (!acpi_check_handle(asus->handle, METHOD_WIMAX, NULL))
+ result = asus_rfkill_setup(asus, &asus->wimax, "asus-wimax",
+ WM_RSTS, RFKILL_TYPE_WIMAX,
+ &asus_rfkill_ops);
+ if (result)
+ goto exit;
+
+exit:
+ if (result)
+ asus_rfkill_exit(asus);
+
+ return result;
}
-static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_pega_rfkill *pega_rfk,
- const char *name, int controlid, int rfkill_type)
+static int pega_rfkill_set(void *data, bool blocked)
{
- int result;
+ struct asus_rfkill *rfk = data;
- pr_warn("Setting up rfk %s, control %d, type %d\n", name, controlid, rfkill_type);
- pega_rfk->control_id = controlid;
- pega_rfk->asus = asus;
- pega_rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev,
- rfkill_type, &pega_rfkill_ops, pega_rfk);
- if (!pega_rfk->rfkill)
- return -EINVAL;
+ int ret = asus_pega_lucid_set(rfk->asus, rfk->control_id, !blocked);
+ return ret;
+}
- result = rfkill_register(pega_rfk->rfkill);
- if (result) {
- rfkill_destroy(pega_rfk->rfkill);
- pega_rfk->rfkill = NULL;
- }
+static const struct rfkill_ops pega_rfkill_ops = {
+ .set_block = pega_rfkill_set,
+};
- return result;
+static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_rfkill *rfk,
+ const char *name, int controlid, int rfkill_type)
+{
+ return asus_rfkill_setup(asus, rfk, name, controlid, rfkill_type,
+ &pega_rfkill_ops);
}
static int pega_rfkill_init(struct asus_laptop *asus)
@@ -1339,22 +1428,22 @@ static int pega_rfkill_init(struct asus_laptop *asus)
if(!asus->is_pega_lucid)
return -ENODEV;
- ret = pega_rfkill_setup(asus, &asus->wlanrfk, "pega-wlan", PEGA_WLAN, RFKILL_TYPE_WLAN);
- if(ret)
- return ret;
- ret = pega_rfkill_setup(asus, &asus->btrfk, "pega-bt", PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH);
+ ret = pega_rfkill_setup(asus, &asus->wlan, "pega-wlan",
+ PEGA_WLAN, RFKILL_TYPE_WLAN);
if(ret)
- goto err_btrfk;
- ret = pega_rfkill_setup(asus, &asus->wwanrfk, "pega-wwan", PEGA_WWAN, RFKILL_TYPE_WWAN);
+ goto exit;
+
+ ret = pega_rfkill_setup(asus, &asus->bluetooth, "pega-bt",
+ PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH);
if(ret)
- goto err_wwanrfk;
+ goto exit;
- pr_warn("Pega rfkill init succeeded\n");
- return 0;
-err_wwanrfk:
- pega_rfkill_terminate(&asus->btrfk);
-err_btrfk:
- pega_rfkill_terminate(&asus->wlanrfk);
+ ret = pega_rfkill_setup(asus, &asus->wwan, "pega-wwan",
+ PEGA_WWAN, RFKILL_TYPE_WWAN);
+
+exit:
+ if (ret)
+ asus_rfkill_exit(asus);
return ret;
}
@@ -1364,8 +1453,10 @@ err_btrfk:
*/
static void asus_input_notify(struct asus_laptop *asus, int event)
{
- if (asus->inputdev)
- sparse_keymap_report_event(asus->inputdev, event, 1, true);
+ if (!asus->inputdev)
+ return ;
+ if (!sparse_keymap_report_event(asus->inputdev, event, 1, true))
+ pr_info("Unknown key %x pressed\n", event);
}
static int asus_input_init(struct asus_laptop *asus)
@@ -1375,7 +1466,7 @@ static int asus_input_init(struct asus_laptop *asus)
input = input_allocate_device();
if (!input) {
- pr_info("Unable to allocate input device\n");
+ pr_warn("Unable to allocate input device\n");
return -ENOMEM;
}
input->name = "Asus Laptop extra buttons";
@@ -1390,7 +1481,7 @@ static int asus_input_init(struct asus_laptop *asus)
}
error = input_register_device(input);
if (error) {
- pr_info("Unable to register input device\n");
+ pr_warn("Unable to register input device\n");
goto err_free_keymap;
}
@@ -1688,7 +1779,16 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
if (result)
return result;
- /* WLED and BLED are on by default */
+ if (!strcmp(bled_type, "led"))
+ asus->bled_type = TYPE_LED;
+ else if (!strcmp(bled_type, "rfkill"))
+ asus->bled_type = TYPE_RFKILL;
+
+ if (!strcmp(wled_type, "led"))
+ asus->wled_type = TYPE_LED;
+ else if (!strcmp(wled_type, "rfkill"))
+ asus->wled_type = TYPE_RFKILL;
+
if (bluetooth_status >= 0)
asus_bluetooth_set(asus, !!bluetooth_status);
@@ -1786,7 +1886,7 @@ static int __devinit asus_acpi_add(struct acpi_device *device)
goto fail_led;
result = asus_rfkill_init(asus);
- if (result)
+ if (result && result != -ENODEV)
goto fail_rfkill;
result = pega_accel_init(asus);
@@ -1828,7 +1928,6 @@ static int asus_acpi_remove(struct acpi_device *device, int type)
asus_led_exit(asus);
asus_input_exit(asus);
pega_accel_exit(asus);
- pega_rfkill_exit(asus);
asus_platform_exit(asus);
kfree(asus->name);
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index b0859d4183e8..99a30b513137 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -25,6 +25,7 @@
#include <linux/init.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
+#include <linux/fb.h>
#include "asus-wmi.h"
@@ -51,9 +52,14 @@ static uint wapf;
module_param(wapf, uint, 0444);
MODULE_PARM_DESC(wapf, "WAPF value");
+static struct quirk_entry quirk_asus_unknown = {
+};
+
static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
{
- driver->wapf = wapf;
+ driver->quirks = &quirk_asus_unknown;
+ driver->quirks->wapf = wapf;
+ driver->panel_power = FB_BLANK_UNBLANK;
}
static const struct key_entry asus_nb_wmi_keymap[] = {
@@ -70,6 +76,8 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x50, { KEY_EMAIL } },
{ KE_KEY, 0x51, { KEY_WWW } },
{ KE_KEY, 0x55, { KEY_CALC } },
+ { KE_IGNORE, 0x57, }, /* Battery mode */
+ { KE_IGNORE, 0x58, }, /* AC mode */
{ KE_KEY, 0x5C, { KEY_F15 } }, /* Power Gear key */
{ KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */
{ KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */
@@ -99,7 +107,7 @@ static struct asus_wmi_driver asus_nb_wmi_driver = {
.keymap = asus_nb_wmi_keymap,
.input_name = "Asus WMI hotkeys",
.input_phys = ASUS_NB_WMI_FILE "/input0",
- .quirks = asus_nb_wmi_quirks,
+ .detect_quirks = asus_nb_wmi_quirks,
};
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 72d731c21d45..77aadde5281c 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -411,7 +411,7 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env)
if (retval >= 0) {
if (level)
- *level = retval & 0x80 ? retval & 0x7F : 0;
+ *level = retval & 0x7F;
if (env)
*env = (retval >> 8) & 0x7F;
retval = 0;
@@ -571,7 +571,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus)
} else {
dev = pci_get_slot(bus, 0);
if (dev) {
- pci_remove_bus_device(dev);
+ pci_stop_and_remove_bus_device(dev);
pci_dev_put(dev);
}
}
@@ -784,7 +784,8 @@ static int asus_new_rfkill(struct asus_wmi *asus,
arfkill->dev_id = dev_id;
arfkill->asus = asus;
- if (dev_id == ASUS_WMI_DEVID_WLAN && asus->driver->hotplug_wireless)
+ if (dev_id == ASUS_WMI_DEVID_WLAN &&
+ asus->driver->quirks->hotplug_wireless)
*rfkill = rfkill_alloc(name, &asus->platform_device->dev, type,
&asus_rfkill_wlan_ops, arfkill);
else
@@ -895,7 +896,7 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus)
if (result && result != -ENODEV)
goto exit;
- if (!asus->driver->hotplug_wireless)
+ if (!asus->driver->quirks->hotplug_wireless)
goto exit;
result = asus_setup_pci_hotplug(asus);
@@ -1075,7 +1076,12 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus)
*/
static int read_backlight_power(struct asus_wmi *asus)
{
- int ret = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_BACKLIGHT);
+ int ret;
+ if (asus->driver->quirks->store_backlight_power)
+ ret = !asus->driver->panel_power;
+ else
+ ret = asus_wmi_get_devstate_simple(asus,
+ ASUS_WMI_DEVID_BACKLIGHT);
if (ret < 0)
return ret;
@@ -1116,26 +1122,51 @@ static int read_brightness(struct backlight_device *bd)
return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
}
-static int update_bl_status(struct backlight_device *bd)
+static u32 get_scalar_command(struct backlight_device *bd)
{
struct asus_wmi *asus = bl_get_data(bd);
- u32 ctrl_param;
- int power, err;
+ u32 ctrl_param = 0;
- ctrl_param = bd->props.brightness;
+ if ((asus->driver->brightness < bd->props.brightness) ||
+ bd->props.brightness == bd->props.max_brightness)
+ ctrl_param = 0x00008001;
+ else if ((asus->driver->brightness > bd->props.brightness) ||
+ bd->props.brightness == 0)
+ ctrl_param = 0x00008000;
- err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS,
- ctrl_param, NULL);
+ asus->driver->brightness = bd->props.brightness;
- if (err < 0)
- return err;
+ return ctrl_param;
+}
+
+static int update_bl_status(struct backlight_device *bd)
+{
+ struct asus_wmi *asus = bl_get_data(bd);
+ u32 ctrl_param;
+ int power, err = 0;
power = read_backlight_power(asus);
if (power != -ENODEV && bd->props.power != power) {
ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK);
err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT,
ctrl_param, NULL);
+ if (asus->driver->quirks->store_backlight_power)
+ asus->driver->panel_power = bd->props.power;
+
+ /* When using scalar brightness, updating the brightness
+ * will mess with the backlight power */
+ if (asus->driver->quirks->scalar_panel_brightness)
+ return err;
}
+
+ if (asus->driver->quirks->scalar_panel_brightness)
+ ctrl_param = get_scalar_command(bd);
+ else
+ ctrl_param = bd->props.brightness;
+
+ err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS,
+ ctrl_param, NULL);
+
return err;
}
@@ -1196,10 +1227,15 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus)
asus->backlight_device = bd;
+ if (asus->driver->quirks->store_backlight_power)
+ asus->driver->panel_power = power;
+
bd->props.brightness = read_brightness(bd);
bd->props.power = power;
backlight_update_status(bd);
+ asus->driver->brightness = bd->props.brightness;
+
return 0;
}
@@ -1441,9 +1477,9 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
/* CWAP allow to define the behavior of the Fn+F2 key,
* this method doesn't seems to be present on Eee PCs */
- if (asus->driver->wapf >= 0)
+ if (asus->driver->quirks->wapf >= 0)
asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP,
- asus->driver->wapf, NULL);
+ asus->driver->quirks->wapf, NULL);
return asus_wmi_sysfs_init(asus->platform_device);
}
@@ -1622,8 +1658,8 @@ static int asus_wmi_add(struct platform_device *pdev)
wdrv->platform_device = pdev;
platform_set_drvdata(asus->platform_device, asus);
- if (wdrv->quirks)
- wdrv->quirks(asus->driver);
+ if (wdrv->detect_quirks)
+ wdrv->detect_quirks(asus->driver);
err = asus_wmi_platform_init(asus);
if (err)
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index 8147c10161cc..d43b66742004 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -35,9 +35,16 @@ struct module;
struct key_entry;
struct asus_wmi;
+struct quirk_entry {
+ bool hotplug_wireless;
+ bool scalar_panel_brightness;
+ bool store_backlight_power;
+ int wapf;
+};
+
struct asus_wmi_driver {
- bool hotplug_wireless;
- int wapf;
+ int brightness;
+ int panel_power;
const char *name;
struct module *owner;
@@ -47,13 +54,14 @@ struct asus_wmi_driver {
const struct key_entry *keymap;
const char *input_name;
const char *input_phys;
+ struct quirk_entry *quirks;
/* Returns new code, value, and autorelease values in arguments.
* Return ASUS_WMI_KEY_IGNORE in code if event should be ignored. */
void (*key_filter) (struct asus_wmi_driver *driver, int *code,
unsigned int *value, bool *autorelease);
int (*probe) (struct platform_device *device);
- void (*quirks) (struct asus_wmi_driver *driver);
+ void (*detect_quirks) (struct asus_wmi_driver *driver);
struct platform_driver platform_driver;
struct platform_device *platform_device;
diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c
deleted file mode 100644
index 6f966d6c062b..000000000000
--- a/drivers/platform/x86/asus_acpi.c
+++ /dev/null
@@ -1,1513 +0,0 @@
-/*
- * asus_acpi.c - Asus Laptop ACPI Extras
- *
- *
- * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
- *
- * 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
- *
- *
- * The development page for this driver is located at
- * http://sourceforge.net/projects/acpi4asus/
- *
- * Credits:
- * Pontus Fuchs - Helper functions, cleanup
- * Johann Wiesner - Small compile fixes
- * John Belmonte - ACPI code for Toshiba laptop was a good starting point.
- * �ic Burghard - LED display support for W1N
- *
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/backlight.h>
-#include <acpi/acpi_drivers.h>
-#include <acpi/acpi_bus.h>
-#include <asm/uaccess.h>
-
-#define ASUS_ACPI_VERSION "0.30"
-
-#define PROC_ASUS "asus" /* The directory */
-#define PROC_MLED "mled"
-#define PROC_WLED "wled"
-#define PROC_TLED "tled"
-#define PROC_BT "bluetooth"
-#define PROC_LEDD "ledd"
-#define PROC_INFO "info"
-#define PROC_LCD "lcd"
-#define PROC_BRN "brn"
-#define PROC_DISP "disp"
-
-#define ACPI_HOTK_NAME "Asus Laptop ACPI Extras Driver"
-#define ACPI_HOTK_CLASS "hotkey"
-#define ACPI_HOTK_DEVICE_NAME "Hotkey"
-
-/*
- * Some events we use, same for all Asus
- */
-#define BR_UP 0x10
-#define BR_DOWN 0x20
-
-/*
- * Flags for hotk status
- */
-#define MLED_ON 0x01 /* Mail LED */
-#define WLED_ON 0x02 /* Wireless LED */
-#define TLED_ON 0x04 /* Touchpad LED */
-#define BT_ON 0x08 /* Internal Bluetooth */
-
-MODULE_AUTHOR("Julien Lerouge, Karol Kozimor");
-MODULE_DESCRIPTION(ACPI_HOTK_NAME);
-MODULE_LICENSE("GPL");
-
-static uid_t asus_uid;
-static gid_t asus_gid;
-module_param(asus_uid, uint, 0);
-MODULE_PARM_DESC(asus_uid, "UID for entries in /proc/acpi/asus");
-module_param(asus_gid, uint, 0);
-MODULE_PARM_DESC(asus_gid, "GID for entries in /proc/acpi/asus");
-
-/* For each model, all features implemented,
- * those marked with R are relative to HOTK, A for absolute */
-struct model_data {
- char *name; /* name of the laptop________________A */
- char *mt_mled; /* method to handle mled_____________R */
- char *mled_status; /* node to handle mled reading_______A */
- char *mt_wled; /* method to handle wled_____________R */
- char *wled_status; /* node to handle wled reading_______A */
- char *mt_tled; /* method to handle tled_____________R */
- char *tled_status; /* node to handle tled reading_______A */
- char *mt_ledd; /* method to handle LED display______R */
- char *mt_bt_switch; /* method to switch Bluetooth on/off_R */
- char *bt_status; /* no model currently supports this__? */
- char *mt_lcd_switch; /* method to turn LCD on/off_________A */
- char *lcd_status; /* node to read LCD panel state______A */
- char *brightness_up; /* method to set brightness up_______A */
- char *brightness_down; /* method to set brightness down ____A */
- char *brightness_set; /* method to set absolute brightness_R */
- char *brightness_get; /* method to get absolute brightness_R */
- char *brightness_status;/* node to get brightness____________A */
- char *display_set; /* method to set video output________R */
- char *display_get; /* method to get video output________R */
-};
-
-/*
- * This is the main structure, we can use it to store anything interesting
- * about the hotk device
- */
-struct asus_hotk {
- struct acpi_device *device; /* the device we are in */
- acpi_handle handle; /* the handle of the hotk device */
- char status; /* status of the hotk, for LEDs */
- u32 ledd_status; /* status of the LED display */
- struct model_data *methods; /* methods available on the laptop */
- u8 brightness; /* brightness level */
- enum {
- A1x = 0, /* A1340D, A1300F */
- A2x, /* A2500H */
- A4G, /* A4700G */
- D1x, /* D1 */
- L2D, /* L2000D */
- L3C, /* L3800C */
- L3D, /* L3400D */
- L3H, /* L3H, L2000E, L5D */
- L4R, /* L4500R */
- L5x, /* L5800C */
- L8L, /* L8400L */
- M1A, /* M1300A */
- M2E, /* M2400E, L4400L */
- M6N, /* M6800N, W3400N */
- M6R, /* M6700R, A3000G */
- P30, /* Samsung P30 */
- S1x, /* S1300A, but also L1400B and M2400A (L84F) */
- S2x, /* S200 (J1 reported), Victor MP-XP7210 */
- W1N, /* W1000N */
- W5A, /* W5A */
- W3V, /* W3030V */
- xxN, /* M2400N, M3700N, M5200N, M6800N,
- S1300N, S5200N*/
- A4S, /* Z81sp */
- F3Sa, /* (Centrino) */
- R1F,
- END_MODEL
- } model; /* Models currently supported */
- u16 event_count[128]; /* Count for each event TODO make this better */
-};
-
-/* Here we go */
-#define A1x_PREFIX "\\_SB.PCI0.ISA.EC0."
-#define L3C_PREFIX "\\_SB.PCI0.PX40.ECD0."
-#define M1A_PREFIX "\\_SB.PCI0.PX40.EC0."
-#define P30_PREFIX "\\_SB.PCI0.LPCB.EC0."
-#define S1x_PREFIX "\\_SB.PCI0.PX40."
-#define S2x_PREFIX A1x_PREFIX
-#define xxN_PREFIX "\\_SB.PCI0.SBRG.EC0."
-
-static struct model_data model_conf[END_MODEL] = {
- /*
- * TODO I have seen a SWBX and AIBX method on some models, like L1400B,
- * it seems to be a kind of switch, but what for ?
- */
-
- {
- .name = "A1x",
- .mt_mled = "MLED",
- .mled_status = "\\MAIL",
- .mt_lcd_switch = A1x_PREFIX "_Q10",
- .lcd_status = "\\BKLI",
- .brightness_up = A1x_PREFIX "_Q0E",
- .brightness_down = A1x_PREFIX "_Q0F"},
-
- {
- .name = "A2x",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .wled_status = "\\SG66",
- .mt_lcd_switch = "\\Q10",
- .lcd_status = "\\BAOF",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "A4G",
- .mt_mled = "MLED",
-/* WLED present, but not controlled by ACPI */
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\ADVG"},
-
- {
- .name = "D1x",
- .mt_mled = "MLED",
- .mt_lcd_switch = "\\Q0D",
- .lcd_status = "\\GP11",
- .brightness_up = "\\Q0C",
- .brightness_down = "\\Q0B",
- .brightness_status = "\\BLVL",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "L2D",
- .mt_mled = "MLED",
- .mled_status = "\\SGP6",
- .mt_wled = "WLED",
- .wled_status = "\\RCP3",
- .mt_lcd_switch = "\\Q10",
- .lcd_status = "\\SGP0",
- .brightness_up = "\\Q0E",
- .brightness_down = "\\Q0F",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "L3C",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = L3C_PREFIX "_Q10",
- .lcd_status = "\\GL32",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\_SB.PCI0.PCI1.VGAC.NMAP"},
-
- {
- .name = "L3D",
- .mt_mled = "MLED",
- .mled_status = "\\MALD",
- .mt_wled = "WLED",
- .mt_lcd_switch = "\\Q10",
- .lcd_status = "\\BKLG",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "L3H",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = "EHK",
- .lcd_status = "\\_SB.PCI0.PM.PBC",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "L4R",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .wled_status = "\\_SB.PCI0.SBRG.SG13",
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"},
-
- {
- .name = "L5x",
- .mt_mled = "MLED",
-/* WLED present, but not controlled by ACPI */
- .mt_tled = "TLED",
- .mt_lcd_switch = "\\Q0D",
- .lcd_status = "\\BAOF",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "L8L"
-/* No features, but at least support the hotkeys */
- },
-
- {
- .name = "M1A",
- .mt_mled = "MLED",
- .mt_lcd_switch = M1A_PREFIX "Q10",
- .lcd_status = "\\PNOF",
- .brightness_up = M1A_PREFIX "Q0E",
- .brightness_down = M1A_PREFIX "Q0F",
- .brightness_status = "\\BRIT",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "M2E",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = "\\Q10",
- .lcd_status = "\\GP06",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "M6N",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .wled_status = "\\_SB.PCI0.SBRG.SG13",
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .lcd_status = "\\_SB.BKLT",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\SSTE"},
-
- {
- .name = "M6R",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"},
-
- {
- .name = "P30",
- .mt_wled = "WLED",
- .mt_lcd_switch = P30_PREFIX "_Q0E",
- .lcd_status = "\\BKLT",
- .brightness_up = P30_PREFIX "_Q68",
- .brightness_down = P30_PREFIX "_Q69",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\DNXT"},
-
- {
- .name = "S1x",
- .mt_mled = "MLED",
- .mled_status = "\\EMLE",
- .mt_wled = "WLED",
- .mt_lcd_switch = S1x_PREFIX "Q10",
- .lcd_status = "\\PNOF",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV"},
-
- {
- .name = "S2x",
- .mt_mled = "MLED",
- .mled_status = "\\MAIL",
- .mt_lcd_switch = S2x_PREFIX "_Q10",
- .lcd_status = "\\BKLI",
- .brightness_up = S2x_PREFIX "_Q0B",
- .brightness_down = S2x_PREFIX "_Q0A"},
-
- {
- .name = "W1N",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_ledd = "SLCM",
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .lcd_status = "\\BKLT",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\ADVG"},
-
- {
- .name = "W5A",
- .mt_bt_switch = "BLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\ADVG"},
-
- {
- .name = "W3V",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .lcd_status = "\\BKLT",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"},
-
- {
- .name = "xxN",
- .mt_mled = "MLED",
-/* WLED present, but not controlled by ACPI */
- .mt_lcd_switch = xxN_PREFIX "_Q10",
- .lcd_status = "\\BKLT",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\ADVG"},
-
- {
- .name = "A4S",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .mt_bt_switch = "BLED",
- .mt_wled = "WLED"
- },
-
- {
- .name = "F3Sa",
- .mt_bt_switch = "BLED",
- .mt_wled = "WLED",
- .mt_mled = "MLED",
- .brightness_get = "GPLV",
- .brightness_set = "SPLV",
- .mt_lcd_switch = "\\_SB.PCI0.SBRG.EC0._Q10",
- .lcd_status = "\\_SB.PCI0.SBRG.EC0.RPIN",
- .display_get = "\\ADVG",
- .display_set = "SDSP",
- },
- {
- .name = "R1F",
- .mt_bt_switch = "BLED",
- .mt_mled = "MLED",
- .mt_wled = "WLED",
- .mt_lcd_switch = "\\Q10",
- .lcd_status = "\\GP06",
- .brightness_set = "SPLV",
- .brightness_get = "GPLV",
- .display_set = "SDSP",
- .display_get = "\\INFB"
- }
-};
-
-/* procdir we use */
-static struct proc_dir_entry *asus_proc_dir;
-
-static struct backlight_device *asus_backlight_device;
-
-/*
- * This header is made available to allow proper configuration given model,
- * revision number , ... this info cannot go in struct asus_hotk because it is
- * available before the hotk
- */
-static struct acpi_table_header *asus_info;
-
-/* The actual device the driver binds to */
-static struct asus_hotk *hotk;
-
-/*
- * The hotkey driver and autoloading declaration
- */
-static int asus_hotk_add(struct acpi_device *device);
-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[] = {
- {"ATK0100", 0},
- {"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, asus_device_ids);
-
-static struct acpi_driver asus_hotk_driver = {
- .name = "asus_acpi",
- .class = ACPI_HOTK_CLASS,
- .owner = THIS_MODULE,
- .ids = asus_device_ids,
- .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
- .ops = {
- .add = asus_hotk_add,
- .remove = asus_hotk_remove,
- .notify = asus_hotk_notify,
- },
-};
-
-/*
- * This function evaluates an ACPI method, given an int as parameter, the
- * method is searched within the scope of the handle, can be NULL. The output
- * of the method is written is output, which can also be NULL
- *
- * returns 1 if write is successful, 0 else.
- */
-static int write_acpi_int(acpi_handle handle, const char *method, int val,
- struct acpi_buffer *output)
-{
- struct acpi_object_list params; /* list of input parameters (int) */
- union acpi_object in_obj; /* the only param we use */
- acpi_status status;
-
- params.count = 1;
- params.pointer = &in_obj;
- in_obj.type = ACPI_TYPE_INTEGER;
- in_obj.integer.value = val;
-
- status = acpi_evaluate_object(handle, (char *)method, &params, output);
- return (status == AE_OK);
-}
-
-static int read_acpi_int(acpi_handle handle, const char *method, int *val)
-{
- struct acpi_buffer output;
- union acpi_object out_obj;
- acpi_status status;
-
- output.length = sizeof(out_obj);
- output.pointer = &out_obj;
-
- status = acpi_evaluate_object(handle, (char *)method, NULL, &output);
- *val = out_obj.integer.value;
- return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
-}
-
-static int asus_info_proc_show(struct seq_file *m, void *v)
-{
- int temp;
-
- seq_printf(m, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n");
- seq_printf(m, "Model reference : %s\n", hotk->methods->name);
- /*
- * The SFUN method probably allows the original driver to get the list
- * of features supported by a given model. For now, 0x0100 or 0x0800
- * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
- * The significance of others is yet to be found.
- */
- if (read_acpi_int(hotk->handle, "SFUN", &temp))
- seq_printf(m, "SFUN value : 0x%04x\n", temp);
- /*
- * Another value for userspace: the ASYM method returns 0x02 for
- * battery low and 0x04 for battery critical, its readings tend to be
- * more accurate than those provided by _BST.
- * Note: since not all the laptops provide this method, errors are
- * silently ignored.
- */
- if (read_acpi_int(hotk->handle, "ASYM", &temp))
- seq_printf(m, "ASYM value : 0x%04x\n", temp);
- if (asus_info) {
- seq_printf(m, "DSDT length : %d\n", asus_info->length);
- seq_printf(m, "DSDT checksum : %d\n", asus_info->checksum);
- seq_printf(m, "DSDT revision : %d\n", asus_info->revision);
- seq_printf(m, "OEM id : %.*s\n", ACPI_OEM_ID_SIZE, asus_info->oem_id);
- seq_printf(m, "OEM table id : %.*s\n", ACPI_OEM_TABLE_ID_SIZE, asus_info->oem_table_id);
- seq_printf(m, "OEM revision : 0x%x\n", asus_info->oem_revision);
- seq_printf(m, "ASL comp vendor id : %.*s\n", ACPI_NAME_SIZE, asus_info->asl_compiler_id);
- seq_printf(m, "ASL comp revision : 0x%x\n", asus_info->asl_compiler_revision);
- }
-
- return 0;
-}
-
-static int asus_info_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, asus_info_proc_show, NULL);
-}
-
-static const struct file_operations asus_info_proc_fops = {
- .owner = THIS_MODULE,
- .open = asus_info_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-/*
- * /proc handlers
- * We write our info in page, we begin at offset off and cannot write more
- * than count bytes. We set eof to 1 if we handle those 2 values. We return the
- * number of bytes written in page
- */
-
-/* Generic LED functions */
-static int read_led(const char *ledname, int ledmask)
-{
- if (ledname) {
- int led_status;
-
- if (read_acpi_int(NULL, ledname, &led_status))
- return led_status;
- else
- pr_warn("Error reading LED status\n");
- }
- return (hotk->status & ledmask) ? 1 : 0;
-}
-
-static int parse_arg(const char __user *buf, unsigned long count, int *val)
-{
- char s[32];
- if (!count)
- return 0;
- if (count > 31)
- return -EINVAL;
- if (copy_from_user(s, buf, count))
- return -EFAULT;
- s[count] = 0;
- if (sscanf(s, "%i", val) != 1)
- return -EINVAL;
- return count;
-}
-
-/* FIXME: kill extraneous args so it can be called independently */
-static int
-write_led(const char __user *buffer, unsigned long count,
- char *ledname, int ledmask, int invert)
-{
- int rv, value;
- int led_out = 0;
-
- rv = parse_arg(buffer, count, &value);
- if (rv > 0)
- led_out = value ? 1 : 0;
-
- hotk->status =
- (led_out) ? (hotk->status | ledmask) : (hotk->status & ~ledmask);
-
- if (invert) /* invert target value */
- led_out = !led_out;
-
- if (!write_acpi_int(hotk->handle, ledname, led_out, NULL))
- pr_warn("LED (%s) write failed\n", ledname);
-
- return rv;
-}
-
-/*
- * Proc handlers for MLED
- */
-static int mled_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "%d\n", read_led(hotk->methods->mled_status, MLED_ON));
- return 0;
-}
-
-static int mled_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, mled_proc_show, NULL);
-}
-
-static ssize_t mled_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- return write_led(buffer, count, hotk->methods->mt_mled, MLED_ON, 1);
-}
-
-static const struct file_operations mled_proc_fops = {
- .owner = THIS_MODULE,
- .open = mled_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = mled_proc_write,
-};
-
-/*
- * Proc handlers for LED display
- */
-static int ledd_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "0x%08x\n", hotk->ledd_status);
- return 0;
-}
-
-static int ledd_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, ledd_proc_show, NULL);
-}
-
-static ssize_t ledd_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- int rv, value;
-
- rv = parse_arg(buffer, count, &value);
- if (rv > 0) {
- if (!write_acpi_int
- (hotk->handle, hotk->methods->mt_ledd, value, NULL))
- pr_warn("LED display write failed\n");
- else
- hotk->ledd_status = (u32) value;
- }
- return rv;
-}
-
-static const struct file_operations ledd_proc_fops = {
- .owner = THIS_MODULE,
- .open = ledd_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = ledd_proc_write,
-};
-
-/*
- * Proc handlers for WLED
- */
-static int wled_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "%d\n", read_led(hotk->methods->wled_status, WLED_ON));
- return 0;
-}
-
-static int wled_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, wled_proc_show, NULL);
-}
-
-static ssize_t wled_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- return write_led(buffer, count, hotk->methods->mt_wled, WLED_ON, 0);
-}
-
-static const struct file_operations wled_proc_fops = {
- .owner = THIS_MODULE,
- .open = wled_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = wled_proc_write,
-};
-
-/*
- * Proc handlers for Bluetooth
- */
-static int bluetooth_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "%d\n", read_led(hotk->methods->bt_status, BT_ON));
- return 0;
-}
-
-static int bluetooth_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, bluetooth_proc_show, NULL);
-}
-
-static ssize_t bluetooth_proc_write(struct file *file,
- const char __user *buffer, size_t count, loff_t *pos)
-{
- /* Note: mt_bt_switch controls both internal Bluetooth adapter's
- presence and its LED */
- return write_led(buffer, count, hotk->methods->mt_bt_switch, BT_ON, 0);
-}
-
-static const struct file_operations bluetooth_proc_fops = {
- .owner = THIS_MODULE,
- .open = bluetooth_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = bluetooth_proc_write,
-};
-
-/*
- * Proc handlers for TLED
- */
-static int tled_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "%d\n", read_led(hotk->methods->tled_status, TLED_ON));
- return 0;
-}
-
-static int tled_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, tled_proc_show, NULL);
-}
-
-static ssize_t tled_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- return write_led(buffer, count, hotk->methods->mt_tled, TLED_ON, 0);
-}
-
-static const struct file_operations tled_proc_fops = {
- .owner = THIS_MODULE,
- .open = tled_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = tled_proc_write,
-};
-
-static int get_lcd_state(void)
-{
- int lcd = 0;
-
- if (hotk->model == L3H) {
- /* L3H and the like have to be handled differently */
- acpi_status status = 0;
- struct acpi_object_list input;
- union acpi_object mt_params[2];
- struct acpi_buffer output;
- union acpi_object out_obj;
-
- input.count = 2;
- input.pointer = mt_params;
- /* Note: the following values are partly guessed up, but
- otherwise they seem to work */
- mt_params[0].type = ACPI_TYPE_INTEGER;
- mt_params[0].integer.value = 0x02;
- mt_params[1].type = ACPI_TYPE_INTEGER;
- mt_params[1].integer.value = 0x02;
-
- output.length = sizeof(out_obj);
- output.pointer = &out_obj;
-
- status =
- acpi_evaluate_object(NULL, hotk->methods->lcd_status,
- &input, &output);
- if (status != AE_OK)
- return -1;
- if (out_obj.type == ACPI_TYPE_INTEGER)
- /* That's what the AML code does */
- lcd = out_obj.integer.value >> 8;
- } else if (hotk->model == F3Sa) {
- unsigned long long tmp;
- union acpi_object param;
- struct acpi_object_list input;
- acpi_status status;
-
- /* Read pin 11 */
- param.type = ACPI_TYPE_INTEGER;
- param.integer.value = 0x11;
- input.count = 1;
- input.pointer = &param;
-
- status = acpi_evaluate_integer(NULL, hotk->methods->lcd_status,
- &input, &tmp);
- if (status != AE_OK)
- return -1;
-
- lcd = tmp;
- } else {
- /* We don't have to check anything if we are here */
- if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd))
- pr_warn("Error reading LCD status\n");
-
- if (hotk->model == L2D)
- lcd = ~lcd;
- }
-
- return (lcd & 1);
-}
-
-static int set_lcd_state(int value)
-{
- int lcd = 0;
- acpi_status status = 0;
-
- lcd = value ? 1 : 0;
- if (lcd != get_lcd_state()) {
- /* switch */
- if (hotk->model != L3H) {
- status =
- acpi_evaluate_object(NULL,
- hotk->methods->mt_lcd_switch,
- NULL, NULL);
- } else {
- /* L3H and the like must be handled differently */
- if (!write_acpi_int
- (hotk->handle, hotk->methods->mt_lcd_switch, 0x07,
- NULL))
- status = AE_ERROR;
- /* L3H's AML executes EHK (0x07) upon Fn+F7 keypress,
- the exact behaviour is simulated here */
- }
- if (ACPI_FAILURE(status))
- pr_warn("Error switching LCD\n");
- }
- return 0;
-
-}
-
-static int lcd_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "%d\n", get_lcd_state());
- return 0;
-}
-
-static int lcd_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, lcd_proc_show, NULL);
-}
-
-static ssize_t lcd_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- int rv, value;
-
- rv = parse_arg(buffer, count, &value);
- if (rv > 0)
- set_lcd_state(value);
- return rv;
-}
-
-static const struct file_operations lcd_proc_fops = {
- .owner = THIS_MODULE,
- .open = lcd_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = lcd_proc_write,
-};
-
-static int read_brightness(struct backlight_device *bd)
-{
- int value;
-
- if (hotk->methods->brightness_get) { /* SPLV/GPLV laptop */
- if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get,
- &value))
- pr_warn("Error reading brightness\n");
- } else if (hotk->methods->brightness_status) { /* For D1 for example */
- if (!read_acpi_int(NULL, hotk->methods->brightness_status,
- &value))
- pr_warn("Error reading brightness\n");
- } else /* No GPLV method */
- value = hotk->brightness;
- return value;
-}
-
-/*
- * Change the brightness level
- */
-static int set_brightness(int value)
-{
- acpi_status status = 0;
- int ret = 0;
-
- /* SPLV laptop */
- if (hotk->methods->brightness_set) {
- if (!write_acpi_int(hotk->handle, hotk->methods->brightness_set,
- value, NULL)) {
- pr_warn("Error changing brightness\n");
- ret = -EIO;
- }
- goto out;
- }
-
- /* No SPLV method if we are here, act as appropriate */
- value -= read_brightness(NULL);
- while (value != 0) {
- status = acpi_evaluate_object(NULL, (value > 0) ?
- hotk->methods->brightness_up :
- hotk->methods->brightness_down,
- NULL, NULL);
- (value > 0) ? value-- : value++;
- if (ACPI_FAILURE(status)) {
- pr_warn("Error changing brightness\n");
- ret = -EIO;
- }
- }
-out:
- return ret;
-}
-
-static int set_brightness_status(struct backlight_device *bd)
-{
- return set_brightness(bd->props.brightness);
-}
-
-static int brn_proc_show(struct seq_file *m, void *v)
-{
- seq_printf(m, "%d\n", read_brightness(NULL));
- return 0;
-}
-
-static int brn_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, brn_proc_show, NULL);
-}
-
-static ssize_t brn_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- int rv, value;
-
- rv = parse_arg(buffer, count, &value);
- if (rv > 0) {
- value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
- /* 0 <= value <= 15 */
- set_brightness(value);
- }
- return rv;
-}
-
-static const struct file_operations brn_proc_fops = {
- .owner = THIS_MODULE,
- .open = brn_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = brn_proc_write,
-};
-
-static void set_display(int value)
-{
- /* no sanity check needed for now */
- if (!write_acpi_int(hotk->handle, hotk->methods->display_set,
- value, NULL))
- pr_warn("Error setting display\n");
- return;
-}
-
-/*
- * Now, *this* one could be more user-friendly, but so far, no-one has
- * complained. The significance of bits is the same as in proc_write_disp()
- */
-static int disp_proc_show(struct seq_file *m, void *v)
-{
- int value = 0;
-
- if (!read_acpi_int(hotk->handle, hotk->methods->display_get, &value))
- pr_warn("Error reading display status\n");
- value &= 0x07; /* needed for some models, shouldn't hurt others */
- seq_printf(m, "%d\n", value);
- return 0;
-}
-
-static int disp_proc_open(struct inode *inode, struct file *file)
-{
- return single_open(file, disp_proc_show, NULL);
-}
-
-/*
- * Experimental support for display switching. As of now: 1 should activate
- * the LCD output, 2 should do for CRT, and 4 for TV-Out. Any combination
- * (bitwise) of these will suffice. I never actually tested 3 displays hooked
- * up simultaneously, so be warned. See the acpi4asus README for more info.
- */
-static ssize_t disp_proc_write(struct file *file, const char __user *buffer,
- size_t count, loff_t *pos)
-{
- int rv, value;
-
- rv = parse_arg(buffer, count, &value);
- if (rv > 0)
- set_display(value);
- return rv;
-}
-
-static const struct file_operations disp_proc_fops = {
- .owner = THIS_MODULE,
- .open = disp_proc_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
- .write = disp_proc_write,
-};
-
-static int
-asus_proc_add(char *name, const struct file_operations *proc_fops, umode_t mode,
- struct acpi_device *device)
-{
- struct proc_dir_entry *proc;
-
- proc = proc_create_data(name, mode, acpi_device_dir(device),
- proc_fops, acpi_driver_data(device));
- if (!proc) {
- pr_warn(" Unable to create %s fs entry\n", name);
- return -1;
- }
- proc->uid = asus_uid;
- proc->gid = asus_gid;
- return 0;
-}
-
-static int asus_hotk_add_fs(struct acpi_device *device)
-{
- struct proc_dir_entry *proc;
- umode_t mode;
-
- if ((asus_uid == 0) && (asus_gid == 0)) {
- mode = S_IFREG | S_IRUGO | S_IWUSR | S_IWGRP;
- } else {
- mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP;
- pr_warn(" asus_uid and asus_gid parameters are "
- "deprecated, use chown and chmod instead!\n");
- }
-
- acpi_device_dir(device) = asus_proc_dir;
- if (!acpi_device_dir(device))
- return -ENODEV;
-
- proc = proc_create(PROC_INFO, mode, acpi_device_dir(device),
- &asus_info_proc_fops);
- if (proc) {
- proc->uid = asus_uid;
- proc->gid = asus_gid;
- } else {
- pr_warn(" Unable to create " PROC_INFO " fs entry\n");
- }
-
- if (hotk->methods->mt_wled) {
- asus_proc_add(PROC_WLED, &wled_proc_fops, mode, device);
- }
-
- if (hotk->methods->mt_ledd) {
- asus_proc_add(PROC_LEDD, &ledd_proc_fops, mode, device);
- }
-
- if (hotk->methods->mt_mled) {
- asus_proc_add(PROC_MLED, &mled_proc_fops, mode, device);
- }
-
- if (hotk->methods->mt_tled) {
- asus_proc_add(PROC_TLED, &tled_proc_fops, mode, device);
- }
-
- if (hotk->methods->mt_bt_switch) {
- asus_proc_add(PROC_BT, &bluetooth_proc_fops, mode, device);
- }
-
- /*
- * We need both read node and write method as LCD switch is also
- * accessible from the keyboard
- */
- if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) {
- asus_proc_add(PROC_LCD, &lcd_proc_fops, mode, device);
- }
-
- if ((hotk->methods->brightness_up && hotk->methods->brightness_down) ||
- (hotk->methods->brightness_get && hotk->methods->brightness_set)) {
- asus_proc_add(PROC_BRN, &brn_proc_fops, mode, device);
- }
-
- if (hotk->methods->display_set) {
- asus_proc_add(PROC_DISP, &disp_proc_fops, mode, device);
- }
-
- return 0;
-}
-
-static int asus_hotk_remove_fs(struct acpi_device *device)
-{
- if (acpi_device_dir(device)) {
- remove_proc_entry(PROC_INFO, acpi_device_dir(device));
- if (hotk->methods->mt_wled)
- remove_proc_entry(PROC_WLED, acpi_device_dir(device));
- if (hotk->methods->mt_mled)
- remove_proc_entry(PROC_MLED, acpi_device_dir(device));
- if (hotk->methods->mt_tled)
- remove_proc_entry(PROC_TLED, acpi_device_dir(device));
- if (hotk->methods->mt_ledd)
- remove_proc_entry(PROC_LEDD, acpi_device_dir(device));
- if (hotk->methods->mt_bt_switch)
- remove_proc_entry(PROC_BT, acpi_device_dir(device));
- if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status)
- remove_proc_entry(PROC_LCD, acpi_device_dir(device));
- if ((hotk->methods->brightness_up
- && hotk->methods->brightness_down)
- || (hotk->methods->brightness_get
- && hotk->methods->brightness_set))
- remove_proc_entry(PROC_BRN, acpi_device_dir(device));
- if (hotk->methods->display_set)
- remove_proc_entry(PROC_DISP, acpi_device_dir(device));
- }
- return 0;
-}
-
-static void asus_hotk_notify(struct acpi_device *device, u32 event)
-{
- /* TODO Find a better way to handle events count. */
- if (!hotk)
- 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)
- hotk->brightness = (event & ~((u32) BR_UP));
- else if ((event & ~((u32) BR_DOWN)) < 16)
- hotk->brightness = (event & ~((u32) BR_DOWN));
-
- acpi_bus_generate_proc_event(hotk->device, event,
- hotk->event_count[event % 128]++);
-
- return;
-}
-
-/*
- * Match the model string to the list of supported models. Return END_MODEL if
- * no match or model is NULL.
- */
-static int asus_model_match(char *model)
-{
- if (model == NULL)
- return END_MODEL;
-
- if (strncmp(model, "L3D", 3) == 0)
- return L3D;
- else if (strncmp(model, "L2E", 3) == 0 ||
- strncmp(model, "L3H", 3) == 0 || strncmp(model, "L5D", 3) == 0)
- return L3H;
- else if (strncmp(model, "L3", 2) == 0 || strncmp(model, "L2B", 3) == 0)
- return L3C;
- else if (strncmp(model, "L8L", 3) == 0)
- return L8L;
- else if (strncmp(model, "L4R", 3) == 0)
- return L4R;
- else if (strncmp(model, "M6N", 3) == 0 || strncmp(model, "W3N", 3) == 0)
- return M6N;
- else if (strncmp(model, "M6R", 3) == 0 || strncmp(model, "A3G", 3) == 0)
- return M6R;
- else if (strncmp(model, "M2N", 3) == 0 ||
- strncmp(model, "M3N", 3) == 0 ||
- strncmp(model, "M5N", 3) == 0 ||
- strncmp(model, "S1N", 3) == 0 ||
- strncmp(model, "S5N", 3) == 0)
- return xxN;
- else if (strncmp(model, "M1", 2) == 0)
- return M1A;
- else if (strncmp(model, "M2", 2) == 0 || strncmp(model, "L4E", 3) == 0)
- return M2E;
- else if (strncmp(model, "L2", 2) == 0)
- return L2D;
- else if (strncmp(model, "L8", 2) == 0)
- return S1x;
- else if (strncmp(model, "D1", 2) == 0)
- return D1x;
- else if (strncmp(model, "A1", 2) == 0)
- return A1x;
- else if (strncmp(model, "A2", 2) == 0)
- return A2x;
- else if (strncmp(model, "J1", 2) == 0)
- return S2x;
- else if (strncmp(model, "L5", 2) == 0)
- return L5x;
- else if (strncmp(model, "A4G", 3) == 0)
- return A4G;
- else if (strncmp(model, "W1N", 3) == 0)
- return W1N;
- else if (strncmp(model, "W3V", 3) == 0)
- return W3V;
- else if (strncmp(model, "W5A", 3) == 0)
- return W5A;
- else if (strncmp(model, "R1F", 3) == 0)
- return R1F;
- else if (strncmp(model, "A4S", 3) == 0)
- return A4S;
- else if (strncmp(model, "F3Sa", 4) == 0)
- return F3Sa;
- else
- return END_MODEL;
-}
-
-/*
- * This function is used to initialize the hotk with right values. In this
- * method, we can make all the detection we want, and modify the hotk struct
- */
-static int asus_hotk_get_info(void)
-{
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- union acpi_object *model = NULL;
- int bsts_result;
- char *string = NULL;
- acpi_status status;
-
- /*
- * Get DSDT headers early enough to allow for differentiating between
- * models, but late enough to allow acpi_bus_register_driver() to fail
- * before doing anything ACPI-specific. Should we encounter a machine,
- * which needs special handling (i.e. its hotkey device has a different
- * HID), this bit will be moved. A global variable asus_info contains
- * the DSDT header.
- */
- status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
- if (ACPI_FAILURE(status))
- pr_warn(" Couldn't get the DSDT table header\n");
-
- /* We have to write 0 on init this far for all ASUS models */
- if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
- pr_err(" Hotkey initialization failed\n");
- return -ENODEV;
- }
-
- /* This needs to be called for some laptops to init properly */
- if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result))
- pr_warn(" Error calling BSTS\n");
- else if (bsts_result)
- pr_notice(" BSTS called, 0x%02x returned\n", bsts_result);
-
- /*
- * Try to match the object returned by INIT to the specific model.
- * Handle every possible object (or the lack of thereof) the DSDT
- * writers might throw at us. When in trouble, we pass NULL to
- * asus_model_match() and try something completely different.
- */
- if (buffer.pointer) {
- model = buffer.pointer;
- switch (model->type) {
- case ACPI_TYPE_STRING:
- string = model->string.pointer;
- break;
- case ACPI_TYPE_BUFFER:
- string = model->buffer.pointer;
- break;
- default:
- kfree(model);
- model = NULL;
- break;
- }
- }
- hotk->model = asus_model_match(string);
- if (hotk->model == END_MODEL) { /* match failed */
- if (asus_info &&
- strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) {
- hotk->model = P30;
- pr_notice(" Samsung P30 detected, supported\n");
- hotk->methods = &model_conf[hotk->model];
- kfree(model);
- return 0;
- } else {
- hotk->model = M2E;
- pr_notice(" unsupported model %s, trying default values\n",
- string);
- pr_notice(" send /proc/acpi/dsdt to the developers\n");
- kfree(model);
- return -ENODEV;
- }
- }
- hotk->methods = &model_conf[hotk->model];
- pr_notice(" %s model detected, supported\n", string);
-
- /* Sort of per-model blacklist */
- if (strncmp(string, "L2B", 3) == 0)
- hotk->methods->lcd_status = NULL;
- /* L2B is similar enough to L3C to use its settings, with this only
- exception */
- else if (strncmp(string, "A3G", 3) == 0)
- hotk->methods->lcd_status = "\\BLFG";
- /* A3G is like M6R */
- else if (strncmp(string, "S5N", 3) == 0 ||
- strncmp(string, "M5N", 3) == 0 ||
- strncmp(string, "W3N", 3) == 0)
- hotk->methods->mt_mled = NULL;
- /* S5N, M5N and W3N have no MLED */
- else if (strncmp(string, "L5D", 3) == 0)
- hotk->methods->mt_wled = NULL;
- /* L5D's WLED is not controlled by ACPI */
- else if (strncmp(string, "M2N", 3) == 0 ||
- strncmp(string, "W3V", 3) == 0 ||
- strncmp(string, "S1N", 3) == 0)
- hotk->methods->mt_wled = "WLED";
- /* M2N, S1N and W3V have a usable WLED */
- else if (asus_info) {
- if (strncmp(asus_info->oem_table_id, "L1", 2) == 0)
- hotk->methods->mled_status = NULL;
- /* S1300A reports L84F, but L1400B too, account for that */
- }
-
- kfree(model);
-
- return 0;
-}
-
-static int asus_hotk_check(void)
-{
- int result = 0;
-
- result = acpi_bus_get_status(hotk->device);
- if (result)
- return result;
-
- if (hotk->device->status.present) {
- result = asus_hotk_get_info();
- } else {
- pr_err(" Hotkey device not present, aborting\n");
- return -EINVAL;
- }
-
- return result;
-}
-
-static int asus_hotk_found;
-
-static int asus_hotk_add(struct acpi_device *device)
-{
- acpi_status status = AE_OK;
- int result;
-
- pr_notice("Asus Laptop ACPI Extras version %s\n", ASUS_ACPI_VERSION);
-
- hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
- if (!hotk)
- return -ENOMEM;
-
- hotk->handle = device->handle;
- strcpy(acpi_device_name(device), ACPI_HOTK_DEVICE_NAME);
- strcpy(acpi_device_class(device), ACPI_HOTK_CLASS);
- device->driver_data = hotk;
- hotk->device = device;
-
- result = asus_hotk_check();
- if (result)
- goto end;
-
- result = asus_hotk_add_fs(device);
- if (result)
- goto end;
-
- /* For laptops without GPLV: init the hotk->brightness value */
- if ((!hotk->methods->brightness_get)
- && (!hotk->methods->brightness_status)
- && (hotk->methods->brightness_up && hotk->methods->brightness_down)) {
- status =
- acpi_evaluate_object(NULL, hotk->methods->brightness_down,
- NULL, NULL);
- if (ACPI_FAILURE(status))
- pr_warn(" Error changing brightness\n");
- else {
- status =
- acpi_evaluate_object(NULL,
- hotk->methods->brightness_up,
- NULL, NULL);
- if (ACPI_FAILURE(status))
- pr_warn(" Strange, error changing brightness\n");
- }
- }
-
- asus_hotk_found = 1;
-
- /* LED display is off by default */
- hotk->ledd_status = 0xFFF;
-
-end:
- if (result)
- kfree(hotk);
-
- return result;
-}
-
-static int asus_hotk_remove(struct acpi_device *device, int type)
-{
- asus_hotk_remove_fs(device);
-
- kfree(hotk);
-
- return 0;
-}
-
-static const struct backlight_ops asus_backlight_data = {
- .get_brightness = read_brightness,
- .update_status = set_brightness_status,
-};
-
-static void asus_acpi_exit(void)
-{
- if (asus_backlight_device)
- backlight_device_unregister(asus_backlight_device);
-
- acpi_bus_unregister_driver(&asus_hotk_driver);
- remove_proc_entry(PROC_ASUS, acpi_root_dir);
-
- return;
-}
-
-static int __init asus_acpi_init(void)
-{
- struct backlight_properties props;
- int result;
-
- result = acpi_bus_register_driver(&asus_hotk_driver);
- if (result < 0)
- return result;
-
- asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir);
- if (!asus_proc_dir) {
- pr_err("Unable to create /proc entry\n");
- acpi_bus_unregister_driver(&asus_hotk_driver);
- return -ENODEV;
- }
-
- /*
- * This is a bit of a kludge. We only want this module loaded
- * for ASUS systems, but there's currently no way to probe the
- * ACPI namespace for ASUS HIDs. So we just return failure if
- * we didn't find one, which will cause the module to be
- * unloaded.
- */
- if (!asus_hotk_found) {
- acpi_bus_unregister_driver(&asus_hotk_driver);
- remove_proc_entry(PROC_ASUS, acpi_root_dir);
- return -ENODEV;
- }
-
- memset(&props, 0, sizeof(struct backlight_properties));
- props.type = BACKLIGHT_PLATFORM;
- props.max_brightness = 15;
- asus_backlight_device = backlight_device_register("asus", NULL, NULL,
- &asus_backlight_data,
- &props);
- if (IS_ERR(asus_backlight_device)) {
- pr_err("Could not register asus backlight device\n");
- asus_backlight_device = NULL;
- asus_acpi_exit();
- return -ENODEV;
- }
-
- return 0;
-}
-
-module_init(asus_acpi_init);
-module_exit(asus_acpi_exit);
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c
index d96734478324..1887e2f166a4 100644
--- a/drivers/platform/x86/compal-laptop.c
+++ b/drivers/platform/x86/compal-laptop.c
@@ -882,6 +882,7 @@ static struct dmi_system_id __initdata compal_dmi_table[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(dmi, compal_dmi_table);
static void initialize_power_supply_data(struct compal_data *data)
{
@@ -1097,16 +1098,3 @@ MODULE_AUTHOR("Roald Frederickx (roald.frederickx@gmail.com)");
MODULE_DESCRIPTION("Compal Laptop Support");
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
-
-MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*");
-MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*");
-MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*");
-MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*");
-MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*");
-MODULE_ALIAS("dmi:*:rnJHL90:rvrREFERENCE:*");
-MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*");
-MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*");
-MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*");
-MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1012:*");
-MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1110:*");
-MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1210:*");
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index d93e962f2610..a05fc9c955d8 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -117,6 +117,7 @@ static const struct dmi_system_id __initdata dell_device_table[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(dmi, dell_device_table);
static struct dmi_system_id __devinitdata dell_blacklist[] = {
/* Supported by compal-laptop */
@@ -184,6 +185,33 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
},
.driver_data = &quirk_dell_vostro_v130,
},
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Vostro 3555",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron N311z",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron M5110",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
};
static struct calling_interface_buffer *buffer;
@@ -236,9 +264,7 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy)
{
switch (dm->type) {
case 0xd4: /* Indexed IO */
- break;
case 0xd5: /* Protected Area Type 1 */
- break;
case 0xd6: /* Protected Area Type 2 */
break;
case 0xda: /* Calling interface */
@@ -615,6 +641,7 @@ static void touchpad_led_set(struct led_classdev *led_cdev,
static struct led_classdev touchpad_led = {
.name = "dell-laptop::touchpad",
.brightness_set = touchpad_led_set,
+ .flags = LED_CORE_SUSPENDRESUME,
};
static int __devinit touchpad_led_init(struct device *dev)
@@ -794,6 +821,3 @@ module_exit(dell_exit);
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
MODULE_DESCRIPTION("Dell laptop driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*");
-MODULE_ALIAS("dmi:*svnDellInc.:*:ct9:*");
-MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*");
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index ea44abd8df48..dab91b48d22c 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -646,7 +646,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
} else {
dev = pci_get_slot(bus, 0);
if (dev) {
- pci_remove_bus_device(dev);
+ pci_stop_and_remove_bus_device(dev);
pci_dev_put(dev);
}
}
@@ -1251,6 +1251,14 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc)
/*
* ACPI driver
*/
+static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event)
+{
+ if (!eeepc->inputdev)
+ return ;
+ if (!sparse_keymap_report_event(eeepc->inputdev, event, 1, true))
+ pr_info("Unknown key %x pressed\n", event);
+}
+
static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
{
struct eeepc_laptop *eeepc = acpi_driver_data(device);
@@ -1287,12 +1295,11 @@ static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
* event will be desired value (or else ignored)
*/
}
- sparse_keymap_report_event(eeepc->inputdev, event,
- 1, true);
+ eeepc_input_notify(eeepc, event);
}
} else {
/* Everything else is a bona-fide keypress event */
- sparse_keymap_report_event(eeepc->inputdev, event, 1, true);
+ eeepc_input_notify(eeepc, event);
}
}
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c
index 9f6e64302b45..656761380342 100644
--- a/drivers/platform/x86/eeepc-wmi.c
+++ b/drivers/platform/x86/eeepc-wmi.c
@@ -32,6 +32,7 @@
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/dmi.h>
+#include <linux/fb.h>
#include <acpi/acpi_bus.h>
#include "asus-wmi.h"
@@ -84,9 +85,81 @@ static const struct key_entry eeepc_wmi_keymap[] = {
{ KE_KEY, 0xed, { KEY_CAMERA_DOWN } },
{ KE_KEY, 0xee, { KEY_CAMERA_LEFT } },
{ KE_KEY, 0xef, { KEY_CAMERA_RIGHT } },
+ { KE_KEY, 0xf3, { KEY_MENU } },
+ { KE_KEY, 0xf5, { KEY_HOMEPAGE } },
+ { KE_KEY, 0xf6, { KEY_ESC } },
{ KE_END, 0},
};
+static struct quirk_entry quirk_asus_unknown = {
+};
+
+static struct quirk_entry quirk_asus_1000h = {
+ .hotplug_wireless = true,
+};
+
+static struct quirk_entry quirk_asus_et2012_type1 = {
+ .store_backlight_power = true,
+};
+
+static struct quirk_entry quirk_asus_et2012_type3 = {
+ .scalar_panel_brightness = true,
+ .store_backlight_power = true,
+};
+
+static struct quirk_entry *quirks;
+
+static void et2012_quirks(void)
+{
+ const struct dmi_device *dev = NULL;
+ char oemstring[30];
+
+ while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
+ if (sscanf(dev->name, "AEMS%24c", oemstring) == 1) {
+ if (oemstring[18] == '1')
+ quirks = &quirk_asus_et2012_type1;
+ else if (oemstring[18] == '3')
+ quirks = &quirk_asus_et2012_type3;
+ break;
+ }
+ }
+}
+
+static int dmi_matched(const struct dmi_system_id *dmi)
+{
+ char *model;
+
+ quirks = dmi->driver_data;
+
+ model = (char *)dmi->matches[1].substr;
+ if (unlikely(strncmp(model, "ET2012", 6) == 0))
+ et2012_quirks();
+
+ return 1;
+}
+
+static struct dmi_system_id asus_quirks[] = {
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK Computer INC. 1000H",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "1000H"),
+ },
+ .driver_data = &quirk_asus_1000h,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK Computer INC. ET2012E/I",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ET2012"),
+ },
+ .driver_data = &quirk_asus_unknown,
+ },
+ {},
+};
+
static void eeepc_wmi_key_filter(struct asus_wmi_driver *asus_wmi, int *code,
unsigned int *value, bool *autorelease)
{
@@ -141,33 +214,16 @@ static int eeepc_wmi_probe(struct platform_device *pdev)
return 0;
}
-static void eeepc_dmi_check(struct asus_wmi_driver *driver)
-{
- const char *model;
-
- model = dmi_get_system_info(DMI_PRODUCT_NAME);
- if (!model)
- return;
-
- /*
- * Whitelist for wlan hotplug
- *
- * Asus 1000H needs the current hotplug code to handle
- * Fn+F2 correctly. We may add other Asus here later, but
- * it seems that most of the laptops supported by asus-wmi
- * don't need to be on this list
- */
- if (strcmp(model, "1000H") == 0) {
- driver->hotplug_wireless = true;
- pr_info("wlan hotplug enabled\n");
- }
-}
-
static void eeepc_wmi_quirks(struct asus_wmi_driver *driver)
{
- driver->hotplug_wireless = hotplug_wireless;
- driver->wapf = -1;
- eeepc_dmi_check(driver);
+ quirks = &quirk_asus_unknown;
+ quirks->hotplug_wireless = hotplug_wireless;
+
+ dmi_check_system(asus_quirks);
+
+ driver->quirks = quirks;
+ driver->quirks->wapf = -1;
+ driver->panel_power = FB_BLANK_UNBLANK;
}
static struct asus_wmi_driver asus_wmi_driver = {
@@ -179,7 +235,7 @@ static struct asus_wmi_driver asus_wmi_driver = {
.input_phys = EEEPC_WMI_FILE "/input0",
.key_filter = eeepc_wmi_key_filter,
.probe = eeepc_wmi_probe,
- .quirks = eeepc_wmi_quirks,
+ .detect_quirks = eeepc_wmi_quirks,
};
diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c
new file mode 100644
index 000000000000..580d80a73c3a
--- /dev/null
+++ b/drivers/platform/x86/fujitsu-tablet.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2006-2012 Robert Gerlach <khnz@gmx.de>
+ * Copyright (C) 2005-2006 Jan Rychter <jan@rychter.com>
+ *
+ * You can redistribute and/or modify this program under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+
+#define MODULENAME "fujitsu-tablet"
+
+#define ACPI_FUJITSU_CLASS "fujitsu"
+
+#define INVERT_TABLET_MODE_BIT 0x01
+#define FORCE_TABLET_MODE_IF_UNDOCK 0x02
+
+#define KEYMAP_LEN 16
+
+static const struct acpi_device_id fujitsu_ids[] = {
+ { .id = "FUJ02BD" },
+ { .id = "FUJ02BF" },
+ { .id = "" }
+};
+
+struct fujitsu_config {
+ unsigned short keymap[KEYMAP_LEN];
+ unsigned int quirks;
+};
+
+static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initconst = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_SCROLLDOWN,
+ KEY_SCROLLUP,
+ KEY_DIRECTION,
+ KEY_LEFTCTRL,
+ KEY_BRIGHTNESSUP,
+ KEY_BRIGHTNESSDOWN,
+ KEY_BRIGHTNESS_ZERO,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_LEFTALT
+};
+
+static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initconst = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_PROG1,
+ KEY_PROG2,
+ KEY_DIRECTION,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_UP,
+ KEY_DOWN,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_LEFTCTRL,
+ KEY_LEFTALT
+};
+
+static unsigned short keymap_Stylistic_Tseries[KEYMAP_LEN] __initconst = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_PRINT,
+ KEY_BACKSPACE,
+ KEY_SPACE,
+ KEY_ENTER,
+ KEY_BRIGHTNESSUP,
+ KEY_BRIGHTNESSDOWN,
+ KEY_DOWN,
+ KEY_UP,
+ KEY_SCROLLUP,
+ KEY_SCROLLDOWN,
+ KEY_LEFTCTRL,
+ KEY_LEFTALT
+};
+
+static unsigned short keymap_Stylistic_ST5xxx[KEYMAP_LEN] __initconst = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_MAIL,
+ KEY_DIRECTION,
+ KEY_ESC,
+ KEY_ENTER,
+ KEY_BRIGHTNESSUP,
+ KEY_BRIGHTNESSDOWN,
+ KEY_DOWN,
+ KEY_UP,
+ KEY_SCROLLUP,
+ KEY_SCROLLDOWN,
+ KEY_LEFTCTRL,
+ KEY_LEFTALT
+};
+
+static struct {
+ struct input_dev *idev;
+ struct fujitsu_config config;
+ unsigned long prev_keymask;
+
+ char phys[21];
+
+ int irq;
+ int io_base;
+ int io_length;
+} fujitsu;
+
+static u8 fujitsu_ack(void)
+{
+ return inb(fujitsu.io_base + 2);
+}
+
+static u8 fujitsu_status(void)
+{
+ return inb(fujitsu.io_base + 6);
+}
+
+static u8 fujitsu_read_register(const u8 addr)
+{
+ outb(addr, fujitsu.io_base);
+ return inb(fujitsu.io_base + 4);
+}
+
+static void fujitsu_send_state(void)
+{
+ int state;
+ int dock, tablet_mode;
+
+ state = fujitsu_read_register(0xdd);
+
+ dock = state & 0x02;
+
+ if ((fujitsu.config.quirks & FORCE_TABLET_MODE_IF_UNDOCK) && (!dock)) {
+ tablet_mode = 1;
+ } else{
+ tablet_mode = state & 0x01;
+ if (fujitsu.config.quirks & INVERT_TABLET_MODE_BIT)
+ tablet_mode = !tablet_mode;
+ }
+
+ input_report_switch(fujitsu.idev, SW_DOCK, dock);
+ input_report_switch(fujitsu.idev, SW_TABLET_MODE, tablet_mode);
+ input_sync(fujitsu.idev);
+}
+
+static void fujitsu_reset(void)
+{
+ int timeout = 50;
+
+ fujitsu_ack();
+
+ while ((fujitsu_status() & 0x02) && (--timeout))
+ msleep(20);
+
+ fujitsu_send_state();
+}
+
+static int __devinit input_fujitsu_setup(struct device *parent,
+ const char *name, const char *phys)
+{
+ struct input_dev *idev;
+ int error;
+ int i;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ idev->dev.parent = parent;
+ idev->phys = phys;
+ idev->name = name;
+ idev->id.bustype = BUS_HOST;
+ idev->id.vendor = 0x1734; /* Fujitsu Siemens Computer GmbH */
+ idev->id.product = 0x0001;
+ idev->id.version = 0x0101;
+
+ idev->keycode = fujitsu.config.keymap;
+ idev->keycodesize = sizeof(fujitsu.config.keymap[0]);
+ idev->keycodemax = ARRAY_SIZE(fujitsu.config.keymap);
+
+ __set_bit(EV_REP, idev->evbit);
+
+ for (i = 0; i < ARRAY_SIZE(fujitsu.config.keymap); i++)
+ if (fujitsu.config.keymap[i])
+ input_set_capability(idev, EV_KEY, fujitsu.config.keymap[i]);
+
+ input_set_capability(idev, EV_MSC, MSC_SCAN);
+
+ input_set_capability(idev, EV_SW, SW_DOCK);
+ input_set_capability(idev, EV_SW, SW_TABLET_MODE);
+
+ input_set_capability(idev, EV_SW, SW_DOCK);
+ input_set_capability(idev, EV_SW, SW_TABLET_MODE);
+
+ error = input_register_device(idev);
+ if (error) {
+ input_free_device(idev);
+ return error;
+ }
+
+ fujitsu.idev = idev;
+ return 0;
+}
+
+static void input_fujitsu_remove(void)
+{
+ input_unregister_device(fujitsu.idev);
+}
+
+static irqreturn_t fujitsu_interrupt(int irq, void *dev_id)
+{
+ unsigned long keymask, changed;
+ unsigned int keycode;
+ int pressed;
+ int i;
+
+ if (unlikely(!(fujitsu_status() & 0x01)))
+ return IRQ_NONE;
+
+ fujitsu_send_state();
+
+ keymask = fujitsu_read_register(0xde);
+ keymask |= fujitsu_read_register(0xdf) << 8;
+ keymask ^= 0xffff;
+
+ changed = keymask ^ fujitsu.prev_keymask;
+ if (changed) {
+ fujitsu.prev_keymask = keymask;
+
+ for_each_set_bit(i, &changed, KEYMAP_LEN) {
+ keycode = fujitsu.config.keymap[i];
+ pressed = keymask & changed & BIT(i);
+
+ if (pressed)
+ input_event(fujitsu.idev, EV_MSC, MSC_SCAN, i);
+
+ input_report_key(fujitsu.idev, keycode, pressed);
+ input_sync(fujitsu.idev);
+ }
+ }
+
+ fujitsu_ack();
+ return IRQ_HANDLED;
+}
+
+static int __devinit fujitsu_dmi_default(const struct dmi_system_id *dmi)
+{
+ printk(KERN_INFO MODULENAME ": %s\n", dmi->ident);
+ memcpy(fujitsu.config.keymap, dmi->driver_data,
+ sizeof(fujitsu.config.keymap));
+ return 1;
+}
+
+static int __devinit fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)
+{
+ fujitsu_dmi_default(dmi);
+ fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK;
+ fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT;
+ return 1;
+}
+
+static struct dmi_system_id dmi_ids[] __initconst = {
+ {
+ .callback = fujitsu_dmi_default,
+ .ident = "Fujitsu Siemens P/T Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK")
+ },
+ .driver_data = keymap_Lifebook_Tseries
+ },
+ {
+ .callback = fujitsu_dmi_default,
+ .ident = "Fujitsu Lifebook T Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T")
+ },
+ .driver_data = keymap_Lifebook_Tseries
+ },
+ {
+ .callback = fujitsu_dmi_stylistic,
+ .ident = "Fujitsu Siemens Stylistic T Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic T")
+ },
+ .driver_data = keymap_Stylistic_Tseries
+ },
+ {
+ .callback = fujitsu_dmi_default,
+ .ident = "Fujitsu LifeBook U810",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook U810")
+ },
+ .driver_data = keymap_Lifebook_U810
+ },
+ {
+ .callback = fujitsu_dmi_stylistic,
+ .ident = "Fujitsu Siemens Stylistic ST5xxx Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "STYLISTIC ST5")
+ },
+ .driver_data = keymap_Stylistic_ST5xxx
+ },
+ {
+ .callback = fujitsu_dmi_stylistic,
+ .ident = "Fujitsu Siemens Stylistic ST5xxx Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic ST5")
+ },
+ .driver_data = keymap_Stylistic_ST5xxx
+ },
+ {
+ .callback = fujitsu_dmi_default,
+ .ident = "Unknown (using defaults)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, ""),
+ DMI_MATCH(DMI_PRODUCT_NAME, "")
+ },
+ .driver_data = keymap_Lifebook_Tseries
+ },
+ { NULL }
+};
+
+static acpi_status __devinit
+fujitsu_walk_resources(struct acpi_resource *res, void *data)
+{
+ switch (res->type) {
+ case ACPI_RESOURCE_TYPE_IRQ:
+ fujitsu.irq = res->data.irq.interrupts[0];
+ return AE_OK;
+
+ case ACPI_RESOURCE_TYPE_IO:
+ fujitsu.io_base = res->data.io.minimum;
+ fujitsu.io_length = res->data.io.address_length;
+ return AE_OK;
+
+ case ACPI_RESOURCE_TYPE_END_TAG:
+ if (fujitsu.irq && fujitsu.io_base)
+ return AE_OK;
+ else
+ return AE_NOT_FOUND;
+
+ default:
+ return AE_ERROR;
+ }
+}
+
+static int __devinit acpi_fujitsu_add(struct acpi_device *adev)
+{
+ acpi_status status;
+ int error;
+
+ if (!adev)
+ return -EINVAL;
+
+ status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
+ fujitsu_walk_resources, NULL);
+ if (ACPI_FAILURE(status) || !fujitsu.irq || !fujitsu.io_base)
+ return -ENODEV;
+
+ sprintf(acpi_device_name(adev), "Fujitsu %s", acpi_device_hid(adev));
+ sprintf(acpi_device_class(adev), "%s", ACPI_FUJITSU_CLASS);
+
+ snprintf(fujitsu.phys, sizeof(fujitsu.phys),
+ "%s/input0", acpi_device_hid(adev));
+
+ error = input_fujitsu_setup(&adev->dev,
+ acpi_device_name(adev), fujitsu.phys);
+ if (error)
+ return error;
+
+ if (!request_region(fujitsu.io_base, fujitsu.io_length, MODULENAME)) {
+ input_fujitsu_remove();
+ return -EBUSY;
+ }
+
+ fujitsu_reset();
+
+ error = request_irq(fujitsu.irq, fujitsu_interrupt,
+ IRQF_SHARED, MODULENAME, fujitsu_interrupt);
+ if (error) {
+ release_region(fujitsu.io_base, fujitsu.io_length);
+ input_fujitsu_remove();
+ return error;
+ }
+
+ return 0;
+}
+
+static int __devexit acpi_fujitsu_remove(struct acpi_device *adev, int type)
+{
+ free_irq(fujitsu.irq, fujitsu_interrupt);
+ release_region(fujitsu.io_base, fujitsu.io_length);
+ input_fujitsu_remove();
+ return 0;
+}
+
+static int acpi_fujitsu_resume(struct acpi_device *adev)
+{
+ fujitsu_reset();
+ return 0;
+}
+
+static struct acpi_driver acpi_fujitsu_driver = {
+ .name = MODULENAME,
+ .class = "hotkey",
+ .ids = fujitsu_ids,
+ .ops = {
+ .add = acpi_fujitsu_add,
+ .remove = acpi_fujitsu_remove,
+ .resume = acpi_fujitsu_resume,
+ }
+};
+
+static int __init fujitsu_module_init(void)
+{
+ int error;
+
+ dmi_check_system(dmi_ids);
+
+ error = acpi_bus_register_driver(&acpi_fujitsu_driver);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static void __exit fujitsu_module_exit(void)
+{
+ acpi_bus_unregister_driver(&acpi_fujitsu_driver);
+}
+
+module_init(fujitsu_module_init);
+module_exit(fujitsu_module_exit);
+
+MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>");
+MODULE_DESCRIPTION("Fujitsu tablet pc extras driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("2.4");
+
+MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c
index ba68d4e7a779..7387f97a2941 100644
--- a/drivers/platform/x86/hdaps.c
+++ b/drivers/platform/x86/hdaps.c
@@ -375,7 +375,7 @@ static ssize_t hdaps_variance_show(struct device *dev,
static ssize_t hdaps_temp1_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- u8 temp;
+ u8 uninitialized_var(temp);
int ret;
ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp);
@@ -388,7 +388,7 @@ static ssize_t hdaps_temp1_show(struct device *dev,
static ssize_t hdaps_temp2_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- u8 temp;
+ u8 uninitialized_var(temp);
int ret;
ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp);
diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c
index 42a7d603c870..7481146a5b47 100644
--- a/drivers/platform/x86/ibm_rtl.c
+++ b/drivers/platform/x86/ibm_rtl.c
@@ -33,6 +33,8 @@
#include <linux/mutex.h>
#include <asm/bios_ebda.h>
+#include <asm-generic/io-64-nonatomic-lo-hi.h>
+
static bool force;
module_param(force, bool, 0);
MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
@@ -83,19 +85,6 @@ static void __iomem *rtl_cmd_addr;
static u8 rtl_cmd_type;
static u8 rtl_cmd_width;
-#ifndef readq
-static inline __u64 readq(const volatile void __iomem *addr)
-{
- const volatile u32 __iomem *p = addr;
- u32 low, high;
-
- low = readl(p);
- high = readl(p + 1);
-
- return low + ((u64)high << 32);
-}
-#endif
-
static void __iomem *rtl_port_map(phys_addr_t addr, unsigned long len)
{
if (rtl_cmd_type == RTL_ADDR_TYPE_MMIO)
diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c
index 809a3ae943c6..f7ba316e0ed6 100644
--- a/drivers/platform/x86/intel_ips.c
+++ b/drivers/platform/x86/intel_ips.c
@@ -77,6 +77,8 @@
#include <asm/processor.h>
#include "intel_ips.h"
+#include <asm-generic/io-64-nonatomic-lo-hi.h>
+
#define PCI_DEVICE_ID_INTEL_THERMAL_SENSOR 0x3b32
/*
@@ -344,19 +346,6 @@ struct ips_driver {
static bool
ips_gpu_turbo_enabled(struct ips_driver *ips);
-#ifndef readq
-static inline __u64 readq(const volatile void __iomem *addr)
-{
- const volatile u32 __iomem *p = addr;
- u32 low, high;
-
- low = readl(p);
- high = readl(p + 1);
-
- return low + ((u64)high << 32);
-}
-#endif
-
/**
* ips_cpu_busy - is CPU busy?
* @ips: IPS driver struct
@@ -620,25 +609,16 @@ static bool mcp_exceeded(struct ips_driver *ips)
bool ret = false;
u32 temp_limit;
u32 avg_power;
- const char *msg = "MCP limit exceeded: ";
spin_lock_irqsave(&ips->turbo_status_lock, flags);
temp_limit = ips->mcp_temp_limit * 100;
- if (ips->mcp_avg_temp > temp_limit) {
- dev_info(&ips->dev->dev,
- "%sAvg temp %u, limit %u\n", msg, ips->mcp_avg_temp,
- temp_limit);
+ if (ips->mcp_avg_temp > temp_limit)
ret = true;
- }
avg_power = ips->cpu_avg_power + ips->mch_avg_power;
- if (avg_power > ips->mcp_power_limit) {
- dev_info(&ips->dev->dev,
- "%sAvg power %u, limit %u\n", msg, avg_power,
- ips->mcp_power_limit);
+ if (avg_power > ips->mcp_power_limit)
ret = true;
- }
spin_unlock_irqrestore(&ips->turbo_status_lock, flags);
diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c
index f1ae5078b7ec..0a3594c7e912 100644
--- a/drivers/platform/x86/intel_mid_powerbtn.c
+++ b/drivers/platform/x86/intel_mid_powerbtn.c
@@ -23,21 +23,27 @@
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/input.h>
-
-#include <asm/intel_scu_ipc.h>
+#include <linux/mfd/intel_msic.h>
#define DRIVER_NAME "msic_power_btn"
-#define MSIC_PB_STATUS 0x3f
#define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */
+/*
+ * MSIC document ti_datasheet defines the 1st bit reg 0x21 is used to mask
+ * power button interrupt
+ */
+#define MSIC_PWRBTNM (1 << 0)
+
static irqreturn_t mfld_pb_isr(int irq, void *dev_id)
{
struct input_dev *input = dev_id;
int ret;
u8 pbstat;
- ret = intel_scu_ipc_ioread8(MSIC_PB_STATUS, &pbstat);
+ ret = intel_msic_reg_read(INTEL_MSIC_PBSTATUS, &pbstat);
+ dev_dbg(input->dev.parent, "PB_INT status= %d\n", pbstat);
+
if (ret < 0) {
dev_err(input->dev.parent, "Read error %d while reading"
" MSIC_PB_STATUS\n", ret);
@@ -88,6 +94,24 @@ static int __devinit mfld_pb_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, input);
+
+ /*
+ * SCU firmware might send power button interrupts to IA core before
+ * kernel boots and doesn't get EOI from IA core. The first bit of
+ * MSIC reg 0x21 is kept masked, and SCU firmware doesn't send new
+ * power interrupt to Android kernel. Unmask the bit when probing
+ * power button in kernel.
+ * There is a very narrow race between irq handler and power button
+ * initialization. The race happens rarely. So we needn't worry
+ * about it.
+ */
+ error = intel_msic_reg_update(INTEL_MSIC_IRQLVL1MSK, 0, MSIC_PWRBTNM);
+ if (error) {
+ dev_err(&pdev->dev, "Unable to clear power button interrupt, "
+ "error: %d\n", error);
+ goto err_free_irq;
+ }
+
return 0;
err_free_irq:
@@ -118,17 +142,7 @@ static struct platform_driver mfld_pb_driver = {
.remove = __devexit_p(mfld_pb_remove),
};
-static int __init mfld_pb_init(void)
-{
- return platform_driver_register(&mfld_pb_driver);
-}
-module_init(mfld_pb_init);
-
-static void __exit mfld_pb_exit(void)
-{
- platform_driver_unregister(&mfld_pb_driver);
-}
-module_exit(mfld_pb_exit);
+module_platform_driver(mfld_pb_driver);
MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>");
MODULE_DESCRIPTION("Intel Medfield Power Button Driver");
diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c
index ccd7b1f83519..5ae9cd9c7e6e 100644
--- a/drivers/platform/x86/intel_mid_thermal.c
+++ b/drivers/platform/x86/intel_mid_thermal.c
@@ -33,18 +33,15 @@
#include <linux/slab.h>
#include <linux/pm.h>
#include <linux/thermal.h>
-
-#include <asm/intel_scu_ipc.h>
+#include <linux/mfd/intel_msic.h>
/* Number of thermal sensors */
#define MSIC_THERMAL_SENSORS 4
/* ADC1 - thermal registers */
-#define MSIC_THERM_ADC1CNTL1 0x1C0
#define MSIC_ADC_ENBL 0x10
#define MSIC_ADC_START 0x08
-#define MSIC_THERM_ADC1CNTL3 0x1C2
#define MSIC_ADCTHERM_ENBL 0x04
#define MSIC_ADCRRDATA_ENBL 0x05
#define MSIC_CHANL_MASK_VAL 0x0F
@@ -75,8 +72,8 @@
#define ADC_VAL60C 315
/* ADC base addresses */
-#define ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */
-#define ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */
+#define ADC_CHNL_START_ADDR INTEL_MSIC_ADC1ADDR0 /* increments by 1 */
+#define ADC_DATA_START_ADDR INTEL_MSIC_ADC1SNS0H /* increments by 2 */
/* MSIC die attributes */
#define MSIC_DIE_ADC_MIN 488
@@ -189,17 +186,17 @@ static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp)
addr = td_info->chnl_addr;
/* Enable the msic for conversion before reading */
- ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCRRDATA_ENBL);
+ ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, MSIC_ADCRRDATA_ENBL);
if (ret)
return ret;
/* Re-toggle the RRDATARD bit (temporary workaround) */
- ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCTHERM_ENBL);
+ ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, MSIC_ADCTHERM_ENBL);
if (ret)
return ret;
/* Read the higher bits of data */
- ret = intel_scu_ipc_ioread8(addr, &data);
+ ret = intel_msic_reg_read(addr, &data);
if (ret)
return ret;
@@ -207,7 +204,7 @@ static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp)
adc_val = (data << 2);
addr++;
- ret = intel_scu_ipc_ioread8(addr, &data);/* Read lower bits */
+ ret = intel_msic_reg_read(addr, &data);/* Read lower bits */
if (ret)
return ret;
@@ -235,7 +232,7 @@ static int configure_adc(int val)
int ret;
uint8_t data;
- ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data);
+ ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL1, &data);
if (ret)
return ret;
@@ -246,7 +243,7 @@ static int configure_adc(int val)
/* Just stop the ADC */
data &= (~MSIC_ADC_START);
}
- return intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL1, data);
+ return intel_msic_reg_write(INTEL_MSIC_ADC1CNTL1, data);
}
/**
@@ -262,21 +259,21 @@ static int set_up_therm_channel(u16 base_addr)
int ret;
/* Enable all the sensor channels */
- ret = intel_scu_ipc_iowrite8(base_addr, SKIN_SENSOR0_CODE);
+ ret = intel_msic_reg_write(base_addr, SKIN_SENSOR0_CODE);
if (ret)
return ret;
- ret = intel_scu_ipc_iowrite8(base_addr + 1, SKIN_SENSOR1_CODE);
+ ret = intel_msic_reg_write(base_addr + 1, SKIN_SENSOR1_CODE);
if (ret)
return ret;
- ret = intel_scu_ipc_iowrite8(base_addr + 2, SYS_SENSOR_CODE);
+ ret = intel_msic_reg_write(base_addr + 2, SYS_SENSOR_CODE);
if (ret)
return ret;
/* Since this is the last channel, set the stop bit
* to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
- ret = intel_scu_ipc_iowrite8(base_addr + 3,
+ ret = intel_msic_reg_write(base_addr + 3,
(MSIC_DIE_SENSOR_CODE | 0x10));
if (ret)
return ret;
@@ -295,11 +292,11 @@ static int reset_stopbit(uint16_t addr)
{
int ret;
uint8_t data;
- ret = intel_scu_ipc_ioread8(addr, &data);
+ ret = intel_msic_reg_read(addr, &data);
if (ret)
return ret;
/* Set the stop bit to zero */
- return intel_scu_ipc_iowrite8(addr, (data & 0xEF));
+ return intel_msic_reg_write(addr, (data & 0xEF));
}
/**
@@ -322,7 +319,7 @@ static int find_free_channel(void)
uint8_t data;
/* check whether ADC is enabled */
- ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data);
+ ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL1, &data);
if (ret)
return ret;
@@ -331,7 +328,7 @@ static int find_free_channel(void)
/* ADC is already enabled; Looking for an empty channel */
for (i = 0; i < ADC_CHANLS_MAX; i++) {
- ret = intel_scu_ipc_ioread8(ADC_CHNL_START_ADDR + i, &data);
+ ret = intel_msic_reg_read(ADC_CHNL_START_ADDR + i, &data);
if (ret)
return ret;
@@ -359,12 +356,14 @@ static int mid_initialize_adc(struct device *dev)
* Ensure that adctherm is disabled before we
* initialize the ADC
*/
- ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL3, &data);
+ ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL3, &data);
if (ret)
return ret;
- if (data & MSIC_ADCTHERM_MASK)
- dev_warn(dev, "ADCTHERM already set");
+ data &= ~MSIC_ADCTHERM_MASK;
+ ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, data);
+ if (ret)
+ return ret;
/* Index of the first channel in which the stop bit is set */
channel_index = find_free_channel();
@@ -546,10 +545,11 @@ static int mid_thermal_remove(struct platform_device *pdev)
return configure_adc(0);
}
-#define DRIVER_NAME "msic_sensor"
+#define DRIVER_NAME "msic_thermal"
static const struct platform_device_id therm_id_table[] = {
{ DRIVER_NAME, 1 },
+ { "msic_thermal", 1 },
{ }
};
@@ -565,18 +565,7 @@ static struct platform_driver mid_thermal_driver = {
.id_table = therm_id_table,
};
-static int __init mid_thermal_module_init(void)
-{
- return platform_driver_register(&mid_thermal_driver);
-}
-
-static void __exit mid_thermal_module_exit(void)
-{
- platform_driver_unregister(&mid_thermal_driver);
-}
-
-module_init(mid_thermal_module_init);
-module_exit(mid_thermal_module_exit);
+module_platform_driver(mid_thermal_driver);
MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>");
MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver");
diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c
index 6ee0b5c90933..79a0c2f6be53 100644
--- a/drivers/platform/x86/intel_oaktrail.c
+++ b/drivers/platform/x86/intel_oaktrail.c
@@ -313,6 +313,7 @@ static struct dmi_system_id __initdata oaktrail_dmi_table[] = {
},
{ }
};
+MODULE_DEVICE_TABLE(dmi, oaktrail_dmi_table);
static int __init oaktrail_init(void)
{
@@ -394,4 +395,3 @@ MODULE_AUTHOR("Yin Kangkai (kangkai.yin@intel.com)");
MODULE_DESCRIPTION("Intel Oaktrail Platform ACPI Extras");
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
-MODULE_ALIAS("dmi:*:svnIntelCorporation:pnOakTrailplatform:*");
diff --git a/drivers/platform/x86/intel_rar_register.c b/drivers/platform/x86/intel_rar_register.c
deleted file mode 100644
index c8a6aed45277..000000000000
--- a/drivers/platform/x86/intel_rar_register.c
+++ /dev/null
@@ -1,669 +0,0 @@
-/*
- * rar_register.c - An Intel Restricted Access Region register driver
- *
- * Copyright(c) 2009 Intel Corporation. All rights reserved.
- *
- * 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.
- *
- * -------------------------------------------------------------------
- * 20091204 Mark Allyn <mark.a.allyn@intel.com>
- * Ossama Othman <ossama.othman@intel.com>
- * Cleanup per feedback from Alan Cox and Arjan Van De Ven
- *
- * 20090806 Ossama Othman <ossama.othman@intel.com>
- * Return zero high address if upper 22 bits is zero.
- * Cleaned up checkpatch errors.
- * Clarified that driver is dealing with bus addresses.
- *
- * 20090702 Ossama Othman <ossama.othman@intel.com>
- * Removed unnecessary include directives
- * Cleaned up spinlocks.
- * Cleaned up logging.
- * Improved invalid parameter checks.
- * Fixed and simplified RAR address retrieval and RAR locking
- * code.
- *
- * 20090626 Mark Allyn <mark.a.allyn@intel.com>
- * Initial publish
- */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/spinlock.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/rar_register.h>
-
-/* === Lincroft Message Bus Interface === */
-#define LNC_MCR_OFFSET 0xD0 /* Message Control Register */
-#define LNC_MDR_OFFSET 0xD4 /* Message Data Register */
-
-/* Message Opcodes */
-#define LNC_MESSAGE_READ_OPCODE 0xD0
-#define LNC_MESSAGE_WRITE_OPCODE 0xE0
-
-/* Message Write Byte Enables */
-#define LNC_MESSAGE_BYTE_WRITE_ENABLES 0xF
-
-/* B-unit Port */
-#define LNC_BUNIT_PORT 0x3
-
-/* === Lincroft B-Unit Registers - Programmed by IA32 firmware === */
-#define LNC_BRAR0L 0x10
-#define LNC_BRAR0H 0x11
-#define LNC_BRAR1L 0x12
-#define LNC_BRAR1H 0x13
-/* Reserved for SeP */
-#define LNC_BRAR2L 0x14
-#define LNC_BRAR2H 0x15
-
-/* Moorestown supports three restricted access regions. */
-#define MRST_NUM_RAR 3
-
-/* RAR Bus Address Range */
-struct rar_addr {
- dma_addr_t low;
- dma_addr_t high;
-};
-
-/*
- * We create one of these for each RAR
- */
-struct client {
- int (*callback)(unsigned long data);
- unsigned long driver_priv;
- bool busy;
-};
-
-static DEFINE_MUTEX(rar_mutex);
-static DEFINE_MUTEX(lnc_reg_mutex);
-
-/*
- * One per RAR device (currently only one device)
- */
-struct rar_device {
- struct rar_addr rar_addr[MRST_NUM_RAR];
- struct pci_dev *rar_dev;
- bool registered;
- bool allocated;
- struct client client[MRST_NUM_RAR];
-};
-
-/* Current platforms have only one rar_device for 3 rar regions */
-static struct rar_device my_rar_device;
-
-/*
- * Abstract out multiple device support. Current platforms only
- * have a single RAR device.
- */
-
-/**
- * alloc_rar_device - return a new RAR structure
- *
- * Return a new (but not yet ready) RAR device object
- */
-static struct rar_device *alloc_rar_device(void)
-{
- if (my_rar_device.allocated)
- return NULL;
- my_rar_device.allocated = 1;
- return &my_rar_device;
-}
-
-/**
- * free_rar_device - free a RAR object
- * @rar: the RAR device being freed
- *
- * Release a RAR object and any attached resources
- */
-static void free_rar_device(struct rar_device *rar)
-{
- pci_dev_put(rar->rar_dev);
- rar->allocated = 0;
-}
-
-/**
- * _rar_to_device - return the device handling this RAR
- * @rar: RAR number
- * @off: returned offset
- *
- * Internal helper for looking up RAR devices. This and alloc are the
- * two functions that need touching to go to multiple RAR devices.
- */
-static struct rar_device *_rar_to_device(int rar, int *off)
-{
- if (rar >= 0 && rar < MRST_NUM_RAR) {
- *off = rar;
- return &my_rar_device;
- }
- return NULL;
-}
-
-/**
- * rar_to_device - return the device handling this RAR
- * @rar: RAR number
- * @off: returned offset
- *
- * Return the device this RAR maps to if one is present, otherwise
- * returns NULL. Reports the offset relative to the base of this
- * RAR device in off.
- */
-static struct rar_device *rar_to_device(int rar, int *off)
-{
- struct rar_device *rar_dev = _rar_to_device(rar, off);
- if (rar_dev == NULL || !rar_dev->registered)
- return NULL;
- return rar_dev;
-}
-
-/**
- * rar_to_client - return the client handling this RAR
- * @rar: RAR number
- *
- * Return the client this RAR maps to if a mapping is known, otherwise
- * returns NULL.
- */
-static struct client *rar_to_client(int rar)
-{
- int idx;
- struct rar_device *r = _rar_to_device(rar, &idx);
- if (r != NULL)
- return &r->client[idx];
- return NULL;
-}
-
-/**
- * rar_read_addr - retrieve a RAR mapping
- * @pdev: PCI device for the RAR
- * @offset: offset for message
- * @addr: returned address
- *
- * Reads the address of a given RAR register. Returns 0 on success
- * or an error code on failure.
- */
-static int rar_read_addr(struct pci_dev *pdev, int offset, dma_addr_t *addr)
-{
- /*
- * ======== The Lincroft Message Bus Interface ========
- * Lincroft registers may be obtained via PCI from
- * the host bridge using the Lincroft Message Bus
- * Interface. That message bus interface is generally
- * comprised of two registers: a control register (MCR, 0xDO)
- * and a data register (MDR, 0xD4).
- *
- * The MCR (message control register) format is the following:
- * 1. [31:24]: Opcode
- * 2. [23:16]: Port
- * 3. [15:8]: Register Offset
- * 4. [7:4]: Byte Enables (use 0xF to set all of these bits
- * to 1)
- * 5. [3:0]: reserved
- *
- * Read (0xD0) and write (0xE0) opcodes are written to the
- * control register when reading and writing to Lincroft
- * registers, respectively.
- *
- * We're interested in registers found in the Lincroft
- * B-unit. The B-unit port is 0x3.
- *
- * The six B-unit RAR register offsets we use are listed
- * earlier in this file.
- *
- * Lastly writing to the MCR register requires the "Byte
- * enables" bits to be set to 1. This may be achieved by
- * writing 0xF at bit 4.
- *
- * The MDR (message data register) format is the following:
- * 1. [31:0]: Read/Write Data
- *
- * Data being read from this register is only available after
- * writing the appropriate control message to the MCR
- * register.
- *
- * Data being written to this register must be written before
- * writing the appropriate control message to the MCR
- * register.
- */
-
- int result;
- u32 addr32;
-
- /* Construct control message */
- u32 const message =
- (LNC_MESSAGE_READ_OPCODE << 24)
- | (LNC_BUNIT_PORT << 16)
- | (offset << 8)
- | (LNC_MESSAGE_BYTE_WRITE_ENABLES << 4);
-
- dev_dbg(&pdev->dev, "Offset for 'get' LNC MSG is %x\n", offset);
-
- /*
- * We synchronize access to the Lincroft MCR and MDR registers
- * until BOTH the command is issued through the MCR register
- * and the corresponding data is read from the MDR register.
- * Otherwise a race condition would exist between accesses to
- * both registers.
- */
-
- mutex_lock(&lnc_reg_mutex);
-
- /* Send the control message */
- result = pci_write_config_dword(pdev, LNC_MCR_OFFSET, message);
- if (!result) {
- /* Read back the address as a 32bit value */
- result = pci_read_config_dword(pdev, LNC_MDR_OFFSET, &addr32);
- *addr = (dma_addr_t)addr32;
- }
- mutex_unlock(&lnc_reg_mutex);
- return result;
-}
-
-/**
- * rar_set_addr - Set a RAR mapping
- * @pdev: PCI device for the RAR
- * @offset: offset for message
- * @addr: address to set
- *
- * Sets the address of a given RAR register. Returns 0 on success
- * or an error code on failure.
- */
-static int rar_set_addr(struct pci_dev *pdev,
- int offset,
- dma_addr_t addr)
-{
- /*
- * Data being written to this register must be written before
- * writing the appropriate control message to the MCR
- * register.
- * See rar_get_addrs() for a description of the
- * message bus interface being used here.
- */
-
- int result;
-
- /* Construct control message */
- u32 const message = (LNC_MESSAGE_WRITE_OPCODE << 24)
- | (LNC_BUNIT_PORT << 16)
- | (offset << 8)
- | (LNC_MESSAGE_BYTE_WRITE_ENABLES << 4);
-
- /*
- * We synchronize access to the Lincroft MCR and MDR registers
- * until BOTH the command is issued through the MCR register
- * and the corresponding data is read from the MDR register.
- * Otherwise a race condition would exist between accesses to
- * both registers.
- */
-
- mutex_lock(&lnc_reg_mutex);
-
- /* Send the control message */
- result = pci_write_config_dword(pdev, LNC_MDR_OFFSET, addr);
- if (!result)
- /* And address */
- result = pci_write_config_dword(pdev, LNC_MCR_OFFSET, message);
-
- mutex_unlock(&lnc_reg_mutex);
- return result;
-}
-
-/*
- * rar_init_params - Initialize RAR parameters
- * @rar: RAR device to initialise
- *
- * Initialize RAR parameters, such as bus addresses, etc. Returns 0
- * on success, or an error code on failure.
- */
-static int init_rar_params(struct rar_device *rar)
-{
- struct pci_dev *pdev = rar->rar_dev;
- unsigned int i;
- int result = 0;
- int offset = 0x10; /* RAR 0 to 2 in order low/high/low/high/... */
-
- /* Retrieve RAR start and end bus addresses.
- * Access the RAR registers through the Lincroft Message Bus
- * Interface on PCI device: 00:00.0 Host bridge.
- */
-
- for (i = 0; i < MRST_NUM_RAR; ++i) {
- struct rar_addr *addr = &rar->rar_addr[i];
-
- result = rar_read_addr(pdev, offset++, &addr->low);
- if (result != 0)
- return result;
-
- result = rar_read_addr(pdev, offset++, &addr->high);
- if (result != 0)
- return result;
-
-
- /*
- * Only the upper 22 bits of the RAR addresses are
- * stored in their corresponding RAR registers so we
- * must set the lower 10 bits accordingly.
-
- * The low address has its lower 10 bits cleared, and
- * the high address has all its lower 10 bits set,
- * e.g.:
- * low = 0x2ffffc00
- */
-
- addr->low &= (dma_addr_t)0xfffffc00u;
-
- /*
- * Set bits 9:0 on uppser address if bits 31:10 are non
- * zero; otherwize clear all bits
- */
-
- if ((addr->high & 0xfffffc00u) == 0)
- addr->high = 0;
- else
- addr->high |= 0x3ffu;
- }
- /* Done accessing the device. */
-
- if (result == 0) {
- for (i = 0; i != MRST_NUM_RAR; ++i) {
- /*
- * "BRAR" refers to the RAR registers in the
- * Lincroft B-unit.
- */
- dev_info(&pdev->dev, "BRAR[%u] bus address range = "
- "[%lx, %lx]\n", i,
- (unsigned long)rar->rar_addr[i].low,
- (unsigned long)rar->rar_addr[i].high);
- }
- }
- return result;
-}
-
-/**
- * rar_get_address - get the bus address in a RAR
- * @start: return value of start address of block
- * @end: return value of end address of block
- *
- * The rar_get_address function is used by other device drivers
- * to obtain RAR address information on a RAR. It takes three
- * parameters:
- *
- * The function returns a 0 upon success or an error if there is no RAR
- * facility on this system.
- */
-int rar_get_address(int rar_index, dma_addr_t *start, dma_addr_t *end)
-{
- int idx;
- struct rar_device *rar = rar_to_device(rar_index, &idx);
-
- if (rar == NULL) {
- WARN_ON(1);
- return -ENODEV;
- }
-
- *start = rar->rar_addr[idx].low;
- *end = rar->rar_addr[idx].high;
- return 0;
-}
-EXPORT_SYMBOL(rar_get_address);
-
-/**
- * rar_lock - lock a RAR register
- * @rar_index: RAR to lock (0-2)
- *
- * The rar_lock function is ued by other device drivers to lock an RAR.
- * once a RAR is locked, it stays locked until the next system reboot.
- *
- * The function returns a 0 upon success or an error if there is no RAR
- * facility on this system, or the locking fails
- */
-int rar_lock(int rar_index)
-{
- struct rar_device *rar;
- int result;
- int idx;
- dma_addr_t low, high;
-
- rar = rar_to_device(rar_index, &idx);
-
- if (rar == NULL) {
- WARN_ON(1);
- return -EINVAL;
- }
-
- low = rar->rar_addr[idx].low & 0xfffffc00u;
- high = rar->rar_addr[idx].high & 0xfffffc00u;
-
- /*
- * Only allow I/O from the graphics and Langwell;
- * not from the x86 processor
- */
-
- if (rar_index == RAR_TYPE_VIDEO) {
- low |= 0x00000009;
- high |= 0x00000015;
- } else if (rar_index == RAR_TYPE_AUDIO) {
- /* Only allow I/O from Langwell; nothing from x86 */
- low |= 0x00000008;
- high |= 0x00000018;
- } else
- /* Read-only from all agents */
- high |= 0x00000018;
-
- /*
- * Now program the register using the Lincroft message
- * bus interface.
- */
- result = rar_set_addr(rar->rar_dev,
- 2 * idx, low);
-
- if (result == 0)
- result = rar_set_addr(rar->rar_dev,
- 2 * idx + 1, high);
-
- return result;
-}
-EXPORT_SYMBOL(rar_lock);
-
-/**
- * register_rar - register a RAR handler
- * @num: RAR we wish to register for
- * @callback: function to call when RAR support is available
- * @data: data to pass to this function
- *
- * The register_rar function is to used by other device drivers
- * to ensure that this driver is ready. As we cannot be sure of
- * the compile/execute order of drivers in the kernel, it is
- * best to give this driver a callback function to call when
- * it is ready to give out addresses. The callback function
- * would have those steps that continue the initialization of
- * a driver that do require a valid RAR address. One of those
- * steps would be to call rar_get_address()
- *
- * This function return 0 on success or an error code on failure.
- */
-int register_rar(int num, int (*callback)(unsigned long data),
- unsigned long data)
-{
- /* For now we hardcode a single RAR device */
- struct rar_device *rar;
- struct client *c;
- int idx;
- int retval = 0;
-
- mutex_lock(&rar_mutex);
-
- /* Do we have a client mapping for this RAR number ? */
- c = rar_to_client(num);
- if (c == NULL) {
- retval = -ERANGE;
- goto done;
- }
- /* Is it claimed ? */
- if (c->busy) {
- retval = -EBUSY;
- goto done;
- }
- c->busy = 1;
-
- /* See if we have a handler for this RAR yet, if we do then fire it */
- rar = rar_to_device(num, &idx);
-
- if (rar) {
- /*
- * if the driver already registered, then we can simply
- * call the callback right now
- */
- (*callback)(data);
- goto done;
- }
-
- /* Arrange to be called back when the hardware is found */
- c->callback = callback;
- c->driver_priv = data;
-done:
- mutex_unlock(&rar_mutex);
- return retval;
-}
-EXPORT_SYMBOL(register_rar);
-
-/**
- * unregister_rar - release a RAR allocation
- * @num: RAR number
- *
- * Releases a RAR allocation, or pending allocation. If a callback is
- * pending then this function will either complete before the unregister
- * returns or not at all.
- */
-
-void unregister_rar(int num)
-{
- struct client *c;
-
- mutex_lock(&rar_mutex);
- c = rar_to_client(num);
- if (c == NULL || !c->busy)
- WARN_ON(1);
- else
- c->busy = 0;
- mutex_unlock(&rar_mutex);
-}
-EXPORT_SYMBOL(unregister_rar);
-
-/**
- * rar_callback - Process callbacks
- * @rar: new RAR device
- *
- * Process the callbacks for a newly found RAR device.
- */
-
-static void rar_callback(struct rar_device *rar)
-{
- struct client *c = &rar->client[0];
- int i;
-
- mutex_lock(&rar_mutex);
-
- rar->registered = 1; /* Ensure no more callbacks queue */
-
- for (i = 0; i < MRST_NUM_RAR; i++) {
- if (c->callback && c->busy) {
- c->callback(c->driver_priv);
- c->callback = NULL;
- }
- c++;
- }
- mutex_unlock(&rar_mutex);
-}
-
-/**
- * rar_probe - PCI probe callback
- * @dev: PCI device
- * @id: matching entry in the match table
- *
- * A RAR device has been discovered. Initialise it and if successful
- * process any pending callbacks that can now be completed.
- */
-static int rar_probe(struct pci_dev *dev, const struct pci_device_id *id)
-{
- int error;
- struct rar_device *rar;
-
- dev_dbg(&dev->dev, "PCI probe starting\n");
-
- rar = alloc_rar_device();
- if (rar == NULL)
- return -EBUSY;
-
- /* Enable the device */
- error = pci_enable_device(dev);
- if (error) {
- dev_err(&dev->dev,
- "Error enabling RAR register PCI device\n");
- goto end_function;
- }
-
- /* Fill in the rar_device structure */
- rar->rar_dev = pci_dev_get(dev);
- pci_set_drvdata(dev, rar);
-
- /*
- * Initialize the RAR parameters, which have to be retrieved
- * via the message bus interface.
- */
- error = init_rar_params(rar);
- if (error) {
- pci_disable_device(dev);
- dev_err(&dev->dev, "Error retrieving RAR addresses\n");
- goto end_function;
- }
- /* now call anyone who has registered (using callbacks) */
- rar_callback(rar);
- return 0;
-end_function:
- free_rar_device(rar);
- return error;
-}
-
-static DEFINE_PCI_DEVICE_TABLE(rar_pci_id_tbl) = {
- { PCI_VDEVICE(INTEL, 0x4110) },
- { 0 }
-};
-
-MODULE_DEVICE_TABLE(pci, rar_pci_id_tbl);
-
-/* field for registering driver to PCI device */
-static struct pci_driver rar_pci_driver = {
- .name = "rar_register_driver",
- .id_table = rar_pci_id_tbl,
- .probe = rar_probe,
- /* Cannot be unplugged - no remove */
-};
-
-static int __init rar_init_handler(void)
-{
- return pci_register_driver(&rar_pci_driver);
-}
-
-static void __exit rar_exit_handler(void)
-{
- pci_unregister_driver(&rar_pci_driver);
-}
-
-module_init(rar_init_handler);
-module_exit(rar_exit_handler);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Intel Restricted Access Region Register Driver");
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index f00d0d1e0653..9215ed72bece 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -159,7 +159,7 @@ static inline int busy_loop(void) /* Wait till scu status is busy */
/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
{
- int i, nc, bytes, d;
+ int nc;
u32 offset = 0;
int err;
u8 cbuf[IPC_WWBUF_SIZE] = { };
@@ -174,55 +174,34 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
return -ENODEV;
}
- if (platform != MRST_CPU_CHIP_PENWELL) {
- bytes = 0;
- d = 0;
- for (i = 0; i < count; i++) {
- cbuf[bytes++] = addr[i];
- cbuf[bytes++] = addr[i] >> 8;
- if (id != IPC_CMD_PCNTRL_R)
- cbuf[bytes++] = data[d++];
- if (id == IPC_CMD_PCNTRL_M)
- cbuf[bytes++] = data[d++];
- }
- for (i = 0; i < bytes; i += 4)
- ipc_data_writel(wbuf[i/4], i);
- ipc_command(bytes << 16 | id << 12 | 0 << 8 | op);
- } else {
- for (nc = 0; nc < count; nc++, offset += 2) {
- cbuf[offset] = addr[nc];
- cbuf[offset + 1] = addr[nc] >> 8;
- }
+ for (nc = 0; nc < count; nc++, offset += 2) {
+ cbuf[offset] = addr[nc];
+ cbuf[offset + 1] = addr[nc] >> 8;
+ }
- if (id == IPC_CMD_PCNTRL_R) {
- for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
- ipc_data_writel(wbuf[nc], offset);
- ipc_command((count*2) << 16 | id << 12 | 0 << 8 | op);
- } else if (id == IPC_CMD_PCNTRL_W) {
- for (nc = 0; nc < count; nc++, offset += 1)
- cbuf[offset] = data[nc];
- for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
- ipc_data_writel(wbuf[nc], offset);
- ipc_command((count*3) << 16 | id << 12 | 0 << 8 | op);
- } else if (id == IPC_CMD_PCNTRL_M) {
- cbuf[offset] = data[0];
- cbuf[offset + 1] = data[1];
- ipc_data_writel(wbuf[0], 0); /* Write wbuff */
- ipc_command(4 << 16 | id << 12 | 0 << 8 | op);
- }
+ if (id == IPC_CMD_PCNTRL_R) {
+ for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
+ ipc_data_writel(wbuf[nc], offset);
+ ipc_command((count*2) << 16 | id << 12 | 0 << 8 | op);
+ } else if (id == IPC_CMD_PCNTRL_W) {
+ for (nc = 0; nc < count; nc++, offset += 1)
+ cbuf[offset] = data[nc];
+ for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
+ ipc_data_writel(wbuf[nc], offset);
+ ipc_command((count*3) << 16 | id << 12 | 0 << 8 | op);
+ } else if (id == IPC_CMD_PCNTRL_M) {
+ cbuf[offset] = data[0];
+ cbuf[offset + 1] = data[1];
+ ipc_data_writel(wbuf[0], 0); /* Write wbuff */
+ ipc_command(4 << 16 | id << 12 | 0 << 8 | op);
}
err = busy_loop();
if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */
/* Workaround: values are read as 0 without memcpy_fromio */
memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16);
- if (platform != MRST_CPU_CHIP_PENWELL) {
- for (nc = 0, offset = 2; nc < count; nc++, offset += 3)
- data[nc] = ipc_data_readb(offset);
- } else {
- for (nc = 0; nc < count; nc++)
- data[nc] = ipc_data_readb(nc);
- }
+ for (nc = 0; nc < count; nc++)
+ data[nc] = ipc_data_readb(nc);
}
mutex_unlock(&ipclock);
return err;
@@ -503,148 +482,6 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data)
}
EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl);
-#define IPC_FW_LOAD_ADDR 0xFFFC0000 /* Storage location for FW image */
-#define IPC_FW_UPDATE_MBOX_ADDR 0xFFFFDFF4 /* Mailbox between ipc and scu */
-#define IPC_MAX_FW_SIZE 262144 /* 256K storage size for loading the FW image */
-#define IPC_FW_MIP_HEADER_SIZE 2048 /* Firmware MIP header size */
-/* IPC inform SCU to get ready for update process */
-#define IPC_CMD_FW_UPDATE_READY 0x10FE
-/* IPC inform SCU to go for update process */
-#define IPC_CMD_FW_UPDATE_GO 0x20FE
-/* Status code for fw update */
-#define IPC_FW_UPDATE_SUCCESS 0x444f4e45 /* Status code 'DONE' */
-#define IPC_FW_UPDATE_BADN 0x4241444E /* Status code 'BADN' */
-#define IPC_FW_TXHIGH 0x54784849 /* Status code 'IPC_FW_TXHIGH' */
-#define IPC_FW_TXLOW 0x54784c4f /* Status code 'IPC_FW_TXLOW' */
-
-struct fw_update_mailbox {
- u32 status;
- u32 scu_flag;
- u32 driver_flag;
-};
-
-
-/**
- * intel_scu_ipc_fw_update - Firmware update utility
- * @buffer: firmware buffer
- * @length: size of firmware buffer
- *
- * This function provides an interface to load the firmware into
- * the SCU. Returns 0 on success or -1 on failure
- */
-int intel_scu_ipc_fw_update(u8 *buffer, u32 length)
-{
- void __iomem *fw_update_base;
- struct fw_update_mailbox __iomem *mailbox = NULL;
- int retry_cnt = 0;
- u32 status;
-
- mutex_lock(&ipclock);
- fw_update_base = ioremap_nocache(IPC_FW_LOAD_ADDR, (128*1024));
- if (fw_update_base == NULL) {
- mutex_unlock(&ipclock);
- return -ENOMEM;
- }
- mailbox = ioremap_nocache(IPC_FW_UPDATE_MBOX_ADDR,
- sizeof(struct fw_update_mailbox));
- if (mailbox == NULL) {
- iounmap(fw_update_base);
- mutex_unlock(&ipclock);
- return -ENOMEM;
- }
-
- ipc_command(IPC_CMD_FW_UPDATE_READY);
-
- /* Intitialize mailbox */
- writel(0, &mailbox->status);
- writel(0, &mailbox->scu_flag);
- writel(0, &mailbox->driver_flag);
-
- /* Driver copies the 2KB MIP header to SRAM at 0xFFFC0000*/
- memcpy_toio(fw_update_base, buffer, 0x800);
-
- /* Driver sends "FW Update" IPC command (CMD_ID 0xFE; MSG_ID 0x02).
- * Upon receiving this command, SCU will write the 2K MIP header
- * from 0xFFFC0000 into NAND.
- * SCU will write a status code into the Mailbox, and then set scu_flag.
- */
-
- ipc_command(IPC_CMD_FW_UPDATE_GO);
-
- /*Driver stalls until scu_flag is set */
- while (readl(&mailbox->scu_flag) != 1) {
- rmb();
- mdelay(1);
- }
-
- /* Driver checks Mailbox status.
- * If the status is 'BADN', then abort (bad NAND).
- * If the status is 'IPC_FW_TXLOW', then continue.
- */
- while (readl(&mailbox->status) != IPC_FW_TXLOW) {
- rmb();
- mdelay(10);
- }
- mdelay(10);
-
-update_retry:
- if (retry_cnt > 5)
- goto update_end;
-
- if (readl(&mailbox->status) != IPC_FW_TXLOW)
- goto update_end;
- buffer = buffer + 0x800;
- memcpy_toio(fw_update_base, buffer, 0x20000);
- writel(1, &mailbox->driver_flag);
- while (readl(&mailbox->scu_flag) == 1) {
- rmb();
- mdelay(1);
- }
-
- /* check for 'BADN' */
- if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN)
- goto update_end;
-
- while (readl(&mailbox->status) != IPC_FW_TXHIGH) {
- rmb();
- mdelay(10);
- }
- mdelay(10);
-
- if (readl(&mailbox->status) != IPC_FW_TXHIGH)
- goto update_end;
-
- buffer = buffer + 0x20000;
- memcpy_toio(fw_update_base, buffer, 0x20000);
- writel(0, &mailbox->driver_flag);
-
- while (mailbox->scu_flag == 0) {
- rmb();
- mdelay(1);
- }
-
- /* check for 'BADN' */
- if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN)
- goto update_end;
-
- if (readl(&mailbox->status) == IPC_FW_TXLOW) {
- ++retry_cnt;
- goto update_retry;
- }
-
-update_end:
- status = readl(&mailbox->status);
-
- iounmap(fw_update_base);
- iounmap(mailbox);
- mutex_unlock(&ipclock);
-
- if (status == IPC_FW_UPDATE_SUCCESS)
- return 0;
- return -EIO;
-}
-EXPORT_SYMBOL(intel_scu_ipc_fw_update);
-
/*
* Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
* When ioc bit is set to 1, caller api must wait for interrupt handler called
@@ -727,7 +564,6 @@ static void ipc_remove(struct pci_dev *pdev)
}
static DEFINE_PCI_DEVICE_TABLE(pci_ids) = {
- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080e)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)},
{ 0,}
};
diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c
index 2d0f9136ea9a..02bc5a6343c3 100644
--- a/drivers/platform/x86/intel_scu_ipcutil.c
+++ b/drivers/platform/x86/intel_scu_ipcutil.c
@@ -26,13 +26,10 @@
static int major;
-#define MAX_FW_SIZE 264192
-
/* ioctl commnds */
#define INTE_SCU_IPC_REGISTER_READ 0
#define INTE_SCU_IPC_REGISTER_WRITE 1
#define INTE_SCU_IPC_REGISTER_UPDATE 2
-#define INTE_SCU_IPC_FW_UPDATE 0xA2
struct scu_ipc_data {
u32 count; /* No. of registers */
@@ -88,27 +85,14 @@ static long scu_ipc_ioctl(struct file *fp, unsigned int cmd,
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
- if (cmd == INTE_SCU_IPC_FW_UPDATE) {
- u8 *fwbuf = kmalloc(MAX_FW_SIZE, GFP_KERNEL);
- if (fwbuf == NULL)
- return -ENOMEM;
- if (copy_from_user(fwbuf, (u8 *)arg, MAX_FW_SIZE)) {
- kfree(fwbuf);
- return -EFAULT;
- }
- ret = intel_scu_ipc_fw_update(fwbuf, MAX_FW_SIZE);
- kfree(fwbuf);
- return ret;
- } else {
- if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data)))
- return -EFAULT;
- ret = scu_reg_access(cmd, &data);
- if (ret < 0)
- return ret;
- if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data)))
- return -EFAULT;
- return 0;
- }
+ if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data)))
+ return -EFAULT;
+ ret = scu_reg_access(cmd, &data);
+ if (ret < 0)
+ return ret;
+ if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data)))
+ return -EFAULT;
+ return 0;
}
static const struct file_operations scu_ipc_fops = {
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index 05be30ee158b..ffff8b4b4949 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -562,8 +562,8 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
num_sifr = acpi_pcc_get_sqty(device);
- if (num_sifr > 255) {
- ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large"));
+ if (num_sifr < 0 || num_sifr > 255) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr out of range"));
return -ENODEV;
}
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c
index fd73ea89b857..e2a34b42ddc1 100644
--- a/drivers/platform/x86/samsung-laptop.c
+++ b/drivers/platform/x86/samsung-laptop.c
@@ -17,10 +17,18 @@
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/backlight.h>
+#include <linux/leds.h>
#include <linux/fb.h>
#include <linux/dmi.h>
#include <linux/platform_device.h>
#include <linux/rfkill.h>
+#include <linux/acpi.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/ctype.h>
+#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
+#include <acpi/video.h>
+#endif
/*
* This driver is needed because a number of Samsung laptops do not hook
@@ -41,9 +49,20 @@
#define SABI_IFACE_COMPLETE 0x04
#define SABI_IFACE_DATA 0x05
-/* Structure to get data back to the calling function */
-struct sabi_retval {
- u8 retval[20];
+#define WL_STATUS_WLAN 0x0
+#define WL_STATUS_BT 0x2
+
+/* Structure get/set data using sabi */
+struct sabi_data {
+ union {
+ struct {
+ u32 d0;
+ u32 d1;
+ u16 d2;
+ u8 d3;
+ };
+ u8 data[11];
+ };
};
struct sabi_header_offsets {
@@ -60,8 +79,8 @@ struct sabi_commands {
* Brightness is 0 - 8, as described above.
* Value 0 is for the BIOS to use
*/
- u8 get_brightness;
- u8 set_brightness;
+ u16 get_brightness;
+ u16 set_brightness;
/*
* first byte:
@@ -72,40 +91,56 @@ struct sabi_commands {
* 0x03 - 3G is on
* TODO, verify 3G is correct, that doesn't seem right...
*/
- u8 get_wireless_button;
- u8 set_wireless_button;
+ u16 get_wireless_button;
+ u16 set_wireless_button;
/* 0 is off, 1 is on */
- u8 get_backlight;
- u8 set_backlight;
+ u16 get_backlight;
+ u16 set_backlight;
/*
* 0x80 or 0x00 - no action
* 0x81 - recovery key pressed
*/
- u8 get_recovery_mode;
- u8 set_recovery_mode;
+ u16 get_recovery_mode;
+ u16 set_recovery_mode;
/*
* on seclinux: 0 is low, 1 is high,
* on swsmi: 0 is normal, 1 is silent, 2 is turbo
*/
- u8 get_performance_level;
- u8 set_performance_level;
+ u16 get_performance_level;
+ u16 set_performance_level;
+
+ /* 0x80 is off, 0x81 is on */
+ u16 get_battery_life_extender;
+ u16 set_battery_life_extender;
+
+ /* 0x80 is off, 0x81 is on */
+ u16 get_usb_charge;
+ u16 set_usb_charge;
+
+ /* the first byte is for bluetooth and the third one is for wlan */
+ u16 get_wireless_status;
+ u16 set_wireless_status;
+
+ /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */
+ u16 kbd_backlight;
/*
* Tell the BIOS that Linux is running on this machine.
* 81 is on, 80 is off
*/
- u8 set_linux;
+ u16 set_linux;
};
struct sabi_performance_level {
const char *name;
- u8 value;
+ u16 value;
};
struct sabi_config {
+ int sabi_version;
const char *test_string;
u16 main_function;
const struct sabi_header_offsets header_offsets;
@@ -117,6 +152,10 @@ struct sabi_config {
static const struct sabi_config sabi_configs[] = {
{
+ /* I don't know if it is really 2, but it it is
+ * less than 3 anyway */
+ .sabi_version = 2,
+
.test_string = "SECLINUX",
.main_function = 0x4c49,
@@ -146,6 +185,17 @@ static const struct sabi_config sabi_configs[] = {
.get_performance_level = 0x08,
.set_performance_level = 0x09,
+ .get_battery_life_extender = 0xFFFF,
+ .set_battery_life_extender = 0xFFFF,
+
+ .get_usb_charge = 0xFFFF,
+ .set_usb_charge = 0xFFFF,
+
+ .get_wireless_status = 0xFFFF,
+ .set_wireless_status = 0xFFFF,
+
+ .kbd_backlight = 0xFFFF,
+
.set_linux = 0x0a,
},
@@ -164,6 +214,8 @@ static const struct sabi_config sabi_configs[] = {
.max_brightness = 8,
},
{
+ .sabi_version = 3,
+
.test_string = "SwSmi@",
.main_function = 0x5843,
@@ -193,6 +245,17 @@ static const struct sabi_config sabi_configs[] = {
.get_performance_level = 0x31,
.set_performance_level = 0x32,
+ .get_battery_life_extender = 0x65,
+ .set_battery_life_extender = 0x66,
+
+ .get_usb_charge = 0x67,
+ .set_usb_charge = 0x68,
+
+ .get_wireless_status = 0x69,
+ .set_wireless_status = 0x6a,
+
+ .kbd_backlight = 0x78,
+
.set_linux = 0xff,
},
@@ -217,16 +280,82 @@ static const struct sabi_config sabi_configs[] = {
{ },
};
-static const struct sabi_config *sabi_config;
+/*
+ * samsung-laptop/ - debugfs root directory
+ * f0000_segment - dump f0000 segment
+ * command - current command
+ * data - current data
+ * d0, d1, d2, d3 - data fields
+ * call - call SABI using command and data
+ *
+ * This allow to call arbitrary sabi commands wihout
+ * modifying the driver at all.
+ * For example, setting the keyboard backlight brightness to 5
+ *
+ * echo 0x78 > command
+ * echo 0x0582 > d0
+ * echo 0 > d1
+ * echo 0 > d2
+ * echo 0 > d3
+ * cat call
+ */
+
+struct samsung_laptop_debug {
+ struct dentry *root;
+ struct sabi_data data;
+ u16 command;
+
+ struct debugfs_blob_wrapper f0000_wrapper;
+ struct debugfs_blob_wrapper data_wrapper;
+ struct debugfs_blob_wrapper sdiag_wrapper;
+};
+
+struct samsung_laptop;
+
+struct samsung_rfkill {
+ struct samsung_laptop *samsung;
+ struct rfkill *rfkill;
+ enum rfkill_type type;
+};
+
+struct samsung_laptop {
+ const struct sabi_config *config;
+
+ void __iomem *sabi;
+ void __iomem *sabi_iface;
+ void __iomem *f0000_segment;
+
+ struct mutex sabi_mutex;
+
+ struct platform_device *platform_device;
+ struct backlight_device *backlight_device;
+
+ struct samsung_rfkill wlan;
+ struct samsung_rfkill bluetooth;
+
+ struct led_classdev kbd_led;
+ int kbd_led_wk;
+ struct workqueue_struct *led_workqueue;
+ struct work_struct kbd_led_work;
+
+ struct samsung_laptop_debug debug;
+ struct samsung_quirks *quirks;
+
+ bool handle_backlight;
+ bool has_stepping_quirk;
+
+ char sdiag[64];
+};
+
+struct samsung_quirks {
+ bool broken_acpi_video;
+};
+
+static struct samsung_quirks samsung_unknown = {};
-static void __iomem *sabi;
-static void __iomem *sabi_iface;
-static void __iomem *f0000_segment;
-static struct backlight_device *backlight_device;
-static struct mutex sabi_mutex;
-static struct platform_device *sdev;
-static struct rfkill *rfk;
-static bool has_stepping_quirk;
+static struct samsung_quirks samsung_broken_acpi_video = {
+ .broken_acpi_video = true,
+};
static bool force;
module_param(force, bool, 0);
@@ -237,176 +366,143 @@ static bool debug;
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled or not");
-static int sabi_get_command(u8 command, struct sabi_retval *sretval)
+static int sabi_command(struct samsung_laptop *samsung, u16 command,
+ struct sabi_data *in,
+ struct sabi_data *out)
{
- int retval = 0;
- u16 port = readw(sabi + sabi_config->header_offsets.port);
+ const struct sabi_config *config = samsung->config;
+ int ret = 0;
+ u16 port = readw(samsung->sabi + config->header_offsets.port);
u8 complete, iface_data;
- mutex_lock(&sabi_mutex);
-
- /* enable memory to be able to write to it */
- outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
-
- /* write out the command */
- writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
- writew(command, sabi_iface + SABI_IFACE_SUB);
- writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
- outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
-
- /* write protect memory to make it safe */
- outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
+ mutex_lock(&samsung->sabi_mutex);
- /* see if the command actually succeeded */
- complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
- iface_data = readb(sabi_iface + SABI_IFACE_DATA);
- if (complete != 0xaa || iface_data == 0xff) {
- pr_warn("SABI get command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
- command, complete, iface_data);
- retval = -EINVAL;
- goto exit;
+ if (debug) {
+ if (in)
+ pr_info("SABI command:0x%04x "
+ "data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}",
+ command, in->d0, in->d1, in->d2, in->d3);
+ else
+ pr_info("SABI command:0x%04x", command);
}
- /*
- * Save off the data into a structure so the caller use it.
- * Right now we only want the first 4 bytes,
- * There are commands that need more, but not for the ones we
- * currently care about.
- */
- sretval->retval[0] = readb(sabi_iface + SABI_IFACE_DATA);
- sretval->retval[1] = readb(sabi_iface + SABI_IFACE_DATA + 1);
- sretval->retval[2] = readb(sabi_iface + SABI_IFACE_DATA + 2);
- sretval->retval[3] = readb(sabi_iface + SABI_IFACE_DATA + 3);
-
-exit:
- mutex_unlock(&sabi_mutex);
- return retval;
-
-}
-
-static int sabi_set_command(u8 command, u8 data)
-{
- int retval = 0;
- u16 port = readw(sabi + sabi_config->header_offsets.port);
- u8 complete, iface_data;
-
- mutex_lock(&sabi_mutex);
/* enable memory to be able to write to it */
- outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
+ outb(readb(samsung->sabi + config->header_offsets.en_mem), port);
/* write out the command */
- writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
- writew(command, sabi_iface + SABI_IFACE_SUB);
- writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
- writeb(data, sabi_iface + SABI_IFACE_DATA);
- outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
+ writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN);
+ writew(command, samsung->sabi_iface + SABI_IFACE_SUB);
+ writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE);
+ if (in) {
+ writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA);
+ writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4);
+ writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8);
+ writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10);
+ }
+ outb(readb(samsung->sabi + config->header_offsets.iface_func), port);
/* write protect memory to make it safe */
- outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
+ outb(readb(samsung->sabi + config->header_offsets.re_mem), port);
/* see if the command actually succeeded */
- complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
- iface_data = readb(sabi_iface + SABI_IFACE_DATA);
+ complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE);
+ iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA);
+
+ /* iface_data = 0xFF happens when a command is not known
+ * so we only add a warning in debug mode since we will
+ * probably issue some unknown command at startup to find
+ * out which features are supported */
+ if (complete != 0xaa || (iface_data == 0xff && debug))
+ pr_warn("SABI command 0x%04x failed with"
+ " completion flag 0x%02x and interface data 0x%02x",
+ command, complete, iface_data);
+
if (complete != 0xaa || iface_data == 0xff) {
- pr_warn("SABI set command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
- command, complete, iface_data);
- retval = -EINVAL;
+ ret = -EINVAL;
+ goto exit;
}
- mutex_unlock(&sabi_mutex);
- return retval;
-}
-
-static void test_backlight(void)
-{
- struct sabi_retval sretval;
-
- sabi_get_command(sabi_config->commands.get_backlight, &sretval);
- printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
-
- sabi_set_command(sabi_config->commands.set_backlight, 0);
- printk(KERN_DEBUG "backlight should be off\n");
-
- sabi_get_command(sabi_config->commands.get_backlight, &sretval);
- printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
-
- msleep(1000);
+ if (out) {
+ out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA);
+ out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4);
+ out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2);
+ out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1);
+ }
- sabi_set_command(sabi_config->commands.set_backlight, 1);
- printk(KERN_DEBUG "backlight should be on\n");
+ if (debug && out) {
+ pr_info("SABI return data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}",
+ out->d0, out->d1, out->d2, out->d3);
+ }
- sabi_get_command(sabi_config->commands.get_backlight, &sretval);
- printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
+exit:
+ mutex_unlock(&samsung->sabi_mutex);
+ return ret;
}
-static void test_wireless(void)
+/* simple wrappers usable with most commands */
+static int sabi_set_commandb(struct samsung_laptop *samsung,
+ u16 command, u8 data)
{
- struct sabi_retval sretval;
-
- sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
- printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
-
- sabi_set_command(sabi_config->commands.set_wireless_button, 0);
- printk(KERN_DEBUG "wireless led should be off\n");
-
- sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
- printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
+ struct sabi_data in = { { { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 } } };
- msleep(1000);
-
- sabi_set_command(sabi_config->commands.set_wireless_button, 1);
- printk(KERN_DEBUG "wireless led should be on\n");
-
- sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
- printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
+ in.data[0] = data;
+ return sabi_command(samsung, command, &in, NULL);
}
-static u8 read_brightness(void)
+static int read_brightness(struct samsung_laptop *samsung)
{
- struct sabi_retval sretval;
+ const struct sabi_config *config = samsung->config;
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data sretval;
int user_brightness = 0;
int retval;
- retval = sabi_get_command(sabi_config->commands.get_brightness,
- &sretval);
- if (!retval) {
- user_brightness = sretval.retval[0];
- if (user_brightness > sabi_config->min_brightness)
- user_brightness -= sabi_config->min_brightness;
- else
- user_brightness = 0;
- }
+ retval = sabi_command(samsung, commands->get_brightness,
+ NULL, &sretval);
+ if (retval)
+ return retval;
+
+ user_brightness = sretval.data[0];
+ if (user_brightness > config->min_brightness)
+ user_brightness -= config->min_brightness;
+ else
+ user_brightness = 0;
+
return user_brightness;
}
-static void set_brightness(u8 user_brightness)
+static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness)
{
- u8 user_level = user_brightness + sabi_config->min_brightness;
+ const struct sabi_config *config = samsung->config;
+ const struct sabi_commands *commands = &samsung->config->commands;
+ u8 user_level = user_brightness + config->min_brightness;
- if (has_stepping_quirk && user_level != 0) {
+ if (samsung->has_stepping_quirk && user_level != 0) {
/*
* short circuit if the specified level is what's already set
* to prevent the screen from flickering needlessly
*/
- if (user_brightness == read_brightness())
+ if (user_brightness == read_brightness(samsung))
return;
- sabi_set_command(sabi_config->commands.set_brightness, 0);
+ sabi_set_commandb(samsung, commands->set_brightness, 0);
}
- sabi_set_command(sabi_config->commands.set_brightness, user_level);
+ sabi_set_commandb(samsung, commands->set_brightness, user_level);
}
static int get_brightness(struct backlight_device *bd)
{
- return (int)read_brightness();
+ struct samsung_laptop *samsung = bl_get_data(bd);
+
+ return read_brightness(samsung);
}
-static void check_for_stepping_quirk(void)
+static void check_for_stepping_quirk(struct samsung_laptop *samsung)
{
- u8 initial_level;
- u8 check_level;
- u8 orig_level = read_brightness();
+ int initial_level;
+ int check_level;
+ int orig_level = read_brightness(samsung);
/*
* Some laptops exhibit the strange behaviour of stepping toward
@@ -416,34 +512,38 @@ static void check_for_stepping_quirk(void)
*/
if (orig_level == 0)
- set_brightness(1);
+ set_brightness(samsung, 1);
- initial_level = read_brightness();
+ initial_level = read_brightness(samsung);
if (initial_level <= 2)
check_level = initial_level + 2;
else
check_level = initial_level - 2;
- has_stepping_quirk = false;
- set_brightness(check_level);
+ samsung->has_stepping_quirk = false;
+ set_brightness(samsung, check_level);
- if (read_brightness() != check_level) {
- has_stepping_quirk = true;
+ if (read_brightness(samsung) != check_level) {
+ samsung->has_stepping_quirk = true;
pr_info("enabled workaround for brightness stepping quirk\n");
}
- set_brightness(orig_level);
+ set_brightness(samsung, orig_level);
}
static int update_status(struct backlight_device *bd)
{
- set_brightness(bd->props.brightness);
+ struct samsung_laptop *samsung = bl_get_data(bd);
+ const struct sabi_commands *commands = &samsung->config->commands;
+
+ set_brightness(samsung, bd->props.brightness);
if (bd->props.power == FB_BLANK_UNBLANK)
- sabi_set_command(sabi_config->commands.set_backlight, 1);
+ sabi_set_commandb(samsung, commands->set_backlight, 1);
else
- sabi_set_command(sabi_config->commands.set_backlight, 0);
+ sabi_set_commandb(samsung, commands->set_backlight, 0);
+
return 0;
}
@@ -452,66 +552,101 @@ static const struct backlight_ops backlight_ops = {
.update_status = update_status,
};
-static int rfkill_set(void *data, bool blocked)
+static int seclinux_rfkill_set(void *data, bool blocked)
{
- /* Do something with blocked...*/
- /*
- * blocked == false is on
- * blocked == true is off
- */
- if (blocked)
- sabi_set_command(sabi_config->commands.set_wireless_button, 0);
- else
- sabi_set_command(sabi_config->commands.set_wireless_button, 1);
+ struct samsung_rfkill *srfkill = data;
+ struct samsung_laptop *samsung = srfkill->samsung;
+ const struct sabi_commands *commands = &samsung->config->commands;
- return 0;
+ return sabi_set_commandb(samsung, commands->set_wireless_button,
+ !blocked);
}
-static struct rfkill_ops rfkill_ops = {
- .set_block = rfkill_set,
+static struct rfkill_ops seclinux_rfkill_ops = {
+ .set_block = seclinux_rfkill_set,
};
-static int init_wireless(struct platform_device *sdev)
+static int swsmi_wireless_status(struct samsung_laptop *samsung,
+ struct sabi_data *data)
{
- int retval;
+ const struct sabi_commands *commands = &samsung->config->commands;
- rfk = rfkill_alloc("samsung-wifi", &sdev->dev, RFKILL_TYPE_WLAN,
- &rfkill_ops, NULL);
- if (!rfk)
- return -ENOMEM;
-
- retval = rfkill_register(rfk);
- if (retval) {
- rfkill_destroy(rfk);
- return -ENODEV;
- }
+ return sabi_command(samsung, commands->get_wireless_status,
+ NULL, data);
+}
- return 0;
+static int swsmi_rfkill_set(void *priv, bool blocked)
+{
+ struct samsung_rfkill *srfkill = priv;
+ struct samsung_laptop *samsung = srfkill->samsung;
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+ int ret, i;
+
+ ret = swsmi_wireless_status(samsung, &data);
+ if (ret)
+ return ret;
+
+ /* Don't set the state for non-present devices */
+ for (i = 0; i < 4; i++)
+ if (data.data[i] == 0x02)
+ data.data[1] = 0;
+
+ if (srfkill->type == RFKILL_TYPE_WLAN)
+ data.data[WL_STATUS_WLAN] = !blocked;
+ else if (srfkill->type == RFKILL_TYPE_BLUETOOTH)
+ data.data[WL_STATUS_BT] = !blocked;
+
+ return sabi_command(samsung, commands->set_wireless_status,
+ &data, &data);
}
-static void destroy_wireless(void)
+static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv)
{
- rfkill_unregister(rfk);
- rfkill_destroy(rfk);
+ struct samsung_rfkill *srfkill = priv;
+ struct samsung_laptop *samsung = srfkill->samsung;
+ struct sabi_data data;
+ int ret;
+
+ ret = swsmi_wireless_status(samsung, &data);
+ if (ret)
+ return ;
+
+ if (srfkill->type == RFKILL_TYPE_WLAN)
+ ret = data.data[WL_STATUS_WLAN];
+ else if (srfkill->type == RFKILL_TYPE_BLUETOOTH)
+ ret = data.data[WL_STATUS_BT];
+ else
+ return ;
+
+ rfkill_set_sw_state(rfkill, !ret);
}
+static struct rfkill_ops swsmi_rfkill_ops = {
+ .set_block = swsmi_rfkill_set,
+ .query = swsmi_rfkill_query,
+};
+
static ssize_t get_performance_level(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct sabi_retval sretval;
+ struct samsung_laptop *samsung = dev_get_drvdata(dev);
+ const struct sabi_config *config = samsung->config;
+ const struct sabi_commands *commands = &config->commands;
+ struct sabi_data sretval;
int retval;
int i;
/* Read the state */
- retval = sabi_get_command(sabi_config->commands.get_performance_level,
- &sretval);
+ retval = sabi_command(samsung, commands->get_performance_level,
+ NULL, &sretval);
if (retval)
return retval;
/* The logic is backwards, yeah, lots of fun... */
- for (i = 0; sabi_config->performance_levels[i].name; ++i) {
- if (sretval.retval[0] == sabi_config->performance_levels[i].value)
- return sprintf(buf, "%s\n", sabi_config->performance_levels[i].name);
+ for (i = 0; config->performance_levels[i].name; ++i) {
+ if (sretval.data[0] == config->performance_levels[i].value)
+ return sprintf(buf, "%s\n", config->performance_levels[i].name);
}
return sprintf(buf, "%s\n", "unknown");
}
@@ -520,269 +655,178 @@ static ssize_t set_performance_level(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
- if (count >= 1) {
- int i;
- for (i = 0; sabi_config->performance_levels[i].name; ++i) {
- const struct sabi_performance_level *level =
- &sabi_config->performance_levels[i];
- if (!strncasecmp(level->name, buf, strlen(level->name))) {
- sabi_set_command(sabi_config->commands.set_performance_level,
- level->value);
- break;
- }
+ struct samsung_laptop *samsung = dev_get_drvdata(dev);
+ const struct sabi_config *config = samsung->config;
+ const struct sabi_commands *commands = &config->commands;
+ int i;
+
+ if (count < 1)
+ return count;
+
+ for (i = 0; config->performance_levels[i].name; ++i) {
+ const struct sabi_performance_level *level =
+ &config->performance_levels[i];
+ if (!strncasecmp(level->name, buf, strlen(level->name))) {
+ sabi_set_commandb(samsung,
+ commands->set_performance_level,
+ level->value);
+ break;
}
- if (!sabi_config->performance_levels[i].name)
- return -EINVAL;
}
+
+ if (!config->performance_levels[i].name)
+ return -EINVAL;
+
return count;
}
+
static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
get_performance_level, set_performance_level);
+static int read_battery_life_extender(struct samsung_laptop *samsung)
+{
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+ int retval;
+
+ if (commands->get_battery_life_extender == 0xFFFF)
+ return -ENODEV;
+
+ memset(&data, 0, sizeof(data));
+ data.data[0] = 0x80;
+ retval = sabi_command(samsung, commands->get_battery_life_extender,
+ &data, &data);
-static int __init dmi_check_cb(const struct dmi_system_id *id)
+ if (retval)
+ return retval;
+
+ if (data.data[0] != 0 && data.data[0] != 1)
+ return -ENODEV;
+
+ return data.data[0];
+}
+
+static int write_battery_life_extender(struct samsung_laptop *samsung,
+ int enabled)
{
- pr_info("found laptop model '%s'\n",
- id->ident);
- return 1;
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+
+ memset(&data, 0, sizeof(data));
+ data.data[0] = 0x80 | enabled;
+ return sabi_command(samsung, commands->set_battery_life_extender,
+ &data, NULL);
}
-static struct dmi_system_id __initdata samsung_dmi_table[] = {
- {
- .ident = "N128",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N128"),
- DMI_MATCH(DMI_BOARD_NAME, "N128"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N130",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N130"),
- DMI_MATCH(DMI_BOARD_NAME, "N130"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N510",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N510"),
- DMI_MATCH(DMI_BOARD_NAME, "N510"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X125",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X125"),
- DMI_MATCH(DMI_BOARD_NAME, "X125"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X120/X170",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"),
- DMI_MATCH(DMI_BOARD_NAME, "X120/X170"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NC10",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
- DMI_MATCH(DMI_BOARD_NAME, "NC10"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NP-Q45",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
- DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X360",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
- DMI_MATCH(DMI_BOARD_NAME, "X360"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R410 Plus",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R410P"),
- DMI_MATCH(DMI_BOARD_NAME, "R460"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R518",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R518"),
- DMI_MATCH(DMI_BOARD_NAME, "R518"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R519/R719",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"),
- DMI_MATCH(DMI_BOARD_NAME, "R519/R719"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N150/N210/N220",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
- DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N220",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N220"),
- DMI_MATCH(DMI_BOARD_NAME, "N220"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N150/N210/N220/N230",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"),
- DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N150P/N210P/N220P",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"),
- DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R700",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "SR700"),
- DMI_MATCH(DMI_BOARD_NAME, "SR700"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R530/R730",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"),
- DMI_MATCH(DMI_BOARD_NAME, "R530/R730"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NF110/NF210/NF310",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
- DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "N145P/N250P/N260P",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
- DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R70/R71",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR,
- "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"),
- DMI_MATCH(DMI_BOARD_NAME, "R70/R71"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "P460",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "P460"),
- DMI_MATCH(DMI_BOARD_NAME, "P460"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "R528/R728",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "R528/R728"),
- DMI_MATCH(DMI_BOARD_NAME, "R528/R728"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "NC210/NC110",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"),
- DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"),
- },
- .callback = dmi_check_cb,
- },
- {
- .ident = "X520",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
- DMI_MATCH(DMI_PRODUCT_NAME, "X520"),
- DMI_MATCH(DMI_BOARD_NAME, "X520"),
- },
- .callback = dmi_check_cb,
- },
- { },
+static ssize_t get_battery_life_extender(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct samsung_laptop *samsung = dev_get_drvdata(dev);
+ int ret;
+
+ ret = read_battery_life_extender(samsung);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t set_battery_life_extender(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct samsung_laptop *samsung = dev_get_drvdata(dev);
+ int ret, value;
+
+ if (!count || sscanf(buf, "%i", &value) != 1)
+ return -EINVAL;
+
+ ret = write_battery_life_extender(samsung, !!value);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO,
+ get_battery_life_extender, set_battery_life_extender);
+
+static int read_usb_charge(struct samsung_laptop *samsung)
+{
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+ int retval;
+
+ if (commands->get_usb_charge == 0xFFFF)
+ return -ENODEV;
+
+ memset(&data, 0, sizeof(data));
+ data.data[0] = 0x80;
+ retval = sabi_command(samsung, commands->get_usb_charge,
+ &data, &data);
+
+ if (retval)
+ return retval;
+
+ if (data.data[0] != 0 && data.data[0] != 1)
+ return -ENODEV;
+
+ return data.data[0];
+}
+
+static int write_usb_charge(struct samsung_laptop *samsung,
+ int enabled)
+{
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+
+ memset(&data, 0, sizeof(data));
+ data.data[0] = 0x80 | enabled;
+ return sabi_command(samsung, commands->set_usb_charge,
+ &data, NULL);
+}
+
+static ssize_t get_usb_charge(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct samsung_laptop *samsung = dev_get_drvdata(dev);
+ int ret;
+
+ ret = read_usb_charge(samsung);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t set_usb_charge(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct samsung_laptop *samsung = dev_get_drvdata(dev);
+ int ret, value;
+
+ if (!count || sscanf(buf, "%i", &value) != 1)
+ return -EINVAL;
+
+ ret = write_usb_charge(samsung, !!value);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO,
+ get_usb_charge, set_usb_charge);
+
+static struct attribute *platform_attributes[] = {
+ &dev_attr_performance_level.attr,
+ &dev_attr_battery_life_extender.attr,
+ &dev_attr_usb_charge.attr,
+ NULL
};
-MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
static int find_signature(void __iomem *memcheck, const char *testStr)
{
@@ -803,153 +847,772 @@ static int find_signature(void __iomem *memcheck, const char *testStr)
return loca;
}
-static int __init samsung_init(void)
+static void samsung_rfkill_exit(struct samsung_laptop *samsung)
{
- struct backlight_properties props;
- struct sabi_retval sretval;
- unsigned int ifaceP;
- int i;
- int loca;
+ if (samsung->wlan.rfkill) {
+ rfkill_unregister(samsung->wlan.rfkill);
+ rfkill_destroy(samsung->wlan.rfkill);
+ samsung->wlan.rfkill = NULL;
+ }
+ if (samsung->bluetooth.rfkill) {
+ rfkill_unregister(samsung->bluetooth.rfkill);
+ rfkill_destroy(samsung->bluetooth.rfkill);
+ samsung->bluetooth.rfkill = NULL;
+ }
+}
+
+static int samsung_new_rfkill(struct samsung_laptop *samsung,
+ struct samsung_rfkill *arfkill,
+ const char *name, enum rfkill_type type,
+ const struct rfkill_ops *ops,
+ int blocked)
+{
+ struct rfkill **rfkill = &arfkill->rfkill;
+ int ret;
+
+ arfkill->type = type;
+ arfkill->samsung = samsung;
+
+ *rfkill = rfkill_alloc(name, &samsung->platform_device->dev,
+ type, ops, arfkill);
+
+ if (!*rfkill)
+ return -EINVAL;
+
+ if (blocked != -1)
+ rfkill_init_sw_state(*rfkill, blocked);
+
+ ret = rfkill_register(*rfkill);
+ if (ret) {
+ rfkill_destroy(*rfkill);
+ *rfkill = NULL;
+ return ret;
+ }
+ return 0;
+}
+
+static int __init samsung_rfkill_init_seclinux(struct samsung_laptop *samsung)
+{
+ return samsung_new_rfkill(samsung, &samsung->wlan, "samsung-wlan",
+ RFKILL_TYPE_WLAN, &seclinux_rfkill_ops, -1);
+}
+
+static int __init samsung_rfkill_init_swsmi(struct samsung_laptop *samsung)
+{
+ struct sabi_data data;
+ int ret;
+
+ ret = swsmi_wireless_status(samsung, &data);
+ if (ret) {
+ /* Some swsmi laptops use the old seclinux way to control
+ * wireless devices */
+ if (ret == -EINVAL)
+ ret = samsung_rfkill_init_seclinux(samsung);
+ return ret;
+ }
+
+ /* 0x02 seems to mean that the device is no present/available */
+
+ if (data.data[WL_STATUS_WLAN] != 0x02)
+ ret = samsung_new_rfkill(samsung, &samsung->wlan,
+ "samsung-wlan",
+ RFKILL_TYPE_WLAN,
+ &swsmi_rfkill_ops,
+ !data.data[WL_STATUS_WLAN]);
+ if (ret)
+ goto exit;
+
+ if (data.data[WL_STATUS_BT] != 0x02)
+ ret = samsung_new_rfkill(samsung, &samsung->bluetooth,
+ "samsung-bluetooth",
+ RFKILL_TYPE_BLUETOOTH,
+ &swsmi_rfkill_ops,
+ !data.data[WL_STATUS_BT]);
+ if (ret)
+ goto exit;
+
+exit:
+ if (ret)
+ samsung_rfkill_exit(samsung);
+
+ return ret;
+}
+
+static int __init samsung_rfkill_init(struct samsung_laptop *samsung)
+{
+ if (samsung->config->sabi_version == 2)
+ return samsung_rfkill_init_seclinux(samsung);
+ if (samsung->config->sabi_version == 3)
+ return samsung_rfkill_init_swsmi(samsung);
+ return 0;
+}
+
+static int kbd_backlight_enable(struct samsung_laptop *samsung)
+{
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
int retval;
- mutex_init(&sabi_mutex);
+ if (commands->kbd_backlight == 0xFFFF)
+ return -ENODEV;
+
+ memset(&data, 0, sizeof(data));
+ data.d0 = 0xaabb;
+ retval = sabi_command(samsung, commands->kbd_backlight,
+ &data, &data);
- if (!force && !dmi_check_system(samsung_dmi_table))
+ if (retval)
+ return retval;
+
+ if (data.d0 != 0xccdd)
return -ENODEV;
+ return 0;
+}
- f0000_segment = ioremap_nocache(0xf0000, 0xffff);
- if (!f0000_segment) {
- pr_err("Can't map the segment at 0xf0000\n");
- return -EINVAL;
+static int kbd_backlight_read(struct samsung_laptop *samsung)
+{
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+ int retval;
+
+ memset(&data, 0, sizeof(data));
+ data.data[0] = 0x81;
+ retval = sabi_command(samsung, commands->kbd_backlight,
+ &data, &data);
+
+ if (retval)
+ return retval;
+
+ return data.data[0];
+}
+
+static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness)
+{
+ const struct sabi_commands *commands = &samsung->config->commands;
+ struct sabi_data data;
+
+ memset(&data, 0, sizeof(data));
+ data.d0 = 0x82 | ((brightness & 0xFF) << 8);
+ return sabi_command(samsung, commands->kbd_backlight,
+ &data, NULL);
+}
+
+static void kbd_led_update(struct work_struct *work)
+{
+ struct samsung_laptop *samsung;
+
+ samsung = container_of(work, struct samsung_laptop, kbd_led_work);
+ kbd_backlight_write(samsung, samsung->kbd_led_wk);
+}
+
+static void kbd_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct samsung_laptop *samsung;
+
+ samsung = container_of(led_cdev, struct samsung_laptop, kbd_led);
+
+ if (value > samsung->kbd_led.max_brightness)
+ value = samsung->kbd_led.max_brightness;
+ else if (value < 0)
+ value = 0;
+
+ samsung->kbd_led_wk = value;
+ queue_work(samsung->led_workqueue, &samsung->kbd_led_work);
+}
+
+static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
+{
+ struct samsung_laptop *samsung;
+
+ samsung = container_of(led_cdev, struct samsung_laptop, kbd_led);
+ return kbd_backlight_read(samsung);
+}
+
+static void samsung_leds_exit(struct samsung_laptop *samsung)
+{
+ if (!IS_ERR_OR_NULL(samsung->kbd_led.dev))
+ led_classdev_unregister(&samsung->kbd_led);
+ if (samsung->led_workqueue)
+ destroy_workqueue(samsung->led_workqueue);
+}
+
+static int __init samsung_leds_init(struct samsung_laptop *samsung)
+{
+ int ret = 0;
+
+ samsung->led_workqueue = create_singlethread_workqueue("led_workqueue");
+ if (!samsung->led_workqueue)
+ return -ENOMEM;
+
+ if (kbd_backlight_enable(samsung) >= 0) {
+ INIT_WORK(&samsung->kbd_led_work, kbd_led_update);
+
+ samsung->kbd_led.name = "samsung::kbd_backlight";
+ samsung->kbd_led.brightness_set = kbd_led_set;
+ samsung->kbd_led.brightness_get = kbd_led_get;
+ samsung->kbd_led.max_brightness = 8;
+
+ ret = led_classdev_register(&samsung->platform_device->dev,
+ &samsung->kbd_led);
+ }
+
+ if (ret)
+ samsung_leds_exit(samsung);
+
+ return ret;
+}
+
+static void samsung_backlight_exit(struct samsung_laptop *samsung)
+{
+ if (samsung->backlight_device) {
+ backlight_device_unregister(samsung->backlight_device);
+ samsung->backlight_device = NULL;
+ }
+}
+
+static int __init samsung_backlight_init(struct samsung_laptop *samsung)
+{
+ struct backlight_device *bd;
+ struct backlight_properties props;
+
+ if (!samsung->handle_backlight)
+ return 0;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = samsung->config->max_brightness -
+ samsung->config->min_brightness;
+
+ bd = backlight_device_register("samsung",
+ &samsung->platform_device->dev,
+ samsung, &backlight_ops,
+ &props);
+ if (IS_ERR(bd))
+ return PTR_ERR(bd);
+
+ samsung->backlight_device = bd;
+ samsung->backlight_device->props.brightness = read_brightness(samsung);
+ samsung->backlight_device->props.power = FB_BLANK_UNBLANK;
+ backlight_update_status(samsung->backlight_device);
+
+ return 0;
+}
+
+static umode_t samsung_sysfs_is_visible(struct kobject *kobj,
+ struct attribute *attr, int idx)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct samsung_laptop *samsung = platform_get_drvdata(pdev);
+ bool ok = true;
+
+ if (attr == &dev_attr_performance_level.attr)
+ ok = !!samsung->config->performance_levels[0].name;
+ if (attr == &dev_attr_battery_life_extender.attr)
+ ok = !!(read_battery_life_extender(samsung) >= 0);
+ if (attr == &dev_attr_usb_charge.attr)
+ ok = !!(read_usb_charge(samsung) >= 0);
+
+ return ok ? attr->mode : 0;
+}
+
+static struct attribute_group platform_attribute_group = {
+ .is_visible = samsung_sysfs_is_visible,
+ .attrs = platform_attributes
+};
+
+static void samsung_sysfs_exit(struct samsung_laptop *samsung)
+{
+ struct platform_device *device = samsung->platform_device;
+
+ sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
+}
+
+static int __init samsung_sysfs_init(struct samsung_laptop *samsung)
+{
+ struct platform_device *device = samsung->platform_device;
+
+ return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
+
+}
+
+static int show_call(struct seq_file *m, void *data)
+{
+ struct samsung_laptop *samsung = m->private;
+ struct sabi_data *sdata = &samsung->debug.data;
+ int ret;
+
+ seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
+ samsung->debug.command,
+ sdata->d0, sdata->d1, sdata->d2, sdata->d3);
+
+ ret = sabi_command(samsung, samsung->debug.command, sdata, sdata);
+
+ if (ret) {
+ seq_printf(m, "SABI command 0x%04x failed\n",
+ samsung->debug.command);
+ return ret;
+ }
+
+ seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n",
+ sdata->d0, sdata->d1, sdata->d2, sdata->d3);
+ return 0;
+}
+
+static int samsung_debugfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, show_call, inode->i_private);
+}
+
+static const struct file_operations samsung_laptop_call_io_ops = {
+ .owner = THIS_MODULE,
+ .open = samsung_debugfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void samsung_debugfs_exit(struct samsung_laptop *samsung)
+{
+ debugfs_remove_recursive(samsung->debug.root);
+}
+
+static int samsung_debugfs_init(struct samsung_laptop *samsung)
+{
+ struct dentry *dent;
+
+ samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL);
+ if (!samsung->debug.root) {
+ pr_err("failed to create debugfs directory");
+ goto error_debugfs;
+ }
+
+ samsung->debug.f0000_wrapper.data = samsung->f0000_segment;
+ samsung->debug.f0000_wrapper.size = 0xffff;
+
+ samsung->debug.data_wrapper.data = &samsung->debug.data;
+ samsung->debug.data_wrapper.size = sizeof(samsung->debug.data);
+
+ samsung->debug.sdiag_wrapper.data = samsung->sdiag;
+ samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag);
+
+ dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR,
+ samsung->debug.root, &samsung->debug.command);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root,
+ &samsung->debug.data.d0);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root,
+ &samsung->debug.data.d1);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root,
+ &samsung->debug.data.d2);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root,
+ &samsung->debug.data.d3);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR,
+ samsung->debug.root,
+ &samsung->debug.data_wrapper);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR,
+ samsung->debug.root,
+ &samsung->debug.f0000_wrapper);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_file("call", S_IFREG | S_IRUGO,
+ samsung->debug.root, samsung,
+ &samsung_laptop_call_io_ops);
+ if (!dent)
+ goto error_debugfs;
+
+ dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR,
+ samsung->debug.root,
+ &samsung->debug.sdiag_wrapper);
+ if (!dent)
+ goto error_debugfs;
+
+ return 0;
+
+error_debugfs:
+ samsung_debugfs_exit(samsung);
+ return -ENOMEM;
+}
+
+static void samsung_sabi_exit(struct samsung_laptop *samsung)
+{
+ const struct sabi_config *config = samsung->config;
+
+ /* Turn off "Linux" mode in the BIOS */
+ if (config && config->commands.set_linux != 0xff)
+ sabi_set_commandb(samsung, config->commands.set_linux, 0x80);
+
+ if (samsung->sabi_iface) {
+ iounmap(samsung->sabi_iface);
+ samsung->sabi_iface = NULL;
+ }
+ if (samsung->f0000_segment) {
+ iounmap(samsung->f0000_segment);
+ samsung->f0000_segment = NULL;
+ }
+
+ samsung->config = NULL;
+}
+
+static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca,
+ unsigned int ifaceP)
+{
+ const struct sabi_config *config = samsung->config;
+
+ printk(KERN_DEBUG "This computer supports SABI==%x\n",
+ loca + 0xf0000 - 6);
+
+ printk(KERN_DEBUG "SABI header:\n");
+ printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
+ readw(samsung->sabi + config->header_offsets.port));
+ printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
+ readb(samsung->sabi + config->header_offsets.iface_func));
+ printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
+ readb(samsung->sabi + config->header_offsets.en_mem));
+ printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
+ readb(samsung->sabi + config->header_offsets.re_mem));
+ printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
+ readw(samsung->sabi + config->header_offsets.data_offset));
+ printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
+ readw(samsung->sabi + config->header_offsets.data_segment));
+
+ printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP);
+}
+
+static void __init samsung_sabi_diag(struct samsung_laptop *samsung)
+{
+ int loca = find_signature(samsung->f0000_segment, "SDiaG@");
+ int i;
+
+ if (loca == 0xffff)
+ return ;
+
+ /* Example:
+ * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A
+ *
+ * Product name: 90X3A
+ * BIOS Version: 07HL
+ */
+ loca += 1;
+ for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) {
+ char temp = readb(samsung->f0000_segment + loca);
+
+ if (isalnum(temp) || temp == '/' || temp == '-')
+ samsung->sdiag[i++] = temp;
+ else
+ break ;
}
+ if (debug && samsung->sdiag[0])
+ pr_info("sdiag: %s", samsung->sdiag);
+}
+
+static int __init samsung_sabi_init(struct samsung_laptop *samsung)
+{
+ const struct sabi_config *config = NULL;
+ const struct sabi_commands *commands;
+ unsigned int ifaceP;
+ int ret = 0;
+ int i;
+ int loca;
+
+ samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff);
+ if (!samsung->f0000_segment) {
+ if (debug || force)
+ pr_err("Can't map the segment at 0xf0000\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ samsung_sabi_diag(samsung);
+
/* Try to find one of the signatures in memory to find the header */
for (i = 0; sabi_configs[i].test_string != 0; ++i) {
- sabi_config = &sabi_configs[i];
- loca = find_signature(f0000_segment, sabi_config->test_string);
+ samsung->config = &sabi_configs[i];
+ loca = find_signature(samsung->f0000_segment,
+ samsung->config->test_string);
if (loca != 0xffff)
break;
}
if (loca == 0xffff) {
- pr_err("This computer does not support SABI\n");
- goto error_no_signature;
+ if (debug || force)
+ pr_err("This computer does not support SABI\n");
+ ret = -ENODEV;
+ goto exit;
}
+ config = samsung->config;
+ commands = &config->commands;
+
/* point to the SMI port Number */
loca += 1;
- sabi = (f0000_segment + loca);
-
- if (debug) {
- printk(KERN_DEBUG "This computer supports SABI==%x\n",
- loca + 0xf0000 - 6);
- printk(KERN_DEBUG "SABI header:\n");
- printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
- readw(sabi + sabi_config->header_offsets.port));
- printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
- readb(sabi + sabi_config->header_offsets.iface_func));
- printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
- readb(sabi + sabi_config->header_offsets.en_mem));
- printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
- readb(sabi + sabi_config->header_offsets.re_mem));
- printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
- readw(sabi + sabi_config->header_offsets.data_offset));
- printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
- readw(sabi + sabi_config->header_offsets.data_segment));
- }
+ samsung->sabi = (samsung->f0000_segment + loca);
/* Get a pointer to the SABI Interface */
- ifaceP = (readw(sabi + sabi_config->header_offsets.data_segment) & 0x0ffff) << 4;
- ifaceP += readw(sabi + sabi_config->header_offsets.data_offset) & 0x0ffff;
- sabi_iface = ioremap_nocache(ifaceP, 16);
- if (!sabi_iface) {
- pr_err("Can't remap %x\n", ifaceP);
- goto error_no_signature;
- }
- if (debug) {
- printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP);
- printk(KERN_DEBUG "sabi_iface = %p\n", sabi_iface);
+ ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4;
+ ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff;
- test_backlight();
- test_wireless();
+ if (debug)
+ samsung_sabi_infos(samsung, loca, ifaceP);
- retval = sabi_get_command(sabi_config->commands.get_brightness,
- &sretval);
- printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]);
+ samsung->sabi_iface = ioremap_nocache(ifaceP, 16);
+ if (!samsung->sabi_iface) {
+ pr_err("Can't remap %x\n", ifaceP);
+ ret = -EINVAL;
+ goto exit;
}
/* Turn on "Linux" mode in the BIOS */
- if (sabi_config->commands.set_linux != 0xff) {
- retval = sabi_set_command(sabi_config->commands.set_linux,
- 0x81);
+ if (commands->set_linux != 0xff) {
+ int retval = sabi_set_commandb(samsung,
+ commands->set_linux, 0x81);
if (retval) {
pr_warn("Linux mode was not set!\n");
- goto error_no_platform;
+ ret = -ENODEV;
+ goto exit;
}
}
/* Check for stepping quirk */
- check_for_stepping_quirk();
+ if (samsung->handle_backlight)
+ check_for_stepping_quirk(samsung);
- /* knock up a platform device to hang stuff off of */
- sdev = platform_device_register_simple("samsung", -1, NULL, 0);
- if (IS_ERR(sdev))
- goto error_no_platform;
+ pr_info("detected SABI interface: %s\n",
+ samsung->config->test_string);
- /* create a backlight device to talk to this one */
- memset(&props, 0, sizeof(struct backlight_properties));
- props.type = BACKLIGHT_PLATFORM;
- props.max_brightness = sabi_config->max_brightness -
- sabi_config->min_brightness;
- backlight_device = backlight_device_register("samsung", &sdev->dev,
- NULL, &backlight_ops,
- &props);
- if (IS_ERR(backlight_device))
- goto error_no_backlight;
-
- backlight_device->props.brightness = read_brightness();
- backlight_device->props.power = FB_BLANK_UNBLANK;
- backlight_update_status(backlight_device);
-
- retval = init_wireless(sdev);
- if (retval)
- goto error_no_rfk;
+exit:
+ if (ret)
+ samsung_sabi_exit(samsung);
- retval = device_create_file(&sdev->dev, &dev_attr_performance_level);
- if (retval)
- goto error_file_create;
+ return ret;
+}
+
+static void samsung_platform_exit(struct samsung_laptop *samsung)
+{
+ if (samsung->platform_device) {
+ platform_device_unregister(samsung->platform_device);
+ samsung->platform_device = NULL;
+ }
+}
+
+static int __init samsung_platform_init(struct samsung_laptop *samsung)
+{
+ struct platform_device *pdev;
+
+ pdev = platform_device_register_simple("samsung", -1, NULL, 0);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+ samsung->platform_device = pdev;
+ platform_set_drvdata(samsung->platform_device, samsung);
return 0;
+}
+
+static struct samsung_quirks *quirks;
+
+static int __init samsung_dmi_matched(const struct dmi_system_id *d)
+{
+ quirks = d->driver_data;
+ return 0;
+}
+
+static struct dmi_system_id __initdata samsung_dmi_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
+ },
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
+ },
+ },
+ /* Specific DMI ids for laptop with quirks */
+ {
+ .callback = samsung_dmi_matched,
+ .ident = "N150P",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N150P"),
+ DMI_MATCH(DMI_BOARD_NAME, "N150P"),
+ },
+ .driver_data = &samsung_broken_acpi_video,
+ },
+ {
+ .callback = samsung_dmi_matched,
+ .ident = "N145P/N250P/N260P",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
+ DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
+ },
+ .driver_data = &samsung_broken_acpi_video,
+ },
+ {
+ .callback = samsung_dmi_matched,
+ .ident = "N150/N210/N220",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
+ DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
+ },
+ .driver_data = &samsung_broken_acpi_video,
+ },
+ {
+ .callback = samsung_dmi_matched,
+ .ident = "NF110/NF210/NF310",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
+ DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
+ },
+ .driver_data = &samsung_broken_acpi_video,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
-error_file_create:
- destroy_wireless();
+static struct platform_device *samsung_platform_device;
-error_no_rfk:
- backlight_device_unregister(backlight_device);
+static int __init samsung_init(void)
+{
+ struct samsung_laptop *samsung;
+ int ret;
-error_no_backlight:
- platform_device_unregister(sdev);
+ quirks = &samsung_unknown;
+ if (!force && !dmi_check_system(samsung_dmi_table))
+ return -ENODEV;
+
+ samsung = kzalloc(sizeof(*samsung), GFP_KERNEL);
+ if (!samsung)
+ return -ENOMEM;
-error_no_platform:
- iounmap(sabi_iface);
+ mutex_init(&samsung->sabi_mutex);
+ samsung->handle_backlight = true;
+ samsung->quirks = quirks;
-error_no_signature:
- iounmap(f0000_segment);
- return -EINVAL;
+
+#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
+ /* Don't handle backlight here if the acpi video already handle it */
+ if (acpi_video_backlight_support()) {
+ if (samsung->quirks->broken_acpi_video) {
+ pr_info("Disabling ACPI video driver\n");
+ acpi_video_unregister();
+ } else {
+ samsung->handle_backlight = false;
+ }
+ }
+#endif
+
+ ret = samsung_platform_init(samsung);
+ if (ret)
+ goto error_platform;
+
+ ret = samsung_sabi_init(samsung);
+ if (ret)
+ goto error_sabi;
+
+#ifdef CONFIG_ACPI
+ /* Only log that if we are really on a sabi platform */
+ if (acpi_video_backlight_support() &&
+ !samsung->quirks->broken_acpi_video)
+ pr_info("Backlight controlled by ACPI video driver\n");
+#endif
+
+ ret = samsung_sysfs_init(samsung);
+ if (ret)
+ goto error_sysfs;
+
+ ret = samsung_backlight_init(samsung);
+ if (ret)
+ goto error_backlight;
+
+ ret = samsung_rfkill_init(samsung);
+ if (ret)
+ goto error_rfkill;
+
+ ret = samsung_leds_init(samsung);
+ if (ret)
+ goto error_leds;
+
+ ret = samsung_debugfs_init(samsung);
+ if (ret)
+ goto error_debugfs;
+
+ samsung_platform_device = samsung->platform_device;
+ return ret;
+
+error_debugfs:
+ samsung_leds_exit(samsung);
+error_leds:
+ samsung_rfkill_exit(samsung);
+error_rfkill:
+ samsung_backlight_exit(samsung);
+error_backlight:
+ samsung_sysfs_exit(samsung);
+error_sysfs:
+ samsung_sabi_exit(samsung);
+error_sabi:
+ samsung_platform_exit(samsung);
+error_platform:
+ kfree(samsung);
+ return ret;
}
static void __exit samsung_exit(void)
{
- /* Turn off "Linux" mode in the BIOS */
- if (sabi_config->commands.set_linux != 0xff)
- sabi_set_command(sabi_config->commands.set_linux, 0x80);
-
- device_remove_file(&sdev->dev, &dev_attr_performance_level);
- backlight_device_unregister(backlight_device);
- destroy_wireless();
- iounmap(sabi_iface);
- iounmap(f0000_segment);
- platform_device_unregister(sdev);
+ struct samsung_laptop *samsung;
+
+ samsung = platform_get_drvdata(samsung_platform_device);
+
+ samsung_debugfs_exit(samsung);
+ samsung_leds_exit(samsung);
+ samsung_rfkill_exit(samsung);
+ samsung_backlight_exit(samsung);
+ samsung_sysfs_exit(samsung);
+ samsung_sabi_exit(samsung);
+ samsung_platform_exit(samsung);
+
+ kfree(samsung);
+ samsung_platform_device = NULL;
}
module_init(samsung_init);
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index c006dee5ebfe..8a51795aa02a 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -127,7 +127,7 @@ MODULE_PARM_DESC(minor,
"default is -1 (automatic)");
#endif
-static int kbd_backlight; /* = 1 */
+static int kbd_backlight = 1;
module_param(kbd_backlight, int, 0444);
MODULE_PARM_DESC(kbd_backlight,
"set this to 0 to disable keyboard backlight, "
@@ -347,6 +347,7 @@ static void sony_laptop_report_input_event(u8 event)
struct input_dev *jog_dev = sony_laptop_input.jog_dev;
struct input_dev *key_dev = sony_laptop_input.key_dev;
struct sony_laptop_keypress kp = { NULL };
+ int scancode = -1;
if (event == SONYPI_EVENT_FNKEY_RELEASED ||
event == SONYPI_EVENT_ANYBUTTON_RELEASED) {
@@ -380,8 +381,8 @@ static void sony_laptop_report_input_event(u8 event)
dprintk("sony_laptop_report_input_event, event not known: %d\n", event);
break;
}
- if (sony_laptop_input_index[event] != -1) {
- kp.key = sony_laptop_input_keycode_map[sony_laptop_input_index[event]];
+ if ((scancode = sony_laptop_input_index[event]) != -1) {
+ kp.key = sony_laptop_input_keycode_map[scancode];
if (kp.key != KEY_UNKNOWN)
kp.dev = key_dev;
}
@@ -389,9 +390,11 @@ static void sony_laptop_report_input_event(u8 event)
}
if (kp.dev) {
+ /* if we have a scancode we emit it so we can always
+ remap the key */
+ if (scancode != -1)
+ input_event(kp.dev, EV_MSC, MSC_SCAN, scancode);
input_report_key(kp.dev, kp.key, 1);
- /* we emit the scancode so we can always remap the key */
- input_event(kp.dev, EV_MSC, MSC_SCAN, event);
input_sync(kp.dev);
/* schedule key release */
@@ -466,7 +469,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
jog_dev->name = "Sony Vaio Jogdial";
jog_dev->id.bustype = BUS_ISA;
jog_dev->id.vendor = PCI_VENDOR_ID_SONY;
- key_dev->dev.parent = &acpi_device->dev;
+ jog_dev->dev.parent = &acpi_device->dev;
input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE);
input_set_capability(jog_dev, EV_REL, REL_WHEEL);
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index ea0c6075b720..d68c0002f4a2 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -8658,7 +8658,7 @@ static int __must_check __init get_thinkpad_model_data(
}
s = dmi_get_system_info(DMI_PRODUCT_VERSION);
- if (s && !strnicmp(s, "ThinkPad", 8)) {
+ if (s && !(strnicmp(s, "ThinkPad", 8) && strnicmp(s, "Lenovo", 6))) {
tp->model_str = kstrdup(s, GFP_KERNEL);
if (!tp->model_str)
return -ENOMEM;
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index dcdc1f4a4624..ee79ce64d9df 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -52,6 +52,8 @@
#include <linux/input/sparse-keymap.h>
#include <linux/leds.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/i8042.h>
#include <asm/uaccess.h>
@@ -61,6 +63,11 @@ MODULE_AUTHOR("John Belmonte");
MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
MODULE_LICENSE("GPL");
+#define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100"
+
+/* Scan code for Fn key on TOS1900 models */
+#define TOS1900_FN_SCAN 0x6e
+
/* Toshiba ACPI method paths */
#define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX"
@@ -95,6 +102,8 @@ MODULE_LICENSE("GPL");
#define HCI_WIRELESS 0x0056
/* field definitions */
+#define HCI_HOTKEY_DISABLE 0x0b
+#define HCI_HOTKEY_ENABLE 0x09
#define HCI_LCD_BRIGHTNESS_BITS 3
#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
#define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS)
@@ -111,6 +120,7 @@ struct toshiba_acpi_dev {
const char *method_hci;
struct rfkill *bt_rfk;
struct input_dev *hotkey_dev;
+ struct work_struct hotkey_work;
struct backlight_device *backlight_dev;
struct led_classdev led_dev;
@@ -118,14 +128,18 @@ struct toshiba_acpi_dev {
int last_key_event;
int key_event_valid;
- int illumination_supported:1;
- int video_supported:1;
- int fan_supported:1;
- int system_event_supported:1;
+ unsigned int illumination_supported:1;
+ unsigned int video_supported:1;
+ unsigned int fan_supported:1;
+ unsigned int system_event_supported:1;
+ unsigned int ntfy_supported:1;
+ unsigned int info_supported:1;
struct mutex mutex;
};
+static struct toshiba_acpi_dev *toshiba_acpi;
+
static const struct acpi_device_id toshiba_device_ids[] = {
{"TOS6200", 0},
{"TOS6208", 0},
@@ -138,6 +152,8 @@ static const struct key_entry toshiba_acpi_keymap[] __devinitconst = {
{ KE_KEY, 0x101, { KEY_MUTE } },
{ KE_KEY, 0x102, { KEY_ZOOMOUT } },
{ KE_KEY, 0x103, { KEY_ZOOMIN } },
+ { KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } },
+ { KE_KEY, 0x139, { KEY_ZOOMRESET } },
{ KE_KEY, 0x13b, { KEY_COFFEE } },
{ KE_KEY, 0x13c, { KEY_BATTERY } },
{ KE_KEY, 0x13d, { KEY_SLEEP } },
@@ -146,7 +162,7 @@ static const struct key_entry toshiba_acpi_keymap[] __devinitconst = {
{ KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY, 0x141, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0x142, { KEY_WLAN } },
- { KE_KEY, 0x143, { KEY_PROG1 } },
+ { KE_KEY, 0x143, { KEY_TOUCHPAD_TOGGLE } },
{ KE_KEY, 0x17f, { KEY_FN } },
{ KE_KEY, 0xb05, { KEY_PROG2 } },
{ KE_KEY, 0xb06, { KEY_WWW } },
@@ -156,6 +172,7 @@ static const struct key_entry toshiba_acpi_keymap[] __devinitconst = {
{ KE_KEY, 0xb32, { KEY_NEXTSONG } },
{ KE_KEY, 0xb33, { KEY_PLAYPAUSE } },
{ KE_KEY, 0xb5a, { KEY_MEDIA } },
+ { KE_IGNORE, 0x1430, { KEY_RESERVED } },
{ KE_END, 0 },
};
@@ -847,10 +864,78 @@ static const struct backlight_ops toshiba_backlight_data = {
.update_status = set_lcd_status,
};
+static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
+ struct serio *port)
+{
+ if (str & 0x20)
+ return false;
+
+ if (unlikely(data == 0xe0))
+ return false;
+
+ if ((data & 0x7f) == TOS1900_FN_SCAN) {
+ schedule_work(&toshiba_acpi->hotkey_work);
+ return true;
+ }
+
+ return false;
+}
+
+static void toshiba_acpi_hotkey_work(struct work_struct *work)
+{
+ acpi_handle ec_handle = ec_get_handle();
+ acpi_status status;
+
+ if (!ec_handle)
+ return;
+
+ status = acpi_evaluate_object(ec_handle, "NTFY", NULL, NULL);
+ if (ACPI_FAILURE(status))
+ pr_err("ACPI NTFY method execution failed\n");
+}
+
+/*
+ * Returns hotkey scancode, or < 0 on failure.
+ */
+static int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev)
+{
+ struct acpi_buffer buf;
+ union acpi_object out_obj;
+ acpi_status status;
+
+ buf.pointer = &out_obj;
+ buf.length = sizeof(out_obj);
+
+ status = acpi_evaluate_object(dev->acpi_dev->handle, "INFO",
+ NULL, &buf);
+ if (ACPI_FAILURE(status) || out_obj.type != ACPI_TYPE_INTEGER) {
+ pr_err("ACPI INFO method execution failed\n");
+ return -EIO;
+ }
+
+ return out_obj.integer.value;
+}
+
+static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,
+ int scancode)
+{
+ if (scancode == 0x100)
+ return;
+
+ /* act on key press; ignore key release */
+ if (scancode & 0x80)
+ return;
+
+ if (!sparse_keymap_report_event(dev->hotkey_dev, scancode, 1, true))
+ pr_info("Unknown key %x\n", scancode);
+}
+
static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
{
acpi_status status;
+ acpi_handle ec_handle, handle;
int error;
+ u32 hci_result;
dev->hotkey_dev = input_allocate_device();
if (!dev->hotkey_dev) {
@@ -866,21 +951,67 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
if (error)
goto err_free_dev;
+ /*
+ * For some machines the SCI responsible for providing hotkey
+ * notification doesn't fire. We can trigger the notification
+ * whenever the Fn key is pressed using the NTFY method, if
+ * supported, so if it's present set up an i8042 key filter
+ * for this purpose.
+ */
+ status = AE_ERROR;
+ ec_handle = ec_get_handle();
+ if (ec_handle)
+ status = acpi_get_handle(ec_handle, "NTFY", &handle);
+
+ if (ACPI_SUCCESS(status)) {
+ INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);
+
+ error = i8042_install_filter(toshiba_acpi_i8042_filter);
+ if (error) {
+ pr_err("Error installing key filter\n");
+ goto err_free_keymap;
+ }
+
+ dev->ntfy_supported = 1;
+ }
+
+ /*
+ * Determine hotkey query interface. Prefer using the INFO
+ * method when it is available.
+ */
+ status = acpi_get_handle(dev->acpi_dev->handle, "INFO", &handle);
+ if (ACPI_SUCCESS(status)) {
+ dev->info_supported = 1;
+ } else {
+ hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
+ if (hci_result == HCI_SUCCESS)
+ dev->system_event_supported = 1;
+ }
+
+ if (!dev->info_supported && !dev->system_event_supported) {
+ pr_warn("No hotkey query interface found\n");
+ goto err_remove_filter;
+ }
+
status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL);
if (ACPI_FAILURE(status)) {
pr_info("Unable to enable hotkeys\n");
error = -ENODEV;
- goto err_free_keymap;
+ goto err_remove_filter;
}
error = input_register_device(dev->hotkey_dev);
if (error) {
pr_info("Unable to register input device\n");
- goto err_free_keymap;
+ goto err_remove_filter;
}
+ hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &hci_result);
return 0;
+ err_remove_filter:
+ if (dev->ntfy_supported)
+ i8042_remove_filter(toshiba_acpi_i8042_filter);
err_free_keymap:
sparse_keymap_free(dev->hotkey_dev);
err_free_dev:
@@ -895,6 +1026,11 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)
remove_toshiba_proc_entries(dev);
+ if (dev->ntfy_supported) {
+ i8042_remove_filter(toshiba_acpi_i8042_filter);
+ cancel_work_sync(&dev->hotkey_work);
+ }
+
if (dev->hotkey_dev) {
input_unregister_device(dev->hotkey_dev);
sparse_keymap_free(dev->hotkey_dev);
@@ -911,6 +1047,9 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)
if (dev->illumination_supported)
led_classdev_unregister(&dev->led_dev);
+ if (toshiba_acpi)
+ toshiba_acpi = NULL;
+
kfree(dev);
return 0;
@@ -936,12 +1075,14 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
{
struct toshiba_acpi_dev *dev;
const char *hci_method;
- u32 hci_result;
u32 dummy;
bool bt_present;
int ret = 0;
struct backlight_properties props;
+ if (toshiba_acpi)
+ return -EBUSY;
+
pr_info("Toshiba Laptop ACPI Extras version %s\n",
TOSHIBA_ACPI_VERSION);
@@ -963,11 +1104,6 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
mutex_init(&dev->mutex);
- /* enable event fifo */
- hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
- if (hci_result == HCI_SUCCESS)
- dev->system_event_supported = 1;
-
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1;
dev->backlight_dev = backlight_device_register("toshiba",
@@ -1024,6 +1160,8 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
create_toshiba_proc_entries(dev);
+ toshiba_acpi = dev;
+
return 0;
error:
@@ -1036,40 +1174,64 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
u32 hci_result, value;
int retries = 3;
+ int scancode;
- if (!dev->system_event_supported || event != 0x80)
+ if (event != 0x80)
return;
- do {
- hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
- switch (hci_result) {
- case HCI_SUCCESS:
- if (value == 0x100)
- continue;
- /* act on key press; ignore key release */
- if (value & 0x80)
- continue;
-
- if (!sparse_keymap_report_event(dev->hotkey_dev,
- value, 1, true)) {
- pr_info("Unknown key %x\n",
- value);
+ if (dev->info_supported) {
+ scancode = toshiba_acpi_query_hotkey(dev);
+ if (scancode < 0)
+ pr_err("Failed to query hotkey event\n");
+ else if (scancode != 0)
+ toshiba_acpi_report_hotkey(dev, scancode);
+ } else if (dev->system_event_supported) {
+ do {
+ hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result);
+ switch (hci_result) {
+ case HCI_SUCCESS:
+ toshiba_acpi_report_hotkey(dev, (int)value);
+ break;
+ case HCI_NOT_SUPPORTED:
+ /*
+ * This is a workaround for an unresolved
+ * issue on some machines where system events
+ * sporadically become disabled.
+ */
+ hci_write1(dev, HCI_SYSTEM_EVENT, 1,
+ &hci_result);
+ pr_notice("Re-enabled hotkeys\n");
+ /* fall through */
+ default:
+ retries--;
+ break;
}
- break;
- case HCI_NOT_SUPPORTED:
- /* This is a workaround for an unresolved issue on
- * some machines where system events sporadically
- * become disabled. */
- hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
- pr_notice("Re-enabled hotkeys\n");
- /* fall through */
- default:
- retries--;
- break;
- }
- } while (retries && hci_result != HCI_EMPTY);
+ } while (retries && hci_result != HCI_EMPTY);
+ }
+}
+
+static int toshiba_acpi_suspend(struct acpi_device *acpi_dev,
+ pm_message_t state)
+{
+ struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
+ u32 result;
+
+ if (dev->hotkey_dev)
+ hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE, &result);
+
+ return 0;
}
+static int toshiba_acpi_resume(struct acpi_device *acpi_dev)
+{
+ struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
+ u32 result;
+
+ if (dev->hotkey_dev)
+ hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &result);
+
+ return 0;
+}
static struct acpi_driver toshiba_acpi_driver = {
.name = "Toshiba ACPI driver",
@@ -1080,6 +1242,8 @@ static struct acpi_driver toshiba_acpi_driver = {
.add = toshiba_acpi_add,
.remove = toshiba_acpi_remove,
.notify = toshiba_acpi_notify,
+ .suspend = toshiba_acpi_suspend,
+ .resume = toshiba_acpi_resume,
},
};
@@ -1087,6 +1251,14 @@ static int __init toshiba_acpi_init(void)
{
int ret;
+ /*
+ * Machines with this WMI guid aren't supported due to bugs in
+ * their AML. This check relies on wmi initializing before
+ * toshiba_acpi to guarantee guids have been identified.
+ */
+ if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
+ return -ENODEV;
+
toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir);
if (!toshiba_proc_dir) {
pr_err("Unable to create proc dir " PROC_TOSHIBA "\n");
diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c
index e549eeeda121..41781ed8301c 100644
--- a/drivers/platform/x86/xo1-rfkill.c
+++ b/drivers/platform/x86/xo1-rfkill.c
@@ -67,19 +67,8 @@ static struct platform_driver xo1_rfkill_driver = {
.remove = __devexit_p(xo1_rfkill_remove),
};
-static int __init xo1_rfkill_init(void)
-{
- return platform_driver_register(&xo1_rfkill_driver);
-}
-
-static void __exit xo1_rfkill_exit(void)
-{
- platform_driver_unregister(&xo1_rfkill_driver);
-}
+module_platform_driver(xo1_rfkill_driver);
MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:xo1-rfkill");
-
-module_init(xo1_rfkill_init);
-module_exit(xo1_rfkill_exit);