Commit 99a7583d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'platform-drivers-x86-v4.12-1' of git://git.infradead.org/linux-platform-drivers-x86

Pull x86 platform-drivers update from Darren Hart:
 "This represents a significantly larger and more complex set of changes
  than those of prior merge windows.

  In particular, we had several changes with dependencies on other
  subsystems which we felt were best managed through merges of immutable
  branches, including one each from input, i2c, and leds. Two patches
  for the watchdog subsystem are included after discussion with Wim and
  Guenter following a collision in linux-next (this should be resolved
  and you should only see these two appear in this pull request). These
  are called out in the "External" section below.

  Summary of changes:
   - significant further cleanup of fujitsu-laptop and hp-wmi
   - new model support for ideapad, asus, silead, and xiaomi
   - new hotkeys for thinkpad and models using intel-vbtn
   - dell keyboard backlight improvements
   - build and dependency improvements
   - intel * ipc fixes, cleanups, and api updates
   - single isolated fixes noted below

  External:
   - watchdog: iTCO_wdt: Add PMC specific noreboot update api
   - watchdog: iTCO_wdt: cleanup set/unset no_reboot_bit functions
   - Merge branch 'ib/4.10-sparse-keymap-managed'
   - Merge branch 'i2c/for-INT33FE'
   - Merge branch 'linux-leds/dell-laptop-changes-for-4.12'

  platform/x86:
   - Add Intel Cherry Trail ACPI INT33FE device driver
   - remove sparse_keymap_free() calls
   - Make SILEAD_DMI depend on TOUCHSCREEN_SILEAD

  asus-wmi:
   - try to set als by default
   - fix cpufv sysfs file permission

  acer-wmi:
   - setup accelerometer when ACPI device was found

  ideapad-laptop:
   - Add IdeaPad V310-15ISK to no_hw_rfkill
   - Add IdeaPad 310-15IKB to no_hw_rfkill

  intel_pmc_ipc:
   - use gcr mem base for S0ix counter read
   - Fix iTCO_wdt GCS memory mapping failure
   - Add pmc gcr read/write/update api's
   - fix gcr offset

  dell-laptop:
   - Add keyboard backlight timeout AC settings
   - Handle return error form dell_get_intensity.
   - Protect kbd_state against races
   - Refactor kbd_led_triggers_store()

  hp-wireless:
   - reuse module_acpi_driver
   - add Xiaomi's hardware id to the supported list

  intel-vbtn:
   - add volume up and down

  INT33FE:
   - add i2c dependency

  hp-wmi:
   - Cleanup exit paths
   - Do not shadow errors in sysfs show functions
   - Use DEVICE_ATTR_(RO|RW) helper macros
   - Refactor dock and tablet state fetchers
   - Cleanup wireless get_(hw|sw)state functions
   - Refactor redundant HPWMI_READ functions
   - Standardize enum usage for constants
   - Cleanup local variable declarations
   - Do not shadow error values
   - Fix detection for dock and tablet mode
   - Fix error value for hp_wmi_tablet_state

  fujitsu-laptop:
   - simplify error handling in acpi_fujitsu_laptop_add()
   - do not log LED registration failures
   - switch to managed LED class devices
   - reorganize LED-related code
   - refactor LED registration
   - select LEDS_CLASS
   - remove redundant fields from struct fujitsu_bl
   - account for backlight power when determining brightness
   - do not log set_lcd_level() failures in bl_update_status()
   - ignore errors when setting backlight power
   - make disable_brightness_adjust a boolean
   - clean up use_alt_lcd_levels handling
   - sync brightness in set_lcd_level()
   - simplify set_lcd_level()
   - merge set_lcd_level_alt() into set_lcd_level()
   - switch to a managed backlight device
   - only handle backlight when appropriate
   - update debug message logged by call_fext_func()
   - rename call_fext_func() arguments
   - simplify call_fext_func()
   - clean up local variables in call_fext_func()
   - remove keycode fields from struct fujitsu_bl
   - model-dependent sparse keymap overrides
   - use a sparse keymap for hotkey event generation
   - switch to a managed hotkey input device
   - refactor hotkey input device setup
   - use a sparse keymap for brightness key events
   - switch to a managed backlight input device
   - refactor backlight input device setup
   - remove pf_device field from struct fujitsu_bl
   - only register platform device if FUJ02E3 is present
   - add and remove platform device in separate functions
   - simplify platform device attribute definitions
   - remove backlight-related attributes from the platform device
   - cleanup error labels in fujitsu_init()
   - only register backlight device if FUJ02B1 is present
   - sync backlight power status in acpi_fujitsu_laptop_add()
   - register backlight device in a separate function
   - simplify brightness key event generation logic
   - decrease indentation in acpi_fujitsu_bl_notify()

  intel-hid:
   - Add missing ->thaw callback
   - do not set parents of input devices explicitly
   - remove redundant set_bit() call
   - use devm_input_allocate_device() for HID events input device
   - make intel_hid_set_enable() take a boolean argument
   - simplify enabling/disabling HID events

  silead_dmi:
   - Add touchscreen info for Surftab Wintron 7.0
   - Abort early if DMI does not match
   - Do not treat all devices as i2c_clients
   - Add entry for Insyde 7W tablets
   - Constify properties arrays

  intel_scu_ipc:
   - Introduce intel_scu_ipc_raw_command()
   - Introduce SCU_DEVICE() macro
   - Remove redundant subarch check
   - Rearrange init sequence
   - Platform data is mandatory

  asus-nb-wmi:
   - Add wapf4 quirk for the X302UA

  dell-*:
   - Call new led hw_changed API on kbd brightness change
   - Add a generic dell-laptop notifier chain

  eeepc-laptop:
   - Skip unknown key messages 0x50 0x51

  thinkpad_acpi:
   - add mapping for new hotkeys
   - guard generic hotkey case"

* tag 'platform-drivers-x86-v4.12-1' of git://git.infradead.org/linux-platform-drivers-x86: (108 commits)
  platform/x86: Make SILEAD_DMI depend on TOUCHSCREEN_SILEAD
  platform/x86: asus-wmi: try to set als by default
  platform/x86: asus-wmi: fix cpufv sysfs file permission
  platform/x86: acer-wmi: setup accelerometer when ACPI device was found
  platform/x86: ideapad-laptop: Add IdeaPad V310-15ISK to no_hw_rfkill
  platform/x86: intel_pmc_ipc: use gcr mem base for S0ix counter read
  platform/x86: intel_pmc_ipc: Fix iTCO_wdt GCS memory mapping failure
  watchdog: iTCO_wdt: Add PMC specific noreboot update api
  watchdog: iTCO_wdt: cleanup set/unset no_reboot_bit functions
  platform/x86: intel_pmc_ipc: Add pmc gcr read/write/update api's
  platform/x86: intel_pmc_ipc: fix gcr offset
  platform/x86: dell-laptop: Add keyboard backlight timeout AC settings
  platform/x86: dell-laptop: Handle return error form dell_get_intensity.
  platform/x86: hp-wireless: reuse module_acpi_driver
  platform/x86: intel-vbtn: add volume up and down
  platform/x86: INT33FE: add i2c dependency
  platform/x86: hp-wmi: Cleanup exit paths
  platform/x86: hp-wmi: Do not shadow errors in sysfs show functions
  platform/x86: hp-wmi: Use DEVICE_ATTR_(RO|RW) helper macros
  platform/x86: hp-wmi: Refactor dock and tablet state fetchers
  ...
