Commit a7726f6b authored by Linus Torvalds's avatar Linus Torvalds

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

Pull x86 platform driver updates from Andy Shevchenko:

 - Dell SMBIOS driver fixed against memory leaks.

 - The fujitsu-laptop driver is cleaned up and now supports hotkeys for
   Lifebook U7x7 models. Besides that the typo introduced by one of
   previous clean up series has been fixed.

 - Specific to x86-based laptops HID device now supports
   KEY_ROTATE_LOCK_TOGGLE event which is emitted, for example, by Wacom
   MobileStudio Pro 13.

 - Turbo MAX 3 technology is enabled for the rest of platforms that
   support Hardware-P-States feature which have core priority described
   by ACPI CPPC table.

 - Mellanox on x86 gets better support of I2C bus in use including
   support of hotpluggable ones.

 - Silead touchscreen is enabled on two tablet models, i.e Yours Y8W81
   and I.T.Works TW701.

 - From now on the second fan on Thinkpad P50 is supported.

 - The topstar-laptop driver is reworked to support new models, in
   particular Topstar U931.

* tag 'platform-drivers-x86-v4.17-1' of git://git.infradead.org/linux-platform-drivers-x86: (41 commits)
  platform/x86: thinkpad_acpi: Add 2nd Fan Support for Thinkpad P50
  platform/x86: dell-smbios: Fix memory leaks in build_tokens_sysfs()
  intel-hid: support KEY_ROTATE_LOCK_TOGGLE
  intel-hid: clean up and sort header files
  platform/x86: silead_dmi: Add entry for the Yours Y8W81 tablet
  platform/x86: fujitsu-laptop: Support Lifebook U7x7 hotkeys
  platform/x86: mlx-platform: Add physical bus number auto detection
  platform/mellanox: mlxreg-hotplug: Change input for device create routine
  platform/x86: mlx-platform: Add deffered bus functionality
  platform/x86: mlx-platform: Use define for the channel numbers
  platform/x86: fujitsu-laptop: Revert UNSUPPORTED_CMD back to an int
  platform/x86: Fix dell driver init order
  platform/x86: dell-smbios: Resolve dependency error on ACPI_WMI
  platform/x86: dell-smbios: Resolve dependency error on DCDBAS
  platform/x86: Allow for SMBIOS backend defaults
  platform/x86: dell-smbios: Link all dell-smbios-* modules together
  platform/x86: dell-smbios: Rename dell-smbios source to dell-smbios-base
  platform/x86: dell-smbios: Correct some style warnings
  platform/x86: wmi: Fix misuse of vsprintf extension %pULL
  platform/x86: intel-hid: Reset wakeup capable flag on removal
  ...
