Commit d1e41ff1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'platform-drivers-x86-v4.4-1' of...

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
parents 2f4bf528 d2f20619
......@@ -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,7 +77,7 @@ 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,
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"
......@@ -84,6 +85,9 @@ static int mfld_pb_probe(struct platform_device *pdev)
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)
return -ENOMEM;
init_completion(&scu->cmd_complete);
init_completion(&ipcdev.cmd_complete);
err = devm_request_irq(&pdev->dev, pdev->irq, ioc, 0, "intel_scu_ipc",
scu);
if (err)
return err;
if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev))
return -EBUSY;
scu->ipc_base = pcim_iomap_table(pdev)[0];
ipcdev.ipc_base = ioremap_nocache(base, pci_resource_len(dev, 0));
if (!ipcdev.ipc_base)
scu->i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);
if (!scu->i2c_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;
......
/*
* 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;
/*
* 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)
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) {
......
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