parents c336bf8e 6df97f85
...@@ -23,6 +23,11 @@ ...@@ -23,6 +23,11 @@
#define IPC_ERR_EMSECURITY 6 #define IPC_ERR_EMSECURITY 6
#define IPC_ERR_UNSIGNEDKERNEL 7 #define IPC_ERR_UNSIGNEDKERNEL 7
/* GCR reg offsets from gcr base*/
#define PMC_GCR_PMC_CFG_REG 0x08
#define PMC_GCR_TELEM_DEEP_S0IX_REG 0x78
#define PMC_GCR_TELEM_SHLW_S0IX_REG 0x80
#if IS_ENABLED(CONFIG_INTEL_PMC_IPC) #if IS_ENABLED(CONFIG_INTEL_PMC_IPC)
int intel_pmc_ipc_simple_command(int cmd, int sub); int intel_pmc_ipc_simple_command(int cmd, int sub);
...@@ -31,6 +36,9 @@ int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen, ...@@ -31,6 +36,9 @@ int intel_pmc_ipc_raw_cmd(u32 cmd, u32 sub, u8 *in, u32 inlen,
int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen, int intel_pmc_ipc_command(u32 cmd, u32 sub, u8 *in, u32 inlen,
u32 *out, u32 outlen); u32 *out, u32 outlen);
int intel_pmc_s0ix_counter_read(u64 *data); int intel_pmc_s0ix_counter_read(u64 *data);
int intel_pmc_gcr_read(u32 offset, u32 *data);
int intel_pmc_gcr_write(u32 offset, u32 data);
int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val);
#else #else
...@@ -56,6 +64,21 @@ static inline int intel_pmc_s0ix_counter_read(u64 *data) ...@@ -56,6 +64,21 @@ static inline int intel_pmc_s0ix_counter_read(u64 *data)
return -EINVAL; return -EINVAL;
} }
static inline int intel_pmc_gcr_read(u32 offset, u32 *data)
{
return -EINVAL;
}
static inline int intel_pmc_gcr_write(u32 offset, u32 data)
{
return -EINVAL;
}
static inline int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
{
return -EINVAL;
}
#endif /*CONFIG_INTEL_PMC_IPC*/ #endif /*CONFIG_INTEL_PMC_IPC*/
#endif #endif
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
#include <linux/notifier.h> #include <linux/notifier.h>
#define IPCMSG_INDIRECT_READ 0x02
#define IPCMSG_INDIRECT_WRITE 0x05
#define IPCMSG_COLD_OFF 0x80 /* Only for Tangier */ #define IPCMSG_COLD_OFF 0x80 /* Only for Tangier */
#define IPCMSG_WARM_RESET 0xF0 #define IPCMSG_WARM_RESET 0xF0
...@@ -46,6 +49,9 @@ int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask); ...@@ -46,6 +49,9 @@ int intel_scu_ipc_update_register(u16 addr, u8 data, u8 mask);
int intel_scu_ipc_simple_command(int cmd, int sub); int intel_scu_ipc_simple_command(int cmd, int sub);
int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
u32 *out, int outlen); u32 *out, int outlen);
int intel_scu_ipc_raw_command(int cmd, int sub, u8 *in, int inlen,
u32 *out, int outlen, u32 dptr, u32 sptr);
/* I2C control api */ /* I2C control api */
int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data); int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data);
......
...@@ -182,7 +182,8 @@ config FUJITSU_LAPTOP ...@@ -182,7 +182,8 @@ config FUJITSU_LAPTOP
depends on INPUT depends on INPUT
depends on BACKLIGHT_CLASS_DEVICE depends on BACKLIGHT_CLASS_DEVICE
depends on ACPI_VIDEO || ACPI_VIDEO = n depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on LEDS_CLASS || LEDS_CLASS=n select INPUT_SPARSEKMAP
select LEDS_CLASS
---help--- ---help---
This is a driver for laptops built by Fujitsu: This is a driver for laptops built by Fujitsu:
...@@ -780,6 +781,19 @@ config ACPI_CMPC ...@@ -780,6 +781,19 @@ config ACPI_CMPC
keys as input device, backlight device, tablet and accelerometer keys as input device, backlight device, tablet and accelerometer
devices. devices.
config INTEL_CHT_INT33FE
tristate "Intel Cherry Trail ACPI INT33FE Driver"
depends on X86 && ACPI && I2C
---help---
This driver add support for the INT33FE ACPI device found on
some Intel Cherry Trail devices.
The INT33FE ACPI device has a CRS table with I2cSerialBusV2
resources for 3 devices: Maxim MAX17047 Fuel Gauge Controller,
FUSB302 USB Type-C Controller and PI3USB30532 USB switch.
This driver instantiates i2c-clients for these, so that standard
i2c drivers for these chips can bind to the them.
config INTEL_HID_EVENT config INTEL_HID_EVENT
tristate "INTEL HID Event" tristate "INTEL HID Event"
depends on ACPI depends on ACPI
...@@ -1087,7 +1101,7 @@ config INTEL_TURBO_MAX_3 ...@@ -1087,7 +1101,7 @@ config INTEL_TURBO_MAX_3
config SILEAD_DMI config SILEAD_DMI
bool "Tablets with Silead touchscreens" bool "Tablets with Silead touchscreens"
depends on ACPI && DMI && I2C=y && INPUT depends on ACPI && DMI && I2C=y && TOUCHSCREEN_SILEAD
---help--- ---help---
Certain ACPI based tablets with Silead touchscreens do not have Certain ACPI based tablets with Silead touchscreens do not have
enough data in ACPI tables for the touchscreen driver to handle enough data in ACPI tables for the touchscreen driver to handle
......
...@@ -45,6 +45,7 @@ obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o ...@@ -45,6 +45,7 @@ obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o
obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o
obj-$(CONFIG_INTEL_CHT_INT33FE) += intel_cht_int33fe.o
obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o obj-$(CONFIG_INTEL_HID_EVENT) += intel-hid.o
obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
......
...@@ -1896,7 +1896,7 @@ static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level, ...@@ -1896,7 +1896,7 @@ static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level,
if (!strcmp(ctx, "SENR")) { if (!strcmp(ctx, "SENR")) {
if (acpi_bus_get_device(ah, &dev)) if (acpi_bus_get_device(ah, &dev))
return AE_OK; return AE_OK;
if (!strcmp(ACER_WMID_ACCEL_HID, acpi_device_hid(dev))) if (strcmp(ACER_WMID_ACCEL_HID, acpi_device_hid(dev)))
return AE_OK; return AE_OK;
} else } else
return AE_OK; return AE_OK;
...@@ -1917,8 +1917,7 @@ static int __init acer_wmi_get_handle(const char *name, const char *prop, ...@@ -1917,8 +1917,7 @@ static int __init acer_wmi_get_handle(const char *name, const char *prop,
handle = NULL; handle = NULL;
status = acpi_get_devices(prop, acer_wmi_get_handle_cb, status = acpi_get_devices(prop, acer_wmi_get_handle_cb,
(void *)name, &handle); (void *)name, &handle);
if (ACPI_SUCCESS(status) && handle) {
if (ACPI_SUCCESS(status)) {
*ah = handle; *ah = handle;
return 0; return 0;
} else { } else {
...@@ -1987,7 +1986,7 @@ static int __init acer_wmi_input_setup(void) ...@@ -1987,7 +1986,7 @@ static int __init acer_wmi_input_setup(void)
acer_wmi_notify, NULL); acer_wmi_notify, NULL);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
err = -EIO; err = -EIO;
goto err_free_keymap; goto err_free_dev;
} }
err = input_register_device(acer_wmi_input_dev); err = input_register_device(acer_wmi_input_dev);
...@@ -1998,8 +1997,6 @@ static int __init acer_wmi_input_setup(void) ...@@ -1998,8 +1997,6 @@ static int __init acer_wmi_input_setup(void)
err_uninstall_notifier: err_uninstall_notifier:
wmi_remove_notify_handler(ACERWMID_EVENT_GUID); wmi_remove_notify_handler(ACERWMID_EVENT_GUID);
err_free_keymap:
sparse_keymap_free(acer_wmi_input_dev);
err_free_dev: err_free_dev:
input_free_device(acer_wmi_input_dev); input_free_device(acer_wmi_input_dev);
return err; return err;
...@@ -2008,7 +2005,6 @@ static int __init acer_wmi_input_setup(void) ...@@ -2008,7 +2005,6 @@ static int __init acer_wmi_input_setup(void)
static void acer_wmi_input_destroy(void) static void acer_wmi_input_destroy(void)
{ {
wmi_remove_notify_handler(ACERWMID_EVENT_GUID); wmi_remove_notify_handler(ACERWMID_EVENT_GUID);
sparse_keymap_free(acer_wmi_input_dev);
input_unregister_device(acer_wmi_input_dev); input_unregister_device(acer_wmi_input_dev);
} }
...@@ -2290,8 +2286,8 @@ static int __init acer_wmi_init(void) ...@@ -2290,8 +2286,8 @@ static int __init acer_wmi_init(void)
if (err) if (err)
return err; return err;
err = acer_wmi_accel_setup(); err = acer_wmi_accel_setup();
if (err) if (err && err != -ENODEV)
return err; pr_warn("Cannot enable accelerometer\n");
} }
err = platform_driver_register(&acer_platform_driver); err = platform_driver_register(&acer_platform_driver);
......
...@@ -1516,14 +1516,12 @@ static int asus_input_init(struct asus_laptop *asus) ...@@ -1516,14 +1516,12 @@ static int asus_input_init(struct asus_laptop *asus)
error = input_register_device(input); error = input_register_device(input);
if (error) { if (error) {
pr_warn("Unable to register input device\n"); pr_warn("Unable to register input device\n");
goto err_free_keymap; goto err_free_dev;
} }
asus->inputdev = input; asus->inputdev = input;
return 0; return 0;
err_free_keymap:
sparse_keymap_free(input);
err_free_dev: err_free_dev:
input_free_device(input); input_free_device(input);
return error; return error;
...@@ -1531,10 +1529,8 @@ static int asus_input_init(struct asus_laptop *asus) ...@@ -1531,10 +1529,8 @@ static int asus_input_init(struct asus_laptop *asus)
static void asus_input_exit(struct asus_laptop *asus) static void asus_input_exit(struct asus_laptop *asus)
{ {
if (asus->inputdev) { if (asus->inputdev)
sparse_keymap_free(asus->inputdev);
input_unregister_device(asus->inputdev); input_unregister_device(asus->inputdev);
}
asus->inputdev = NULL; asus->inputdev = NULL;
} }
......
...@@ -111,6 +111,10 @@ static struct quirk_entry quirk_asus_x550lb = { ...@@ -111,6 +111,10 @@ static struct quirk_entry quirk_asus_x550lb = {
.xusb2pr = 0x01D9, .xusb2pr = 0x01D9,
}; };
static struct quirk_entry quirk_asus_ux330uak = {
.wmi_force_als_set = true,
};
static int dmi_matched(const struct dmi_system_id *dmi) static int dmi_matched(const struct dmi_system_id *dmi)
{ {
pr_info("Identified laptop model '%s'\n", dmi->ident); pr_info("Identified laptop model '%s'\n", dmi->ident);
...@@ -142,6 +146,15 @@ static const struct dmi_system_id asus_quirks[] = { ...@@ -142,6 +146,15 @@ static const struct dmi_system_id asus_quirks[] = {
*/ */
.driver_data = &quirk_asus_wapf4, .driver_data = &quirk_asus_wapf4,
}, },
{
.callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X302UA",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X302UA"),
},
.driver_data = &quirk_asus_wapf4,
},
{ {
.callback = dmi_matched, .callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X401U", .ident = "ASUSTeK COMPUTER INC. X401U",
...@@ -367,6 +380,15 @@ static const struct dmi_system_id asus_quirks[] = { ...@@ -367,6 +380,15 @@ static const struct dmi_system_id asus_quirks[] = {
}, },
.driver_data = &quirk_asus_ux303ub, .driver_data = &quirk_asus_ux303ub,
}, },
{
.callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. UX330UAK",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "UX330UAK"),
},
.driver_data = &quirk_asus_ux330uak,
},
{ {
.callback = dmi_matched, .callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X550LB", .ident = "ASUSTeK COMPUTER INC. X550LB",
......
...@@ -269,12 +269,10 @@ static int asus_wmi_input_init(struct asus_wmi *asus) ...@@ -269,12 +269,10 @@ static int asus_wmi_input_init(struct asus_wmi *asus)
err = input_register_device(asus->inputdev); err = input_register_device(asus->inputdev);
if (err) if (err)
goto err_free_keymap; goto err_free_dev;
return 0; return 0;
err_free_keymap:
sparse_keymap_free(asus->inputdev);
err_free_dev: err_free_dev:
input_free_device(asus->inputdev); input_free_device(asus->inputdev);
return err; return err;
...@@ -282,10 +280,8 @@ static int asus_wmi_input_init(struct asus_wmi *asus) ...@@ -282,10 +280,8 @@ static int asus_wmi_input_init(struct asus_wmi *asus)
static void asus_wmi_input_exit(struct asus_wmi *asus) static void asus_wmi_input_exit(struct asus_wmi *asus)
{ {
if (asus->inputdev) { if (asus->inputdev)
sparse_keymap_free(asus->inputdev);
input_unregister_device(asus->inputdev); input_unregister_device(asus->inputdev);
}
asus->inputdev = NULL; asus->inputdev = NULL;
} }
...@@ -1108,6 +1104,15 @@ static void asus_wmi_set_xusb2pr(struct asus_wmi *asus) ...@@ -1108,6 +1104,15 @@ static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
orig_ports_available, ports_available); orig_ports_available, ports_available);
} }
/*
* Some devices dont support or have borcken get_als method
* but still support set method.
*/
static void asus_wmi_set_als(void)
{
asus_wmi_set_devstate(ASUS_WMI_DEVID_ALS_ENABLE, 1, NULL);
}
/* /*
* Hwmon device * Hwmon device
*/ */
...@@ -1761,7 +1766,7 @@ ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER); ...@@ -1761,7 +1766,7 @@ ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER);
ASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME); ASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME);
ASUS_WMI_CREATE_DEVICE_ATTR(als_enable, 0644, ASUS_WMI_DEVID_ALS_ENABLE); ASUS_WMI_CREATE_DEVICE_ATTR(als_enable, 0644, ASUS_WMI_DEVID_ALS_ENABLE);
static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, static ssize_t cpufv_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
int value, rv; int value, rv;
...@@ -1778,7 +1783,7 @@ static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, ...@@ -1778,7 +1783,7 @@ static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
return count; return count;
} }
static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv); static DEVICE_ATTR_WO(cpufv);
static struct attribute *platform_attributes[] = { static struct attribute *platform_attributes[] = {
&dev_attr_cpufv.attr, &dev_attr_cpufv.attr,
...@@ -2117,6 +2122,9 @@ static int asus_wmi_add(struct platform_device *pdev) ...@@ -2117,6 +2122,9 @@ static int asus_wmi_add(struct platform_device *pdev)
goto fail_rfkill; goto fail_rfkill;
} }
if (asus->driver->quirks->wmi_force_als_set)
asus_wmi_set_als();
/* Some Asus desktop boards export an acpi-video backlight interface, /* Some Asus desktop boards export an acpi-video backlight interface,
stop this from showing up */ stop this from showing up */
chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE); chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
......
...@@ -44,6 +44,7 @@ struct quirk_entry { ...@@ -44,6 +44,7 @@ struct quirk_entry {
bool store_backlight_power; bool store_backlight_power;
bool wmi_backlight_power; bool wmi_backlight_power;
bool wmi_backlight_native; bool wmi_backlight_native;
bool wmi_force_als_set;
int wapf; int wapf;
/* /*
* For machines with AMD graphic chips, it will send out WMI event * For machines with AMD graphic chips, it will send out WMI event
......
This diff is collapsed.
...@@ -105,6 +105,26 @@ struct calling_interface_token *dell_smbios_find_token(int tokenid) ...@@ -105,6 +105,26 @@ struct calling_interface_token *dell_smbios_find_token(int tokenid)
} }
EXPORT_SYMBOL_GPL(dell_smbios_find_token); EXPORT_SYMBOL_GPL(dell_smbios_find_token);
static BLOCKING_NOTIFIER_HEAD(dell_laptop_chain_head);
int dell_laptop_register_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&dell_laptop_chain_head, nb);
}
EXPORT_SYMBOL_GPL(dell_laptop_register_notifier);
int dell_laptop_unregister_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&dell_laptop_chain_head, nb);
}
EXPORT_SYMBOL_GPL(dell_laptop_unregister_notifier);
void dell_laptop_call_notifier(unsigned long action, void *data)
{
blocking_notifier_call_chain(&dell_laptop_chain_head, action, data);
}
EXPORT_SYMBOL_GPL(dell_laptop_call_notifier);
static void __init parse_da_table(const struct dmi_header *dm) static void __init parse_da_table(const struct dmi_header *dm)
{ {
/* Final token is a terminator, so we don't want to copy it */ /* Final token is a terminator, so we don't want to copy it */
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#ifndef _DELL_SMBIOS_H_ #ifndef _DELL_SMBIOS_H_
#define _DELL_SMBIOS_H_ #define _DELL_SMBIOS_H_
struct notifier_block;
/* This structure will be modified by the firmware when we enter /* This structure will be modified by the firmware when we enter
* system management mode, hence the volatiles */ * system management mode, hence the volatiles */
...@@ -43,4 +45,13 @@ void dell_smbios_release_buffer(void); ...@@ -43,4 +45,13 @@ void dell_smbios_release_buffer(void);
void dell_smbios_send_request(int class, int select); void dell_smbios_send_request(int class, int select);
struct calling_interface_token *dell_smbios_find_token(int tokenid); struct calling_interface_token *dell_smbios_find_token(int tokenid);
enum dell_laptop_notifier_actions {
DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED,
};
int dell_laptop_register_notifier(struct notifier_block *nb);
int dell_laptop_unregister_notifier(struct notifier_block *nb);
void dell_laptop_call_notifier(unsigned long action, void *data);
#endif #endif
...@@ -152,12 +152,10 @@ static int __init dell_wmi_aio_input_setup(void) ...@@ -152,12 +152,10 @@ static int __init dell_wmi_aio_input_setup(void)
err = input_register_device(dell_wmi_aio_input_dev); err = input_register_device(dell_wmi_aio_input_dev);
if (err) { if (err) {
pr_info("Unable to register input device\n"); pr_info("Unable to register input device\n");
goto err_free_keymap; goto err_free_dev;
} }
return 0; return 0;
err_free_keymap:
sparse_keymap_free(dell_wmi_aio_input_dev);
err_free_dev: err_free_dev:
input_free_device(dell_wmi_aio_input_dev); input_free_device(dell_wmi_aio_input_dev);
return err; return err;
...@@ -192,7 +190,6 @@ static int __init dell_wmi_aio_init(void) ...@@ -192,7 +190,6 @@ static int __init dell_wmi_aio_init(void)
err = wmi_install_notify_handler(guid, dell_wmi_aio_notify, NULL); err = wmi_install_notify_handler(guid, dell_wmi_aio_notify, NULL);
if (err) { if (err) {
pr_err("Unable to register notify handler - %d\n", err); pr_err("Unable to register notify handler - %d\n", err);
sparse_keymap_free(dell_wmi_aio_input_dev);
input_unregister_device(dell_wmi_aio_input_dev); input_unregister_device(dell_wmi_aio_input_dev);
return err; return err;
} }
...@@ -206,7 +203,6 @@ static void __exit dell_wmi_aio_exit(void) ...@@ -206,7 +203,6 @@ static void __exit dell_wmi_aio_exit(void)
guid = dell_wmi_aio_find(); guid = dell_wmi_aio_find();
wmi_remove_notify_handler(guid); wmi_remove_notify_handler(guid);
sparse_keymap_free(dell_wmi_aio_input_dev);
input_unregister_device(dell_wmi_aio_input_dev); input_unregister_device(dell_wmi_aio_input_dev);
} }
......
...@@ -329,6 +329,10 @@ static void dell_wmi_process_key(int type, int code) ...@@ -329,6 +329,10 @@ static void dell_wmi_process_key(int type, int code)
if (type == 0x0000 && code == 0xe025 && !wmi_requires_smbios_request) if (type == 0x0000 && code == 0xe025 && !wmi_requires_smbios_request)
return; return;
if (key->keycode == KEY_KBDILLUMTOGGLE)
dell_laptop_call_notifier(
DELL_LAPTOP_KBD_BACKLIGHT_BRIGHTNESS_CHANGED, NULL);
sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true); sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
} }
...@@ -603,23 +607,15 @@ static int __init dell_wmi_input_setup(void) ...@@ -603,23 +607,15 @@ static int __init dell_wmi_input_setup(void)
err = input_register_device(dell_wmi_input_dev); err = input_register_device(dell_wmi_input_dev);
if (err) if (err)
goto err_free_keymap; goto err_free_dev;
return 0; return 0;
err_free_keymap:
sparse_keymap_free(dell_wmi_input_dev);
err_free_dev: err_free_dev:
input_free_device(dell_wmi_input_dev); input_free_device(dell_wmi_input_dev);
return err; return err;
} }
static void dell_wmi_input_destroy(void)
{
sparse_keymap_free(dell_wmi_input_dev);
input_unregister_device(dell_wmi_input_dev);
}
/* /*
* Descriptor buffer is 128 byte long and contains: * Descriptor buffer is 128 byte long and contains:
* *
...@@ -740,7 +736,7 @@ static int __init dell_wmi_init(void) ...@@ -740,7 +736,7 @@ static int __init dell_wmi_init(void)
status = wmi_install_notify_handler(DELL_EVENT_GUID, status = wmi_install_notify_handler(DELL_EVENT_GUID,
dell_wmi_notify, NULL); dell_wmi_notify, NULL);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
dell_wmi_input_destroy(); input_unregister_device(dell_wmi_input_dev);
pr_err("Unable to register notify handler - %d\n", status); pr_err("Unable to register notify handler - %d\n", status);
return -ENODEV; return -ENODEV;
} }
...@@ -752,7 +748,7 @@ static int __init dell_wmi_init(void) ...@@ -752,7 +748,7 @@ static int __init dell_wmi_init(void)
if (err) { if (err) {
pr_err("Failed to enable WMI events\n"); pr_err("Failed to enable WMI events\n");
wmi_remove_notify_handler(DELL_EVENT_GUID); wmi_remove_notify_handler(DELL_EVENT_GUID);
dell_wmi_input_destroy(); input_unregister_device(dell_wmi_input_dev);
return err; return err;
} }
} }
...@@ -766,6 +762,6 @@ static void __exit dell_wmi_exit(void) ...@@ -766,6 +762,6 @@ static void __exit dell_wmi_exit(void)
if (wmi_requires_smbios_request) if (wmi_requires_smbios_request)
dell_wmi_events_set_enabled(false); dell_wmi_events_set_enabled(false);
wmi_remove_notify_handler(DELL_EVENT_GUID); wmi_remove_notify_handler(DELL_EVENT_GUID);
dell_wmi_input_destroy(); input_unregister_device(dell_wmi_input_dev);
} }
module_exit(dell_wmi_exit); module_exit(dell_wmi_exit);
...@@ -150,6 +150,8 @@ static const struct key_entry eeepc_keymap[] = { ...@@ -150,6 +150,8 @@ static const struct key_entry eeepc_keymap[] = {
{ KE_KEY, 0x32, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 0x32, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0x37, { KEY_F13 } }, /* Disable Touchpad */ { KE_KEY, 0x37, { KEY_F13 } }, /* Disable Touchpad */
{ KE_KEY, 0x38, { KEY_F14 } }, { KE_KEY, 0x38, { KEY_F14 } },
{ KE_IGNORE, 0x50, { KEY_RESERVED } }, /* AC plugged */
{ KE_IGNORE, 0x51, { KEY_RESERVED } }, /* AC unplugged */
{ KE_END, 0 }, { KE_END, 0 },
}; };
...@@ -1205,14 +1207,12 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc) ...@@ -1205,14 +1207,12 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc)
error = input_register_device(input); error = input_register_device(input);
if (error) { if (error) {
pr_err("Unable to register input device\n"); pr_err("Unable to register input device\n");
goto err_free_keymap; goto err_free_dev;
} }
eeepc->inputdev = input; eeepc->inputdev = input;
return 0; return 0;
err_free_keymap:
sparse_keymap_free(input);
err_free_dev: err_free_dev:
input_free_device(input); input_free_device(input);
return error; return error;
...@@ -1220,10 +1220,8 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc) ...@@ -1220,10 +1220,8 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc)
static void eeepc_input_exit(struct eeepc_laptop *eeepc) static void eeepc_input_exit(struct eeepc_laptop *eeepc)
{ {
if (eeepc->inputdev) { if (eeepc->inputdev)
sparse_keymap_free(eeepc->inputdev);
input_unregister_device(eeepc->inputdev); input_unregister_device(eeepc->inputdev);
}
eeepc->inputdev = NULL; eeepc->inputdev = NULL;
} }
......
This diff is collapsed.
/* /*
* hp-wireless button for Windows 8 * Airplane mode button for HP & Xiaomi laptops
* *
* Copyright (C) 2014 Alex Hung <alex.hung@canonical.com> * Copyright (C) 2014-2017 Alex Hung <alex.hung@canonical.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -29,11 +29,13 @@ ...@@ -29,11 +29,13 @@
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alex Hung"); MODULE_AUTHOR("Alex Hung");
MODULE_ALIAS("acpi*:HPQ6001:*"); MODULE_ALIAS("acpi*:HPQ6001:*");
MODULE_ALIAS("acpi*:WSTADEF:*");
static struct input_dev *hpwl_input_dev; static struct input_dev *hpwl_input_dev;
static const struct acpi_device_id hpwl_ids[] = { static const struct acpi_device_id hpwl_ids[] = {
{"HPQ6001", 0}, {"HPQ6001", 0},
{"WSTADEF", 0},
{"", 0}, {"", 0},
}; };
...@@ -108,23 +110,4 @@ static struct acpi_driver hpwl_driver = { ...@@ -108,23 +110,4 @@ static struct acpi_driver hpwl_driver = {
}, },
}; };
static int __init hpwl_init(void) module_acpi_driver(hpwl_driver);
{
int err;
pr_info("Initializing HPQ6001 module\n");
err = acpi_bus_register_driver(&hpwl_driver);
if (err)
pr_err("Unable to register HP wireless control driver.\n");
return err;
}
static void __exit hpwl_exit(void)
{
pr_info("Exiting HPQ6001 module\n");
acpi_bus_unregister_driver(&hpwl_driver);
}
module_init(hpwl_init);
module_exit(hpwl_exit);
This diff is collapsed.
...@@ -604,14 +604,12 @@ static int ideapad_input_init(struct ideapad_private *priv) ...@@ -604,14 +604,12 @@ static int ideapad_input_init(struct ideapad_private *priv)
error = input_register_device(inputdev); error = input_register_device(inputdev);
if (error) { if (error) {
pr_err("Unable to register input device\n"); pr_err("Unable to register input device\n");
goto err_free_keymap; goto err_free_dev;
} }
priv->inputdev = inputdev; priv->inputdev = inputdev;
return 0; return 0;
err_free_keymap:
sparse_keymap_free(inputdev);
err_free_dev: err_free_dev:
input_free_device(inputdev); input_free_device(inputdev);
return error; return error;
...@@ -619,7 +617,6 @@ static int ideapad_input_init(struct ideapad_private *priv) ...@@ -619,7 +617,6 @@ static int ideapad_input_init(struct ideapad_private *priv)
static void ideapad_input_exit(struct ideapad_private *priv) static void ideapad_input_exit(struct ideapad_private *priv)
{ {
sparse_keymap_free(priv->inputdev);
input_unregister_device(priv->inputdev); input_unregister_device(priv->inputdev);
priv->inputdev = NULL; priv->inputdev = NULL;
} }
...@@ -871,6 +868,20 @@ static const struct dmi_system_id no_hw_rfkill_list[] = { ...@@ -871,6 +868,20 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo G50-30"), DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo G50-30"),
}, },
}, },
{
.ident = "Lenovo V310-15ISK",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo V310-15ISK"),
},
},
{
.ident = "Lenovo ideapad 310-15IKB",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 310-15IKB"),
},
},
{ {
.ident = "Lenovo ideapad Y700-15ACZ", .ident = "Lenovo ideapad Y700-15ACZ",
.matches = { .matches = {
......
...@@ -77,14 +77,12 @@ struct intel_hid_priv { ...@@ -77,14 +77,12 @@ struct intel_hid_priv {
struct input_dev *array; struct input_dev *array;
}; };
static int intel_hid_set_enable(struct device *device, int enable) static int intel_hid_set_enable(struct device *device, bool enable)
{ {
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
acpi_status status; acpi_status status;
arg0.integer.value = enable; status = acpi_execute_simple_method(ACPI_HANDLE(device), "HDSM",
status = acpi_evaluate_object(ACPI_HANDLE(device), "HDSM", &args, NULL); enable);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
dev_warn(device, "failed to %sable hotkeys\n", dev_warn(device, "failed to %sable hotkeys\n",
enable ? "en" : "dis"); enable ? "en" : "dis");
...@@ -120,7 +118,7 @@ static void intel_button_array_enable(struct device *device, bool enable) ...@@ -120,7 +118,7 @@ static void intel_button_array_enable(struct device *device, bool enable)
static int intel_hid_pl_suspend_handler(struct device *device) static int intel_hid_pl_suspend_handler(struct device *device)
{ {
intel_hid_set_enable(device, 0); intel_hid_set_enable(device, false);
intel_button_array_enable(device, false); intel_button_array_enable(device, false);
return 0; return 0;
...@@ -128,7 +126,7 @@ static int intel_hid_pl_suspend_handler(struct device *device) ...@@ -128,7 +126,7 @@ static int intel_hid_pl_suspend_handler(struct device *device)
static int intel_hid_pl_resume_handler(struct device *device) static int intel_hid_pl_resume_handler(struct device *device)
{ {
intel_hid_set_enable(device, 1); intel_hid_set_enable(device, true);
intel_button_array_enable(device, true); intel_button_array_enable(device, true);
return 0; return 0;
...@@ -136,6 +134,7 @@ static int intel_hid_pl_resume_handler(struct device *device) ...@@ -136,6 +134,7 @@ static int intel_hid_pl_resume_handler(struct device *device)
static const struct dev_pm_ops intel_hid_pl_pm_ops = { static const struct dev_pm_ops intel_hid_pl_pm_ops = {
.freeze = intel_hid_pl_suspend_handler, .freeze = intel_hid_pl_suspend_handler,
.thaw = intel_hid_pl_resume_handler,
.restore = intel_hid_pl_resume_handler, .restore = intel_hid_pl_resume_handler,
.suspend = intel_hid_pl_suspend_handler, .suspend = intel_hid_pl_suspend_handler,
.resume = intel_hid_pl_resume_handler, .resume = intel_hid_pl_resume_handler,
...@@ -146,28 +145,18 @@ static int intel_hid_input_setup(struct platform_device *device) ...@@ -146,28 +145,18 @@ static int intel_hid_input_setup(struct platform_device *device)
struct intel_hid_priv *priv = dev_get_drvdata(&device->dev); struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
int ret; int ret;
priv->input_dev = input_allocate_device(); priv->input_dev = devm_input_allocate_device(&device->dev);
if (!priv->input_dev) if (!priv->input_dev)
return -ENOMEM; return -ENOMEM;
ret = sparse_keymap_setup(priv->input_dev, intel_hid_keymap, NULL); ret = sparse_keymap_setup(priv->input_dev, intel_hid_keymap, NULL);
if (ret) if (ret)
goto err_free_device; return ret;
priv->input_dev->dev.parent = &device->dev;
priv->input_dev->name = "Intel HID events"; priv->input_dev->name = "Intel HID events";
priv->input_dev->id.bustype = BUS_HOST; priv->input_dev->id.bustype = BUS_HOST;
set_bit(KEY_RFKILL, priv->input_dev->keybit);
ret = input_register_device(priv->input_dev);
if (ret)
goto err_free_device;
return 0;
err_free_device: return input_register_device(priv->input_dev);
input_free_device(priv->input_dev);
return ret;
} }
static int intel_button_array_input_setup(struct platform_device *device) static int intel_button_array_input_setup(struct platform_device *device)
...@@ -184,20 +173,12 @@ static int intel_button_array_input_setup(struct platform_device *device) ...@@ -184,20 +173,12 @@ static int intel_button_array_input_setup(struct platform_device *device)
if (ret) if (ret)
return ret; return ret;
priv->array->dev.parent = &device->dev;
priv->array->name = "Intel HID 5 button array"; priv->array->name = "Intel HID 5 button array";
priv->array->id.bustype = BUS_HOST; priv->array->id.bustype = BUS_HOST;
return input_register_device(priv->array); return input_register_device(priv->array);
} }
static void intel_hid_input_destroy(struct platform_device *device)
{
struct intel_hid_priv *priv = dev_get_drvdata(&device->dev);
input_unregister_device(priv->input_dev);
}
static void notify_handler(acpi_handle handle, u32 event, void *context) static void notify_handler(acpi_handle handle, u32 event, void *context)
{ {
struct platform_device *device = context; struct platform_device *device = context;
...@@ -272,12 +253,10 @@ static int intel_hid_probe(struct platform_device *device) ...@@ -272,12 +253,10 @@ static int intel_hid_probe(struct platform_device *device)
ACPI_DEVICE_NOTIFY, ACPI_DEVICE_NOTIFY,
notify_handler, notify_handler,
device); device);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status))
err = -EBUSY; return -EBUSY;
goto err_remove_input;
}
err = intel_hid_set_enable(&device->dev, 1); err = intel_hid_set_enable(&device->dev, true);
if (err) if (err)
goto err_remove_notify; goto err_remove_notify;
...@@ -296,9 +275,6 @@ static int intel_hid_probe(struct platform_device *device) ...@@ -296,9 +275,6 @@ static int intel_hid_probe(struct platform_device *device)
err_remove_notify: err_remove_notify:
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
err_remove_input:
intel_hid_input_destroy(device);
return err; return err;
} }
...@@ -307,8 +283,7 @@ static int intel_hid_remove(struct platform_device *device) ...@@ -307,8 +283,7 @@ static int intel_hid_remove(struct platform_device *device)
acpi_handle handle = ACPI_HANDLE(&device->dev); acpi_handle handle = ACPI_HANDLE(&device->dev);
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
intel_hid_input_destroy(device); intel_hid_set_enable(&device->dev, false);
intel_hid_set_enable(&device->dev, 0);
intel_button_array_enable(&device->dev, false); intel_button_array_enable(&device->dev, false);
/* /*
......
...@@ -37,6 +37,10 @@ static const struct acpi_device_id intel_vbtn_ids[] = { ...@@ -37,6 +37,10 @@ static const struct acpi_device_id intel_vbtn_ids[] = {
static const struct key_entry intel_vbtn_keymap[] = { static const struct key_entry intel_vbtn_keymap[] = {
{ KE_IGNORE, 0xC0, { KEY_POWER } }, /* power key press */ { KE_IGNORE, 0xC0, { KEY_POWER } }, /* power key press */
{ KE_KEY, 0xC1, { KEY_POWER } }, /* power key release */ { KE_KEY, 0xC1, { KEY_POWER } }, /* power key release */
{ KE_KEY, 0xC4, { KEY_VOLUMEUP } }, /* volume-up key press */
{ KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* volume-up key release */
{ KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* volume-down key press */
{ KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* volume-down key release */
{ KE_END }, { KE_END },
}; };
......
/*
* Intel Cherry Trail ACPI INT33FE pseudo device driver
*
* Copyright (C) 2017 Hans de Goede <hdegoede@redhat.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.
*
* Some Intel Cherry Trail based device which ship with Windows 10, have
* this weird INT33FE ACPI device with a CRS table with 4 I2cSerialBusV2
* resources, for 4 different chips attached to various i2c busses:
* 1. The Whiskey Cove pmic, which is also described by the INT34D3 ACPI device
* 2. Maxim MAX17047 Fuel Gauge Controller
* 3. FUSB302 USB Type-C Controller
* 4. PI3USB30532 USB switch
*
* So this driver is a stub / pseudo driver whose only purpose is to
* instantiate i2c-clients for chips 2 - 4, so that standard i2c drivers
* for these chips can bind to the them.
*/
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/slab.h>
#define EXPECTED_PTYPE 4
struct cht_int33fe_data {
struct i2c_client *max17047;
struct i2c_client *fusb302;
struct i2c_client *pi3usb30532;
};
static int cht_int33fe_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct i2c_board_info board_info;
struct cht_int33fe_data *data;
unsigned long long ptyp;
acpi_status status;
int fusb302_irq;
status = acpi_evaluate_integer(ACPI_HANDLE(dev), "PTYP", NULL, &ptyp);
if (ACPI_FAILURE(status)) {
dev_err(dev, "Error getting PTYPE\n");
return -ENODEV;
}
/*
* The same ACPI HID is used for different configurations check PTYP
* to ensure that we are dealing with the expected config.
*/
if (ptyp != EXPECTED_PTYPE)
return -ENODEV;
/* The FUSB302 uses the irq at index 1 and is the only irq user */
fusb302_irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 1);
if (fusb302_irq < 0) {
if (fusb302_irq != -EPROBE_DEFER)
dev_err(dev, "Error getting FUSB302 irq\n");
return fusb302_irq;
}
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
memset(&board_info, 0, sizeof(board_info));
strlcpy(board_info.type, "max17047", I2C_NAME_SIZE);
data->max17047 = i2c_acpi_new_device(dev, 1, &board_info);
if (!data->max17047)
return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */
memset(&board_info, 0, sizeof(board_info));
strlcpy(board_info.type, "fusb302", I2C_NAME_SIZE);
board_info.irq = fusb302_irq;
data->fusb302 = i2c_acpi_new_device(dev, 2, &board_info);
if (!data->fusb302)
goto out_unregister_max17047;
memset(&board_info, 0, sizeof(board_info));
strlcpy(board_info.type, "pi3usb30532", I2C_NAME_SIZE);
data->pi3usb30532 = i2c_acpi_new_device(dev, 3, &board_info);
if (!data->pi3usb30532)
goto out_unregister_fusb302;
i2c_set_clientdata(client, data);
return 0;
out_unregister_fusb302:
i2c_unregister_device(data->fusb302);
out_unregister_max17047:
i2c_unregister_device(data->max17047);
return -EPROBE_DEFER; /* Wait for the i2c-adapter to load */
}
static int cht_int33fe_remove(struct i2c_client *i2c)
{
struct cht_int33fe_data *data = i2c_get_clientdata(i2c);
i2c_unregister_device(data->pi3usb30532);
i2c_unregister_device(data->fusb302);
i2c_unregister_device(data->max17047);
return 0;
}
static const struct i2c_device_id cht_int33fe_i2c_id[] = {
{ }
};
MODULE_DEVICE_TABLE(i2c, cht_int33fe_i2c_id);
static const struct acpi_device_id cht_int33fe_acpi_ids[] = {
{ "INT33FE", },
{ }
};
MODULE_DEVICE_TABLE(acpi, cht_int33fe_acpi_ids);
static struct i2c_driver cht_int33fe_driver = {
.driver = {
.name = "Intel Cherry Trail ACPI INT33FE driver",
.acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids),
},
.probe_new = cht_int33fe_probe,
.remove = cht_int33fe_remove,
.id_table = cht_int33fe_i2c_id,
.disable_i2c_core_irq_mapping = true,
};
module_i2c_driver(cht_int33fe_driver);
MODULE_DESCRIPTION("Intel Cherry Trail ACPI INT33FE pseudo device driver");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_LICENSE("GPL");
...@@ -57,10 +57,6 @@ ...@@ -57,10 +57,6 @@
#define IPC_WRITE_BUFFER 0x80 #define IPC_WRITE_BUFFER 0x80
#define IPC_READ_BUFFER 0x90 #define IPC_READ_BUFFER 0x90
/* PMC Global Control Registers */
#define GCR_TELEM_DEEP_S0IX_OFFSET 0x1078
#define GCR_TELEM_SHLW_S0IX_OFFSET 0x1080
/* Residency with clock rate at 19.2MHz to usecs */ /* Residency with clock rate at 19.2MHz to usecs */
#define S0IX_RESIDENCY_IN_USECS(d, s) \ #define S0IX_RESIDENCY_IN_USECS(d, s) \
({ \ ({ \
...@@ -82,7 +78,7 @@ ...@@ -82,7 +78,7 @@
/* exported resources from IFWI */ /* exported resources from IFWI */
#define PLAT_RESOURCE_IPC_INDEX 0 #define PLAT_RESOURCE_IPC_INDEX 0
#define PLAT_RESOURCE_IPC_SIZE 0x1000 #define PLAT_RESOURCE_IPC_SIZE 0x1000
#define PLAT_RESOURCE_GCR_OFFSET 0x1008 #define PLAT_RESOURCE_GCR_OFFSET 0x1000
#define PLAT_RESOURCE_GCR_SIZE 0x1000 #define PLAT_RESOURCE_GCR_SIZE 0x1000
#define PLAT_RESOURCE_BIOS_DATA_INDEX 1 #define PLAT_RESOURCE_BIOS_DATA_INDEX 1
#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2 #define PLAT_RESOURCE_BIOS_IFACE_INDEX 2
...@@ -112,6 +108,13 @@ ...@@ -112,6 +108,13 @@
#define TCO_PMC_OFFSET 0x8 #define TCO_PMC_OFFSET 0x8
#define TCO_PMC_SIZE 0x4 #define TCO_PMC_SIZE 0x4
/* PMC register bit definitions */
/* PMC_CFG_REG bit masks */
#define PMC_CFG_NO_REBOOT_MASK (1 << 4)
#define PMC_CFG_NO_REBOOT_EN (1 << 4)
#define PMC_CFG_NO_REBOOT_DIS (0 << 4)
static struct intel_pmc_ipc_dev { static struct intel_pmc_ipc_dev {
struct device *dev; struct device *dev;
void __iomem *ipc_base; void __iomem *ipc_base;
...@@ -126,8 +129,7 @@ static struct intel_pmc_ipc_dev { ...@@ -126,8 +129,7 @@ static struct intel_pmc_ipc_dev {
struct platform_device *tco_dev; struct platform_device *tco_dev;
/* gcr */ /* gcr */
resource_size_t gcr_base; void __iomem *gcr_mem_base;
int gcr_size;
bool has_gcr_regs; bool has_gcr_regs;
/* punit */ /* punit */
...@@ -196,7 +198,128 @@ static inline u32 ipc_data_readl(u32 offset) ...@@ -196,7 +198,128 @@ static inline u32 ipc_data_readl(u32 offset)
static inline u64 gcr_data_readq(u32 offset) static inline u64 gcr_data_readq(u32 offset)
{ {
return readq(ipcdev.ipc_base + offset); return readq(ipcdev.gcr_mem_base + offset);
}
static inline int is_gcr_valid(u32 offset)
{
if (!ipcdev.has_gcr_regs)
return -EACCES;
if (offset > PLAT_RESOURCE_GCR_SIZE)
return -EINVAL;
return 0;
}
/**
* intel_pmc_gcr_read() - Read PMC GCR register
* @offset: offset of GCR register from GCR address base
* @data: data pointer for storing the register output
*
* Reads the PMC GCR register of given offset.
*
* Return: negative value on error or 0 on success.
*/
int intel_pmc_gcr_read(u32 offset, u32 *data)
{
int ret;
mutex_lock(&ipclock);
ret = is_gcr_valid(offset);
if (ret < 0) {
mutex_unlock(&ipclock);
return ret;
}
*data = readl(ipcdev.gcr_mem_base + offset);
mutex_unlock(&ipclock);
return 0;
}
EXPORT_SYMBOL_GPL(intel_pmc_gcr_read);
/**
* intel_pmc_gcr_write() - Write PMC GCR register
* @offset: offset of GCR register from GCR address base
* @data: register update value
*
* Writes the PMC GCR register of given offset with given
* value.
*
* Return: negative value on error or 0 on success.
*/
int intel_pmc_gcr_write(u32 offset, u32 data)
{
int ret;
mutex_lock(&ipclock);
ret = is_gcr_valid(offset);
if (ret < 0) {
mutex_unlock(&ipclock);
return ret;
}
writel(data, ipcdev.gcr_mem_base + offset);
mutex_unlock(&ipclock);
return 0;
}
EXPORT_SYMBOL_GPL(intel_pmc_gcr_write);
/**
* intel_pmc_gcr_update() - Update PMC GCR register bits
* @offset: offset of GCR register from GCR address base
* @mask: bit mask for update operation
* @val: update value
*
* Updates the bits of given GCR register as specified by
* @mask and @val.
*
* Return: negative value on error or 0 on success.
*/
int intel_pmc_gcr_update(u32 offset, u32 mask, u32 val)
{
u32 new_val;
int ret = 0;
mutex_lock(&ipclock);
ret = is_gcr_valid(offset);
if (ret < 0)
goto gcr_ipc_unlock;
new_val = readl(ipcdev.gcr_mem_base + offset);
new_val &= ~mask;
new_val |= val & mask;
writel(new_val, ipcdev.gcr_mem_base + offset);
new_val = readl(ipcdev.gcr_mem_base + offset);
/* check whether the bit update is successful */
if ((new_val & mask) != (val & mask)) {
ret = -EIO;
goto gcr_ipc_unlock;
}
gcr_ipc_unlock:
mutex_unlock(&ipclock);
return ret;
}
EXPORT_SYMBOL_GPL(intel_pmc_gcr_update);
static int update_no_reboot_bit(void *priv, bool set)
{
u32 value = set ? PMC_CFG_NO_REBOOT_EN : PMC_CFG_NO_REBOOT_DIS;
return intel_pmc_gcr_update(PMC_GCR_PMC_CFG_REG,
PMC_CFG_NO_REBOOT_MASK, value);
} }
static int intel_pmc_ipc_check_status(void) static int intel_pmc_ipc_check_status(void)
...@@ -516,15 +639,13 @@ static struct resource tco_res[] = { ...@@ -516,15 +639,13 @@ static struct resource tco_res[] = {
{ {
.flags = IORESOURCE_IO, .flags = IORESOURCE_IO,
}, },
/* GCS */
{
.flags = IORESOURCE_MEM,
},
}; };
static struct itco_wdt_platform_data tco_info = { static struct itco_wdt_platform_data tco_info = {
.name = "Apollo Lake SoC", .name = "Apollo Lake SoC",
.version = 5, .version = 5,
.no_reboot_priv = &ipcdev,
.update_no_reboot_bit = update_no_reboot_bit,
}; };
#define TELEMETRY_RESOURCE_PUNIT_SSRAM 0 #define TELEMETRY_RESOURCE_PUNIT_SSRAM 0
...@@ -581,10 +702,6 @@ static int ipc_create_tco_device(void) ...@@ -581,10 +702,6 @@ static int ipc_create_tco_device(void)
res->start = ipcdev.acpi_io_base + SMI_EN_OFFSET; res->start = ipcdev.acpi_io_base + SMI_EN_OFFSET;
res->end = res->start + SMI_EN_SIZE - 1; res->end = res->start + SMI_EN_SIZE - 1;
res = tco_res + TCO_RESOURCE_GCR_MEM;
res->start = ipcdev.gcr_base + TCO_PMC_OFFSET;
res->end = res->start + TCO_PMC_SIZE - 1;
pdev = platform_device_register_full(&pdevinfo); pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(pdev)) if (IS_ERR(pdev))
return PTR_ERR(pdev); return PTR_ERR(pdev);
...@@ -746,8 +863,7 @@ static int ipc_plat_get_res(struct platform_device *pdev) ...@@ -746,8 +863,7 @@ static int ipc_plat_get_res(struct platform_device *pdev)
} }
ipcdev.ipc_base = addr; ipcdev.ipc_base = addr;
ipcdev.gcr_base = res->start + PLAT_RESOURCE_GCR_OFFSET; ipcdev.gcr_mem_base = addr + PLAT_RESOURCE_GCR_OFFSET;
ipcdev.gcr_size = PLAT_RESOURCE_GCR_SIZE;
dev_info(&pdev->dev, "ipc res: %pR\n", res); dev_info(&pdev->dev, "ipc res: %pR\n", res);
ipcdev.telem_res_inval = 0; ipcdev.telem_res_inval = 0;
...@@ -782,8 +898,8 @@ int intel_pmc_s0ix_counter_read(u64 *data) ...@@ -782,8 +898,8 @@ int intel_pmc_s0ix_counter_read(u64 *data)
if (!ipcdev.has_gcr_regs) if (!ipcdev.has_gcr_regs)
return -EACCES; return -EACCES;
deep = gcr_data_readq(GCR_TELEM_DEEP_S0IX_OFFSET); deep = gcr_data_readq(PMC_GCR_TELEM_DEEP_S0IX_REG);
shlw = gcr_data_readq(GCR_TELEM_SHLW_S0IX_OFFSET); shlw = gcr_data_readq(PMC_GCR_TELEM_SHLW_S0IX_REG);
*data = S0IX_RESIDENCY_IN_USECS(deep, shlw); *data = S0IX_RESIDENCY_IN_USECS(deep, shlw);
......
...@@ -491,6 +491,69 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen, ...@@ -491,6 +491,69 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
} }
EXPORT_SYMBOL(intel_scu_ipc_command); EXPORT_SYMBOL(intel_scu_ipc_command);
#define IPC_SPTR 0x08
#define IPC_DPTR 0x0C
/**
* intel_scu_ipc_raw_command() - IPC command with data and pointers
* @cmd: IPC command code.
* @sub: IPC command sub type.
* @in: input data of this IPC command.
* @inlen: input data length in dwords.
* @out: output data of this IPC command.
* @outlen: output data length in dwords.
* @sptr: data writing to SPTR register.
* @dptr: data writing to DPTR register.
*
* Send an IPC command to SCU with input/output data and source/dest pointers.
*
* Return: an IPC error code or 0 on success.
*/
int intel_scu_ipc_raw_command(int cmd, int sub, u8 *in, int inlen,
u32 *out, int outlen, u32 dptr, u32 sptr)
{
struct intel_scu_ipc_dev *scu = &ipcdev;
int inbuflen = DIV_ROUND_UP(inlen, 4);
u32 inbuf[4];
int i, err;
/* Up to 16 bytes */
if (inbuflen > 4)
return -EINVAL;
mutex_lock(&ipclock);
if (scu->dev == NULL) {
mutex_unlock(&ipclock);
return -ENODEV;
}
writel(dptr, scu->ipc_base + IPC_DPTR);
writel(sptr, scu->ipc_base + IPC_SPTR);
/*
* SRAM controller doesn't support 8-bit writes, it only
* supports 32-bit writes, so we have to copy input data into
* the temporary buffer, and SCU FW will use the inlen to
* determine the actual input data length in the temporary
* buffer.
*/
memcpy(inbuf, in, inlen);
for (i = 0; i < inbuflen; i++)
ipc_data_writel(scu, inbuf[i], 4 * i);
ipc_command(scu, (inlen << 16) | (sub << 12) | cmd);
err = intel_scu_ipc_check_status(scu);
if (!err) {
for (i = 0; i < outlen; i++)
*out++ = ipc_data_readl(scu, 4 * i);
}
mutex_unlock(&ipclock);
return err;
}
EXPORT_SYMBOL_GPL(intel_scu_ipc_raw_command);
/* I2C commands */ /* I2C commands */
#define IPC_I2C_WRITE 1 /* I2C Write command */ #define IPC_I2C_WRITE 1 /* I2C Write command */
#define IPC_I2C_READ 2 /* I2C Read command */ #define IPC_I2C_READ 2 /* I2C Read command */
...@@ -566,21 +629,17 @@ static irqreturn_t ioc(int irq, void *dev_id) ...@@ -566,21 +629,17 @@ static irqreturn_t ioc(int irq, void *dev_id)
*/ */
static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id) static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{ {
int platform; /* Platform type */
int err; int err;
struct intel_scu_ipc_dev *scu = &ipcdev; struct intel_scu_ipc_dev *scu = &ipcdev;
struct intel_scu_ipc_pdata_t *pdata; struct intel_scu_ipc_pdata_t *pdata;
platform = intel_mid_identify_cpu();
if (platform == 0)
return -ENODEV;
if (scu->dev) /* We support only one SCU */ if (scu->dev) /* We support only one SCU */
return -EBUSY; return -EBUSY;
pdata = (struct intel_scu_ipc_pdata_t *)id->driver_data; pdata = (struct intel_scu_ipc_pdata_t *)id->driver_data;
if (!pdata)
return -ENODEV;
scu->dev = &pdev->dev;
scu->irq_mode = pdata->irq_mode; scu->irq_mode = pdata->irq_mode;
err = pcim_enable_device(pdev); err = pcim_enable_device(pdev);
...@@ -593,39 +652,34 @@ static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -593,39 +652,34 @@ static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
init_completion(&scu->cmd_complete); init_completion(&scu->cmd_complete);
err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc",
scu);
if (err)
return err;
scu->ipc_base = pcim_iomap_table(pdev)[0]; scu->ipc_base = pcim_iomap_table(pdev)[0];
scu->i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len); scu->i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);
if (!scu->i2c_base) if (!scu->i2c_base)
return -ENOMEM; return -ENOMEM;
err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc",
scu);
if (err)
return err;
/* Assign device at last */
scu->dev = &pdev->dev;
intel_scu_devices_create(); intel_scu_devices_create();
pci_set_drvdata(pdev, scu); pci_set_drvdata(pdev, scu);
return 0; return 0;
} }
#define SCU_DEVICE(id, pdata) {PCI_VDEVICE(INTEL, id), (kernel_ulong_t)&pdata}
static const struct pci_device_id pci_ids[] = { static const struct pci_device_id pci_ids[] = {
{ SCU_DEVICE(PCI_DEVICE_ID_LINCROFT, intel_scu_ipc_lincroft_pdata),
PCI_VDEVICE(INTEL, PCI_DEVICE_ID_LINCROFT), SCU_DEVICE(PCI_DEVICE_ID_PENWELL, intel_scu_ipc_penwell_pdata),
(kernel_ulong_t)&intel_scu_ipc_lincroft_pdata, SCU_DEVICE(PCI_DEVICE_ID_CLOVERVIEW, intel_scu_ipc_penwell_pdata),
}, { SCU_DEVICE(PCI_DEVICE_ID_TANGIER, intel_scu_ipc_tangier_pdata),
PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PENWELL), {}
(kernel_ulong_t)&intel_scu_ipc_penwell_pdata,
}, {
PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CLOVERVIEW),
(kernel_ulong_t)&intel_scu_ipc_penwell_pdata,
}, {
PCI_VDEVICE(INTEL, PCI_DEVICE_ID_TANGIER),
(kernel_ulong_t)&intel_scu_ipc_tangier_pdata,
}, {
0,
}
}; };
static struct pci_driver ipc_driver = { static struct pci_driver ipc_driver = {
......
...@@ -976,23 +976,15 @@ static int __init msi_laptop_input_setup(void) ...@@ -976,23 +976,15 @@ static int __init msi_laptop_input_setup(void)
err = input_register_device(msi_laptop_input_dev); err = input_register_device(msi_laptop_input_dev);
if (err) if (err)
goto err_free_keymap; goto err_free_dev;
return 0; return 0;
err_free_keymap:
sparse_keymap_free(msi_laptop_input_dev);
err_free_dev: err_free_dev:
input_free_device(msi_laptop_input_dev); input_free_device(msi_laptop_input_dev);
return err; return err;
} }
static void msi_laptop_input_destroy(void)
{
sparse_keymap_free(msi_laptop_input_dev);
input_unregister_device(msi_laptop_input_dev);
}
static int __init load_scm_model_init(struct platform_device *sdev) static int __init load_scm_model_init(struct platform_device *sdev)
{ {
u8 data; u8 data;
...@@ -1037,7 +1029,7 @@ static int __init load_scm_model_init(struct platform_device *sdev) ...@@ -1037,7 +1029,7 @@ static int __init load_scm_model_init(struct platform_device *sdev)
return 0; return 0;
fail_filter: fail_filter:
msi_laptop_input_destroy(); input_unregister_device(msi_laptop_input_dev);
fail_input: fail_input:
rfkill_cleanup(); rfkill_cleanup();
...@@ -1158,7 +1150,7 @@ static void __exit msi_cleanup(void) ...@@ -1158,7 +1150,7 @@ static void __exit msi_cleanup(void)
{ {
if (quirks->load_scm_model) { if (quirks->load_scm_model) {
i8042_remove_filter(msi_laptop_i8042_filter); i8042_remove_filter(msi_laptop_i8042_filter);
msi_laptop_input_destroy(); input_unregister_device(msi_laptop_input_dev);
cancel_delayed_work_sync(&msi_rfkill_dwork); cancel_delayed_work_sync(&msi_rfkill_dwork);
cancel_work_sync(&msi_rfkill_work); cancel_work_sync(&msi_rfkill_work);
rfkill_cleanup(); rfkill_cleanup();
......
...@@ -281,14 +281,12 @@ static int __init msi_wmi_input_setup(void) ...@@ -281,14 +281,12 @@ static int __init msi_wmi_input_setup(void)
err = input_register_device(msi_wmi_input_dev); err = input_register_device(msi_wmi_input_dev);
if (err) if (err)
goto err_free_keymap; goto err_free_dev;
last_pressed = 0; last_pressed = 0;
return 0; return 0;
err_free_keymap:
sparse_keymap_free(msi_wmi_input_dev);
err_free_dev: err_free_dev:
input_free_device(msi_wmi_input_dev); input_free_device(msi_wmi_input_dev);
return err; return err;
...@@ -342,10 +340,8 @@ static int __init msi_wmi_init(void) ...@@ -342,10 +340,8 @@ static int __init msi_wmi_init(void)
if (event_wmi) if (event_wmi)
wmi_remove_notify_handler(event_wmi->guid); wmi_remove_notify_handler(event_wmi->guid);
err_free_input: err_free_input:
if (event_wmi) { if (event_wmi)
sparse_keymap_free(msi_wmi_input_dev);
input_unregister_device(msi_wmi_input_dev); input_unregister_device(msi_wmi_input_dev);
}
return err; return err;
} }
...@@ -353,7 +349,6 @@ static void __exit msi_wmi_exit(void) ...@@ -353,7 +349,6 @@ static void __exit msi_wmi_exit(void)
{ {
if (event_wmi) { if (event_wmi) {
wmi_remove_notify_handler(event_wmi->guid); wmi_remove_notify_handler(event_wmi->guid);
sparse_keymap_free(msi_wmi_input_dev);
input_unregister_device(msi_wmi_input_dev); input_unregister_device(msi_wmi_input_dev);
} }
backlight_device_unregister(backlight); backlight_device_unregister(backlight);
......
...@@ -520,29 +520,17 @@ static int acpi_pcc_init_input(struct pcc_acpi *pcc) ...@@ -520,29 +520,17 @@ static int acpi_pcc_init_input(struct pcc_acpi *pcc)
if (error) { if (error) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
"Unable to register input device\n")); "Unable to register input device\n"));
goto err_free_keymap; goto err_free_dev;
} }
pcc->input_dev = input_dev; pcc->input_dev = input_dev;
return 0; return 0;
err_free_keymap:
sparse_keymap_free(input_dev);
err_free_dev: err_free_dev:
input_free_device(input_dev); input_free_device(input_dev);
return error; return error;
} }
static void acpi_pcc_destroy_input(struct pcc_acpi *pcc)
{
sparse_keymap_free(pcc->input_dev);
input_unregister_device(pcc->input_dev);
/*
* No need to input_free_device() since core input API refcounts
* and free()s the device.
*/
}
/* kernel module interface */ /* kernel module interface */
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
...@@ -640,7 +628,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) ...@@ -640,7 +628,7 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
out_backlight: out_backlight:
backlight_device_unregister(pcc->backlight); backlight_device_unregister(pcc->backlight);
out_input: out_input:
acpi_pcc_destroy_input(pcc); input_unregister_device(pcc->input_dev);
out_sinf: out_sinf:
kfree(pcc->sinf); kfree(pcc->sinf);
out_hotkey: out_hotkey:
...@@ -660,7 +648,7 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device) ...@@ -660,7 +648,7 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device)
backlight_device_unregister(pcc->backlight); backlight_device_unregister(pcc->backlight);
acpi_pcc_destroy_input(pcc); input_unregister_device(pcc->input_dev);
kfree(pcc->sinf); kfree(pcc->sinf);
kfree(pcc); kfree(pcc);
......
...@@ -22,10 +22,10 @@ ...@@ -22,10 +22,10 @@
struct silead_ts_dmi_data { struct silead_ts_dmi_data {
const char *acpi_name; const char *acpi_name;
struct property_entry *properties; const struct property_entry *properties;
}; };
static struct property_entry cube_iwork8_air_props[] = { static const struct property_entry cube_iwork8_air_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1660), PROPERTY_ENTRY_U32("touchscreen-size-x", 1660),
PROPERTY_ENTRY_U32("touchscreen-size-y", 900), PROPERTY_ENTRY_U32("touchscreen-size-y", 900),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
...@@ -39,7 +39,7 @@ static const struct silead_ts_dmi_data cube_iwork8_air_data = { ...@@ -39,7 +39,7 @@ static const struct silead_ts_dmi_data cube_iwork8_air_data = {
.properties = cube_iwork8_air_props, .properties = cube_iwork8_air_props,
}; };
static struct property_entry jumper_ezpad_mini3_props[] = { static const struct property_entry jumper_ezpad_mini3_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 1700), PROPERTY_ENTRY_U32("touchscreen-size-x", 1700),
PROPERTY_ENTRY_U32("touchscreen-size-y", 1150), PROPERTY_ENTRY_U32("touchscreen-size-y", 1150),
PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
...@@ -53,6 +53,33 @@ static const struct silead_ts_dmi_data jumper_ezpad_mini3_data = { ...@@ -53,6 +53,33 @@ static const struct silead_ts_dmi_data jumper_ezpad_mini3_data = {
.properties = jumper_ezpad_mini3_props, .properties = jumper_ezpad_mini3_props,
}; };
static const struct property_entry dexp_ursus_7w_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 890),
PROPERTY_ENTRY_U32("touchscreen-size-y", 630),
PROPERTY_ENTRY_STRING("firmware-name", "gsl1686-dexp-ursus-7w.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
static const struct silead_ts_dmi_data dexp_ursus_7w_data = {
.acpi_name = "MSSL1680:00",
.properties = dexp_ursus_7w_props,
};
static const struct property_entry surftab_wintron70_st70416_6_props[] = {
PROPERTY_ENTRY_U32("touchscreen-size-x", 884),
PROPERTY_ENTRY_U32("touchscreen-size-y", 632),
PROPERTY_ENTRY_STRING("firmware-name",
"gsl1686-surftab-wintron70-st70416-6.fw"),
PROPERTY_ENTRY_U32("silead,max-fingers", 10),
{ }
};
static const struct silead_ts_dmi_data surftab_wintron70_st70416_6_data = {
.acpi_name = "MSSL1680:00",
.properties = surftab_wintron70_st70416_6_props,
};
static const struct dmi_system_id silead_ts_dmi_table[] = { static const struct dmi_system_id silead_ts_dmi_table[] = {
{ {
/* CUBE iwork8 Air */ /* CUBE iwork8 Air */
...@@ -72,24 +99,37 @@ static const struct dmi_system_id silead_ts_dmi_table[] = { ...@@ -72,24 +99,37 @@ static const struct dmi_system_id silead_ts_dmi_table[] = {
DMI_MATCH(DMI_BIOS_VERSION, "jumperx.T87.KFBNEEA"), DMI_MATCH(DMI_BIOS_VERSION, "jumperx.T87.KFBNEEA"),
}, },
}, },
{
/* DEXP Ursus 7W */
.driver_data = (void *)&dexp_ursus_7w_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
DMI_MATCH(DMI_PRODUCT_NAME, "7W"),
},
},
{
/* Trekstor Surftab Wintron 7.0 ST70416-6 */
.driver_data = (void *)&surftab_wintron70_st70416_6_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
DMI_MATCH(DMI_PRODUCT_NAME, "ST70416-6"),
/* Exact match, different versions need different fw */
DMI_MATCH(DMI_BIOS_VERSION, "TREK.G.WI71C.JGBMRBA04"),
},
},
{ }, { },
}; };
static void silead_ts_dmi_add_props(struct device *dev) static const struct silead_ts_dmi_data *silead_ts_data;
static void silead_ts_dmi_add_props(struct i2c_client *client)
{ {
struct i2c_client *client = to_i2c_client(dev); struct device *dev = &client->dev;
const struct dmi_system_id *dmi_id;
const struct silead_ts_dmi_data *ts_data;
int error; int error;
dmi_id = dmi_first_match(silead_ts_dmi_table);
if (!dmi_id)
return;
ts_data = dmi_id->driver_data;
if (has_acpi_companion(dev) && if (has_acpi_companion(dev) &&
!strncmp(ts_data->acpi_name, client->name, I2C_NAME_SIZE)) { !strncmp(silead_ts_data->acpi_name, client->name, I2C_NAME_SIZE)) {
error = device_add_properties(dev, ts_data->properties); error = device_add_properties(dev, silead_ts_data->properties);
if (error) if (error)
dev_err(dev, "failed to add properties: %d\n", error); dev_err(dev, "failed to add properties: %d\n", error);
} }
...@@ -99,10 +139,13 @@ static int silead_ts_dmi_notifier_call(struct notifier_block *nb, ...@@ -99,10 +139,13 @@ static int silead_ts_dmi_notifier_call(struct notifier_block *nb,
unsigned long action, void *data) unsigned long action, void *data)
{ {
struct device *dev = data; struct device *dev = data;
struct i2c_client *client;
switch (action) { switch (action) {
case BUS_NOTIFY_ADD_DEVICE: case BUS_NOTIFY_ADD_DEVICE:
silead_ts_dmi_add_props(dev); client = i2c_verify_client(dev);
if (client)
silead_ts_dmi_add_props(client);
break; break;
default: default:
...@@ -118,8 +161,15 @@ static struct notifier_block silead_ts_dmi_notifier = { ...@@ -118,8 +161,15 @@ static struct notifier_block silead_ts_dmi_notifier = {
static int __init silead_ts_dmi_init(void) static int __init silead_ts_dmi_init(void)
{ {
const struct dmi_system_id *dmi_id;
int error; int error;
dmi_id = dmi_first_match(silead_ts_dmi_table);
if (!dmi_id)
return 0; /* Not an error */
silead_ts_data = dmi_id->driver_data;
error = bus_register_notifier(&i2c_bus_type, &silead_ts_dmi_notifier); error = bus_register_notifier(&i2c_bus_type, &silead_ts_dmi_notifier);
if (error) if (error)
pr_err("%s: failed to register i2c bus notifier: %d\n", pr_err("%s: failed to register i2c bus notifier: %d\n",
......
...@@ -1922,7 +1922,9 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ ...@@ -1922,7 +1922,9 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */
TP_ACPI_HOTKEYSCAN_UNK7, TP_ACPI_HOTKEYSCAN_UNK7,
TP_ACPI_HOTKEYSCAN_UNK8, TP_ACPI_HOTKEYSCAN_UNK8,
TP_ACPI_HOTKEYSCAN_MUTE2, /* Adaptive keyboard keycodes */
TP_ACPI_HOTKEYSCAN_ADAPTIVE_START,
TP_ACPI_HOTKEYSCAN_MUTE2 = TP_ACPI_HOTKEYSCAN_ADAPTIVE_START,
TP_ACPI_HOTKEYSCAN_BRIGHTNESS_ZERO, TP_ACPI_HOTKEYSCAN_BRIGHTNESS_ZERO,
TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL, TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL,
TP_ACPI_HOTKEYSCAN_CLOUD, TP_ACPI_HOTKEYSCAN_CLOUD,
...@@ -1943,6 +1945,15 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */ ...@@ -1943,6 +1945,15 @@ enum { /* hot key scan codes (derived from ACPI DSDT) */
TP_ACPI_HOTKEYSCAN_CAMERA_MODE, TP_ACPI_HOTKEYSCAN_CAMERA_MODE,
TP_ACPI_HOTKEYSCAN_ROTATE_DISPLAY, TP_ACPI_HOTKEYSCAN_ROTATE_DISPLAY,
/* Lenovo extended keymap, starting at 0x1300 */
TP_ACPI_HOTKEYSCAN_EXTENDED_START,
/* first new observed key (star, favorites) is 0x1311 */
TP_ACPI_HOTKEYSCAN_STAR = 69,
TP_ACPI_HOTKEYSCAN_CLIPPING_TOOL2,
TP_ACPI_HOTKEYSCAN_UNK25,
TP_ACPI_HOTKEYSCAN_BLUETOOTH,
TP_ACPI_HOTKEYSCAN_KEYBOARD,
/* Hotkey keymap size */ /* Hotkey keymap size */
TPACPI_HOTKEY_MAP_LEN TPACPI_HOTKEY_MAP_LEN
}; };
...@@ -3250,6 +3261,15 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -3250,6 +3261,15 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
/* No assignment, used for newer Lenovo models */
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN, KEY_UNKNOWN
}, },
/* Generic keymap for Lenovo ThinkPads */ /* Generic keymap for Lenovo ThinkPads */
...@@ -3335,6 +3355,29 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) ...@@ -3335,6 +3355,29 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
KEY_RESERVED, /* Microphone cancellation */ KEY_RESERVED, /* Microphone cancellation */
KEY_RESERVED, /* Camera mode */ KEY_RESERVED, /* Camera mode */
KEY_RESERVED, /* Rotate display, 0x116 */ KEY_RESERVED, /* Rotate display, 0x116 */
/*
* These are found in 2017 models (e.g. T470s, X270).
* The lowest known value is 0x311, which according to
* the manual should launch a user defined favorite
* application.
*
* The offset for these is TP_ACPI_HOTKEYSCAN_EXTENDED_START,
* corresponding to 0x34.
*/
/* (assignments unknown, please report if found) */
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN,
KEY_FAVORITES, /* Favorite app, 0x311 */
KEY_RESERVED, /* Clipping tool */
KEY_RESERVED,
KEY_BLUETOOTH, /* Bluetooth */
KEY_KEYBOARD /* Keyboard, 0x315 */
}, },
}; };
...@@ -3656,7 +3699,6 @@ static const int adaptive_keyboard_modes[] = { ...@@ -3656,7 +3699,6 @@ static const int adaptive_keyboard_modes[] = {
#define DFR_CHANGE_ROW 0x101 #define DFR_CHANGE_ROW 0x101
#define DFR_SHOW_QUICKVIEW_ROW 0x102 #define DFR_SHOW_QUICKVIEW_ROW 0x102
#define FIRST_ADAPTIVE_KEY 0x103 #define FIRST_ADAPTIVE_KEY 0x103
#define ADAPTIVE_KEY_OFFSET 0x020
/* press Fn key a while second, it will switch to Function Mode. Then /* press Fn key a while second, it will switch to Function Mode. Then
* release Fn key, previous mode be restored. * release Fn key, previous mode be restored.
...@@ -3746,13 +3788,15 @@ static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) ...@@ -3746,13 +3788,15 @@ static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode)
default: default:
if (scancode < FIRST_ADAPTIVE_KEY || if (scancode < FIRST_ADAPTIVE_KEY ||
scancode >= FIRST_ADAPTIVE_KEY + TPACPI_HOTKEY_MAP_LEN - scancode >= FIRST_ADAPTIVE_KEY +
ADAPTIVE_KEY_OFFSET) { TP_ACPI_HOTKEYSCAN_EXTENDED_START -
TP_ACPI_HOTKEYSCAN_ADAPTIVE_START) {
pr_info("Unhandled adaptive keyboard key: 0x%x\n", pr_info("Unhandled adaptive keyboard key: 0x%x\n",
scancode); scancode);
return false; return false;
} }
keycode = hotkey_keycode_map[scancode - FIRST_ADAPTIVE_KEY + ADAPTIVE_KEY_OFFSET]; keycode = hotkey_keycode_map[scancode - FIRST_ADAPTIVE_KEY +
TP_ACPI_HOTKEYSCAN_ADAPTIVE_START];
if (keycode != KEY_RESERVED) { if (keycode != KEY_RESERVED) {
mutex_lock(&tpacpi_inputdev_send_mutex); mutex_lock(&tpacpi_inputdev_send_mutex);
...@@ -3777,8 +3821,16 @@ static bool hotkey_notify_hotkey(const u32 hkey, ...@@ -3777,8 +3821,16 @@ static bool hotkey_notify_hotkey(const u32 hkey,
*send_acpi_ev = true; *send_acpi_ev = true;
*ignore_acpi_ev = false; *ignore_acpi_ev = false;
/*
* Original events are in the 0x10XX range, the adaptive keyboard
* found in 2014 X1 Carbon emits events are of 0x11XX. In 2017
* models, additional keys are emitted through 0x13XX.
*/
switch ((hkey >> 8) & 0xf) {
case 0:
if (scancode > 0 &&
scancode <= TP_ACPI_HOTKEYSCAN_ADAPTIVE_START) {
/* HKEY event 0x1001 is scancode 0x00 */ /* HKEY event 0x1001 is scancode 0x00 */
if (scancode > 0 && scancode <= TPACPI_HOTKEY_MAP_LEN) {
scancode--; scancode--;
if (!(hotkey_source_mask & (1 << scancode))) { if (!(hotkey_source_mask & (1 << scancode))) {
tpacpi_input_send_key_masked(scancode); tpacpi_input_send_key_masked(scancode);
...@@ -3787,9 +3839,26 @@ static bool hotkey_notify_hotkey(const u32 hkey, ...@@ -3787,9 +3839,26 @@ static bool hotkey_notify_hotkey(const u32 hkey,
*ignore_acpi_ev = true; *ignore_acpi_ev = true;
} }
return true; return true;
} else { }
break;
case 1:
return adaptive_keyboard_hotkey_notify_hotkey(scancode); return adaptive_keyboard_hotkey_notify_hotkey(scancode);
case 3:
/* Extended keycodes start at 0x300 and our offset into the map
* TP_ACPI_HOTKEYSCAN_EXTENDED_START. The calculated scancode
* will be positive, but might not be in the correct range.
*/
scancode -= (0x300 - TP_ACPI_HOTKEYSCAN_EXTENDED_START);
if (scancode >= TP_ACPI_HOTKEYSCAN_EXTENDED_START &&
scancode < TPACPI_HOTKEY_MAP_LEN) {
tpacpi_input_send_key(scancode);
return true;
}
break;
} }
return false; return false;
} }
......
...@@ -113,14 +113,12 @@ static int acpi_topstar_init_hkey(struct topstar_hkey *hkey) ...@@ -113,14 +113,12 @@ static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
error = input_register_device(input); error = input_register_device(input);
if (error) { if (error) {
pr_err("Unable to register input device\n"); pr_err("Unable to register input device\n");
goto err_free_keymap; goto err_free_dev;
} }
hkey->inputdev = input; hkey->inputdev = input;
return 0; return 0;
err_free_keymap:
sparse_keymap_free(input);
err_free_dev: err_free_dev:
input_free_device(input); input_free_device(input);
return error; return error;
...@@ -157,7 +155,6 @@ static int acpi_topstar_remove(struct acpi_device *device) ...@@ -157,7 +155,6 @@ static int acpi_topstar_remove(struct acpi_device *device)
acpi_topstar_fncx_switch(device, false); acpi_topstar_fncx_switch(device, false);
sparse_keymap_free(tps_hkey->inputdev);
input_unregister_device(tps_hkey->inputdev); input_unregister_device(tps_hkey->inputdev);
kfree(tps_hkey); kfree(tps_hkey);
......
...@@ -96,7 +96,7 @@ static int __init toshiba_wmi_input_setup(void) ...@@ -96,7 +96,7 @@ static int __init toshiba_wmi_input_setup(void)
toshiba_wmi_notify, NULL); toshiba_wmi_notify, NULL);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
err = -EIO; err = -EIO;
goto err_free_keymap; goto err_free_dev;
} }
err = input_register_device(toshiba_wmi_input_dev); err = input_register_device(toshiba_wmi_input_dev);
...@@ -107,8 +107,6 @@ static int __init toshiba_wmi_input_setup(void) ...@@ -107,8 +107,6 @@ static int __init toshiba_wmi_input_setup(void)
err_remove_notifier: err_remove_notifier:
wmi_remove_notify_handler(WMI_EVENT_GUID); wmi_remove_notify_handler(WMI_EVENT_GUID);
err_free_keymap:
sparse_keymap_free(toshiba_wmi_input_dev);
err_free_dev: err_free_dev:
input_free_device(toshiba_wmi_input_dev); input_free_device(toshiba_wmi_input_dev);
return err; return err;
...@@ -117,7 +115,6 @@ static int __init toshiba_wmi_input_setup(void) ...@@ -117,7 +115,6 @@ static int __init toshiba_wmi_input_setup(void)
static void toshiba_wmi_input_destroy(void) static void toshiba_wmi_input_destroy(void)
{ {
wmi_remove_notify_handler(WMI_EVENT_GUID); wmi_remove_notify_handler(WMI_EVENT_GUID);
sparse_keymap_free(toshiba_wmi_input_dev);
input_unregister_device(toshiba_wmi_input_dev); input_unregister_device(toshiba_wmi_input_dev);
} }
......
...@@ -2849,7 +2849,7 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) ...@@ -2849,7 +2849,7 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
error = i8042_install_filter(toshiba_acpi_i8042_filter); error = i8042_install_filter(toshiba_acpi_i8042_filter);
if (error) { if (error) {
pr_err("Error installing key filter\n"); pr_err("Error installing key filter\n");
goto err_free_keymap; goto err_free_dev;
} }
dev->ntfy_supported = 1; dev->ntfy_supported = 1;
...@@ -2880,8 +2880,6 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) ...@@ -2880,8 +2880,6 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
err_remove_filter: err_remove_filter:
if (dev->ntfy_supported) if (dev->ntfy_supported)
i8042_remove_filter(toshiba_acpi_i8042_filter); i8042_remove_filter(toshiba_acpi_i8042_filter);
err_free_keymap:
sparse_keymap_free(dev->hotkey_dev);
err_free_dev: err_free_dev:
input_free_device(dev->hotkey_dev); input_free_device(dev->hotkey_dev);
dev->hotkey_dev = NULL; dev->hotkey_dev = NULL;
...@@ -3018,10 +3016,8 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) ...@@ -3018,10 +3016,8 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
cancel_work_sync(&dev->hotkey_work); cancel_work_sync(&dev->hotkey_work);
} }
if (dev->hotkey_dev) { if (dev->hotkey_dev)
input_unregister_device(dev->hotkey_dev); input_unregister_device(dev->hotkey_dev);
sparse_keymap_free(dev->hotkey_dev);
}
backlight_device_unregister(dev->backlight_dev); backlight_device_unregister(dev->backlight_dev);
......
...@@ -106,6 +106,10 @@ struct iTCO_wdt_private { ...@@ -106,6 +106,10 @@ struct iTCO_wdt_private {
struct pci_dev *pci_dev; struct pci_dev *pci_dev;
/* whether or not the watchdog has been suspended */ /* whether or not the watchdog has been suspended */
bool suspended; bool suspended;
/* no reboot API private data */
void *no_reboot_priv;
/* no reboot update function pointer */
int (*update_no_reboot_bit)(void *p, bool set);
}; };
/* module parameters */ /* module parameters */
...@@ -170,46 +174,68 @@ static inline u32 no_reboot_bit(struct iTCO_wdt_private *p) ...@@ -170,46 +174,68 @@ static inline u32 no_reboot_bit(struct iTCO_wdt_private *p)
return enable_bit; return enable_bit;
} }
static void iTCO_wdt_set_NO_REBOOT_bit(struct iTCO_wdt_private *p) static int update_no_reboot_bit_def(void *priv, bool set)
{ {
u32 val32; return 0;
}
static int update_no_reboot_bit_pci(void *priv, bool set)
{
struct iTCO_wdt_private *p = priv;
u32 val32 = 0, newval32 = 0;
/* Set the NO_REBOOT bit: this disables reboots */
if (p->iTCO_version >= 2) {
val32 = readl(p->gcs_pmc);
val32 |= no_reboot_bit(p);
writel(val32, p->gcs_pmc);
} else if (p->iTCO_version == 1) {
pci_read_config_dword(p->pci_dev, 0xd4, &val32); pci_read_config_dword(p->pci_dev, 0xd4, &val32);
if (set)
val32 |= no_reboot_bit(p); val32 |= no_reboot_bit(p);
else
val32 &= ~no_reboot_bit(p);
pci_write_config_dword(p->pci_dev, 0xd4, val32); pci_write_config_dword(p->pci_dev, 0xd4, val32);
} pci_read_config_dword(p->pci_dev, 0xd4, &newval32);
/* make sure the update is successful */
if (val32 != newval32)
return -EIO;
return 0;
} }
static int iTCO_wdt_unset_NO_REBOOT_bit(struct iTCO_wdt_private *p) static int update_no_reboot_bit_mem(void *priv, bool set)
{ {
u32 enable_bit = no_reboot_bit(p); struct iTCO_wdt_private *p = priv;
u32 val32 = 0; u32 val32 = 0, newval32 = 0;
/* Unset the NO_REBOOT bit: this enables reboots */
if (p->iTCO_version >= 2) {
val32 = readl(p->gcs_pmc); val32 = readl(p->gcs_pmc);
val32 &= ~enable_bit; if (set)
val32 |= no_reboot_bit(p);
else
val32 &= ~no_reboot_bit(p);
writel(val32, p->gcs_pmc); writel(val32, p->gcs_pmc);
newval32 = readl(p->gcs_pmc);
val32 = readl(p->gcs_pmc); /* make sure the update is successful */
} else if (p->iTCO_version == 1) { if (val32 != newval32)
pci_read_config_dword(p->pci_dev, 0xd4, &val32); return -EIO;
val32 &= ~enable_bit;
pci_write_config_dword(p->pci_dev, 0xd4, val32);
pci_read_config_dword(p->pci_dev, 0xd4, &val32); return 0;
}
static void iTCO_wdt_no_reboot_bit_setup(struct iTCO_wdt_private *p,
struct itco_wdt_platform_data *pdata)
{
if (pdata->update_no_reboot_bit) {
p->update_no_reboot_bit = pdata->update_no_reboot_bit;
p->no_reboot_priv = pdata->no_reboot_priv;
return;
} }
if (val32 & enable_bit) if (p->iTCO_version >= 2)
return -EIO; p->update_no_reboot_bit = update_no_reboot_bit_mem;
else if (p->iTCO_version == 1)
p->update_no_reboot_bit = update_no_reboot_bit_pci;
else
p->update_no_reboot_bit = update_no_reboot_bit_def;
return 0; p->no_reboot_priv = p;
} }
static int iTCO_wdt_start(struct watchdog_device *wd_dev) static int iTCO_wdt_start(struct watchdog_device *wd_dev)
...@@ -222,7 +248,7 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev) ...@@ -222,7 +248,7 @@ static int iTCO_wdt_start(struct watchdog_device *wd_dev)
iTCO_vendor_pre_start(p->smi_res, wd_dev->timeout); iTCO_vendor_pre_start(p->smi_res, wd_dev->timeout);
/* disable chipset's NO_REBOOT bit */ /* disable chipset's NO_REBOOT bit */
if (iTCO_wdt_unset_NO_REBOOT_bit(p)) { if (p->update_no_reboot_bit(p->no_reboot_priv, false)) {
spin_unlock(&p->io_lock); spin_unlock(&p->io_lock);
pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n"); pr_err("failed to reset NO_REBOOT flag, reboot disabled by hardware/BIOS\n");
return -EIO; return -EIO;
...@@ -263,7 +289,7 @@ static int iTCO_wdt_stop(struct watchdog_device *wd_dev) ...@@ -263,7 +289,7 @@ static int iTCO_wdt_stop(struct watchdog_device *wd_dev)
val = inw(TCO1_CNT(p)); val = inw(TCO1_CNT(p));
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */ /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
iTCO_wdt_set_NO_REBOOT_bit(p); p->update_no_reboot_bit(p->no_reboot_priv, true);
spin_unlock(&p->io_lock); spin_unlock(&p->io_lock);
...@@ -428,11 +454,13 @@ static int iTCO_wdt_probe(struct platform_device *pdev) ...@@ -428,11 +454,13 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
p->iTCO_version = pdata->version; p->iTCO_version = pdata->version;
p->pci_dev = to_pci_dev(dev->parent); p->pci_dev = to_pci_dev(dev->parent);
iTCO_wdt_no_reboot_bit_setup(p, pdata);
/* /*
* Get the Memory-Mapped GCS or PMC register, we need it for the * Get the Memory-Mapped GCS or PMC register, we need it for the
* NO_REBOOT flag (TCO v2 and v3). * NO_REBOOT flag (TCO v2 and v3).
*/ */
if (p->iTCO_version >= 2) { if (p->iTCO_version >= 2 && !pdata->update_no_reboot_bit) {
p->gcs_pmc_res = platform_get_resource(pdev, p->gcs_pmc_res = platform_get_resource(pdev,
IORESOURCE_MEM, IORESOURCE_MEM,
ICH_RES_MEM_GCS_PMC); ICH_RES_MEM_GCS_PMC);
...@@ -442,14 +470,14 @@ static int iTCO_wdt_probe(struct platform_device *pdev) ...@@ -442,14 +470,14 @@ static int iTCO_wdt_probe(struct platform_device *pdev)
} }
/* Check chipset's NO_REBOOT bit */ /* Check chipset's NO_REBOOT bit */
if (iTCO_wdt_unset_NO_REBOOT_bit(p) && if (p->update_no_reboot_bit(p->no_reboot_priv, false) &&
iTCO_vendor_check_noreboot_on()) { iTCO_vendor_check_noreboot_on()) {
pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n"); pr_info("unable to reset NO_REBOOT flag, device disabled by hardware/BIOS\n");
return -ENODEV; /* Cannot reset NO_REBOOT bit */ return -ENODEV; /* Cannot reset NO_REBOOT bit */
} }
/* Set the NO_REBOOT bit to prevent later reboots, just for sure */ /* Set the NO_REBOOT bit to prevent later reboots, just for sure */
iTCO_wdt_set_NO_REBOOT_bit(p); p->update_no_reboot_bit(p->no_reboot_priv, true);
/* The TCO logic uses the TCO_EN bit in the SMI_EN register */ /* The TCO logic uses the TCO_EN bit in the SMI_EN register */
if (!devm_request_region(dev, p->smi_res->start, if (!devm_request_region(dev, p->smi_res->start,
......
...@@ -14,6 +14,10 @@ ...@@ -14,6 +14,10 @@
struct itco_wdt_platform_data { struct itco_wdt_platform_data {
char name[32]; char name[32];
unsigned int version; unsigned int version;
/* private data to be passed to update_no_reboot_bit API */
void *no_reboot_priv;
/* pointer for platform specific no reboot update function */
int (*update_no_reboot_bit)(void *priv, bool set);
}; };
#endif /* _ITCO_WDT_H_ */ #endif /* _ITCO_WDT_H_ */
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