platform-drivers-x86 for 4.4-1
Various toshiba hotkey and keyboard related fixes and a new WMI driver. Several intel_scu_ipc cleanups and a locking fix. A spattering of small single fixes across various platforms. I was asked to pick up an OLPC cleanup as the driver appeared unmaintained and it seemed similar to what is maintained in platform/drivers/x86. I have included the patch and an update to the MAINTAINERS file. toshiba_acpi: - Initialize hotkey_event_type variable - Remove unneeded u32 variables from *setup_keyboard - Add 0x prefix to available_kbd_modes_show function - Change default Hotkey enabling value - Unify hotkey enabling functions toshiba-wmi: - Toshiba WMI Hotkey Driver intel_scu_ipc: - Protect dev member assignment on ->remove() - Switch to use module_pci_driver() macro - Convert to use struct device * - Propagate pointer to struct intel_scu_ipc_dev - Fix error path by turning to devm_* / pcim_* acer-wmi: - remove threeg and interface sysfs interfaces OLPC: - Use %*ph specifier instead of passing direct values MAINTAINERS: - Add drivers/platform/olpc to drivers/platform/x86 sony-laptop: - Fix handling sony_nc_hotkeys_decode result intel_mid_powerbtn: - Remove misuse of IRQF_NO_SUSPEND flag compal-laptop: - Add charge control limit asus-wmi: - restore kbd led level after resume -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJWO/RnAAoJEKbMaAwKp364KSEIAIi++AwZc6+ZWuQ/s/H0Hmka d8RVN7wKHwYKAyaIhvso3gsx/Nipxk4ktfG0f8odAyz3CB8bHxI35E4nfx/ytBJv E1cuLWeomu2BU+tFcGCnxkGxT15qWIhfMuC/Cdhbv9+PF/4FybLRk+tOa1bqkqE/ FTv4M00oQBmbFBM27igcWKDaWaU87Qt47DxVDOD8fxfGVJe7VmqVegGz3m52m30U oAn4eZGc1e1TlSHu6zmGfnzSu2NCgtqo8a/1DirI/+p2iViKxjwPVKY3SsiZVJZf YQFB/lexC2GEO0rzdqRHhFYAo4Rvgg/FWxvySnD8MNKxUi7h3qQ8TI4pFimmp7k= =QEqn -----END PGP SIGNATURE----- Merge tag 'platform-drivers-x86-v4.4-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86 Pull x86 platform driver update from Darren Hart: "Various toshiba hotkey and keyboard related fixes and a new WMI driver. Several intel_scu_ipc cleanups and a locking fix. A spattering of small single fixes across various platforms. I was asked to pick up an OLPC cleanup as the driver appeared unmaintained and it seemed similar to what is maintained in platform/drivers/x86. I have included the patch and an update to the MAINTAINERS file. toshiba_acpi: - Initialize hotkey_event_type variable - Remove unneeded u32 variables from *setup_keyboard - Add 0x prefix to available_kbd_modes_show function - Change default Hotkey enabling value - Unify hotkey enabling functions toshiba-wmi: - Toshiba WMI Hotkey Driver intel_scu_ipc: - Protect dev member assignment on ->remove() - Switch to use module_pci_driver() macro - Convert to use struct device * - Propagate pointer to struct intel_scu_ipc_dev - Fix error path by turning to devm_* / pcim_* acer-wmi: - remove threeg and interface sysfs interfaces OLPC: - Use %*ph specifier instead of passing direct values MAINTAINERS: - Add drivers/platform/olpc to drivers/platform/x86 sony-laptop: - Fix handling sony_nc_hotkeys_decode result intel_mid_powerbtn: - Remove misuse of IRQF_NO_SUSPEND flag compal-laptop: - Add charge control limit asus-wmi: - restore kbd led level after resume" * tag 'platform-drivers-x86-v4.4-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86: toshiba_acpi: Initialize hotkey_event_type variable intel_scu_ipc: Protect dev member assignment on ->remove() intel_scu_ipc: Switch to use module_pci_driver() macro intel_scu_ipc: Convert to use struct device * intel_scu_ipc: Propagate pointer to struct intel_scu_ipc_dev intel_scu_ipc: Fix error path by turning to devm_* / pcim_* acer-wmi: remove threeg and interface sysfs interfaces OLPC: Use %*ph specifier instead of passing direct values MAINTAINERS: Add drivers/platform/olpc to drivers/platform/x86 platform/x86: Toshiba WMI Hotkey Driver sony-laptop: Fix handling sony_nc_hotkeys_decode result intel_mid_powerbtn: Remove misuse of IRQF_NO_SUSPEND flag compal-laptop: Add charge control limit asus-wmi: restore kbd led level after resume toshiba_acpi: Remove unneeded u32 variables from *setup_keyboard toshiba_acpi: Add 0x prefix to available_kbd_modes_show function toshiba_acpi: Change default Hotkey enabling value toshiba_acpi: Unify hotkey enabling functions
This commit is contained in:
commit
d1e41ff119
|
@ -10699,6 +10699,12 @@ L: platform-driver-x86@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/platform/x86/toshiba_haps.c
|
||||
|
||||
TOSHIBA WMI HOTKEYS DRIVER
|
||||
M: Azael Avalos <coproscefalo@gmail.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/toshiba-wmi.c
|
||||
|
||||
TOSHIBA SMM DRIVER
|
||||
M: Jonathan Buzzard <jonathan@buzzard.org.uk>
|
||||
W: http://www.buzzard.org.uk/toshiba/
|
||||
|
@ -11621,6 +11627,7 @@ L: platform-driver-x86@vger.kernel.org
|
|||
T: git git://git.infradead.org/users/dvhart/linux-platform-drivers-x86.git
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/
|
||||
F: drivers/platform/olpc/
|
||||
|
||||
X86 MCE INFRASTRUCTURE
|
||||
M: Tony Luck <tony.luck@intel.com>
|
||||
|
|
|
@ -192,18 +192,15 @@ static ssize_t ec_dbgfs_cmd_write(struct file *file, const char __user *buf,
|
|||
for (i = 0; i <= ec_cmd_bytes; i++)
|
||||
ec_cmd[i] = ec_cmd_int[i];
|
||||
|
||||
pr_debug("olpc-ec: debugfs cmd 0x%02x with %d args %02x %02x %02x %02x %02x, want %d returns\n",
|
||||
ec_cmd[0], ec_cmd_bytes, ec_cmd[1], ec_cmd[2],
|
||||
ec_cmd[3], ec_cmd[4], ec_cmd[5], ec_dbgfs_resp_bytes);
|
||||
pr_debug("olpc-ec: debugfs cmd 0x%02x with %d args %5ph, want %d returns\n",
|
||||
ec_cmd[0], ec_cmd_bytes, ec_cmd + 1,
|
||||
ec_dbgfs_resp_bytes);
|
||||
|
||||
olpc_ec_cmd(ec_cmd[0], (ec_cmd_bytes == 0) ? NULL : &ec_cmd[1],
|
||||
ec_cmd_bytes, ec_dbgfs_resp, ec_dbgfs_resp_bytes);
|
||||
|
||||
pr_debug("olpc-ec: response %02x %02x %02x %02x %02x %02x %02x %02x (%d bytes expected)\n",
|
||||
ec_dbgfs_resp[0], ec_dbgfs_resp[1], ec_dbgfs_resp[2],
|
||||
ec_dbgfs_resp[3], ec_dbgfs_resp[4], ec_dbgfs_resp[5],
|
||||
ec_dbgfs_resp[6], ec_dbgfs_resp[7],
|
||||
ec_dbgfs_resp_bytes);
|
||||
pr_debug("olpc-ec: response %8ph (%d bytes expected)\n",
|
||||
ec_dbgfs_resp, ec_dbgfs_resp_bytes);
|
||||
|
||||
out:
|
||||
mutex_unlock(&ec_dbgfs_lock);
|
||||
|
|
|
@ -309,8 +309,8 @@ config COMPAL_LAPTOP
|
|||
This is a driver for laptops built by Compal, and some models by
|
||||
other brands (e.g. Dell, Toshiba).
|
||||
|
||||
It adds support for rfkill, Bluetooth, WLAN and LCD brightness
|
||||
control.
|
||||
It adds support for rfkill, Bluetooth, WLAN, LCD brightness, hwmon
|
||||
and battery charging level control.
|
||||
|
||||
For a (possibly incomplete) list of supported laptops, please refer
|
||||
to: Documentation/platform/x86-laptop-drivers.txt
|
||||
|
@ -700,6 +700,24 @@ config TOSHIBA_HAPS
|
|||
If you have a recent Toshiba laptop with a built-in accelerometer
|
||||
device, say Y.
|
||||
|
||||
config TOSHIBA_WMI
|
||||
tristate "Toshiba WMI Hotkeys Driver (EXPERIMENTAL)"
|
||||
default n
|
||||
depends on ACPI_WMI
|
||||
depends on INPUT
|
||||
select INPUT_SPARSEKMAP
|
||||
---help---
|
||||
This driver adds hotkey monitoring support to some Toshiba models
|
||||
that manage the hotkeys via WMI events.
|
||||
|
||||
WARNING: This driver is incomplete as it lacks a proper keymap and the
|
||||
*notify function only prints the ACPI event type value. Be warned that
|
||||
you will need to provide some information if you have a Toshiba model
|
||||
with WMI event hotkeys and want to help with the develpment of this
|
||||
driver.
|
||||
|
||||
If you have a WMI-based hotkeys Toshiba laptop, say Y or M here.
|
||||
|
||||
config ACPI_CMPC
|
||||
tristate "CMPC Laptop Extras"
|
||||
depends on X86 && ACPI
|
||||
|
|
|
@ -40,6 +40,7 @@ obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
|
|||
|
||||
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
|
||||
obj-$(CONFIG_TOSHIBA_HAPS) += toshiba_haps.o
|
||||
obj-$(CONFIG_TOSHIBA_WMI) += toshiba-wmi.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
|
||||
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
|
||||
|
|
|
@ -1662,58 +1662,6 @@ static void acer_rfkill_exit(void)
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* sysfs interface
|
||||
*/
|
||||
static ssize_t show_bool_threeg(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
u32 result; \
|
||||
acpi_status status;
|
||||
|
||||
pr_info("This threeg sysfs will be removed in 2014 - used by: %s\n",
|
||||
current->comm);
|
||||
status = get_u32(&result, ACER_CAP_THREEG);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return sprintf(buf, "%u\n", result);
|
||||
return sprintf(buf, "Read error\n");
|
||||
}
|
||||
|
||||
static ssize_t set_bool_threeg(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
u32 tmp = simple_strtoul(buf, NULL, 10);
|
||||
acpi_status status = set_u32(tmp, ACER_CAP_THREEG);
|
||||
pr_info("This threeg sysfs will be removed in 2014 - used by: %s\n",
|
||||
current->comm);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EINVAL;
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg,
|
||||
set_bool_threeg);
|
||||
|
||||
static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
pr_info("This interface sysfs will be removed in 2014 - used by: %s\n",
|
||||
current->comm);
|
||||
switch (interface->type) {
|
||||
case ACER_AMW0:
|
||||
return sprintf(buf, "AMW0\n");
|
||||
case ACER_AMW0_V2:
|
||||
return sprintf(buf, "AMW0 v2\n");
|
||||
case ACER_WMID:
|
||||
return sprintf(buf, "WMID\n");
|
||||
case ACER_WMID_v2:
|
||||
return sprintf(buf, "WMID v2\n");
|
||||
default:
|
||||
return sprintf(buf, "Error!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(interface, S_IRUGO, show_interface, NULL);
|
||||
|
||||
static void acer_wmi_notify(u32 value, void *context)
|
||||
{
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
|
@ -2127,39 +2075,6 @@ static struct platform_driver acer_platform_driver = {
|
|||
|
||||
static struct platform_device *acer_platform_device;
|
||||
|
||||
static int remove_sysfs(struct platform_device *device)
|
||||
{
|
||||
if (has_cap(ACER_CAP_THREEG))
|
||||
device_remove_file(&device->dev, &dev_attr_threeg);
|
||||
|
||||
device_remove_file(&device->dev, &dev_attr_interface);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init create_sysfs(void)
|
||||
{
|
||||
int retval = -ENOMEM;
|
||||
|
||||
if (has_cap(ACER_CAP_THREEG)) {
|
||||
retval = device_create_file(&acer_platform_device->dev,
|
||||
&dev_attr_threeg);
|
||||
if (retval)
|
||||
goto error_sysfs;
|
||||
}
|
||||
|
||||
retval = device_create_file(&acer_platform_device->dev,
|
||||
&dev_attr_interface);
|
||||
if (retval)
|
||||
goto error_sysfs;
|
||||
|
||||
return 0;
|
||||
|
||||
error_sysfs:
|
||||
remove_sysfs(acer_platform_device);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void remove_debugfs(void)
|
||||
{
|
||||
debugfs_remove(interface->debug.devices);
|
||||
|
@ -2290,10 +2205,6 @@ static int __init acer_wmi_init(void)
|
|||
if (err)
|
||||
goto error_device_add;
|
||||
|
||||
err = create_sysfs();
|
||||
if (err)
|
||||
goto error_create_sys;
|
||||
|
||||
if (wmi_has_guid(WMID_GUID2)) {
|
||||
interface->debug.wmid_devices = get_wmid_devices();
|
||||
err = create_debugfs();
|
||||
|
@ -2307,8 +2218,6 @@ static int __init acer_wmi_init(void)
|
|||
return 0;
|
||||
|
||||
error_create_debugfs:
|
||||
remove_sysfs(acer_platform_device);
|
||||
error_create_sys:
|
||||
platform_device_del(acer_platform_device);
|
||||
error_device_add:
|
||||
platform_device_put(acer_platform_device);
|
||||
|
@ -2331,7 +2240,6 @@ static void __exit acer_wmi_exit(void)
|
|||
if (has_cap(ACER_CAP_ACCEL))
|
||||
acer_wmi_accel_destroy();
|
||||
|
||||
remove_sysfs(acer_platform_device);
|
||||
remove_debugfs();
|
||||
platform_device_unregister(acer_platform_device);
|
||||
platform_driver_unregister(&acer_platform_driver);
|
||||
|
|
|
@ -582,7 +582,7 @@ static void asus_wmi_led_exit(struct asus_wmi *asus)
|
|||
|
||||
static int asus_wmi_led_init(struct asus_wmi *asus)
|
||||
{
|
||||
int rv = 0;
|
||||
int rv = 0, led_val;
|
||||
|
||||
asus->led_workqueue = create_singlethread_workqueue("led_workqueue");
|
||||
if (!asus->led_workqueue)
|
||||
|
@ -602,9 +602,11 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
|
|||
goto error;
|
||||
}
|
||||
|
||||
if (kbd_led_read(asus, NULL, NULL) >= 0) {
|
||||
led_val = kbd_led_read(asus, NULL, NULL);
|
||||
if (led_val >= 0) {
|
||||
INIT_WORK(&asus->kbd_led_work, kbd_led_update);
|
||||
|
||||
asus->kbd_led_wk = led_val;
|
||||
asus->kbd_led.name = "asus::kbd_backlight";
|
||||
asus->kbd_led.brightness_set = kbd_led_set;
|
||||
asus->kbd_led.brightness_get = kbd_led_get;
|
||||
|
@ -2160,6 +2162,16 @@ static int asus_hotk_thaw(struct device *device)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int asus_hotk_resume(struct device *device)
|
||||
{
|
||||
struct asus_wmi *asus = dev_get_drvdata(device);
|
||||
|
||||
if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
|
||||
queue_work(asus->led_workqueue, &asus->kbd_led_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int asus_hotk_restore(struct device *device)
|
||||
{
|
||||
struct asus_wmi *asus = dev_get_drvdata(device);
|
||||
|
@ -2190,6 +2202,8 @@ static int asus_hotk_restore(struct device *device)
|
|||
bl = !asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_UWB);
|
||||
rfkill_set_sw_state(asus->uwb.rfkill, bl);
|
||||
}
|
||||
if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
|
||||
queue_work(asus->led_workqueue, &asus->kbd_led_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2197,6 +2211,7 @@ static int asus_hotk_restore(struct device *device)
|
|||
static const struct dev_pm_ops asus_pm_ops = {
|
||||
.thaw = asus_hotk_thaw,
|
||||
.restore = asus_hotk_restore,
|
||||
.resume = asus_hotk_resume,
|
||||
};
|
||||
|
||||
static int asus_wmi_probe(struct platform_device *pdev)
|
||||
|
|
|
@ -151,6 +151,8 @@
|
|||
#define BAT_STATUS2 0xF1
|
||||
#define BAT_STOP_CHARGE1 0xF2
|
||||
#define BAT_STOP_CHARGE2 0xF3
|
||||
#define BAT_CHARGE_LIMIT 0x03
|
||||
#define BAT_CHARGE_LIMIT_MAX 100
|
||||
|
||||
#define BAT_S0_DISCHARGE (1 << 0)
|
||||
#define BAT_S0_DISCHRG_CRITICAL (1 << 2)
|
||||
|
@ -601,6 +603,12 @@ static int bat_get_property(struct power_supply *psy,
|
|||
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||
val->intval = ec_read_u16(BAT_CHARGE_NOW) * 1000;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
val->intval = ec_read_u8(BAT_CHARGE_LIMIT);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
|
||||
val->intval = BAT_CHARGE_LIMIT_MAX;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
val->intval = ec_read_u8(BAT_CAPACITY);
|
||||
break;
|
||||
|
@ -634,6 +642,36 @@ static int bat_get_property(struct power_supply *psy,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bat_set_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
const union power_supply_propval *val)
|
||||
{
|
||||
int level;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
level = val->intval;
|
||||
if (level < 0 || level > BAT_CHARGE_LIMIT_MAX)
|
||||
return -EINVAL;
|
||||
if (ec_write(BAT_CHARGE_LIMIT, level) < 0)
|
||||
return -EIO;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bat_writeable_property(struct power_supply *psy,
|
||||
enum power_supply_property psp)
|
||||
{
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -726,6 +764,8 @@ static enum power_supply_property compal_bat_properties[] = {
|
|||
POWER_SUPPLY_PROP_POWER_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
|
||||
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
|
@ -880,11 +920,12 @@ static const struct power_supply_desc psy_bat_desc = {
|
|||
.properties = compal_bat_properties,
|
||||
.num_properties = ARRAY_SIZE(compal_bat_properties),
|
||||
.get_property = bat_get_property,
|
||||
.set_property = bat_set_property,
|
||||
.property_is_writeable = bat_writeable_property,
|
||||
};
|
||||
|
||||
static void initialize_power_supply_data(struct compal_data *data)
|
||||
{
|
||||
|
||||
ec_read_sequence(BAT_MANUFACTURER_NAME_ADDR,
|
||||
data->bat_manufacturer_name,
|
||||
BAT_MANUFACTURER_NAME_LEN);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/mfd/intel_msic.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
|
||||
#define DRIVER_NAME "msic_power_btn"
|
||||
|
||||
|
@ -76,14 +77,17 @@ static int mfld_pb_probe(struct platform_device *pdev)
|
|||
|
||||
input_set_capability(input, EV_KEY, KEY_POWER);
|
||||
|
||||
error = request_threaded_irq(irq, NULL, mfld_pb_isr, IRQF_NO_SUSPEND,
|
||||
DRIVER_NAME, input);
|
||||
error = request_threaded_irq(irq, NULL, mfld_pb_isr, 0,
|
||||
DRIVER_NAME, input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "Unable to request irq %d for mfld power"
|
||||
"button\n", irq);
|
||||
goto err_free_input;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
dev_pm_set_wake_irq(&pdev->dev, irq);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "Unable to register input dev, error "
|
||||
|
@ -124,6 +128,8 @@ static int mfld_pb_remove(struct platform_device *pdev)
|
|||
struct input_dev *input = platform_get_drvdata(pdev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
dev_pm_clear_wake_irq(&pdev->dev);
|
||||
device_init_wakeup(&pdev->dev, false);
|
||||
free_irq(irq, input);
|
||||
input_unregister_device(input);
|
||||
|
||||
|
|
|
@ -92,11 +92,8 @@ static struct intel_scu_ipc_pdata_t intel_scu_ipc_tangier_pdata = {
|
|||
.irq_mode = 0,
|
||||
};
|
||||
|
||||
static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id);
|
||||
static void ipc_remove(struct pci_dev *pdev);
|
||||
|
||||
struct intel_scu_ipc_dev {
|
||||
struct pci_dev *pdev;
|
||||
struct device *dev;
|
||||
void __iomem *ipc_base;
|
||||
void __iomem *i2c_base;
|
||||
struct completion cmd_complete;
|
||||
|
@ -118,28 +115,30 @@ static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
|
|||
static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
|
||||
|
||||
/*
|
||||
* Send ipc command
|
||||
* Command Register (Write Only):
|
||||
* A write to this register results in an interrupt to the SCU core processor
|
||||
* Format:
|
||||
* |rfu2(8) | size(8) | command id(4) | rfu1(3) | ioc(1) | command(8)|
|
||||
*/
|
||||
static inline void ipc_command(u32 cmd) /* Send ipc command */
|
||||
static inline void ipc_command(struct intel_scu_ipc_dev *scu, u32 cmd)
|
||||
{
|
||||
if (ipcdev.irq_mode) {
|
||||
reinit_completion(&ipcdev.cmd_complete);
|
||||
writel(cmd | IPC_IOC, ipcdev.ipc_base);
|
||||
if (scu->irq_mode) {
|
||||
reinit_completion(&scu->cmd_complete);
|
||||
writel(cmd | IPC_IOC, scu->ipc_base);
|
||||
}
|
||||
writel(cmd, ipcdev.ipc_base);
|
||||
writel(cmd, scu->ipc_base);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write ipc data
|
||||
* IPC Write Buffer (Write Only):
|
||||
* 16-byte buffer for sending data associated with IPC command to
|
||||
* SCU. Size of the data is specified in the IPC_COMMAND_REG register
|
||||
*/
|
||||
static inline void ipc_data_writel(u32 data, u32 offset) /* Write ipc data */
|
||||
static inline void ipc_data_writel(struct intel_scu_ipc_dev *scu, u32 data, u32 offset)
|
||||
{
|
||||
writel(data, ipcdev.ipc_base + 0x80 + offset);
|
||||
writel(data, scu->ipc_base + 0x80 + offset);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -149,35 +148,37 @@ static inline void ipc_data_writel(u32 data, u32 offset) /* Write ipc data */
|
|||
* Format:
|
||||
* |rfu3(8)|error code(8)|initiator id(8)|cmd id(4)|rfu1(2)|error(1)|busy(1)|
|
||||
*/
|
||||
static inline u8 ipc_read_status(void)
|
||||
static inline u8 ipc_read_status(struct intel_scu_ipc_dev *scu)
|
||||
{
|
||||
return __raw_readl(ipcdev.ipc_base + 0x04);
|
||||
return __raw_readl(scu->ipc_base + 0x04);
|
||||
}
|
||||
|
||||
static inline u8 ipc_data_readb(u32 offset) /* Read ipc byte data */
|
||||
/* Read ipc byte data */
|
||||
static inline u8 ipc_data_readb(struct intel_scu_ipc_dev *scu, u32 offset)
|
||||
{
|
||||
return readb(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
|
||||
return readb(scu->ipc_base + IPC_READ_BUFFER + offset);
|
||||
}
|
||||
|
||||
static inline u32 ipc_data_readl(u32 offset) /* Read ipc u32 data */
|
||||
/* Read ipc u32 data */
|
||||
static inline u32 ipc_data_readl(struct intel_scu_ipc_dev *scu, u32 offset)
|
||||
{
|
||||
return readl(ipcdev.ipc_base + IPC_READ_BUFFER + offset);
|
||||
return readl(scu->ipc_base + IPC_READ_BUFFER + offset);
|
||||
}
|
||||
|
||||
/* Wait till scu status is busy */
|
||||
static inline int busy_loop(void)
|
||||
static inline int busy_loop(struct intel_scu_ipc_dev *scu)
|
||||
{
|
||||
u32 status = ipc_read_status();
|
||||
u32 status = ipc_read_status(scu);
|
||||
u32 loop_count = 100000;
|
||||
|
||||
/* break if scu doesn't reset busy bit after huge retry */
|
||||
while ((status & BIT(0)) && --loop_count) {
|
||||
udelay(1); /* scu processing time is in few u secods */
|
||||
status = ipc_read_status();
|
||||
status = ipc_read_status(scu);
|
||||
}
|
||||
|
||||
if (status & BIT(0)) {
|
||||
dev_err(&ipcdev.pdev->dev, "IPC timed out");
|
||||
dev_err(scu->dev, "IPC timed out");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
|
@ -188,31 +189,31 @@ static inline int busy_loop(void)
|
|||
}
|
||||
|
||||
/* Wait till ipc ioc interrupt is received or timeout in 3 HZ */
|
||||
static inline int ipc_wait_for_interrupt(void)
|
||||
static inline int ipc_wait_for_interrupt(struct intel_scu_ipc_dev *scu)
|
||||
{
|
||||
int status;
|
||||
|
||||
if (!wait_for_completion_timeout(&ipcdev.cmd_complete, 3 * HZ)) {
|
||||
struct device *dev = &ipcdev.pdev->dev;
|
||||
dev_err(dev, "IPC timed out\n");
|
||||
if (!wait_for_completion_timeout(&scu->cmd_complete, 3 * HZ)) {
|
||||
dev_err(scu->dev, "IPC timed out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
status = ipc_read_status();
|
||||
status = ipc_read_status(scu);
|
||||
if (status & BIT(1))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_scu_ipc_check_status(void)
|
||||
static int intel_scu_ipc_check_status(struct intel_scu_ipc_dev *scu)
|
||||
{
|
||||
return ipcdev.irq_mode ? ipc_wait_for_interrupt() : busy_loop();
|
||||
return scu->irq_mode ? ipc_wait_for_interrupt(scu) : busy_loop(scu);
|
||||
}
|
||||
|
||||
/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
|
||||
static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = &ipcdev;
|
||||
int nc;
|
||||
u32 offset = 0;
|
||||
int err;
|
||||
|
@ -223,7 +224,7 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
|
|||
|
||||
mutex_lock(&ipclock);
|
||||
|
||||
if (ipcdev.pdev == NULL) {
|
||||
if (scu->dev == NULL) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -235,27 +236,27 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
|
|||
|
||||
if (id == IPC_CMD_PCNTRL_R) {
|
||||
for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
|
||||
ipc_data_writel(wbuf[nc], offset);
|
||||
ipc_command((count * 2) << 16 | id << 12 | 0 << 8 | op);
|
||||
ipc_data_writel(scu, wbuf[nc], offset);
|
||||
ipc_command(scu, (count * 2) << 16 | id << 12 | 0 << 8 | op);
|
||||
} else if (id == IPC_CMD_PCNTRL_W) {
|
||||
for (nc = 0; nc < count; nc++, offset += 1)
|
||||
cbuf[offset] = data[nc];
|
||||
for (nc = 0, offset = 0; nc < count; nc++, offset += 4)
|
||||
ipc_data_writel(wbuf[nc], offset);
|
||||
ipc_command((count * 3) << 16 | id << 12 | 0 << 8 | op);
|
||||
ipc_data_writel(scu, wbuf[nc], offset);
|
||||
ipc_command(scu, (count * 3) << 16 | id << 12 | 0 << 8 | op);
|
||||
} else if (id == IPC_CMD_PCNTRL_M) {
|
||||
cbuf[offset] = data[0];
|
||||
cbuf[offset + 1] = data[1];
|
||||
ipc_data_writel(wbuf[0], 0); /* Write wbuff */
|
||||
ipc_command(4 << 16 | id << 12 | 0 << 8 | op);
|
||||
ipc_data_writel(scu, wbuf[0], 0); /* Write wbuff */
|
||||
ipc_command(scu, 4 << 16 | id << 12 | 0 << 8 | op);
|
||||
}
|
||||
|
||||
err = intel_scu_ipc_check_status();
|
||||
err = intel_scu_ipc_check_status(scu);
|
||||
if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */
|
||||
/* Workaround: values are read as 0 without memcpy_fromio */
|
||||
memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16);
|
||||
memcpy_fromio(cbuf, scu->ipc_base + 0x90, 16);
|
||||
for (nc = 0; nc < count; nc++)
|
||||
data[nc] = ipc_data_readb(nc);
|
||||
data[nc] = ipc_data_readb(scu, nc);
|
||||
}
|
||||
mutex_unlock(&ipclock);
|
||||
return err;
|
||||
|
@ -436,15 +437,16 @@ EXPORT_SYMBOL(intel_scu_ipc_update_register);
|
|||
*/
|
||||
int intel_scu_ipc_simple_command(int cmd, int sub)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = &ipcdev;
|
||||
int err;
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
if (ipcdev.pdev == NULL) {
|
||||
if (scu->dev == NULL) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
ipc_command(sub << 12 | cmd);
|
||||
err = intel_scu_ipc_check_status();
|
||||
ipc_command(scu, sub << 12 | cmd);
|
||||
err = intel_scu_ipc_check_status(scu);
|
||||
mutex_unlock(&ipclock);
|
||||
return err;
|
||||
}
|
||||
|
@ -465,23 +467,24 @@ EXPORT_SYMBOL(intel_scu_ipc_simple_command);
|
|||
int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
|
||||
u32 *out, int outlen)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = &ipcdev;
|
||||
int i, err;
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
if (ipcdev.pdev == NULL) {
|
||||
if (scu->dev == NULL) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; i < inlen; i++)
|
||||
ipc_data_writel(*in++, 4 * i);
|
||||
ipc_data_writel(scu, *in++, 4 * i);
|
||||
|
||||
ipc_command((inlen << 16) | (sub << 12) | cmd);
|
||||
err = intel_scu_ipc_check_status();
|
||||
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(4 * i);
|
||||
*out++ = ipc_data_readl(scu, 4 * i);
|
||||
}
|
||||
|
||||
mutex_unlock(&ipclock);
|
||||
|
@ -507,25 +510,26 @@ EXPORT_SYMBOL(intel_scu_ipc_command);
|
|||
*/
|
||||
int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = &ipcdev;
|
||||
u32 cmd = 0;
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
if (ipcdev.pdev == NULL) {
|
||||
if (scu->dev == NULL) {
|
||||
mutex_unlock(&ipclock);
|
||||
return -ENODEV;
|
||||
}
|
||||
cmd = (addr >> 24) & 0xFF;
|
||||
if (cmd == IPC_I2C_READ) {
|
||||
writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR);
|
||||
writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR);
|
||||
/* Write not getting updated without delay */
|
||||
mdelay(1);
|
||||
*data = readl(ipcdev.i2c_base + I2C_DATA_ADDR);
|
||||
*data = readl(scu->i2c_base + I2C_DATA_ADDR);
|
||||
} else if (cmd == IPC_I2C_WRITE) {
|
||||
writel(*data, ipcdev.i2c_base + I2C_DATA_ADDR);
|
||||
writel(*data, scu->i2c_base + I2C_DATA_ADDR);
|
||||
mdelay(1);
|
||||
writel(addr, ipcdev.i2c_base + IPC_I2C_CNTRL_ADDR);
|
||||
writel(addr, scu->i2c_base + IPC_I2C_CNTRL_ADDR);
|
||||
} else {
|
||||
dev_err(&ipcdev.pdev->dev,
|
||||
dev_err(scu->dev,
|
||||
"intel_scu_ipc: I2C INVALID_CMD = 0x%x\n", cmd);
|
||||
|
||||
mutex_unlock(&ipclock);
|
||||
|
@ -545,63 +549,65 @@ EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl);
|
|||
*/
|
||||
static irqreturn_t ioc(int irq, void *dev_id)
|
||||
{
|
||||
if (ipcdev.irq_mode)
|
||||
complete(&ipcdev.cmd_complete);
|
||||
struct intel_scu_ipc_dev *scu = dev_id;
|
||||
|
||||
if (scu->irq_mode)
|
||||
complete(&scu->cmd_complete);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipc_probe - probe an Intel SCU IPC
|
||||
* @dev: the PCI device matching
|
||||
* @pdev: the PCI device matching
|
||||
* @id: entry in the match table
|
||||
*
|
||||
* Enable and install an intel SCU IPC. This appears in the PCI space
|
||||
* but uses some hard coded addresses as well.
|
||||
*/
|
||||
static int ipc_probe(struct pci_dev *dev, 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;
|
||||
struct intel_scu_ipc_dev *scu = &ipcdev;
|
||||
struct intel_scu_ipc_pdata_t *pdata;
|
||||
resource_size_t base;
|
||||
|
||||
if (ipcdev.pdev) /* We support only one SCU */
|
||||
platform = intel_mid_identify_cpu();
|
||||
if (platform == 0)
|
||||
return -ENODEV;
|
||||
|
||||
if (scu->dev) /* We support only one SCU */
|
||||
return -EBUSY;
|
||||
|
||||
pdata = (struct intel_scu_ipc_pdata_t *)id->driver_data;
|
||||
|
||||
ipcdev.pdev = pci_dev_get(dev);
|
||||
ipcdev.irq_mode = pdata->irq_mode;
|
||||
scu->dev = &pdev->dev;
|
||||
scu->irq_mode = pdata->irq_mode;
|
||||
|
||||
err = pci_enable_device(dev);
|
||||
err = pcim_enable_device(pdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = pci_request_regions(dev, "intel_scu_ipc");
|
||||
err = pcim_iomap_regions(pdev, 1 << 0, pci_name(pdev));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
base = pci_resource_start(dev, 0);
|
||||
if (!base)
|
||||
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->i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);
|
||||
if (!scu->i2c_base)
|
||||
return -ENOMEM;
|
||||
|
||||
init_completion(&ipcdev.cmd_complete);
|
||||
|
||||
if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev))
|
||||
return -EBUSY;
|
||||
|
||||
ipcdev.ipc_base = ioremap_nocache(base, pci_resource_len(dev, 0));
|
||||
if (!ipcdev.ipc_base)
|
||||
return -ENOMEM;
|
||||
|
||||
ipcdev.i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);
|
||||
if (!ipcdev.i2c_base) {
|
||||
iounmap(ipcdev.ipc_base);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
intel_scu_devices_create();
|
||||
|
||||
pci_set_drvdata(pdev, scu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -617,12 +623,13 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
*/
|
||||
static void ipc_remove(struct pci_dev *pdev)
|
||||
{
|
||||
free_irq(pdev->irq, &ipcdev);
|
||||
pci_release_regions(pdev);
|
||||
pci_dev_put(ipcdev.pdev);
|
||||
iounmap(ipcdev.ipc_base);
|
||||
iounmap(ipcdev.i2c_base);
|
||||
ipcdev.pdev = NULL;
|
||||
struct intel_scu_ipc_dev *scu = pci_get_drvdata(pdev);
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
scu->dev = NULL;
|
||||
mutex_unlock(&ipclock);
|
||||
|
||||
iounmap(scu->i2c_base);
|
||||
intel_scu_devices_destroy();
|
||||
}
|
||||
|
||||
|
@ -652,24 +659,8 @@ static struct pci_driver ipc_driver = {
|
|||
.remove = ipc_remove,
|
||||
};
|
||||
|
||||
static int __init intel_scu_ipc_init(void)
|
||||
{
|
||||
int platform; /* Platform type */
|
||||
|
||||
platform = intel_mid_identify_cpu();
|
||||
if (platform == 0)
|
||||
return -ENODEV;
|
||||
return pci_register_driver(&ipc_driver);
|
||||
}
|
||||
|
||||
static void __exit intel_scu_ipc_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&ipc_driver);
|
||||
}
|
||||
module_pci_driver(ipc_driver);
|
||||
|
||||
MODULE_AUTHOR("Sreedhara DS <sreedhara.ds@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel SCU IPC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(intel_scu_ipc_init);
|
||||
module_exit(intel_scu_ipc_exit);
|
||||
|
|
|
@ -1204,6 +1204,8 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
|
|||
{
|
||||
u32 real_ev = event;
|
||||
u8 ev_type = 0;
|
||||
int ret;
|
||||
|
||||
dprintk("sony_nc_notify, event: 0x%.2x\n", event);
|
||||
|
||||
if (event >= 0x90) {
|
||||
|
@ -1225,13 +1227,12 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
|
|||
case 0x0100:
|
||||
case 0x0127:
|
||||
ev_type = HOTKEY;
|
||||
real_ev = sony_nc_hotkeys_decode(event, handle);
|
||||
ret = sony_nc_hotkeys_decode(event, handle);
|
||||
|
||||
if (real_ev > 0)
|
||||
sony_laptop_report_input_event(real_ev);
|
||||
else
|
||||
/* restore the original event for reporting */
|
||||
real_ev = event;
|
||||
if (ret > 0) {
|
||||
sony_laptop_report_input_event(ret);
|
||||
real_ev = ret;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
|
138
drivers/platform/x86/toshiba-wmi.c
Normal file
138
drivers/platform/x86/toshiba-wmi.c
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* toshiba_wmi.c - Toshiba WMI Hotkey Driver
|
||||
*
|
||||
* Copyright (C) 2015 Azael Avalos <coproscefalo@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
|
||||
MODULE_AUTHOR("Azael Avalos");
|
||||
MODULE_DESCRIPTION("Toshiba WMI Hotkey Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100"
|
||||
|
||||
MODULE_ALIAS("wmi:"TOSHIBA_WMI_EVENT_GUID);
|
||||
|
||||
static struct input_dev *toshiba_wmi_input_dev;
|
||||
|
||||
static const struct key_entry toshiba_wmi_keymap[] __initconst = {
|
||||
/* TODO: Add keymap values once found... */
|
||||
/*{ KE_KEY, 0x00, { KEY_ } },*/
|
||||
{ KE_END, 0 }
|
||||
};
|
||||
|
||||
static void toshiba_wmi_notify(u32 value, void *context)
|
||||
{
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
|
||||
status = wmi_get_event_data(value, &response);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("Bad event status 0x%x\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)response.pointer;
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
/* TODO: Add proper checks once we have data */
|
||||
pr_debug("Unknown event received, obj type %x\n", obj->type);
|
||||
|
||||
kfree(response.pointer);
|
||||
}
|
||||
|
||||
static int __init toshiba_wmi_input_setup(void)
|
||||
{
|
||||
acpi_status status;
|
||||
int err;
|
||||
|
||||
toshiba_wmi_input_dev = input_allocate_device();
|
||||
if (!toshiba_wmi_input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
toshiba_wmi_input_dev->name = "Toshiba WMI hotkeys";
|
||||
toshiba_wmi_input_dev->phys = "wmi/input0";
|
||||
toshiba_wmi_input_dev->id.bustype = BUS_HOST;
|
||||
|
||||
err = sparse_keymap_setup(toshiba_wmi_input_dev,
|
||||
toshiba_wmi_keymap, NULL);
|
||||
if (err)
|
||||
goto err_free_dev;
|
||||
|
||||
status = wmi_install_notify_handler(TOSHIBA_WMI_EVENT_GUID,
|
||||
toshiba_wmi_notify, NULL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
err = -EIO;
|
||||
goto err_free_keymap;
|
||||
}
|
||||
|
||||
err = input_register_device(toshiba_wmi_input_dev);
|
||||
if (err)
|
||||
goto err_remove_notifier;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_notifier:
|
||||
wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID);
|
||||
err_free_keymap:
|
||||
sparse_keymap_free(toshiba_wmi_input_dev);
|
||||
err_free_dev:
|
||||
input_free_device(toshiba_wmi_input_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void toshiba_wmi_input_destroy(void)
|
||||
{
|
||||
wmi_remove_notify_handler(TOSHIBA_WMI_EVENT_GUID);
|
||||
sparse_keymap_free(toshiba_wmi_input_dev);
|
||||
input_unregister_device(toshiba_wmi_input_dev);
|
||||
}
|
||||
|
||||
static int __init toshiba_wmi_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
|
||||
return -ENODEV;
|
||||
|
||||
ret = toshiba_wmi_input_setup();
|
||||
if (ret) {
|
||||
pr_err("Failed to setup input device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pr_info("Toshiba WMI Hotkey Driver\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit toshiba_wmi_exit(void)
|
||||
{
|
||||
if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID))
|
||||
toshiba_wmi_input_destroy();
|
||||
}
|
||||
|
||||
module_init(toshiba_wmi_init);
|
||||
module_exit(toshiba_wmi_exit);
|
|
@ -131,7 +131,7 @@ MODULE_LICENSE("GPL");
|
|||
/* Field definitions */
|
||||
#define HCI_ACCEL_MASK 0x7fff
|
||||
#define HCI_HOTKEY_DISABLE 0x0b
|
||||
#define HCI_HOTKEY_ENABLE 0x09
|
||||
#define HCI_HOTKEY_ENABLE 0x01
|
||||
#define HCI_HOTKEY_SPECIAL_FUNCTIONS 0x10
|
||||
#define HCI_LCD_BRIGHTNESS_BITS 3
|
||||
#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
|
||||
|
@ -198,6 +198,7 @@ struct toshiba_acpi_dev {
|
|||
unsigned int panel_power_on_supported:1;
|
||||
unsigned int usb_three_supported:1;
|
||||
unsigned int sysfs_created:1;
|
||||
unsigned int special_functions;
|
||||
|
||||
bool kbd_led_registered;
|
||||
bool illumination_led_registered;
|
||||
|
@ -1668,10 +1669,10 @@ static ssize_t available_kbd_modes_show(struct device *dev,
|
|||
struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
|
||||
|
||||
if (toshiba->kbd_type == 1)
|
||||
return sprintf(buf, "%x %x\n",
|
||||
return sprintf(buf, "0x%x 0x%x\n",
|
||||
SCI_KBD_MODE_FNZ, SCI_KBD_MODE_AUTO);
|
||||
|
||||
return sprintf(buf, "%x %x %x\n",
|
||||
return sprintf(buf, "0x%x 0x%x 0x%x\n",
|
||||
SCI_KBD_MODE_AUTO, SCI_KBD_MODE_ON, SCI_KBD_MODE_OFF);
|
||||
}
|
||||
static DEVICE_ATTR_RO(available_kbd_modes);
|
||||
|
@ -2253,7 +2254,16 @@ static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev)
|
|||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
result = hci_write(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
|
||||
/*
|
||||
* Enable the "Special Functions" mode only if they are
|
||||
* supported and if they are activated.
|
||||
*/
|
||||
if (dev->kbd_function_keys_supported && dev->special_functions)
|
||||
result = hci_write(dev, HCI_HOTKEY_EVENT,
|
||||
HCI_HOTKEY_SPECIAL_FUNCTIONS);
|
||||
else
|
||||
result = hci_write(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE);
|
||||
|
||||
if (result == TOS_FAILURE)
|
||||
return -EIO;
|
||||
else if (result == TOS_NOT_SUPPORTED)
|
||||
|
@ -2262,20 +2272,6 @@ static int toshiba_acpi_enable_hotkeys(struct toshiba_acpi_dev *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void toshiba_acpi_enable_special_functions(struct toshiba_acpi_dev *dev)
|
||||
{
|
||||
u32 result;
|
||||
|
||||
/*
|
||||
* Re-activate the hotkeys, but this time, we are using the
|
||||
* "Special Functions" mode.
|
||||
*/
|
||||
result = hci_write(dev, HCI_HOTKEY_EVENT,
|
||||
HCI_HOTKEY_SPECIAL_FUNCTIONS);
|
||||
if (result != TOS_SUCCESS)
|
||||
pr_err("Could not enable the Special Function mode\n");
|
||||
}
|
||||
|
||||
static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
|
||||
struct serio *port)
|
||||
{
|
||||
|
@ -2385,8 +2381,6 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
|
|||
{
|
||||
const struct key_entry *keymap = toshiba_acpi_keymap;
|
||||
acpi_handle ec_handle;
|
||||
u32 events_type;
|
||||
u32 hci_result;
|
||||
int error;
|
||||
|
||||
if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) {
|
||||
|
@ -2398,11 +2392,9 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
if (toshiba_hotkey_event_type_get(dev, &events_type))
|
||||
if (toshiba_hotkey_event_type_get(dev, &dev->hotkey_event_type))
|
||||
pr_notice("Unable to query Hotkey Event Type\n");
|
||||
|
||||
dev->hotkey_event_type = events_type;
|
||||
|
||||
dev->hotkey_dev = input_allocate_device();
|
||||
if (!dev->hotkey_dev)
|
||||
return -ENOMEM;
|
||||
|
@ -2411,14 +2403,15 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
|
|||
dev->hotkey_dev->phys = "toshiba_acpi/input0";
|
||||
dev->hotkey_dev->id.bustype = BUS_HOST;
|
||||
|
||||
if (events_type == HCI_SYSTEM_TYPE1 ||
|
||||
if (dev->hotkey_event_type == HCI_SYSTEM_TYPE1 ||
|
||||
!dev->kbd_function_keys_supported)
|
||||
keymap = toshiba_acpi_keymap;
|
||||
else if (events_type == HCI_SYSTEM_TYPE2 ||
|
||||
else if (dev->hotkey_event_type == HCI_SYSTEM_TYPE2 ||
|
||||
dev->kbd_function_keys_supported)
|
||||
keymap = toshiba_acpi_alt_keymap;
|
||||
else
|
||||
pr_info("Unknown event type received %x\n", events_type);
|
||||
pr_info("Unknown event type received %x\n",
|
||||
dev->hotkey_event_type);
|
||||
error = sparse_keymap_setup(dev->hotkey_dev, keymap, NULL);
|
||||
if (error)
|
||||
goto err_free_dev;
|
||||
|
@ -2449,11 +2442,8 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
|
|||
*/
|
||||
if (acpi_has_method(dev->acpi_dev->handle, "INFO"))
|
||||
dev->info_supported = 1;
|
||||
else {
|
||||
hci_result = hci_write(dev, HCI_SYSTEM_EVENT, 1);
|
||||
if (hci_result == TOS_SUCCESS)
|
||||
dev->system_event_supported = 1;
|
||||
}
|
||||
else if (hci_write(dev, HCI_SYSTEM_EVENT, 1) == TOS_SUCCESS)
|
||||
dev->system_event_supported = 1;
|
||||
|
||||
if (!dev->info_supported && !dev->system_event_supported) {
|
||||
pr_warn("No hotkey query interface found\n");
|
||||
|
@ -2631,7 +2621,6 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
|
|||
{
|
||||
struct toshiba_acpi_dev *dev;
|
||||
const char *hci_method;
|
||||
u32 special_functions;
|
||||
u32 dummy;
|
||||
int ret = 0;
|
||||
|
||||
|
@ -2673,9 +2662,10 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
|
|||
* with the new keyboard layout, query for its presence to help
|
||||
* determine the keymap layout to use.
|
||||
*/
|
||||
ret = toshiba_function_keys_get(dev, &special_functions);
|
||||
ret = toshiba_function_keys_get(dev, &dev->special_functions);
|
||||
dev->kbd_function_keys_supported = !ret;
|
||||
|
||||
dev->hotkey_event_type = 0;
|
||||
if (toshiba_acpi_setup_keyboard(dev))
|
||||
pr_info("Unable to activate hotkeys\n");
|
||||
|
||||
|
@ -2748,13 +2738,6 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
|
|||
|
||||
print_supported_features(dev);
|
||||
|
||||
/*
|
||||
* Enable the "Special Functions" mode only if they are
|
||||
* supported and if they are activated.
|
||||
*/
|
||||
if (dev->kbd_function_keys_supported && special_functions)
|
||||
toshiba_acpi_enable_special_functions(dev);
|
||||
|
||||
ret = sysfs_create_group(&dev->acpi_dev->dev.kobj,
|
||||
&toshiba_attr_group);
|
||||
if (ret) {
|
||||
|
|
Loading…
Reference in a new issue