Commit 2e7babfa authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86

Pull x86 platform driver updates from Matthew Garrett:
 "A moderate diffstat, but it's almost entirely just moving the
  chromebook driver into its own directory in order to ease ARM support,
  adding back rfkill support to the one Dell laptop model where it's
  expected to work, updates to the Intel IPC driver for hardware I've
  never actually seen and the usual set of small fixes"

[ This actually came in before the merge window closed, and I had just
  missed it because it didn't match my git pull email pattern.  - Linus ]

* 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86: (24 commits)
  x86, wmi fix modalias_show return values
  ipc: Added support for IPC interrupt mode
  ipc: Handle error conditions in ipc command
  ipc: Enabled ipc support for additional intel platforms
  ipc: Added platform data structure
  thinkpad_acpi: Fix build error when CONFIG_SND_MAX_CARDS > 32
  platform: add chrome platform directory
  hp-wmi: detect "2009 BIOS or later" flag by WMI 0x0d for wireless cmd
  dell-wmi: Add KEY_MICMUTE to bios_to_linux_keycode
  platform:x86: Remove OOM message after input_allocate_device
  sony-laptop: fixe typos in sony_laptop_input_keycode_map
  sony-laptop: warn on multiple KBD backlight handles
  dell-laptop: Only enable rfkill functionality on laptops with a hw killswitch
  dell-laptop: Add a force_rfkill module parameter
  dell-laptop: Wait less long before updating rfkill after an rfkill keypress
  dell-laptop: Do not skip setting blocked bit rfkill_set while hw-blocked
  dell-laptop: Sync current block state to BIOS on hw switch change
  dell-laptop: Allow changing the sw_state while the radio is blocked by hw
  dell-laptop: Don't read-back sw_state on machines with a hardware switch
  dell-laptop: Don't set sw_state from the query callback
  ...
parents 6ce4eac1 a80e1053
...@@ -2142,6 +2142,11 @@ L: linux-usb@vger.kernel.org ...@@ -2142,6 +2142,11 @@ L: linux-usb@vger.kernel.org
S: Maintained S: Maintained
F: drivers/usb/chipidea/ F: drivers/usb/chipidea/
CHROME HARDWARE PLATFORM SUPPORT
M: Olof Johansson <olof@lixom.net>
S: Maintained
F: drivers/platform/chrome/
CISCO VIC ETHERNET NIC DRIVER CISCO VIC ETHERNET NIC DRIVER
M: Christian Benvenuti <benve@cisco.com> M: Christian Benvenuti <benve@cisco.com>
M: Sujith Sankar <ssujith@cisco.com> M: Sujith Sankar <ssujith@cisco.com>
......
...@@ -5,3 +5,4 @@ if GOLDFISH ...@@ -5,3 +5,4 @@ if GOLDFISH
source "drivers/platform/goldfish/Kconfig" source "drivers/platform/goldfish/Kconfig"
endif endif
source "drivers/platform/chrome/Kconfig"
...@@ -5,3 +5,4 @@ ...@@ -5,3 +5,4 @@
obj-$(CONFIG_X86) += x86/ obj-$(CONFIG_X86) += x86/
obj-$(CONFIG_OLPC) += olpc/ obj-$(CONFIG_OLPC) += olpc/
obj-$(CONFIG_GOLDFISH) += goldfish/ obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
#
# Platform support for Chrome OS hardware (Chromebooks and Chromeboxes)
#
menuconfig CHROME_PLATFORMS
bool "Platform support for Chrome hardware"
depends on X86
---help---
Say Y here to get to see options for platform support for
various Chromebooks and Chromeboxes. This option alone does
not add any kernel code.
If you say N, all options in this submenu will be skipped and disabled.
if CHROME_PLATFORMS
config CHROMEOS_LAPTOP
tristate "Chrome OS Laptop"
depends on I2C
depends on DMI
---help---
This driver instantiates i2c and smbus devices such as
light sensors and touchpads.
If you have a supported Chromebook, choose Y or M here.
The module will be called chromeos_laptop.
endif # CHROMEOS_PLATFORMS
obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
...@@ -79,17 +79,6 @@ config ASUS_LAPTOP ...@@ -79,17 +79,6 @@ config ASUS_LAPTOP
If you have an ACPI-compatible ASUS laptop, say Y or M here. If you have an ACPI-compatible ASUS laptop, say Y or M here.
config CHROMEOS_LAPTOP
tristate "Chrome OS Laptop"
depends on I2C
depends on DMI
---help---
This driver instantiates i2c and smbus devices such as
light sensors and touchpads.
If you have a supported Chromebook, choose Y or M here.
The module will be called chromeos_laptop.
config DELL_LAPTOP config DELL_LAPTOP
tristate "Dell Laptop Extras" tristate "Dell Laptop Extras"
depends on X86 depends on X86
......
...@@ -50,7 +50,6 @@ obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o ...@@ -50,7 +50,6 @@ obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
obj-$(CONFIG_INTEL_RST) += intel-rst.o obj-$(CONFIG_INTEL_RST) += intel-rst.o
obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o
......
...@@ -1494,10 +1494,9 @@ static int asus_input_init(struct asus_laptop *asus) ...@@ -1494,10 +1494,9 @@ static int asus_input_init(struct asus_laptop *asus)
int error; int error;
input = input_allocate_device(); input = input_allocate_device();
if (!input) { if (!input)
pr_warn("Unable to allocate input device\n");
return -ENOMEM; return -ENOMEM;
}
input->name = "Asus Laptop extra buttons"; input->name = "Asus Laptop extra buttons";
input->phys = ASUS_LAPTOP_FILE "/input0"; input->phys = ASUS_LAPTOP_FILE "/input0";
input->id.bustype = BUS_HOST; input->id.bustype = BUS_HOST;
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/rfkill.h>
#include <linux/power_supply.h> #include <linux/power_supply.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/mm.h> #include <linux/mm.h>
...@@ -89,6 +90,13 @@ static struct platform_driver platform_driver = { ...@@ -89,6 +90,13 @@ static struct platform_driver platform_driver = {
static struct platform_device *platform_device; static struct platform_device *platform_device;
static struct backlight_device *dell_backlight_device; static struct backlight_device *dell_backlight_device;
static struct rfkill *wifi_rfkill;
static struct rfkill *bluetooth_rfkill;
static struct rfkill *wwan_rfkill;
static bool force_rfkill;
module_param(force_rfkill, bool, 0444);
MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models");
static const struct dmi_system_id dell_device_table[] __initconst = { static const struct dmi_system_id dell_device_table[] __initconst = {
{ {
...@@ -355,6 +363,108 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, ...@@ -355,6 +363,108 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
return buffer; return buffer;
} }
/* Derived from information in DellWirelessCtl.cpp:
Class 17, select 11 is radio control. It returns an array of 32-bit values.
Input byte 0 = 0: Wireless information
result[0]: return code
result[1]:
Bit 0: Hardware switch supported
Bit 1: Wifi locator supported
Bit 2: Wifi is supported
Bit 3: Bluetooth is supported
Bit 4: WWAN is supported
Bit 5: Wireless keyboard supported
Bits 6-7: Reserved
Bit 8: Wifi is installed
Bit 9: Bluetooth is installed
Bit 10: WWAN is installed
Bits 11-15: Reserved
Bit 16: Hardware switch is on
Bit 17: Wifi is blocked
Bit 18: Bluetooth is blocked
Bit 19: WWAN is blocked
Bits 20-31: Reserved
result[2]: NVRAM size in bytes
result[3]: NVRAM format version number
Input byte 0 = 2: Wireless switch configuration
result[0]: return code
result[1]:
Bit 0: Wifi controlled by switch
Bit 1: Bluetooth controlled by switch
Bit 2: WWAN controlled by switch
Bits 3-6: Reserved
Bit 7: Wireless switch config locked
Bit 8: Wifi locator enabled
Bits 9-14: Reserved
Bit 15: Wifi locator setting locked
Bits 16-31: Reserved
*/
static int dell_rfkill_set(void *data, bool blocked)
{
int disable = blocked ? 1 : 0;
unsigned long radio = (unsigned long)data;
int hwswitch_bit = (unsigned long)data - 1;
get_buffer();
dell_send_request(buffer, 17, 11);
/* If the hardware switch controls this radio, and the hardware
switch is disabled, always disable the radio */
if ((hwswitch_state & BIT(hwswitch_bit)) &&
!(buffer->output[1] & BIT(16)))
disable = 1;
buffer->input[0] = (1 | (radio<<8) | (disable << 16));
dell_send_request(buffer, 17, 11);
release_buffer();
return 0;
}
/* Must be called with the buffer held */
static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
int status)
{
if (status & BIT(0)) {
/* Has hw-switch, sync sw_state to BIOS */
int block = rfkill_blocked(rfkill);
buffer->input[0] = (1 | (radio << 8) | (block << 16));
dell_send_request(buffer, 17, 11);
} else {
/* No hw-switch, sync BIOS state to sw_state */
rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
}
}
static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
int status)
{
if (hwswitch_state & (BIT(radio - 1)))
rfkill_set_hw_state(rfkill, !(status & BIT(16)));
}
static void dell_rfkill_query(struct rfkill *rfkill, void *data)
{
int status;
get_buffer();
dell_send_request(buffer, 17, 11);
status = buffer->output[1];
dell_rfkill_update_hw_state(rfkill, (unsigned long)data, status);
release_buffer();
}
static const struct rfkill_ops dell_rfkill_ops = {
.set_block = dell_rfkill_set,
.query = dell_rfkill_query,
};
static struct dentry *dell_laptop_dir; static struct dentry *dell_laptop_dir;
static int dell_debugfs_show(struct seq_file *s, void *data) static int dell_debugfs_show(struct seq_file *s, void *data)
...@@ -424,6 +534,136 @@ static const struct file_operations dell_debugfs_fops = { ...@@ -424,6 +534,136 @@ static const struct file_operations dell_debugfs_fops = {
.release = single_release, .release = single_release,
}; };
static void dell_update_rfkill(struct work_struct *ignored)
{
int status;
get_buffer();
dell_send_request(buffer, 17, 11);
status = buffer->output[1];
if (wifi_rfkill) {
dell_rfkill_update_hw_state(wifi_rfkill, 1, status);
dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
}
if (bluetooth_rfkill) {
dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status);
dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
}
if (wwan_rfkill) {
dell_rfkill_update_hw_state(wwan_rfkill, 3, status);
dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
}
release_buffer();
}
static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
static int __init dell_setup_rfkill(void)
{
int status;
int ret;
const char *product;
/*
* rfkill causes trouble on various non Latitudes, according to Dell
* actually testing the rfkill functionality is only done on Latitudes.
*/
product = dmi_get_system_info(DMI_PRODUCT_NAME);
if (!force_rfkill && (!product || strncmp(product, "Latitude", 8)))
return 0;
get_buffer();
dell_send_request(buffer, 17, 11);
status = buffer->output[1];
buffer->input[0] = 0x2;
dell_send_request(buffer, 17, 11);
hwswitch_state = buffer->output[1];
release_buffer();
if (!(status & BIT(0))) {
if (force_rfkill) {
/* No hwsitch, clear all hw-controlled bits */
hwswitch_state &= ~7;
} else {
/* rfkill is only tested on laptops with a hwswitch */
return 0;
}
}
if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
RFKILL_TYPE_WLAN,
&dell_rfkill_ops, (void *) 1);
if (!wifi_rfkill) {
ret = -ENOMEM;
goto err_wifi;
}
ret = rfkill_register(wifi_rfkill);
if (ret)
goto err_wifi;
}
if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
&platform_device->dev,
RFKILL_TYPE_BLUETOOTH,
&dell_rfkill_ops, (void *) 2);
if (!bluetooth_rfkill) {
ret = -ENOMEM;
goto err_bluetooth;
}
ret = rfkill_register(bluetooth_rfkill);
if (ret)
goto err_bluetooth;
}
if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
wwan_rfkill = rfkill_alloc("dell-wwan",
&platform_device->dev,
RFKILL_TYPE_WWAN,
&dell_rfkill_ops, (void *) 3);
if (!wwan_rfkill) {
ret = -ENOMEM;
goto err_wwan;
}
ret = rfkill_register(wwan_rfkill);
if (ret)
goto err_wwan;
}
return 0;
err_wwan:
rfkill_destroy(wwan_rfkill);
if (bluetooth_rfkill)
rfkill_unregister(bluetooth_rfkill);
err_bluetooth:
rfkill_destroy(bluetooth_rfkill);
if (wifi_rfkill)
rfkill_unregister(wifi_rfkill);
err_wifi:
rfkill_destroy(wifi_rfkill);
return ret;
}
static void dell_cleanup_rfkill(void)
{
if (wifi_rfkill) {
rfkill_unregister(wifi_rfkill);
rfkill_destroy(wifi_rfkill);
}
if (bluetooth_rfkill) {
rfkill_unregister(bluetooth_rfkill);
rfkill_destroy(bluetooth_rfkill);
}
if (wwan_rfkill) {
rfkill_unregister(wwan_rfkill);
rfkill_destroy(wwan_rfkill);
}
}
static int dell_send_intensity(struct backlight_device *bd) static int dell_send_intensity(struct backlight_device *bd)
{ {
int ret = 0; int ret = 0;
...@@ -515,6 +755,30 @@ static void touchpad_led_exit(void) ...@@ -515,6 +755,30 @@ static void touchpad_led_exit(void)
led_classdev_unregister(&touchpad_led); led_classdev_unregister(&touchpad_led);
} }
static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
{
static bool extended;
if (str & 0x20)
return false;
if (unlikely(data == 0xe0)) {
extended = true;
return false;
} else if (unlikely(extended)) {
switch (data) {
case 0x8:
schedule_delayed_work(&dell_rfkill_work,
round_jiffies_relative(HZ / 4));
break;
}
extended = false;
}
return false;
}
static int __init dell_init(void) static int __init dell_init(void)
{ {
int max_intensity = 0; int max_intensity = 0;
...@@ -557,10 +821,26 @@ static int __init dell_init(void) ...@@ -557,10 +821,26 @@ static int __init dell_init(void)
} }
buffer = page_address(bufferpage); buffer = page_address(bufferpage);
ret = dell_setup_rfkill();
if (ret) {
pr_warn("Unable to setup rfkill\n");
goto fail_rfkill;
}
ret = i8042_install_filter(dell_laptop_i8042_filter);
if (ret) {
pr_warn("Unable to install key filter\n");
goto fail_filter;
}
if (quirks && quirks->touchpad_led) if (quirks && quirks->touchpad_led)
touchpad_led_init(&platform_device->dev); touchpad_led_init(&platform_device->dev);
dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
if (dell_laptop_dir != NULL)
debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
&dell_debugfs_fops);
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
/* In the event of an ACPI backlight being available, don't /* In the event of an ACPI backlight being available, don't
...@@ -603,6 +883,11 @@ static int __init dell_init(void) ...@@ -603,6 +883,11 @@ static int __init dell_init(void)
return 0; return 0;
fail_backlight: fail_backlight:
i8042_remove_filter(dell_laptop_i8042_filter);
cancel_delayed_work_sync(&dell_rfkill_work);
fail_filter:
dell_cleanup_rfkill();
fail_rfkill:
free_page((unsigned long)bufferpage); free_page((unsigned long)bufferpage);
fail_buffer: fail_buffer:
platform_device_del(platform_device); platform_device_del(platform_device);
...@@ -620,7 +905,10 @@ static void __exit dell_exit(void) ...@@ -620,7 +905,10 @@ static void __exit dell_exit(void)
debugfs_remove_recursive(dell_laptop_dir); debugfs_remove_recursive(dell_laptop_dir);
if (quirks && quirks->touchpad_led) if (quirks && quirks->touchpad_led)
touchpad_led_exit(); touchpad_led_exit();
i8042_remove_filter(dell_laptop_i8042_filter);
cancel_delayed_work_sync(&dell_rfkill_work);
backlight_device_unregister(dell_backlight_device); backlight_device_unregister(dell_backlight_device);
dell_cleanup_rfkill();
if (platform_device) { if (platform_device) {
platform_device_unregister(platform_device); platform_device_unregister(platform_device);
platform_driver_unregister(&platform_driver); platform_driver_unregister(&platform_driver);
......
...@@ -130,7 +130,8 @@ static const u16 bios_to_linux_keycode[256] __initconst = { ...@@ -130,7 +130,8 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
KEY_BRIGHTNESSUP, KEY_UNKNOWN, KEY_KBDILLUMTOGGLE, KEY_BRIGHTNESSUP, KEY_UNKNOWN, KEY_KBDILLUMTOGGLE,
KEY_UNKNOWN, KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, KEY_PROG2, KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, KEY_PROG2,
KEY_UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_MICMUTE,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
...@@ -139,8 +140,8 @@ static const u16 bios_to_linux_keycode[256] __initconst = { ...@@ -139,8 +140,8 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
KEY_PROG3 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PROG3
}; };
static struct input_dev *dell_wmi_input_dev; static struct input_dev *dell_wmi_input_dev;
......
...@@ -1203,10 +1203,8 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc) ...@@ -1203,10 +1203,8 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc)
int error; int error;
input = input_allocate_device(); input = input_allocate_device();
if (!input) { if (!input)
pr_info("Unable to allocate input device\n");
return -ENOMEM; return -ENOMEM;
}
input->name = "Asus EeePC extra buttons"; input->name = "Asus EeePC extra buttons";
input->phys = EEEPC_LAPTOP_FILE "/input0"; input->phys = EEEPC_LAPTOP_FILE "/input0";
......
...@@ -54,6 +54,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); ...@@ -54,6 +54,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
#define HPWMI_HARDWARE_QUERY 0x4 #define HPWMI_HARDWARE_QUERY 0x4
#define HPWMI_WIRELESS_QUERY 0x5 #define HPWMI_WIRELESS_QUERY 0x5
#define HPWMI_HOTKEY_QUERY 0xc #define HPWMI_HOTKEY_QUERY 0xc
#define HPWMI_FEATURE_QUERY 0xd
#define HPWMI_WIRELESS2_QUERY 0x1b #define HPWMI_WIRELESS2_QUERY 0x1b
#define HPWMI_POSTCODEERROR_QUERY 0x2a #define HPWMI_POSTCODEERROR_QUERY 0x2a
...@@ -292,6 +293,17 @@ static int hp_wmi_tablet_state(void) ...@@ -292,6 +293,17 @@ static int hp_wmi_tablet_state(void)
return (state & 0x4) ? 1 : 0; return (state & 0x4) ? 1 : 0;
} }
static int hp_wmi_bios_2009_later(void)
{
int state = 0;
int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, 0, &state,
sizeof(state), sizeof(state));
if (ret)
return ret;
return (state & 0x10) ? 1 : 0;
}
static int hp_wmi_set_block(void *data, bool blocked) static int hp_wmi_set_block(void *data, bool blocked)
{ {
enum hp_wmi_radio r = (enum hp_wmi_radio) data; enum hp_wmi_radio r = (enum hp_wmi_radio) data;
...@@ -871,7 +883,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) ...@@ -871,7 +883,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
gps_rfkill = NULL; gps_rfkill = NULL;
rfkill2_count = 0; rfkill2_count = 0;
if (hp_wmi_rfkill_setup(device)) if (hp_wmi_bios_2009_later() || hp_wmi_rfkill_setup(device))
hp_wmi_rfkill2_setup(device); hp_wmi_rfkill2_setup(device);
err = device_create_file(&device->dev, &dev_attr_display); err = device_create_file(&device->dev, &dev_attr_display);
......
...@@ -570,10 +570,8 @@ static int ideapad_input_init(struct ideapad_private *priv) ...@@ -570,10 +570,8 @@ static int ideapad_input_init(struct ideapad_private *priv)
int error; int error;
inputdev = input_allocate_device(); inputdev = input_allocate_device();
if (!inputdev) { if (!inputdev)
pr_info("Unable to allocate input device\n");
return -ENOMEM; return -ENOMEM;
}
inputdev->name = "Ideapad extra buttons"; inputdev->name = "Ideapad extra buttons";
inputdev->phys = "ideapad/input0"; inputdev->phys = "ideapad/input0";
......
...@@ -66,10 +66,8 @@ static int mfld_pb_probe(struct platform_device *pdev) ...@@ -66,10 +66,8 @@ static int mfld_pb_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
input = input_allocate_device(); input = input_allocate_device();
if (!input) { if (!input)
dev_err(&pdev->dev, "Input device allocation error\n");
return -ENOMEM; return -ENOMEM;
}
input->name = pdev->name; input->name = pdev->name;
input->phys = "power-button/input0"; input->phys = "power-button/input0";
......
...@@ -58,12 +58,56 @@ ...@@ -58,12 +58,56 @@
* message handler is called within firmware. * message handler is called within firmware.
*/ */
#define IPC_BASE_ADDR 0xFF11C000 /* IPC1 base register address */
#define IPC_MAX_ADDR 0x100 /* Maximum IPC regisers */
#define IPC_WWBUF_SIZE 20 /* IPC Write buffer Size */ #define IPC_WWBUF_SIZE 20 /* IPC Write buffer Size */
#define IPC_RWBUF_SIZE 20 /* IPC Read buffer Size */ #define IPC_RWBUF_SIZE 20 /* IPC Read buffer Size */
#define IPC_I2C_BASE 0xFF12B000 /* I2C control register base address */ #define IPC_IOC 0x100 /* IPC command register IOC bit */
#define IPC_I2C_MAX_ADDR 0x10 /* Maximum I2C regisers */
enum {
SCU_IPC_LINCROFT,
SCU_IPC_PENWELL,
SCU_IPC_CLOVERVIEW,
SCU_IPC_TANGIER,
};
/* intel scu ipc driver data*/
struct intel_scu_ipc_pdata_t {
u32 ipc_base;
u32 i2c_base;
u32 ipc_len;
u32 i2c_len;
u8 irq_mode;
};
static struct intel_scu_ipc_pdata_t intel_scu_ipc_pdata[] = {
[SCU_IPC_LINCROFT] = {
.ipc_base = 0xff11c000,
.i2c_base = 0xff12b000,
.ipc_len = 0x100,
.i2c_len = 0x10,
.irq_mode = 0,
},
[SCU_IPC_PENWELL] = {
.ipc_base = 0xff11c000,
.i2c_base = 0xff12b000,
.ipc_len = 0x100,
.i2c_len = 0x10,
.irq_mode = 1,
},
[SCU_IPC_CLOVERVIEW] = {
.ipc_base = 0xff11c000,
.i2c_base = 0xff12b000,
.ipc_len = 0x100,
.i2c_len = 0x10,
.irq_mode = 1,
},
[SCU_IPC_TANGIER] = {
.ipc_base = 0xff009000,
.i2c_base = 0xff00d000,
.ipc_len = 0x100,
.i2c_len = 0x10,
.irq_mode = 0,
},
};
static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id); static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id);
static void ipc_remove(struct pci_dev *pdev); static void ipc_remove(struct pci_dev *pdev);
...@@ -72,6 +116,8 @@ struct intel_scu_ipc_dev { ...@@ -72,6 +116,8 @@ struct intel_scu_ipc_dev {
struct pci_dev *pdev; struct pci_dev *pdev;
void __iomem *ipc_base; void __iomem *ipc_base;
void __iomem *i2c_base; void __iomem *i2c_base;
struct completion cmd_complete;
u8 irq_mode;
}; };
static struct intel_scu_ipc_dev ipcdev; /* Only one for now */ static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
...@@ -98,6 +144,10 @@ static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */ ...@@ -98,6 +144,10 @@ static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
*/ */
static inline void ipc_command(u32 cmd) /* Send ipc command */ static inline void ipc_command(u32 cmd) /* Send ipc command */
{ {
if (ipcdev.irq_mode) {
reinit_completion(&ipcdev.cmd_complete);
writel(cmd | IPC_IOC, ipcdev.ipc_base);
}
writel(cmd, ipcdev.ipc_base); writel(cmd, ipcdev.ipc_base);
} }
...@@ -156,6 +206,30 @@ static inline int busy_loop(void) /* Wait till scu status is busy */ ...@@ -156,6 +206,30 @@ static inline int busy_loop(void) /* Wait till scu status is busy */
return 0; return 0;
} }
/* Wait till ipc ioc interrupt is received or timeout in 3 HZ */
static inline int ipc_wait_for_interrupt(void)
{
int status;
if (!wait_for_completion_timeout(&ipcdev.cmd_complete, 3 * HZ)) {
struct device *dev = &ipcdev.pdev->dev;
dev_err(dev, "IPC timed out\n");
return -ETIMEDOUT;
}
status = ipc_read_status();
if ((status >> 1) & 1)
return -EIO;
return 0;
}
int intel_scu_ipc_check_status(void)
{
return ipcdev.irq_mode ? ipc_wait_for_interrupt() : busy_loop();
}
/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */ /* 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) static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
{ {
...@@ -196,8 +270,8 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) ...@@ -196,8 +270,8 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
ipc_command(4 << 16 | id << 12 | 0 << 8 | op); ipc_command(4 << 16 | id << 12 | 0 << 8 | op);
} }
err = busy_loop(); err = intel_scu_ipc_check_status();
if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */
/* Workaround: values are read as 0 without memcpy_fromio */ /* Workaround: values are read as 0 without memcpy_fromio */
memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16); memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16);
for (nc = 0; nc < count; nc++) for (nc = 0; nc < count; nc++)
...@@ -391,7 +465,7 @@ int intel_scu_ipc_simple_command(int cmd, int sub) ...@@ -391,7 +465,7 @@ int intel_scu_ipc_simple_command(int cmd, int sub)
return -ENODEV; return -ENODEV;
} }
ipc_command(sub << 12 | cmd); ipc_command(sub << 12 | cmd);
err = busy_loop(); err = intel_scu_ipc_check_status();
mutex_unlock(&ipclock); mutex_unlock(&ipclock);
return err; return err;
} }
...@@ -425,10 +499,12 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, ...@@ -425,10 +499,12 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
ipc_data_writel(*in++, 4 * i); ipc_data_writel(*in++, 4 * i);
ipc_command((inlen << 16) | (sub << 12) | cmd); ipc_command((inlen << 16) | (sub << 12) | cmd);
err = busy_loop(); err = intel_scu_ipc_check_status();
if (!err) {
for (i = 0; i < outlen; i++) for (i = 0; i < outlen; i++)
*out++ = ipc_data_readl(4 * i); *out++ = ipc_data_readl(4 * i);
}
mutex_unlock(&ipclock); mutex_unlock(&ipclock);
return err; return err;
...@@ -491,6 +567,9 @@ EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl); ...@@ -491,6 +567,9 @@ EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl);
*/ */
static irqreturn_t ioc(int irq, void *dev_id) static irqreturn_t ioc(int irq, void *dev_id)
{ {
if (ipcdev.irq_mode)
complete(&ipcdev.cmd_complete);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -504,13 +583,18 @@ static irqreturn_t ioc(int irq, void *dev_id) ...@@ -504,13 +583,18 @@ static irqreturn_t ioc(int irq, void *dev_id)
*/ */
static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)
{ {
int err; int err, pid;
struct intel_scu_ipc_pdata_t *pdata;
resource_size_t pci_resource; resource_size_t pci_resource;
if (ipcdev.pdev) /* We support only one SCU */ if (ipcdev.pdev) /* We support only one SCU */
return -EBUSY; return -EBUSY;
pid = id->driver_data;
pdata = &intel_scu_ipc_pdata[pid];
ipcdev.pdev = pci_dev_get(dev); ipcdev.pdev = pci_dev_get(dev);
ipcdev.irq_mode = pdata->irq_mode;
err = pci_enable_device(dev); err = pci_enable_device(dev);
if (err) if (err)
...@@ -524,14 +608,16 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) ...@@ -524,14 +608,16 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (!pci_resource) if (!pci_resource)
return -ENOMEM; return -ENOMEM;
init_completion(&ipcdev.cmd_complete);
if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev)) if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev))
return -EBUSY; return -EBUSY;
ipcdev.ipc_base = ioremap_nocache(IPC_BASE_ADDR, IPC_MAX_ADDR); ipcdev.ipc_base = ioremap_nocache(pdata->ipc_base, pdata->ipc_len);
if (!ipcdev.ipc_base) if (!ipcdev.ipc_base)
return -ENOMEM; return -ENOMEM;
ipcdev.i2c_base = ioremap_nocache(IPC_I2C_BASE, IPC_I2C_MAX_ADDR); ipcdev.i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);
if (!ipcdev.i2c_base) { if (!ipcdev.i2c_base) {
iounmap(ipcdev.ipc_base); iounmap(ipcdev.ipc_base);
return -ENOMEM; return -ENOMEM;
...@@ -564,7 +650,10 @@ static void ipc_remove(struct pci_dev *pdev) ...@@ -564,7 +650,10 @@ static void ipc_remove(struct pci_dev *pdev)
} }
static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { static DEFINE_PCI_DEVICE_TABLE(pci_ids) = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)}, {PCI_VDEVICE(INTEL, 0x082a), SCU_IPC_LINCROFT},
{PCI_VDEVICE(INTEL, 0x080e), SCU_IPC_PENWELL},
{PCI_VDEVICE(INTEL, 0x08ea), SCU_IPC_CLOVERVIEW},
{PCI_VDEVICE(INTEL, 0x11a0), SCU_IPC_TANGIER},
{ 0,} { 0,}
}; };
MODULE_DEVICE_TABLE(pci, pci_ids); MODULE_DEVICE_TABLE(pci, pci_ids);
......
...@@ -490,11 +490,8 @@ static int acpi_pcc_init_input(struct pcc_acpi *pcc) ...@@ -490,11 +490,8 @@ static int acpi_pcc_init_input(struct pcc_acpi *pcc)
int error; int error;
input_dev = input_allocate_device(); input_dev = input_allocate_device();
if (!input_dev) { if (!input_dev)
ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Couldn't allocate input device for hotkey"));
return -ENOMEM; return -ENOMEM;
}
input_dev->name = ACPI_PCC_DRIVER_NAME; input_dev->name = ACPI_PCC_DRIVER_NAME;
input_dev->phys = ACPI_PCC_INPUT_PHYS; input_dev->phys = ACPI_PCC_INPUT_PHYS;
......
...@@ -145,7 +145,8 @@ static void sony_nc_thermal_resume(void); ...@@ -145,7 +145,8 @@ static void sony_nc_thermal_resume(void);
#endif #endif
static int sony_nc_kbd_backlight_setup(struct platform_device *pd, static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
unsigned int handle); unsigned int handle);
static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd); static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd,
unsigned int handle);
static int sony_nc_battery_care_setup(struct platform_device *pd, static int sony_nc_battery_care_setup(struct platform_device *pd,
unsigned int handle); unsigned int handle);
...@@ -304,8 +305,8 @@ static int sony_laptop_input_keycode_map[] = { ...@@ -304,8 +305,8 @@ static int sony_laptop_input_keycode_map[] = {
KEY_FN_F10, /* 14 SONYPI_EVENT_FNKEY_F10 */ KEY_FN_F10, /* 14 SONYPI_EVENT_FNKEY_F10 */
KEY_FN_F11, /* 15 SONYPI_EVENT_FNKEY_F11 */ KEY_FN_F11, /* 15 SONYPI_EVENT_FNKEY_F11 */
KEY_FN_F12, /* 16 SONYPI_EVENT_FNKEY_F12 */ KEY_FN_F12, /* 16 SONYPI_EVENT_FNKEY_F12 */
KEY_FN_F1, /* 17 SONYPI_EVENT_FNKEY_1 */ KEY_FN_1, /* 17 SONYPI_EVENT_FNKEY_1 */
KEY_FN_F2, /* 18 SONYPI_EVENT_FNKEY_2 */ KEY_FN_2, /* 18 SONYPI_EVENT_FNKEY_2 */
KEY_FN_D, /* 19 SONYPI_EVENT_FNKEY_D */ KEY_FN_D, /* 19 SONYPI_EVENT_FNKEY_D */
KEY_FN_E, /* 20 SONYPI_EVENT_FNKEY_E */ KEY_FN_E, /* 20 SONYPI_EVENT_FNKEY_E */
KEY_FN_F, /* 21 SONYPI_EVENT_FNKEY_F */ KEY_FN_F, /* 21 SONYPI_EVENT_FNKEY_F */
...@@ -1444,7 +1445,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd) ...@@ -1444,7 +1445,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
case 0x014b: case 0x014b:
case 0x014c: case 0x014c:
case 0x0163: case 0x0163:
sony_nc_kbd_backlight_cleanup(pd); sony_nc_kbd_backlight_cleanup(pd, handle);
break; break;
default: default:
continue; continue;
...@@ -1822,6 +1823,12 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd, ...@@ -1822,6 +1823,12 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
int result; int result;
int ret = 0; int ret = 0;
if (kbdbl_ctl) {
pr_warn("handle 0x%.4x: keyboard backlight setup already done for 0x%.4x\n",
handle, kbdbl_ctl->handle);
return -EBUSY;
}
/* verify the kbd backlight presence, these handles are not used for /* verify the kbd backlight presence, these handles are not used for
* keyboard backlight only * keyboard backlight only
*/ */
...@@ -1881,9 +1888,10 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd, ...@@ -1881,9 +1888,10 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
return ret; return ret;
} }
static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd) static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd,
unsigned int handle)
{ {
if (kbdbl_ctl) { if (kbdbl_ctl && handle == kbdbl_ctl->handle) {
device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr); device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr);
device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr); device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr);
kfree(kbdbl_ctl); kfree(kbdbl_ctl);
......
...@@ -6438,7 +6438,12 @@ static struct ibm_struct brightness_driver_data = { ...@@ -6438,7 +6438,12 @@ static struct ibm_struct brightness_driver_data = {
#define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control" #define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control"
#define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME #define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME
static int alsa_index = ~((1 << (SNDRV_CARDS - 3)) - 1); /* last three slots */ #if SNDRV_CARDS <= 32
#define DEFAULT_ALSA_IDX ~((1 << (SNDRV_CARDS - 3)) - 1)
#else
#define DEFAULT_ALSA_IDX ~((1 << (32 - 3)) - 1)
#endif
static int alsa_index = DEFAULT_ALSA_IDX; /* last three slots */
static char *alsa_id = "ThinkPadEC"; static char *alsa_id = "ThinkPadEC";
static bool alsa_enable = SNDRV_DEFAULT_ENABLE1; static bool alsa_enable = SNDRV_DEFAULT_ENABLE1;
...@@ -9163,7 +9168,6 @@ static int __init thinkpad_acpi_module_init(void) ...@@ -9163,7 +9168,6 @@ static int __init thinkpad_acpi_module_init(void)
mutex_init(&tpacpi_inputdev_send_mutex); mutex_init(&tpacpi_inputdev_send_mutex);
tpacpi_inputdev = input_allocate_device(); tpacpi_inputdev = input_allocate_device();
if (!tpacpi_inputdev) { if (!tpacpi_inputdev) {
pr_err("unable to allocate input device\n");
thinkpad_acpi_module_exit(); thinkpad_acpi_module_exit();
return -ENOMEM; return -ENOMEM;
} else { } else {
......
...@@ -97,10 +97,8 @@ static int acpi_topstar_init_hkey(struct topstar_hkey *hkey) ...@@ -97,10 +97,8 @@ static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
int error; int error;
input = input_allocate_device(); input = input_allocate_device();
if (!input) { if (!input)
pr_err("Unable to allocate input device\n");
return -ENOMEM; return -ENOMEM;
}
input->name = "Topstar Laptop extra buttons"; input->name = "Topstar Laptop extra buttons";
input->phys = "topstar/input0"; input->phys = "topstar/input0";
......
...@@ -975,10 +975,8 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) ...@@ -975,10 +975,8 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
u32 hci_result; u32 hci_result;
dev->hotkey_dev = input_allocate_device(); dev->hotkey_dev = input_allocate_device();
if (!dev->hotkey_dev) { if (!dev->hotkey_dev)
pr_info("Unable to register input device\n");
return -ENOMEM; return -ENOMEM;
}
dev->hotkey_dev->name = "Toshiba input device"; dev->hotkey_dev->name = "Toshiba input device";
dev->hotkey_dev->phys = "toshiba_acpi/input0"; dev->hotkey_dev->phys = "toshiba_acpi/input0";
......
...@@ -672,8 +672,10 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, ...@@ -672,8 +672,10 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
struct wmi_block *wblock; struct wmi_block *wblock;
wblock = dev_get_drvdata(dev); wblock = dev_get_drvdata(dev);
if (!wblock) if (!wblock) {
return -ENOMEM; strcat(buf, "\n");
return strlen(buf);
}
wmi_gtoa(wblock->gblock.guid, guid_string); wmi_gtoa(wblock->gblock.guid, guid_string);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment