diff options
22 files changed, 477 insertions, 62 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 8285ed4676b6..f216db847022 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2142,6 +2142,11 @@ L: linux-usb@vger.kernel.org S: Maintained 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 M: Christian Benvenuti <benve@cisco.com> M: Sujith Sankar <ssujith@cisco.com> diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig index 69616aeaa966..09fde58b12e0 100644 --- a/drivers/platform/Kconfig +++ b/drivers/platform/Kconfig @@ -5,3 +5,4 @@ if GOLDFISH source "drivers/platform/goldfish/Kconfig" endif +source "drivers/platform/chrome/Kconfig" diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile index 8a44a4cd6d1e..3656b7b17b99 100644 --- a/drivers/platform/Makefile +++ b/drivers/platform/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_X86) += x86/ obj-$(CONFIG_OLPC) += olpc/ obj-$(CONFIG_GOLDFISH) += goldfish/ +obj-$(CONFIG_CHROME_PLATFORMS) += chrome/ diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig new file mode 100644 index 000000000000..b13303e75a34 --- /dev/null +++ b/drivers/platform/chrome/Kconfig @@ -0,0 +1,28 @@ +# +# 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 diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile new file mode 100644 index 000000000000..015e9195e226 --- /dev/null +++ b/drivers/platform/chrome/Makefile @@ -0,0 +1,2 @@ + +obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o diff --git a/drivers/platform/x86/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 3e5b4497a1d0..3e5b4497a1d0 100644 --- a/drivers/platform/x86/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index b51a7460cc49..d9dcd37b5a52 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -79,17 +79,6 @@ config ASUS_LAPTOP 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 tristate "Dell Laptop Extras" depends on X86 diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 5dbe19324351..f0e6aa407ffb 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -50,7 +50,6 @@ 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 -obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o obj-$(CONFIG_INTEL_RST) += intel-rst.o obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 0e9c169b42f8..594323a926cf 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1494,10 +1494,9 @@ static int asus_input_init(struct asus_laptop *asus) int error; input = input_allocate_device(); - if (!input) { - pr_warn("Unable to allocate input device\n"); + if (!input) return -ENOMEM; - } + input->name = "Asus Laptop extra buttons"; input->phys = ASUS_LAPTOP_FILE "/input0"; input->id.bustype = BUS_HOST; diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index bb77e18b3dd4..c608b1d33f4a 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -21,6 +21,7 @@ #include <linux/err.h> #include <linux/dmi.h> #include <linux/io.h> +#include <linux/rfkill.h> #include <linux/power_supply.h> #include <linux/acpi.h> #include <linux/mm.h> @@ -89,6 +90,13 @@ static struct platform_driver platform_driver = { static struct platform_device *platform_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 = { { @@ -355,6 +363,108 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, 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 int dell_debugfs_show(struct seq_file *s, void *data) @@ -424,6 +534,136 @@ static const struct file_operations dell_debugfs_fops = { .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) { int ret = 0; @@ -515,6 +755,30 @@ static void touchpad_led_exit(void) 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) { int max_intensity = 0; @@ -557,10 +821,26 @@ static int __init dell_init(void) } 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) touchpad_led_init(&platform_device->dev); 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 /* In the event of an ACPI backlight being available, don't @@ -603,6 +883,11 @@ static int __init dell_init(void) return 0; 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); fail_buffer: platform_device_del(platform_device); @@ -620,7 +905,10 @@ static void __exit dell_exit(void) debugfs_remove_recursive(dell_laptop_dir); if (quirks && quirks->touchpad_led) touchpad_led_exit(); + i8042_remove_filter(dell_laptop_i8042_filter); + cancel_delayed_work_sync(&dell_rfkill_work); backlight_device_unregister(dell_backlight_device); + dell_cleanup_rfkill(); if (platform_device) { platform_device_unregister(platform_device); platform_driver_unregister(&platform_driver); diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index fa9a2171cc13..60e0900bc117 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -130,7 +130,8 @@ static const u16 bios_to_linux_keycode[256] __initconst = { KEY_BRIGHTNESSUP, KEY_UNKNOWN, KEY_KBDILLUMTOGGLE, KEY_UNKNOWN, KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, 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, @@ -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, - KEY_PROG3 + 0, 0, 0, 0, 0, 0, 0, 0, 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 }; static struct input_dev *dell_wmi_input_dev; diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index aefcc32e5634..dec68e7a99c7 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1203,10 +1203,8 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc) int error; input = input_allocate_device(); - if (!input) { - pr_info("Unable to allocate input device\n"); + if (!input) return -ENOMEM; - } input->name = "Asus EeePC extra buttons"; input->phys = EEEPC_LAPTOP_FILE "/input0"; diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 1c86fa0857c8..8ba8956b5a48 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -54,6 +54,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); #define HPWMI_HARDWARE_QUERY 0x4 #define HPWMI_WIRELESS_QUERY 0x5 #define HPWMI_HOTKEY_QUERY 0xc +#define HPWMI_FEATURE_QUERY 0xd #define HPWMI_WIRELESS2_QUERY 0x1b #define HPWMI_POSTCODEERROR_QUERY 0x2a @@ -292,6 +293,17 @@ static int hp_wmi_tablet_state(void) 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) { 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) gps_rfkill = NULL; 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); err = device_create_file(&device->dev, &dev_attr_display); diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 6788acc22ab9..19ec95147f69 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -570,10 +570,8 @@ static int ideapad_input_init(struct ideapad_private *priv) int error; inputdev = input_allocate_device(); - if (!inputdev) { - pr_info("Unable to allocate input device\n"); + if (!inputdev) return -ENOMEM; - } inputdev->name = "Ideapad extra buttons"; inputdev->phys = "ideapad/input0"; diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index 6b18aba82cfa..8d6775266d66 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -66,10 +66,8 @@ static int mfld_pb_probe(struct platform_device *pdev) return -EINVAL; input = input_allocate_device(); - if (!input) { - dev_err(&pdev->dev, "Input device allocation error\n"); + if (!input) return -ENOMEM; - } input->name = pdev->name; input->phys = "power-button/input0"; diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index d654f831410d..60ea476a9130 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -58,12 +58,56 @@ * 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_RWBUF_SIZE 20 /* IPC Read buffer Size */ -#define IPC_I2C_BASE 0xFF12B000 /* I2C control register base address */ -#define IPC_I2C_MAX_ADDR 0x10 /* Maximum I2C regisers */ +#define IPC_IOC 0x100 /* IPC command register IOC bit */ + +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 void ipc_remove(struct pci_dev *pdev); @@ -72,6 +116,8 @@ struct intel_scu_ipc_dev { struct pci_dev *pdev; void __iomem *ipc_base; void __iomem *i2c_base; + struct completion cmd_complete; + u8 irq_mode; }; 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 */ */ 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); } @@ -156,6 +206,30 @@ static inline int busy_loop(void) /* Wait till scu status is busy */ 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 */ 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); } - err = busy_loop(); - if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ + err = intel_scu_ipc_check_status(); + if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ /* Workaround: values are read as 0 without memcpy_fromio */ memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16); for (nc = 0; nc < count; nc++) @@ -391,7 +465,7 @@ int intel_scu_ipc_simple_command(int cmd, int sub) return -ENODEV; } ipc_command(sub << 12 | cmd); - err = busy_loop(); + err = intel_scu_ipc_check_status(); mutex_unlock(&ipclock); return err; } @@ -425,10 +499,12 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, ipc_data_writel(*in++, 4 * i); ipc_command((inlen << 16) | (sub << 12) | cmd); - err = busy_loop(); + err = intel_scu_ipc_check_status(); - for (i = 0; i < outlen; i++) - *out++ = ipc_data_readl(4 * i); + if (!err) { + for (i = 0; i < outlen; i++) + *out++ = ipc_data_readl(4 * i); + } mutex_unlock(&ipclock); return err; @@ -491,6 +567,9 @@ EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl); */ static irqreturn_t ioc(int irq, void *dev_id) { + if (ipcdev.irq_mode) + complete(&ipcdev.cmd_complete); + return IRQ_HANDLED; } @@ -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) { - int err; + int err, pid; + struct intel_scu_ipc_pdata_t *pdata; resource_size_t pci_resource; if (ipcdev.pdev) /* We support only one SCU */ return -EBUSY; + pid = id->driver_data; + pdata = &intel_scu_ipc_pdata[pid]; + ipcdev.pdev = pci_dev_get(dev); + ipcdev.irq_mode = pdata->irq_mode; err = pci_enable_device(dev); if (err) @@ -524,14 +608,16 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id) if (!pci_resource) return -ENOMEM; + init_completion(&ipcdev.cmd_complete); + if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev)) 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) 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) { iounmap(ipcdev.ipc_base); return -ENOMEM; @@ -564,7 +650,10 @@ static void ipc_remove(struct pci_dev *pdev) } 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,} }; MODULE_DEVICE_TABLE(pci, pci_ids); diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 10d12b221601..3008fd20572e 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -490,11 +490,8 @@ static int acpi_pcc_init_input(struct pcc_acpi *pcc) int error; input_dev = input_allocate_device(); - if (!input_dev) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Couldn't allocate input device for hotkey")); + if (!input_dev) return -ENOMEM; - } input_dev->name = ACPI_PCC_DRIVER_NAME; input_dev->phys = ACPI_PCC_INPUT_PHYS; diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 47caab0ea7a1..c45c60f7ea2f 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -145,7 +145,8 @@ static void sony_nc_thermal_resume(void); #endif static int sony_nc_kbd_backlight_setup(struct platform_device *pd, 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, unsigned int handle); @@ -304,8 +305,8 @@ static int sony_laptop_input_keycode_map[] = { KEY_FN_F10, /* 14 SONYPI_EVENT_FNKEY_F10 */ KEY_FN_F11, /* 15 SONYPI_EVENT_FNKEY_F11 */ KEY_FN_F12, /* 16 SONYPI_EVENT_FNKEY_F12 */ - KEY_FN_F1, /* 17 SONYPI_EVENT_FNKEY_1 */ - KEY_FN_F2, /* 18 SONYPI_EVENT_FNKEY_2 */ + KEY_FN_1, /* 17 SONYPI_EVENT_FNKEY_1 */ + KEY_FN_2, /* 18 SONYPI_EVENT_FNKEY_2 */ KEY_FN_D, /* 19 SONYPI_EVENT_FNKEY_D */ KEY_FN_E, /* 20 SONYPI_EVENT_FNKEY_E */ KEY_FN_F, /* 21 SONYPI_EVENT_FNKEY_F */ @@ -1444,7 +1445,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd) case 0x014b: case 0x014c: case 0x0163: - sony_nc_kbd_backlight_cleanup(pd); + sony_nc_kbd_backlight_cleanup(pd, handle); break; default: continue; @@ -1822,6 +1823,12 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd, int result; 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 * keyboard backlight only */ @@ -1881,9 +1888,10 @@ outkzalloc: 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->timeout_attr); kfree(kbdbl_ctl); diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 05e046aa5e31..58b0274d24cc 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -6438,7 +6438,12 @@ static struct ibm_struct brightness_driver_data = { #define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control" #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 bool alsa_enable = SNDRV_DEFAULT_ENABLE1; @@ -9163,7 +9168,6 @@ static int __init thinkpad_acpi_module_init(void) mutex_init(&tpacpi_inputdev_send_mutex); tpacpi_inputdev = input_allocate_device(); if (!tpacpi_inputdev) { - pr_err("unable to allocate input device\n"); thinkpad_acpi_module_exit(); return -ENOMEM; } else { diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index 67897c8740ba..e597de05e6c2 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -97,10 +97,8 @@ static int acpi_topstar_init_hkey(struct topstar_hkey *hkey) int error; input = inp |