parents 1b02dcb9 a986c75a
...@@ -93,9 +93,11 @@ struct mlxreg_hotplug_priv_data { ...@@ -93,9 +93,11 @@ struct mlxreg_hotplug_priv_data {
bool after_probe; bool after_probe;
}; };
static int mlxreg_hotplug_device_create(struct device *dev, static int mlxreg_hotplug_device_create(struct mlxreg_hotplug_priv_data *priv,
struct mlxreg_core_data *data) struct mlxreg_core_data *data)
{ {
struct mlxreg_core_hotplug_platform_data *pdata;
/* /*
* Return if adapter number is negative. It could be in case hotplug * Return if adapter number is negative. It could be in case hotplug
* event is not associated with hotplug device. * event is not associated with hotplug device.
...@@ -103,19 +105,21 @@ static int mlxreg_hotplug_device_create(struct device *dev, ...@@ -103,19 +105,21 @@ static int mlxreg_hotplug_device_create(struct device *dev,
if (data->hpdev.nr < 0) if (data->hpdev.nr < 0)
return 0; return 0;
data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr); pdata = dev_get_platdata(&priv->pdev->dev);
data->hpdev.adapter = i2c_get_adapter(data->hpdev.nr +
pdata->shift_nr);
if (!data->hpdev.adapter) { if (!data->hpdev.adapter) {
dev_err(dev, "Failed to get adapter for bus %d\n", dev_err(priv->dev, "Failed to get adapter for bus %d\n",
data->hpdev.nr); data->hpdev.nr + pdata->shift_nr);
return -EFAULT; return -EFAULT;
} }
data->hpdev.client = i2c_new_device(data->hpdev.adapter, data->hpdev.client = i2c_new_device(data->hpdev.adapter,
data->hpdev.brdinfo); data->hpdev.brdinfo);
if (!data->hpdev.client) { if (!data->hpdev.client) {
dev_err(dev, "Failed to create client %s at bus %d at addr 0x%02x\n", dev_err(priv->dev, "Failed to create client %s at bus %d at addr 0x%02x\n",
data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->type, data->hpdev.nr +
data->hpdev.brdinfo->addr); pdata->shift_nr, data->hpdev.brdinfo->addr);
i2c_put_adapter(data->hpdev.adapter); i2c_put_adapter(data->hpdev.adapter);
data->hpdev.adapter = NULL; data->hpdev.adapter = NULL;
...@@ -270,10 +274,10 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv, ...@@ -270,10 +274,10 @@ mlxreg_hotplug_work_helper(struct mlxreg_hotplug_priv_data *priv,
if (item->inversed) if (item->inversed)
mlxreg_hotplug_device_destroy(data); mlxreg_hotplug_device_destroy(data);
else else
mlxreg_hotplug_device_create(priv->dev, data); mlxreg_hotplug_device_create(priv, data);
} else { } else {
if (item->inversed) if (item->inversed)
mlxreg_hotplug_device_create(priv->dev, data); mlxreg_hotplug_device_create(priv, data);
else else
mlxreg_hotplug_device_destroy(data); mlxreg_hotplug_device_destroy(data);
} }
...@@ -319,7 +323,7 @@ mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv, ...@@ -319,7 +323,7 @@ mlxreg_hotplug_health_work_helper(struct mlxreg_hotplug_priv_data *priv,
if (regval == MLXREG_HOTPLUG_HEALTH_MASK) { if (regval == MLXREG_HOTPLUG_HEALTH_MASK) {
if ((data->health_cntr++ == MLXREG_HOTPLUG_RST_CNTR) || if ((data->health_cntr++ == MLXREG_HOTPLUG_RST_CNTR) ||
!priv->after_probe) { !priv->after_probe) {
mlxreg_hotplug_device_create(priv->dev, data); mlxreg_hotplug_device_create(priv, data);
data->attached = true; data->attached = true;
} }
} else { } else {
...@@ -550,6 +554,7 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev) ...@@ -550,6 +554,7 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev)
{ {
struct mlxreg_core_hotplug_platform_data *pdata; struct mlxreg_core_hotplug_platform_data *pdata;
struct mlxreg_hotplug_priv_data *priv; struct mlxreg_hotplug_priv_data *priv;
struct i2c_adapter *deferred_adap;
int err; int err;
pdata = dev_get_platdata(&pdev->dev); pdata = dev_get_platdata(&pdev->dev);
...@@ -558,6 +563,12 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev) ...@@ -558,6 +563,12 @@ static int mlxreg_hotplug_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
/* Defer probing if the necessary adapter is not configured yet. */
deferred_adap = i2c_get_adapter(pdata->deferred_nr);
if (!deferred_adap)
return -EPROBE_DEFER;
i2c_put_adapter(deferred_adap);
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
......
...@@ -757,6 +757,8 @@ config TOPSTAR_LAPTOP ...@@ -757,6 +757,8 @@ config TOPSTAR_LAPTOP
depends on ACPI depends on ACPI
depends on INPUT depends on INPUT
select INPUT_SPARSEKMAP select INPUT_SPARSEKMAP
select LEDS_CLASS
select NEW_LEDS
---help--- ---help---
This driver adds support for hotkeys found on Topstar laptops. This driver adds support for hotkeys found on Topstar laptops.
...@@ -1174,6 +1176,7 @@ config INTEL_TELEMETRY ...@@ -1174,6 +1176,7 @@ config INTEL_TELEMETRY
config MLX_PLATFORM config MLX_PLATFORM
tristate "Mellanox Technologies platform support" tristate "Mellanox Technologies platform support"
depends on I2C && REGMAP
---help--- ---help---
This option enables system support for the Mellanox Technologies This option enables system support for the Mellanox Technologies
platform. The Mellanox systems provide data center networking platform. The Mellanox systems provide data center networking
......
...@@ -514,7 +514,7 @@ static int build_tokens_sysfs(struct platform_device *dev) ...@@ -514,7 +514,7 @@ static int build_tokens_sysfs(struct platform_device *dev)
continue; continue;
loop_fail_create_value: loop_fail_create_value:
kfree(value_name); kfree(location_name);
goto out_unwind_strings; goto out_unwind_strings;
} }
smbios_attribute_group.attrs = token_attrs; smbios_attribute_group.attrs = token_attrs;
...@@ -525,7 +525,7 @@ static int build_tokens_sysfs(struct platform_device *dev) ...@@ -525,7 +525,7 @@ static int build_tokens_sysfs(struct platform_device *dev)
return 0; return 0;
out_unwind_strings: out_unwind_strings:
for (i = i-1; i > 0; i--) { while (i--) {
kfree(token_location_attrs[i].attr.name); kfree(token_location_attrs[i].attr.name);
kfree(token_value_attrs[i].attr.name); kfree(token_value_attrs[i].attr.name);
} }
......
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/bitops.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/fb.h> #include <linux/fb.h>
...@@ -61,7 +62,6 @@ ...@@ -61,7 +62,6 @@
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h>
#include <acpi/video.h> #include <acpi/video.h>
#define FUJITSU_DRIVER_VERSION "0.6.0" #define FUJITSU_DRIVER_VERSION "0.6.0"
...@@ -76,42 +76,52 @@ ...@@ -76,42 +76,52 @@
#define ACPI_FUJITSU_LAPTOP_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver" #define ACPI_FUJITSU_LAPTOP_DRIVER_NAME "Fujitsu laptop FUJ02E3 ACPI hotkeys driver"
#define ACPI_FUJITSU_LAPTOP_DEVICE_NAME "Fujitsu FUJ02E3" #define ACPI_FUJITSU_LAPTOP_DEVICE_NAME "Fujitsu FUJ02E3"
#define ACPI_FUJITSU_NOTIFY_CODE1 0x80 #define ACPI_FUJITSU_NOTIFY_CODE 0x80
/* FUNC interface - command values */ /* FUNC interface - command values */
#define FUNC_FLAGS 0x1000 #define FUNC_FLAGS BIT(12)
#define FUNC_LEDS 0x1001 #define FUNC_LEDS (BIT(12) | BIT(0))
#define FUNC_BUTTONS 0x1002 #define FUNC_BUTTONS (BIT(12) | BIT(1))
#define FUNC_BACKLIGHT 0x1004 #define FUNC_BACKLIGHT (BIT(12) | BIT(2))
/* FUNC interface - responses */ /* FUNC interface - responses */
#define UNSUPPORTED_CMD 0x80000000 #define UNSUPPORTED_CMD 0x80000000
/* FUNC interface - status flags */ /* FUNC interface - status flags */
#define FLAG_RFKILL 0x020 #define FLAG_RFKILL BIT(5)
#define FLAG_LID 0x100 #define FLAG_LID BIT(8)
#define FLAG_DOCK 0x200 #define FLAG_DOCK BIT(9)
/* FUNC interface - LED control */ /* FUNC interface - LED control */
#define FUNC_LED_OFF 0x1 #define FUNC_LED_OFF BIT(0)
#define FUNC_LED_ON 0x30001 #define FUNC_LED_ON (BIT(0) | BIT(16) | BIT(17))
#define KEYBOARD_LAMPS 0x100 #define LOGOLAMP_POWERON BIT(13)
#define LOGOLAMP_POWERON 0x2000 #define LOGOLAMP_ALWAYS BIT(14)
#define LOGOLAMP_ALWAYS 0x4000 #define KEYBOARD_LAMPS BIT(8)
#define RADIO_LED_ON 0x20 #define RADIO_LED_ON BIT(5)
#define ECO_LED 0x10000 #define ECO_LED BIT(16)
#define ECO_LED_ON 0x80000 #define ECO_LED_ON BIT(19)
/* Hotkey details */ /* FUNC interface - backlight power control */
#define KEY1_CODE 0x410 /* codes for the keys in the GIRB register */ #define BACKLIGHT_PARAM_POWER BIT(2)
#define BACKLIGHT_OFF (BIT(0) | BIT(1))
#define BACKLIGHT_ON 0
/* Scancodes read from the GIRB register */
#define KEY1_CODE 0x410
#define KEY2_CODE 0x411 #define KEY2_CODE 0x411
#define KEY3_CODE 0x412 #define KEY3_CODE 0x412
#define KEY4_CODE 0x413 #define KEY4_CODE 0x413
#define KEY5_CODE 0x420 #define KEY5_CODE 0x420
/* Hotkey ringbuffer limits */
#define MAX_HOTKEY_RINGBUFFER_SIZE 100 #define MAX_HOTKEY_RINGBUFFER_SIZE 100
#define RINGBUFFERSIZE 40 #define RINGBUFFERSIZE 40
/* Module parameters */
static int use_alt_lcd_levels = -1;
static bool disable_brightness_adjust;
/* Device controlling the backlight and associated keys */ /* Device controlling the backlight and associated keys */
struct fujitsu_bl { struct fujitsu_bl {
struct input_dev *input; struct input_dev *input;
...@@ -122,8 +132,6 @@ struct fujitsu_bl { ...@@ -122,8 +132,6 @@ struct fujitsu_bl {
}; };
static struct fujitsu_bl *fujitsu_bl; static struct fujitsu_bl *fujitsu_bl;
static int use_alt_lcd_levels = -1;
static bool disable_brightness_adjust;
/* Device used to access hotkeys and other features on the laptop */ /* Device used to access hotkeys and other features on the laptop */
struct fujitsu_laptop { struct fujitsu_laptop {
...@@ -256,9 +264,11 @@ static int bl_update_status(struct backlight_device *b) ...@@ -256,9 +264,11 @@ static int bl_update_status(struct backlight_device *b)
if (fext) { if (fext) {
if (b->props.power == FB_BLANK_POWERDOWN) if (b->props.power == FB_BLANK_POWERDOWN)
call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x3); call_fext_func(fext, FUNC_BACKLIGHT, 0x1,
BACKLIGHT_PARAM_POWER, BACKLIGHT_OFF);
else else
call_fext_func(fext, FUNC_BACKLIGHT, 0x1, 0x4, 0x0); call_fext_func(fext, FUNC_BACKLIGHT, 0x1,
BACKLIGHT_PARAM_POWER, BACKLIGHT_ON);
} }
return set_lcd_level(device, b->props.brightness); return set_lcd_level(device, b->props.brightness);
...@@ -385,7 +395,7 @@ static int fujitsu_backlight_register(struct acpi_device *device) ...@@ -385,7 +395,7 @@ static int fujitsu_backlight_register(struct acpi_device *device)
static int acpi_fujitsu_bl_add(struct acpi_device *device) static int acpi_fujitsu_bl_add(struct acpi_device *device)
{ {
struct fujitsu_bl *priv; struct fujitsu_bl *priv;
int error; int ret;
if (acpi_video_get_backlight_type() != acpi_backlight_vendor) if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
return -ENODEV; return -ENODEV;
...@@ -399,10 +409,6 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device) ...@@ -399,10 +409,6 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device)
strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); strcpy(acpi_device_class(device), ACPI_FUJITSU_CLASS);
device->driver_data = priv; device->driver_data = priv;
error = acpi_fujitsu_bl_input_setup(device);
if (error)
return error;
pr_info("ACPI: %s [%s]\n", pr_info("ACPI: %s [%s]\n",
acpi_device_name(device), acpi_device_bid(device)); acpi_device_name(device), acpi_device_bid(device));
...@@ -410,11 +416,11 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device) ...@@ -410,11 +416,11 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device)
priv->max_brightness = FUJITSU_LCD_N_LEVELS; priv->max_brightness = FUJITSU_LCD_N_LEVELS;
get_lcd_level(device); get_lcd_level(device);
error = fujitsu_backlight_register(device); ret = acpi_fujitsu_bl_input_setup(device);
if (error) if (ret)
return error; return ret;
return 0; return fujitsu_backlight_register(device);
} }
/* Brightness notify */ /* Brightness notify */
...@@ -424,7 +430,7 @@ static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event) ...@@ -424,7 +430,7 @@ static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event)
struct fujitsu_bl *priv = acpi_driver_data(device); struct fujitsu_bl *priv = acpi_driver_data(device);
int oldb, newb; int oldb, newb;
if (event != ACPI_FUJITSU_NOTIFY_CODE1) { if (event != ACPI_FUJITSU_NOTIFY_CODE) {
acpi_handle_info(device->handle, "unsupported event [0x%x]\n", acpi_handle_info(device->handle, "unsupported event [0x%x]\n",
event); event);
sparse_keymap_report_event(priv->input, -1, 1, true); sparse_keymap_report_event(priv->input, -1, 1, true);
...@@ -455,7 +461,9 @@ static const struct key_entry keymap_default[] = { ...@@ -455,7 +461,9 @@ static const struct key_entry keymap_default[] = {
{ KE_KEY, KEY3_CODE, { KEY_PROG3 } }, { KE_KEY, KEY3_CODE, { KEY_PROG3 } },
{ KE_KEY, KEY4_CODE, { KEY_PROG4 } }, { KE_KEY, KEY4_CODE, { KEY_PROG4 } },
{ KE_KEY, KEY5_CODE, { KEY_RFKILL } }, { KE_KEY, KEY5_CODE, { KEY_RFKILL } },
{ KE_KEY, BIT(5), { KEY_RFKILL } },
{ KE_KEY, BIT(26), { KEY_TOUCHPAD_TOGGLE } }, { KE_KEY, BIT(26), { KEY_TOUCHPAD_TOGGLE } },
{ KE_KEY, BIT(29), { KEY_MICMUTE } },
{ KE_END, 0 } { KE_END, 0 }
}; };
...@@ -693,7 +701,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) ...@@ -693,7 +701,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
{ {
struct fujitsu_laptop *priv = acpi_driver_data(device); struct fujitsu_laptop *priv = acpi_driver_data(device);
struct led_classdev *led; struct led_classdev *led;
int result; int ret;
if (call_fext_func(device, if (call_fext_func(device,
FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
...@@ -704,9 +712,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) ...@@ -704,9 +712,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
led->name = "fujitsu::logolamp"; led->name = "fujitsu::logolamp";
led->brightness_set_blocking = logolamp_set; led->brightness_set_blocking = logolamp_set;
led->brightness_get = logolamp_get; led->brightness_get = logolamp_get;
result = devm_led_classdev_register(&device->dev, led); ret = devm_led_classdev_register(&device->dev, led);
if (result) if (ret)
return result; return ret;
} }
if ((call_fext_func(device, if ((call_fext_func(device,
...@@ -719,9 +727,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) ...@@ -719,9 +727,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
led->name = "fujitsu::kblamps"; led->name = "fujitsu::kblamps";
led->brightness_set_blocking = kblamps_set; led->brightness_set_blocking = kblamps_set;
led->brightness_get = kblamps_get; led->brightness_get = kblamps_get;
result = devm_led_classdev_register(&device->dev, led); ret = devm_led_classdev_register(&device->dev, led);
if (result) if (ret)
return result; return ret;
} }
/* /*
...@@ -742,9 +750,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) ...@@ -742,9 +750,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
led->brightness_set_blocking = radio_led_set; led->brightness_set_blocking = radio_led_set;
led->brightness_get = radio_led_get; led->brightness_get = radio_led_get;
led->default_trigger = "rfkill-any"; led->default_trigger = "rfkill-any";
result = devm_led_classdev_register(&device->dev, led); ret = devm_led_classdev_register(&device->dev, led);
if (result) if (ret)
return result; return ret;
} }
/* Support for eco led is not always signaled in bit corresponding /* Support for eco led is not always signaled in bit corresponding
...@@ -762,9 +770,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) ...@@ -762,9 +770,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
led->name = "fujitsu::eco_led"; led->name = "fujitsu::eco_led";
led->brightness_set_blocking = eco_led_set; led->brightness_set_blocking = eco_led_set;
led->brightness_get = eco_led_get; led->brightness_get = eco_led_get;
result = devm_led_classdev_register(&device->dev, led); ret = devm_led_classdev_register(&device->dev, led);
if (result) if (ret)
return result; return ret;
} }
return 0; return 0;
...@@ -773,8 +781,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) ...@@ -773,8 +781,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device)
static int acpi_fujitsu_laptop_add(struct acpi_device *device) static int acpi_fujitsu_laptop_add(struct acpi_device *device)
{ {
struct fujitsu_laptop *priv; struct fujitsu_laptop *priv;
int error; int ret, i = 0;
int i;
priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) if (!priv)
...@@ -789,23 +796,16 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) ...@@ -789,23 +796,16 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
/* kfifo */ /* kfifo */
spin_lock_init(&priv->fifo_lock); spin_lock_init(&priv->fifo_lock);
error = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int), ret = kfifo_alloc(&priv->fifo, RINGBUFFERSIZE * sizeof(int),
GFP_KERNEL); GFP_KERNEL);
if (error) { if (ret)
pr_err("kfifo_alloc failed\n"); return ret;
goto err_stop;
}
error = acpi_fujitsu_laptop_input_setup(device);
if (error)
goto err_free_fifo;
pr_info("ACPI: %s [%s]\n", pr_info("ACPI: %s [%s]\n",
acpi_device_name(device), acpi_device_bid(device)); acpi_device_name(device), acpi_device_bid(device));
i = 0; while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 &&
while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 i++ < MAX_HOTKEY_RINGBUFFER_SIZE)
&& (i++) < MAX_HOTKEY_RINGBUFFER_SIZE)
; /* No action, result is discarded */ ; /* No action, result is discarded */
acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n", acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n",
i); i);
...@@ -829,26 +829,31 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) ...@@ -829,26 +829,31 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device)
/* Sync backlight power status */ /* Sync backlight power status */
if (fujitsu_bl && fujitsu_bl->bl_device && if (fujitsu_bl && fujitsu_bl->bl_device &&
acpi_video_get_backlight_type() == acpi_backlight_vendor) { acpi_video_get_backlight_type() == acpi_backlight_vendor) {
if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2, 0x4, 0x0) == 3) if (call_fext_func(fext, FUNC_BACKLIGHT, 0x2,
BACKLIGHT_PARAM_POWER, 0x0) == BACKLIGHT_OFF)
fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN; fujitsu_bl->bl_device->props.power = FB_BLANK_POWERDOWN;
else else
fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK; fujitsu_bl->bl_device->props.power = FB_BLANK_UNBLANK;
} }
error = acpi_fujitsu_laptop_leds_register(device); ret = acpi_fujitsu_laptop_input_setup(device);
if (error) if (ret)
goto err_free_fifo;
ret = acpi_fujitsu_laptop_leds_register(device);
if (ret)
goto err_free_fifo; goto err_free_fifo;
error = fujitsu_laptop_platform_add(device); ret = fujitsu_laptop_platform_add(device);
if (error) if (ret)
goto err_free_fifo; goto err_free_fifo;
return 0; return 0;
err_free_fifo: err_free_fifo:
kfifo_free(&priv->fifo); kfifo_free(&priv->fifo);
err_stop:
return error; return ret;
} }
static int acpi_fujitsu_laptop_remove(struct acpi_device *device) static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
...@@ -865,11 +870,11 @@ static int acpi_fujitsu_laptop_remove(struct acpi_device *device) ...@@ -865,11 +870,11 @@ static int acpi_fujitsu_laptop_remove(struct acpi_device *device)
static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode) static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode)
{ {
struct fujitsu_laptop *priv = acpi_driver_data(device); struct fujitsu_laptop *priv = acpi_driver_data(device);
int status; int ret;
status = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode, ret = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode,
sizeof(scancode), &priv->fifo_lock); sizeof(scancode), &priv->fifo_lock);
if (status != sizeof(scancode)) { if (ret != sizeof(scancode)) {
dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n", dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n",
scancode); scancode);
return; return;
...@@ -882,13 +887,12 @@ static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode) ...@@ -882,13 +887,12 @@ static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode)
static void acpi_fujitsu_laptop_release(struct acpi_device *device) static void acpi_fujitsu_laptop_release(struct acpi_device *device)
{ {
struct fujitsu_laptop *priv = acpi_driver_data(device); struct fujitsu_laptop *priv = acpi_driver_data(device);
int scancode, status; int scancode, ret;
while (true) { while (true) {
status = kfifo_out_locked(&priv->fifo, ret = kfifo_out_locked(&priv->fifo, (unsigned char *)&scancode,
(unsigned char *)&scancode,
sizeof(scancode), &priv->fifo_lock); sizeof(scancode), &priv->fifo_lock);
if (status != sizeof(scancode)) if (ret != sizeof(scancode))
return; return;
sparse_keymap_report_event(priv->input, scancode, 0, false); sparse_keymap_report_event(priv->input, scancode, 0, false);
dev_dbg(&priv->input->dev, dev_dbg(&priv->input->dev,
...@@ -899,10 +903,10 @@ static void acpi_fujitsu_laptop_release(struct acpi_device *device) ...@@ -899,10 +903,10 @@ static void acpi_fujitsu_laptop_release(struct acpi_device *device)
static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event) static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
{ {
struct fujitsu_laptop *priv = acpi_driver_data(device); struct fujitsu_laptop *priv = acpi_driver_data(device);
int scancode, i = 0; int scancode, i = 0, ret;
unsigned int irb; unsigned int irb;
if (event != ACPI_FUJITSU_NOTIFY_CODE1) { if (event != ACPI_FUJITSU_NOTIFY_CODE) {
acpi_handle_info(device->handle, "Unsupported event [0x%x]\n", acpi_handle_info(device->handle, "Unsupported event [0x%x]\n",
event); event);
sparse_keymap_report_event(priv->input, -1, 1, true); sparse_keymap_report_event(priv->input, -1, 1, true);
...@@ -930,9 +934,18 @@ static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event) ...@@ -930,9 +934,18 @@ static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event)
* E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is * E736/E746/E756), the touchpad toggle hotkey (Fn+F4) is
* handled in software; its state is queried using FUNC_FLAGS * handled in software; its state is queried using FUNC_FLAGS
*/ */
if ((priv->flags_supported & BIT(26)) && if (priv->flags_supported & (BIT(5) | BIT(26) | BIT(29))) {
(call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0) & BIT(26))) ret = call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0);
sparse_keymap_report_event(priv->input, BIT(26), 1, true); if (ret & BIT(5))
sparse_keymap_report_event(priv->input,
BIT(5), 1, true);
if (ret & BIT(26))
sparse_keymap_report_event(priv->input,
BIT(26), 1, true);
if (ret & BIT(29))
sparse_keymap_report_event(priv->input,
BIT(29), 1, true);
}
} }
/* Initialization */ /* Initialization */
......
...@@ -19,12 +19,12 @@ ...@@ -19,12 +19,12 @@
static int temp_limits[3] = { 55000, 60000, 65000 }; static int temp_limits[3] = { 55000, 60000, 65000 };
module_param_array(temp_limits, int, NULL, 0444); module_param_array(temp_limits, int, NULL, 0444);
MODULE_PARM_DESC(temp_limits, MODULE_PARM_DESC(temp_limits,
"Milli-celcius values above which the fan speed increases"); "Millicelsius values above which the fan speed increases");
static int hysteresis = 3000; static int hysteresis = 3000;
module_param(hysteresis, int, 0444); module_param(hysteresis, int, 0444);
MODULE_PARM_DESC(hysteresis, MODULE_PARM_DESC(hysteresis,
"Hysteresis in milli-celcius before lowering the fan speed"); "Hysteresis in millicelsius before lowering the fan speed");
static int speed_on_ac = 2; static int speed_on_ac = 2;
module_param(speed_on_ac, int, 0444); module_param(speed_on_ac, int, 0444);
......
...@@ -16,16 +16,14 @@ ...@@ -16,16 +16,14 @@
* *
*/ */
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/input/sparse-keymap.h>
#include <linux/acpi.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <acpi/acpi_bus.h>
#include <linux/dmi.h>
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alex Hung"); MODULE_AUTHOR("Alex Hung");
...@@ -67,8 +65,8 @@ static const struct key_entry intel_array_keymap[] = { ...@@ -67,8 +65,8 @@ static const struct key_entry intel_array_keymap[] = {
{ KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* Release */ { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* Release */
{ KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* Press */ { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* Press */
{ KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* Release */ { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* Release */
{ KE_SW, 0xC8, { .sw = { SW_ROTATE_LOCK, 1 } } }, /* Press */ { KE_KEY, 0xC8, { KEY_ROTATE_LOCK_TOGGLE } }, /* Press */
{ KE_SW, 0xC9, { .sw = { SW_ROTATE_LOCK, 0 } } }, /* Release */ { KE_IGNORE, 0xC9, { KEY_ROTATE_LOCK_TOGGLE } }, /* Release */
{ KE_KEY, 0xCE, { KEY_POWER } }, /* Press */ { KE_KEY, 0xCE, { KEY_POWER } }, /* Press */
{ KE_IGNORE, 0xCF, { KEY_POWER } }, /* Release */ { KE_IGNORE, 0xCF, { KEY_POWER } }, /* Release */
{ KE_END }, { KE_END },
......
...@@ -138,9 +138,6 @@ static int __init itmt_legacy_init(void) ...@@ -138,9 +138,6 @@ static int __init itmt_legacy_init(void)
if (!id) if (!id)
return -ENODEV; return -ENODEV;
if (boot_cpu_has(X86_FEATURE_HWP))
return -ENODEV;
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"platform/x86/turbo_max_3:online", "platform/x86/turbo_max_3:online",
itmt_legacy_cpu_online, NULL); itmt_legacy_cpu_online, NULL);
......
...@@ -85,6 +85,15 @@ ...@@ -85,6 +85,15 @@
#define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0) #define MLXPLAT_CPLD_FAN_MASK GENMASK(3, 0)
#define MLXPLAT_CPLD_FAN_NG_MASK GENMASK(5, 0) #define MLXPLAT_CPLD_FAN_NG_MASK GENMASK(5, 0)
/* Default I2C parent bus number */
#define MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR 1
/* Maximum number of possible physical buses equipped on system */
#define MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM 16
/* Number of channels in group */
#define MLXPLAT_CPLD_GRP_CHNL_NUM 8
/* Start channel numbers */ /* Start channel numbers */
#define MLXPLAT_CPLD_CH1 2 #define MLXPLAT_CPLD_CH1 2
#define MLXPLAT_CPLD_CH2 10 #define MLXPLAT_CPLD_CH2 10
...@@ -124,7 +133,7 @@ static const struct resource mlxplat_lpc_resources[] = { ...@@ -124,7 +133,7 @@ static const struct resource mlxplat_lpc_resources[] = {
}; };
/* Platform default channels */ /* Platform default channels */
static const int mlxplat_default_channels[][8] = { static const int mlxplat_default_channels[][MLXPLAT_CPLD_GRP_CHNL_NUM] = {
{ {
MLXPLAT_CPLD_CH1, MLXPLAT_CPLD_CH1 + 1, MLXPLAT_CPLD_CH1 + 2, MLXPLAT_CPLD_CH1, MLXPLAT_CPLD_CH1 + 1, MLXPLAT_CPLD_CH1 + 2,
MLXPLAT_CPLD_CH1 + 3, MLXPLAT_CPLD_CH1 + 4, MLXPLAT_CPLD_CH1 + MLXPLAT_CPLD_CH1 + 3, MLXPLAT_CPLD_CH1 + 4, MLXPLAT_CPLD_CH1 +
...@@ -694,6 +703,8 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi) ...@@ -694,6 +703,8 @@ static int __init mlxplat_dmi_default_matched(const struct dmi_system_id *dmi)
ARRAY_SIZE(mlxplat_default_channels[i]); ARRAY_SIZE(mlxplat_default_channels[i]);
} }
mlxplat_hotplug = &mlxplat_mlxcpld_default_data; mlxplat_hotplug = &mlxplat_mlxcpld_default_data;
mlxplat_hotplug->deferred_nr =
mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
return 1; return 1;
}; };
...@@ -708,6 +719,8 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) ...@@ -708,6 +719,8 @@ static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi)
ARRAY_SIZE(mlxplat_msn21xx_channels); ARRAY_SIZE(mlxplat_msn21xx_channels);
} }
mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data; mlxplat_hotplug = &mlxplat_mlxcpld_msn21xx_data;
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
return 1; return 1;
}; };
...@@ -722,6 +735,8 @@ static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi) ...@@ -722,6 +735,8 @@ static int __init mlxplat_dmi_msn274x_matched(const struct dmi_system_id *dmi)
ARRAY_SIZE(mlxplat_msn21xx_channels); ARRAY_SIZE(mlxplat_msn21xx_channels);
} }
mlxplat_hotplug = &mlxplat_mlxcpld_msn274x_data; mlxplat_hotplug = &mlxplat_mlxcpld_msn274x_data;
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
return 1; return 1;
}; };
...@@ -736,6 +751,8 @@ static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi) ...@@ -736,6 +751,8 @@ static int __init mlxplat_dmi_msn201x_matched(const struct dmi_system_id *dmi)
ARRAY_SIZE(mlxplat_msn21xx_channels); ARRAY_SIZE(mlxplat_msn21xx_channels);
} }
mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data; mlxplat_hotplug = &mlxplat_mlxcpld_msn201x_data;
mlxplat_hotplug->deferred_nr =
mlxplat_default_channels[i - 1][MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
return 1; return 1;
}; };
...@@ -750,6 +767,8 @@ static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi) ...@@ -750,6 +767,8 @@ static int __init mlxplat_dmi_qmb7xx_matched(const struct dmi_system_id *dmi)
ARRAY_SIZE(mlxplat_msn21xx_channels); ARRAY_SIZE(mlxplat_msn21xx_channels);
} }
mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data; mlxplat_hotplug = &mlxplat_mlxcpld_default_ng_data;
mlxplat_hotplug->deferred_nr =
mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1];
return 1; return 1;
}; };
...@@ -830,10 +849,48 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { ...@@ -830,10 +849,48 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = {
MODULE_DEVICE_TABLE(dmi, mlxplat_dmi_table); MODULE_DEVICE_TABLE(dmi, mlxplat_dmi_table);
static int mlxplat_mlxcpld_verify_bus_topology(int *nr)
{
struct i2c_adapter *search_adap;
int shift, i;
/* Scan adapters from expected id to verify it is free. */
*nr = MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR;
for (i = MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR; i <
MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM; i++) {
search_adap = i2c_get_adapter(i);
if (search_adap) {
i2c_put_adapter(search_adap);
continue;
}
/* Return if expected parent adapter is free. */
if (i == MLXPLAT_CPLD_PHYS_ADAPTER_DEF_NR)
return 0;
break;
}
/* Return with error if free id for adapter is not found. */
if (i == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM)
return -ENODEV;
/* Shift adapter ids, since expected parent adapter is not free. */
*nr = i;
for (i = 0; i < ARRAY_SIZE(mlxplat_mux_data); i++) {
shift = *nr - mlxplat_mux_data[i].parent;
mlxplat_mux_data[i].parent = *nr;
mlxplat_mux_data[i].base_nr += shift;
if (shift > 0)
mlxplat_hotplug->shift_nr = shift;
}
return 0;
}
static int __init mlxplat_init(void) static int __init mlxplat_init(void)
{ {
struct mlxplat_priv *priv; struct mlxplat_priv *priv;
int i, err; int i, nr, err;
if (!dmi_check_system(mlxplat_dmi_table)) if (!dmi_check_system(mlxplat_dmi_table))
return -ENODEV; return -ENODEV;
...@@ -853,7 +910,12 @@ static int __init mlxplat_init(void) ...@@ -853,7 +910,12 @@ static int __init mlxplat_init(void)
} }
platform_set_drvdata(mlxplat_dev, priv); platform_set_drvdata(mlxplat_dev, priv);
priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", -1, err = mlxplat_mlxcpld_verify_bus_topology(&nr);
if (nr < 0)
goto fail_alloc;
nr = (nr == MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM) ? -1 : nr;
priv->pdev_i2c = platform_device_register_simple("i2c_mlxcpld", nr,
NULL, 0); NULL, 0);
if (IS_ERR(priv->pdev_i2c)) { if (IS_ERR(priv->pdev_i2c)) {
err = PTR_ERR(priv->pdev_i2c); err = PTR_ERR(priv->pdev_i2c);
......
...@@ -446,6 +446,23 @@ static const struct dmi_system_id silead_ts_dmi_table[] = { ...@@ -446,6 +446,23 @@ static const struct dmi_system_id silead_ts_dmi_table[] = {
DMI_MATCH(DMI_BOARD_NAME, "X3 Plus"), DMI_MATCH(DMI_BOARD_NAME, "X3 Plus"),
}, },
}, },
{
/* I.T.Works TW701 */
.driver_data = (void *)&surftab_wintron70_st70416_6_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
DMI_MATCH(DMI_PRODUCT_NAME, "i71c"),
DMI_MATCH(DMI_BIOS_VERSION, "itWORKS.G.WI71C.JGBMRB"),
},
},
{
/* Yours Y8W81, same case and touchscreen as Chuwi Vi8 */
.driver_data = (void *)&chuwi_vi8_data,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "YOURS"),
DMI_MATCH(DMI_PRODUCT_NAME, "Y8W81"),
},
},
{ }, { },
}; };
......
...@@ -8703,16 +8703,24 @@ static const struct attribute_group fan_attr_group = { ...@@ -8703,16 +8703,24 @@ static const struct attribute_group fan_attr_group = {
.ec = TPID(__id1, __id2), \ .ec = TPID(__id1, __id2), \
.quirks = __quirks } .quirks = __quirks }
#define TPACPI_FAN_QB(__id1, __id2, __quirks) \
{ .vendor = PCI_VENDOR_ID_LENOVO, \
.bios = TPID(__id1, __id2), \
.ec = TPACPI_MATCH_ANY, \
.quirks = __quirks }
static const struct tpacpi_quirk fan_quirk_table[] __initconst = { static const struct tpacpi_quirk fan_quirk_table[] __initconst = {
TPACPI_FAN_QI('1', 'Y', TPACPI_FAN_Q1), TPACPI_FAN_QI('1', 'Y', TPACPI_FAN_Q1),
TPACPI_FAN_QI('7', '8', TPACPI_FAN_Q1), TPACPI_FAN_QI('7', '8', TPACPI_FAN_Q1),
TPACPI_FAN_QI('7', '6', TPACPI_FAN_Q1), TPACPI_FAN_QI('7', '6', TPACPI_FAN_Q1),
TPACPI_FAN_QI('7', '0', TPACPI_FAN_Q1), TPACPI_FAN_QI('7', '0', TPACPI_FAN_Q1),
TPACPI_FAN_QL('7', 'M', TPACPI_FAN_2FAN), TPACPI_FAN_QL('7', 'M', TPACPI_FAN_2FAN),
TPACPI_FAN_QB('N', '1', TPACPI_FAN_2FAN),
}; };
#undef TPACPI_FAN_QL #undef TPACPI_FAN_QL
#undef TPACPI_FAN_QI #undef TPACPI_FAN_QI
#undef TPACPI_FAN_QB
static int __init fan_init(struct ibm_init_struct *iibm) static int __init fan_init(struct ibm_init_struct *iibm)
{ {
......
// SPDX-License-Identifier: GPL-2.0
/* /*
* ACPI driver for Topstar notebooks (hotkeys support only) * Topstar Laptop ACPI Extras driver
* *
* Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@mandriva.com.br> * Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@mandriva.com.br>
* Copyright (c) 2018 Guillaume Douézan-Grard
* *
* Implementation inspired by existing x86 platform drivers, in special * Implementation inspired by existing x86 platform drivers, in special
* asus/eepc/fujitsu-laptop, thanks to their authors * asus/eepc/fujitsu-laptop, thanks to their authors.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
...@@ -18,15 +16,93 @@ ...@@ -18,15 +16,93 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h> #include <linux/input/sparse-keymap.h>
#include <linux/leds.h>
#include <linux/platform_device.h>
#define ACPI_TOPSTAR_CLASS "topstar" #define TOPSTAR_LAPTOP_CLASS "topstar"
struct topstar_hkey { struct topstar_laptop {
struct input_dev *inputdev; struct acpi_device *device;
struct platform_device *platform;
struct input_dev *input;
struct led_classdev led;
}; };
/*
* LED
*/
static enum led_brightness topstar_led_get(struct led_classdev *led)
{
return led->brightness;
}
static int topstar_led_set(struct led_classdev *led,
enum led_brightness state)
{
struct topstar_laptop *topstar = container_of(led,
struct topstar_laptop, led);
struct acpi_object_list params;
union acpi_object in_obj;
unsigned long long int ret;
acpi_status status;
params.count = 1;
params.pointer = &in_obj;
in_obj.type = ACPI_TYPE_INTEGER;
in_obj.integer.value = 0x83;
/*
* Topstar ACPI returns 0x30001 when the LED is ON and 0x30000 when it
* is OFF.
*/
status = acpi_evaluate_integer(topstar->device->handle,
"GETX", &params, &ret);
if (ACPI_FAILURE(status))
return -1;
/*
* FNCX(0x83) toggles the LED (more precisely, it is supposed to
* act as an hardware switch and disconnect the WLAN adapter but
* it seems to be faulty on some models like the Topstar U931
* Notebook).
*/
if ((ret == 0x30001 && state == LED_OFF)
|| (ret == 0x30000 && state != LED_OFF)) {
status = acpi_execute_simple_method(topstar->device->handle,
"FNCX", 0x83);
if (ACPI_FAILURE(status))
return -1;
}
return 0;
}
static int topstar_led_init(struct topstar_laptop *topstar)
{
topstar->led = (struct led_classdev) {
.default_trigger = "rfkill0",
.brightness_get = topstar_led_get,
.brightness_set_blocking = topstar_led_set,
.name = TOPSTAR_LAPTOP_CLASS "::wlan",
};
return led_classdev_register(&topstar->platform->dev, &topstar->led);
}
static void topstar_led_exit(struct topstar_laptop *topstar)
{
led_classdev_unregister(&topstar->led);
}
/*
* Input
*/
static const struct key_entry topstar_keymap[] = { static const struct key_entry topstar_keymap[] = {
{ KE_KEY, 0x80, { KEY_BRIGHTNESSUP } }, { KE_KEY, 0x80, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } }, { KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } },
...@@ -57,108 +133,218 @@ static const struct key_entry topstar_keymap[] = { ...@@ -57,108 +133,218 @@ static const struct key_entry topstar_keymap[] = {
{ KE_END, 0 } { KE_END, 0 }
}; };
static void acpi_topstar_notify(struct acpi_device *device, u32 event) static void topstar_input_notify(struct topstar_laptop *topstar, int event)
{ {
static bool dup_evnt[2]; if (!sparse_keymap_report_event(topstar->input, event, 1, true))
bool *dup;
struct topstar_hkey *hkey = acpi_driver_data(device);
/* 0x83 and 0x84 key events comes duplicated... */
if (event == 0x83 || event == 0x84) {
dup = &dup_evnt[event - 0x83];
if (*dup) {
*dup = false;
return;
}
*dup = true;
}
if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true))
pr_info("unknown event = 0x%02x\n", event); pr_info("unknown event = 0x%02x\n", event);
} }
static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) static int topstar_input_init(struct topstar_laptop *topstar)
{
acpi_status status;
status = acpi_execute_simple_method(device->handle, "FNCX",
state ? 0x86 : 0x87);
if (ACPI_FAILURE(status)) {
pr_err("Unable to switch FNCX notifications\n");
return -ENODEV;
}
return 0;
}
static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
{ {
struct input_dev *input; struct input_dev *input;
int error; int err;
input = input_allocate_device(); input = input_allocate_device();
if (!input) if (!input)
return -ENOMEM; return -ENOMEM;
input->name = "Topstar Laptop extra buttons"; input->name = "Topstar Laptop extra buttons";
input->phys = "topstar/input0"; input->phys = TOPSTAR_LAPTOP_CLASS "/input0";
input->id.bustype = BUS_HOST; input->id.bustype = BUS_HOST;
input->dev.parent = &topstar->platform->dev;
error = sparse_keymap_setup(input, topstar_keymap, NULL); err = sparse_keymap_setup(input, topstar_keymap, NULL);
if (error) { if (err) {
pr_err("Unable to setup input device keymap\n"); pr_err("Unable to setup input device keymap\n");
goto err_free_dev; goto err_free_dev;
} }
error = input_register_device(input); err = input_register_device(input);
if (error) { if (err) {
pr_err("Unable to register input device\n"); pr_err("Unable to register input device\n");
goto err_free_dev; goto err_free_dev;
} }
hkey->inputdev = input; topstar->input = input;
return 0; return 0;
err_free_dev: err_free_dev:
input_free_device(input); input_free_device(input);
return error; return err;
} }
static int acpi_topstar_add(struct acpi_device *device) static void topstar_input_exit(struct topstar_laptop *topstar)
{ {
struct topstar_hkey *tps_hkey; input_unregister_device(topstar->input);
}
tps_hkey = kzalloc(sizeof(struct topstar_hkey), GFP_KERNEL); /*
if (!tps_hkey) * Platform
return -ENOMEM; */
strcpy(acpi_device_name(device), "Topstar TPSACPI"); static struct platform_driver topstar_platform_driver = {
strcpy(acpi_device_class(device), ACPI_TOPSTAR_CLASS); .driver = {
.name = TOPSTAR_LAPTOP_CLASS,
},
};
static int topstar_platform_init(struct topstar_laptop *topstar)
{
int err;
topstar->platform = platform_device_alloc(TOPSTAR_LAPTOP_CLASS, -1);
if (!topstar->platform)
return -ENOMEM;
if (acpi_topstar_fncx_switch(device, true)) platform_set_drvdata(topstar->platform, topstar);
goto add_err;
if (acpi_topstar_init_hkey(tps_hkey)) err = platform_device_add(topstar->platform);
goto add_err; if (err)
goto err_device_put;
device->driver_data = tps_hkey;
return 0; return 0;
add_err: err_device_put:
kfree(tps_hkey); platform_device_put(topstar->platform);
return err;
}
static void topstar_platform_exit(struct topstar_laptop *topstar)
{
platform_device_unregister(topstar->platform);
}
/*
* ACPI
*/
static int topstar_acpi_fncx_switch(struct acpi_device *device, bool state)
{
acpi_status status;
u64 arg = state ? 0x86 : 0x87;
status = acpi_execute_simple_method(device->handle, "FNCX", arg);
if (ACPI_FAILURE(status)) {
pr_err("Unable to switch FNCX notifications\n");
return -ENODEV; return -ENODEV;
}
return 0;
}
static void topstar_acpi_notify(struct acpi_device *device, u32 event)
{
struct topstar_laptop *topstar = acpi_driver_data(device);
static bool dup_evnt[2];
bool *dup;
/* 0x83 and 0x84 key events comes duplicated... */
if (event == 0x83 || event == 0x84) {
dup = &dup_evnt[event - 0x83];
if (*dup) {
*dup = false;
return;
}
*dup = true;
}
topstar_input_notify(topstar, event);
}
static int topstar_acpi_init(struct topstar_laptop *topstar)
{
return topstar_acpi_fncx_switch(topstar->device, true);
}
static void topstar_acpi_exit(struct topstar_laptop *topstar)
{
topstar_acpi_fncx_switch(topstar->device, false);
} }
static int acpi_topstar_remove(struct acpi_device *device) /*
* Enable software-based WLAN LED control on systems with defective
* hardware switch.
*/
static bool led_workaround;
static int dmi_led_workaround(const struct dmi_system_id *id)
{
led_workaround = true;
return 0;
}
static const struct dmi_system_id topstar_dmi_ids[] = {
{
.callback = dmi_led_workaround,
.ident = "Topstar U931/RVP7",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "U931"),
DMI_MATCH(DMI_BOARD_VERSION, "RVP7"),
},
},
{}
};
static int topstar_acpi_add(struct acpi_device *device)
{ {
struct topstar_hkey *tps_hkey = acpi_driver_data(device); struct topstar_laptop *topstar;
int err;
acpi_topstar_fncx_switch(device, false); dmi_check_system(topstar_dmi_ids);
input_unregister_device(tps_hkey->inputdev); topstar = kzalloc(sizeof(struct topstar_laptop), GFP_KERNEL);
kfree(tps_hkey); if (!topstar)
return -ENOMEM;
strcpy(acpi_device_name(device), "Topstar TPSACPI");
strcpy(acpi_device_class(device), TOPSTAR_LAPTOP_CLASS);
device->driver_data = topstar;
topstar->device = device;
err = topstar_acpi_init(topstar);
if (err)
goto err_free;
err = topstar_platform_init(topstar);
if (err)
goto err_acpi_exit;
err = topstar_input_init(topstar);
if (err)
goto err_platform_exit;
if (led_workaround) {
err = topstar_led_init(topstar);
if (err)
goto err_input_exit;
}
return 0; return 0;
err_input_exit:
topstar_input_exit(topstar);
err_platform_exit:
topstar_platform_exit(topstar);
err_acpi_exit:
topstar_acpi_exit(topstar);
err_free:
kfree(topstar);
return err;
}
static int topstar_acpi_remove(struct acpi_device *device)
{
struct topstar_laptop *topstar = acpi_driver_data(device);
if (led_workaround)
topstar_led_exit(topstar);
topstar_input_exit(topstar);
topstar_platform_exit(topstar);
topstar_acpi_exit(topstar);
kfree(topstar);
return 0;
} }
static const struct acpi_device_id topstar_device_ids[] = { static const struct acpi_device_id topstar_device_ids[] = {
...@@ -168,18 +354,47 @@ static const struct acpi_device_id topstar_device_ids[] = { ...@@ -168,18 +354,47 @@ static const struct acpi_device_id topstar_device_ids[] = {
}; };
MODULE_DEVICE_TABLE(acpi, topstar_device_ids); MODULE_DEVICE_TABLE(acpi, topstar_device_ids);
static struct acpi_driver acpi_topstar_driver = { static struct acpi_driver topstar_acpi_driver = {
.name = "Topstar laptop ACPI driver", .name = "Topstar laptop ACPI driver",
.class = ACPI_TOPSTAR_CLASS, .class = TOPSTAR_LAPTOP_CLASS,
.ids = topstar_device_ids, .ids = topstar_device_ids,
.ops = { .ops = {
.add = acpi_topstar_add, .add = topstar_acpi_add,
.remove = acpi_topstar_remove, .remove = topstar_acpi_remove,
.notify = acpi_topstar_notify, .notify = topstar_acpi_notify,
}, },
}; };
module_acpi_driver(acpi_topstar_driver);
static int __init topstar_laptop_init(void)
{
int ret;
ret = platform_driver_register(&topstar_platform_driver);
if (ret < 0)
return ret;
ret = acpi_bus_register_driver(&topstar_acpi_driver);
if (ret < 0)
goto err_driver_unreg;
pr_info("ACPI extras driver loaded\n");
return 0;
err_driver_unreg:
platform_driver_unregister(&topstar_platform_driver);
return ret;
}
static void __exit topstar_laptop_exit(void)
{
acpi_bus_unregister_driver(&topstar_acpi_driver);
platform_driver_unregister(&topstar_platform_driver);
}
module_init(topstar_laptop_init);
module_exit(topstar_laptop_exit);
MODULE_AUTHOR("Herton Ronaldo Krzesinski"); MODULE_AUTHOR("Herton Ronaldo Krzesinski");
MODULE_AUTHOR("Guillaume Douézan-Grard");
MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver"); MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -130,13 +130,11 @@ static bool find_guid(const char *guid_string, struct wmi_block **out) ...@@ -130,13 +130,11 @@ static bool find_guid(const char *guid_string, struct wmi_block **out)
uuid_le guid_input; uuid_le guid_input;
struct wmi_block *wblock; struct wmi_block *wblock;
struct guid_block *block; struct guid_block *block;
struct list_head *p;
if (uuid_le_to_bin(guid_string, &guid_input)) if (uuid_le_to_bin(guid_string, &guid_input))
return false; return false;
list_for_each(p, &wmi_block_list) { list_for_each_entry(wblock, &wmi_block_list, list) {
wblock = list_entry(p, struct wmi_block, list);
block = &wblock->gblock; block = &wblock->gblock;
if (memcmp(block->guid, &guid_input, 16) == 0) { if (memcmp(block->guid, &guid_input, 16) == 0) {
...@@ -519,7 +517,6 @@ wmi_notify_handler handler, void *data) ...@@ -519,7 +517,6 @@ wmi_notify_handler handler, void *data)
struct wmi_block *block; struct wmi_block *block;
acpi_status status = AE_NOT_EXIST; acpi_status status = AE_NOT_EXIST;
uuid_le guid_input; uuid_le guid_input;
struct list_head *p;
if (!guid || !handler) if (!guid || !handler)
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
...@@ -527,9 +524,8 @@ wmi_notify_handler handler, void *data) ...@@ -527,9 +524,8 @@ wmi_notify_handler handler, void *data)
if (uuid_le_to_bin(guid, &guid_input)) if (uuid_le_to_bin(guid, &guid_input))
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
list_for_each(p, &wmi_block_list) { list_for_each_entry(block, &wmi_block_list, list) {
acpi_status wmi_status; acpi_status wmi_status;
block = list_entry(p, struct wmi_block, list);
if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { if (memcmp(block->gblock.guid, &guid_input, 16) == 0) {
if (block->handler && if (block->handler &&
...@@ -560,7 +556,6 @@ acpi_status wmi_remove_notify_handler(const char *guid) ...@@ -560,7 +556,6 @@ acpi_status wmi_remove_notify_handler(const char *guid)
struct wmi_block *block; struct wmi_block *block;
acpi_status status = AE_NOT_EXIST; acpi_status status = AE_NOT_EXIST;
uuid_le guid_input; uuid_le guid_input;
struct list_head *p;
if (!guid) if (!guid)
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
...@@ -568,9 +563,8 @@ acpi_status wmi_remove_notify_handler(const char *guid) ...@@ -568,9 +563,8 @@ acpi_status wmi_remove_notify_handler(const char *guid)
if (uuid_le_to_bin(guid, &guid_input)) if (uuid_le_to_bin(guid, &guid_input))
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
list_for_each(p, &wmi_block_list) { list_for_each_entry(block, &wmi_block_list, list) {
acpi_status wmi_status; acpi_status wmi_status;
block = list_entry(p, struct wmi_block, list);
if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { if (memcmp(block->gblock.guid, &guid_input, 16) == 0) {
if (!block->handler || if (!block->handler ||
...@@ -610,15 +604,13 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out) ...@@ -610,15 +604,13 @@ acpi_status wmi_get_event_data(u32 event, struct acpi_buffer *out)
union acpi_object params[1]; union acpi_object params[1];
struct guid_block *gblock; struct guid_block *gblock;
struct wmi_block *wblock; struct wmi_block *wblock;
struct list_head *p;
input.count = 1; input.count = 1;
input.pointer = params; input.pointer = params;
params[0].type = ACPI_TYPE_INTEGER; params[0].type = ACPI_TYPE_INTEGER;
params[0].integer.value = event; params[0].integer.value = event;
list_for_each(p, &wmi_block_list) { list_for_each_entry(wblock, &wmi_block_list, list) {
wblock = list_entry(p, struct wmi_block, list);
gblock = &wblock->gblock; gblock = &wblock->gblock;
if ((gblock->flags & ACPI_WMI_EVENT) && if ((gblock->flags & ACPI_WMI_EVENT) &&
...@@ -933,12 +925,11 @@ static int wmi_dev_probe(struct device *dev) ...@@ -933,12 +925,11 @@ static int wmi_dev_probe(struct device *dev)
goto probe_failure; goto probe_failure;
} }
buf = kmalloc(strlen(wdriver->driver.name) + 5, GFP_KERNEL); buf = kasprintf(GFP_KERNEL, "wmi/%s", wdriver->driver.name);
if (!buf) { if (!buf) {
ret = -ENOMEM; ret = -ENOMEM;
goto probe_string_failure; goto probe_string_failure;
} }
sprintf(buf, "wmi/%s", wdriver->driver.name);
wblock->char_dev.minor = MISC_DYNAMIC_MINOR; wblock->char_dev.minor = MISC_DYNAMIC_MINOR;
wblock->char_dev.name = buf; wblock->char_dev.name = buf;
wblock->char_dev.fops = &wmi_fops; wblock->char_dev.fops = &wmi_fops;
...@@ -1261,11 +1252,9 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event, ...@@ -1261,11 +1252,9 @@ static void acpi_wmi_notify_handler(acpi_handle handle, u32 event,
{ {
struct guid_block *block; struct guid_block *block;
struct wmi_block *wblock; struct wmi_block *wblock;
struct list_head *p;
bool found_it = false; bool found_it = false;
list_for_each(p, &wmi_block_list) { list_for_each_entry(wblock, &wmi_block_list, list) {
wblock = list_entry(p, struct wmi_block, list);
block = &wblock->gblock; block = &wblock->gblock;
if (wblock->acpi_device->handle == handle && if (wblock->acpi_device->handle == handle &&
......
...@@ -129,6 +129,8 @@ struct mlxreg_core_platform_data { ...@@ -129,6 +129,8 @@ struct mlxreg_core_platform_data {
* @mask: top aggregation interrupt common mask; * @mask: top aggregation interrupt common mask;
* @cell_low: location of low aggregation interrupt register; * @cell_low: location of low aggregation interrupt register;
* @mask_low: low aggregation interrupt common mask; * @mask_low: low aggregation interrupt common mask;
* @deferred_nr: I2C adapter number must be exist prior probing execution;
* @shift_nr: I2C adapter numbers must be incremented by this value;
*/ */
struct mlxreg_core_hotplug_platform_data { struct mlxreg_core_hotplug_platform_data {
struct mlxreg_core_item *items; struct mlxreg_core_item *items;
...@@ -139,6 +141,8 @@ struct mlxreg_core_hotplug_platform_data { ...@@ -139,6 +141,8 @@ struct mlxreg_core_hotplug_platform_data {
u32 mask; u32 mask;
u32 cell_low; u32 cell_low;
u32 mask_low; u32 mask_low;
int deferred_nr;
int shift_nr;
}; };
#endif /* __LINUX_PLATFORM_DATA_MLXREG_H */ #endif /* __LINUX_PLATFORM_DATA_MLXREG_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