summaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/Kconfig48
-rw-r--r--drivers/platform/x86/Makefile3
-rw-r--r--drivers/platform/x86/acer-wmi.c30
-rw-r--r--drivers/platform/x86/amilo-rfkill.c173
-rw-r--r--drivers/platform/x86/asus-wmi.c2
-rw-r--r--drivers/platform/x86/eeepc-laptop.c2
-rw-r--r--drivers/platform/x86/fujitsu-tablet.c478
-rw-r--r--drivers/platform/x86/hdaps.c4
-rw-r--r--drivers/platform/x86/intel_mid_powerbtn.c32
-rw-r--r--drivers/platform/x86/intel_mid_thermal.c47
-rw-r--r--drivers/platform/x86/intel_rar_register.c669
-rw-r--r--drivers/platform/x86/intel_scu_ipc.c208
-rw-r--r--drivers/platform/x86/intel_scu_ipcutil.c32
-rw-r--r--drivers/platform/x86/panasonic-laptop.c4
14 files changed, 794 insertions, 938 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index f995e6e2f78c..2dc02c972ce9 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -143,6 +143,30 @@ config FUJITSU_LAPTOP_DEBUG
If you are not sure, say N here.
+config FUJITSU_TABLET
+ tristate "Fujitsu Tablet Extras"
+ depends on ACPI
+ depends on INPUT
+ ---help---
+ This is a driver for tablets built by Fujitsu:
+
+ * Lifebook P1510/P1610/P1620/Txxxx
+ * Stylistic ST5xxx
+ * Possibly other Fujitsu tablet models
+
+ It adds support for the panel buttons, docking station detection,
+ tablet/notebook mode detection for convertible and
+ orientation detection for docked slates.
+
+ If you have a Fujitsu convertible or slate, say Y or M here.
+
+config AMILO_RFKILL
+ tristate "Fujitsu-Siemens Amilo rfkill support"
+ depends on RFKILL
+ ---help---
+ This is a driver for enabling wifi on some Fujitsu-Siemens Amilo
+ laptops.
+
config TC1100_WMI
tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)"
depends on !X86_64
@@ -672,33 +696,11 @@ config INTEL_MID_POWER_BUTTON
config INTEL_MFLD_THERMAL
tristate "Thermal driver for Intel Medfield platform"
- depends on INTEL_SCU_IPC && THERMAL
+ depends on MFD_INTEL_MSIC && THERMAL
help
Say Y here to enable thermal driver support for the Intel Medfield
platform.
-config RAR_REGISTER
- bool "Restricted Access Region Register Driver"
- depends on PCI && X86_MRST
- default n
- ---help---
- This driver allows other kernel drivers access to the
- contents of the restricted access region control registers.
-
- The restricted access region control registers
- (rar_registers) are used to pass address and
- locking information on restricted access regions
- to other drivers that use restricted access regions.
-
- The restricted access regions are regions of memory
- on the Intel MID Platform that are not accessible to
- the x86 processor, but are accessible to dedicated
- processors on board peripheral devices.
-
- The purpose of the restricted access regions is to
- protect sensitive data from compromise by unauthorized
- programs running on the x86 processor.
-
config INTEL_IPS
tristate "Intel Intelligent Power Sharing"
depends on ACPI
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 293a320d9faa..bb947657d490 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -17,12 +17,14 @@ obj-$(CONFIG_ACER_WMI) += acer-wmi.o
obj-$(CONFIG_ACERHDF) += acerhdf.o
obj-$(CONFIG_HP_ACCEL) += hp_accel.o
obj-$(CONFIG_HP_WMI) += hp-wmi.o
+obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
obj-$(CONFIG_IDEAPAD_LAPTOP) += ideapad-laptop.o
obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_FUJITSU_LAPTOP) += fujitsu-laptop.o
+obj-$(CONFIG_FUJITSU_TABLET) += fujitsu-tablet.o
obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o
obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o
obj-$(CONFIG_ACPI_WMI) += wmi.o
@@ -34,7 +36,6 @@ obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.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
-obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index b848277171a4..1e5290b5396d 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -679,6 +679,32 @@ static acpi_status AMW0_find_mailled(void)
return AE_OK;
}
+static int AMW0_set_cap_acpi_check_device_found;
+
+static acpi_status AMW0_set_cap_acpi_check_device_cb(acpi_handle handle,
+ u32 level, void *context, void **retval)
+{
+ AMW0_set_cap_acpi_check_device_found = 1;
+ return AE_OK;
+}
+
+static const struct acpi_device_id norfkill_ids[] = {
+ { "VPC2004", 0},
+ { "IBM0068", 0},
+ { "LEN0068", 0},
+ { "", 0},
+};
+
+static int AMW0_set_cap_acpi_check_device(void)
+{
+ const struct acpi_device_id *id;
+
+ for (id = norfkill_ids; id->id[0]; id++)
+ acpi_get_devices(id->id, AMW0_set_cap_acpi_check_device_cb,
+ NULL, NULL);
+ return AMW0_set_cap_acpi_check_device_found;
+}
+
static acpi_status AMW0_set_capabilities(void)
{
struct wmab_args args;
@@ -692,7 +718,9 @@ static acpi_status AMW0_set_capabilities(void)
* work.
*/
if (wmi_has_guid(AMW0_GUID2)) {
- interface->capability |= ACER_CAP_WIRELESS;
+ if ((quirks != &quirk_unknown) ||
+ !AMW0_set_cap_acpi_check_device())
+ interface->capability |= ACER_CAP_WIRELESS;
return AE_OK;
}
diff --git a/drivers/platform/x86/amilo-rfkill.c b/drivers/platform/x86/amilo-rfkill.c
new file mode 100644
index 000000000000..19170bb7700b
--- /dev/null
+++ b/drivers/platform/x86/amilo-rfkill.c
@@ -0,0 +1,173 @@
+/*
+ * Support for rfkill on some Fujitsu-Siemens Amilo laptops.
+ * Copyright 2011 Ben Hutchings.
+ *
+ * Based in part on the fsam7440 driver, which is:
+ * Copyright 2005 Alejandro Vidal Mata & Javier Vidal Mata.
+ * and on the fsaa1655g driver, which is:
+ * Copyright 2006 Martin Večeřa.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/dmi.h>
+#include <linux/i8042.h>
+#include <linux/io.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/rfkill.h>
+
+/*
+ * These values were obtained from disassembling and debugging the
+ * PM.exe program installed in the Fujitsu-Siemens AMILO A1655G
+ */
+#define A1655_WIFI_COMMAND 0x10C5
+#define A1655_WIFI_ON 0x25
+#define A1655_WIFI_OFF 0x45
+
+static int amilo_a1655_rfkill_set_block(void *data, bool blocked)
+{
+ u8 param = blocked ? A1655_WIFI_OFF : A1655_WIFI_ON;
+ int rc;
+
+ i8042_lock_chip();
+ rc = i8042_command(&param, A1655_WIFI_COMMAND);
+ i8042_unlock_chip();
+ return rc;
+}
+
+static const struct rfkill_ops amilo_a1655_rfkill_ops = {
+ .set_block = amilo_a1655_rfkill_set_block
+};
+
+/*
+ * These values were obtained from disassembling the PM.exe program
+ * installed in the Fujitsu-Siemens AMILO M 7440
+ */
+#define M7440_PORT1 0x118f
+#define M7440_PORT2 0x118e
+#define M7440_RADIO_ON1 0x12
+#define M7440_RADIO_ON2 0x80
+#define M7440_RADIO_OFF1 0x10
+#define M7440_RADIO_OFF2 0x00
+
+static int amilo_m7440_rfkill_set_block(void *data, bool blocked)
+{
+ u8 val1 = blocked ? M7440_RADIO_OFF1 : M7440_RADIO_ON1;
+ u8 val2 = blocked ? M7440_RADIO_OFF2 : M7440_RADIO_ON2;
+
+ outb(val1, M7440_PORT1);
+ outb(val2, M7440_PORT2);
+
+ /* Check whether the state has changed correctly */
+ if (inb(M7440_PORT1) != val1 || inb(M7440_PORT2) != val2)
+ return -EIO;
+
+ return 0;
+}
+
+static const struct rfkill_ops amilo_m7440_rfkill_ops = {
+ .set_block = amilo_m7440_rfkill_set_block
+};
+
+static const struct dmi_system_id __devinitdata amilo_rfkill_id_table[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_BOARD_NAME, "AMILO A1655"),
+ },
+ .driver_data = (void *)&amilo_a1655_rfkill_ops
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_BOARD_NAME, "AMILO M7440"),
+ },
+ .driver_data = (void *)&amilo_m7440_rfkill_ops
+ },
+ {}
+};
+
+static struct platform_device *amilo_rfkill_pdev;
+static struct rfkill *amilo_rfkill_dev;
+
+static int __devinit amilo_rfkill_probe(struct platform_device *device)
+{
+ const struct dmi_system_id *system_id =
+ dmi_first_match(amilo_rfkill_id_table);
+ int rc;
+
+ amilo_rfkill_dev = rfkill_alloc(KBUILD_MODNAME, &device->dev,
+ RFKILL_TYPE_WLAN,
+ system_id->driver_data, NULL);
+ if (!amilo_rfkill_dev)
+ return -ENOMEM;
+
+ rc = rfkill_register(amilo_rfkill_dev);
+ if (rc)
+ goto fail;
+
+ return 0;
+
+fail:
+ rfkill_destroy(amilo_rfkill_dev);
+ return rc;
+}
+
+static int amilo_rfkill_remove(struct platform_device *device)
+{
+ rfkill_unregister(amilo_rfkill_dev);
+ rfkill_destroy(amilo_rfkill_dev);
+ return 0;
+}
+
+static struct platform_driver amilo_rfkill_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = amilo_rfkill_probe,
+ .remove = amilo_rfkill_remove,
+};
+
+static int __init amilo_rfkill_init(void)
+{
+ int rc;
+
+ if (dmi_first_match(amilo_rfkill_id_table) == NULL)
+ return -ENODEV;
+
+ rc = platform_driver_register(&amilo_rfkill_driver);
+ if (rc)
+ return rc;
+
+ amilo_rfkill_pdev = platform_device_register_simple(KBUILD_MODNAME, -1,
+ NULL, 0);
+ if (IS_ERR(amilo_rfkill_pdev)) {
+ rc = PTR_ERR(amilo_rfkill_pdev);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ platform_driver_unregister(&amilo_rfkill_driver);
+ return rc;
+}
+
+static void __exit amilo_rfkill_exit(void)
+{
+ platform_device_unregister(amilo_rfkill_pdev);
+ platform_driver_unregister(&amilo_rfkill_driver);
+}
+
+MODULE_AUTHOR("Ben Hutchings <ben@decadent.org.uk>");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(dmi, amilo_rfkill_id_table);
+
+module_init(amilo_rfkill_init);
+module_exit(amilo_rfkill_exit);
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 72d731c21d45..9929246895de 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -571,7 +571,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus)
} else {
dev = pci_get_slot(bus, 0);
if (dev) {
- pci_remove_bus_device(dev);
+ pci_stop_and_remove_bus_device(dev);
pci_dev_put(dev);
}
}
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index ea44abd8df48..d9a9e2bedb30 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -646,7 +646,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
} else {
dev = pci_get_slot(bus, 0);
if (dev) {
- pci_remove_bus_device(dev);
+ pci_stop_and_remove_bus_device(dev);
pci_dev_put(dev);
}
}
diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c
new file mode 100644
index 000000000000..580d80a73c3a
--- /dev/null
+++ b/drivers/platform/x86/fujitsu-tablet.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2006-2012 Robert Gerlach <khnz@gmx.de>
+ * Copyright (C) 2005-2006 Jan Rychter <jan@rychter.com>
+ *
+ * You can redistribute and/or modify this program under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+
+#define MODULENAME "fujitsu-tablet"
+
+#define ACPI_FUJITSU_CLASS "fujitsu"
+
+#define INVERT_TABLET_MODE_BIT 0x01
+#define FORCE_TABLET_MODE_IF_UNDOCK 0x02
+
+#define KEYMAP_LEN 16
+
+static const struct acpi_device_id fujitsu_ids[] = {
+ { .id = "FUJ02BD" },
+ { .id = "FUJ02BF" },
+ { .id = "" }
+};
+
+struct fujitsu_config {
+ unsigned short keymap[KEYMAP_LEN];
+ unsigned int quirks;
+};
+
+static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initconst = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_SCROLLDOWN,
+ KEY_SCROLLUP,
+ KEY_DIRECTION,
+ KEY_LEFTCTRL,
+ KEY_BRIGHTNESSUP,
+ KEY_BRIGHTNESSDOWN,
+ KEY_BRIGHTNESS_ZERO,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_LEFTALT
+};
+
+static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initconst = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_PROG1,
+ KEY_PROG2,
+ KEY_DIRECTION,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_UP,
+ KEY_DOWN,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_LEFTCTRL,
+ KEY_LEFTALT
+};
+
+static unsigned short keymap_Stylistic_Tseries[KEYMAP_LEN] __initconst = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_PRINT,
+ KEY_BACKSPACE,
+ KEY_SPACE,
+ KEY_ENTER,
+ KEY_BRIGHTNESSUP,
+ KEY_BRIGHTNESSDOWN,
+ KEY_DOWN,
+ KEY_UP,
+ KEY_SCROLLUP,
+ KEY_SCROLLDOWN,
+ KEY_LEFTCTRL,
+ KEY_LEFTALT
+};
+
+static unsigned short keymap_Stylistic_ST5xxx[KEYMAP_LEN] __initconst = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_MAIL,
+ KEY_DIRECTION,
+ KEY_ESC,
+ KEY_ENTER,
+ KEY_BRIGHTNESSUP,
+ KEY_BRIGHTNESSDOWN,
+ KEY_DOWN,
+ KEY_UP,
+ KEY_SCROLLUP,
+ KEY_SCROLLDOWN,
+ KEY_LEFTCTRL,
+ KEY_LEFTALT
+};
+
+static struct {
+ struct input_dev *idev;
+ struct fujitsu_config config;
+ unsigned long prev_keymask;
+
+ char phys[21];
+
+ int irq;
+ int io_base;
+ int io_length;
+} fujitsu;
+
+static u8 fujitsu_ack(void)
+{
+ return inb(fujitsu.io_base + 2);
+}
+
+static u8 fujitsu_status(void)
+{
+ return inb(fujitsu.io_base + 6);
+}
+
+static u8 fujitsu_read_register(const u8 addr)
+{
+ outb(addr, fujitsu.io_base);
+ return inb(fujitsu.io_base + 4);
+}
+
+static void fujitsu_send_state(void)
+{
+ int state;
+ int dock, tablet_mode;
+
+ state = fujitsu_read_register(0xdd);
+
+ dock = state & 0x02;
+
+ if ((fujitsu.config.quirks & FORCE_TABLET_MODE_IF_UNDOCK) && (!dock)) {
+ tablet_mode = 1;
+ } else{
+ tablet_mode = state & 0x01;
+ if (fujitsu.config.quirks & INVERT_TABLET_MODE_BIT)
+ tablet_mode = !tablet_mode;
+ }
+
+ input_report_switch(fujitsu.idev, SW_DOCK, dock);
+ input_report_switch(fujitsu.idev, SW_TABLET_MODE, tablet_mode);
+ input_sync(fujitsu.idev);
+}
+
+static void fujitsu_reset(void)
+{
+ int timeout = 50;
+
+ fujitsu_ack();
+
+ while ((fujitsu_status() & 0x02) && (--timeout))
+ msleep(20);
+
+ fujitsu_send_state();
+}
+
+static int __devinit input_fujitsu_setup(struct device *parent,
+ const char *name, const char *phys)
+{
+ struct input_dev *idev;
+ int error;
+ int i;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ idev->dev.parent = parent;
+ idev->phys = phys;
+ idev->name = name;
+ idev->id.bustype = BUS_HOST;
+ idev->id.vendor = 0x1734; /* Fujitsu Siemens Computer GmbH */
+ idev->id.product = 0x0001;
+ idev->id.version = 0x0101;
+
+ idev->keycode = fujitsu.config.keymap;
+ idev->keycodesize = sizeof(fujitsu.config.keymap[0]);
+ idev->keycodemax = ARRAY_SIZE(fujitsu.config.keymap);
+
+ __set_bit(EV_REP, idev->evbit);
+
+ for (i = 0; i < ARRAY_SIZE(fujitsu.config.keymap); i++)
+ if (fujitsu.config.keymap[i])
+ input_set_capability(idev, EV_KEY, fujitsu.config.keymap[i]);
+
+ input_set_capability(idev, EV_MSC, MSC_SCAN);
+
+ input_set_capability(idev, EV_SW, SW_DOCK);
+ input_set_capability(idev, EV_SW, SW_TABLET_MODE);
+
+ input_set_capability(idev, EV_SW, SW_DOCK);
+ input_set_capability(idev, EV_SW, SW_TABLET_MODE);
+
+ error = input_register_device(idev);
+ if (error) {
+ input_free_device(idev);
+ return error;
+ }
+
+ fujitsu.idev = idev;
+ return 0;
+}
+
+static void input_fujitsu_remove(void)
+{
+ input_unregister_device(fujitsu.idev);
+}
+
+static irqreturn_t fujitsu_interrupt(int irq, void *dev_id)
+{
+ unsigned long keymask, changed;
+ unsigned int keycode;
+ int pressed;
+ int i;
+
+ if (unlikely(!(fujitsu_status() & 0x01)))
+ return IRQ_NONE;
+
+ fujitsu_send_state();
+
+ keymask = fujitsu_read_register(0xde);
+ keymask |= fujitsu_read_register(0xdf) << 8;
+ keymask ^= 0xffff;
+
+ changed = keymask ^ fujitsu.prev_keymask;
+ if (changed) {
+ fujitsu.prev_keymask = keymask;
+
+ for_each_set_bit(i, &changed, KEYMAP_LEN) {
+ keycode = fujitsu.config.keymap[i];
+ pressed = keymask & changed & BIT(i);
+
+ if (pressed)
+ input_event(fujitsu.idev, EV_MSC, MSC_SCAN, i);
+
+ input_report_key(fujitsu.idev, keycode, pressed);
+ input_sync(fujitsu.idev);
+ }
+ }
+
+ fujitsu_ack();
+ return IRQ_HANDLED;
+}
+
+static int __devinit fujitsu_dmi_default(const struct dmi_system_id *dmi)
+{
+ printk(KERN_INFO MODULENAME ": %s\n", dmi->ident);
+ memcpy(fujitsu.config.keymap, dmi->driver_data,
+ sizeof(fujitsu.config.keymap));
+ return 1;
+}
+
+static int __devinit fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)
+{
+ fujitsu_dmi_default(dmi);
+ fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK;
+ fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT;
+ return 1;
+}
+
+static struct dmi_system_id dmi_ids[] __initconst = {
+ {
+ .callback = fujitsu_dmi_default,
+ .ident = "Fujitsu Siemens P/T Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK")
+ },
+ .driver_data = keymap_Lifebook_Tseries
+ },
+ {
+ .callback = fujitsu_dmi_default,
+ .ident = "Fujitsu Lifebook T Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T")
+ },
+ .driver_data = keymap_Lifebook_Tseries
+ },
+ {
+ .callback = fujitsu_dmi_stylistic,
+ .ident = "Fujitsu Siemens Stylistic T Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic T")
+ },
+ .driver_data = keymap_Stylistic_Tseries
+ },
+ {
+ .callback = fujitsu_dmi_default,
+ .ident = "Fujitsu LifeBook U810",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook U810")
+ },
+ .driver_data = keymap_Lifebook_U810
+ },
+ {
+ .callback = fujitsu_dmi_stylistic,
+ .ident = "Fujitsu Siemens Stylistic ST5xxx Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "STYLISTIC ST5")
+ },
+ .driver_data = keymap_Stylistic_ST5xxx
+ },
+ {
+ .callback = fujitsu_dmi_stylistic,
+ .ident = "Fujitsu Siemens Stylistic ST5xxx Series",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Stylistic ST5")
+ },
+ .driver_data = keymap_Stylistic_ST5xxx
+ },
+ {
+ .callback = fujitsu_dmi_default,
+ .ident = "Unknown (using defaults)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, ""),
+ DMI_MATCH(DMI_PRODUCT_NAME, "")
+ },
+ .driver_data = keymap_Lifebook_Tseries
+ },
+ { NULL }
+};
+
+static acpi_status __devinit
+fujitsu_walk_resources(struct acpi_resource *res, void *data)
+{
+ switch (res->type) {
+ case ACPI_RESOURCE_TYPE_IRQ:
+ fujitsu.irq = res->data.irq.interrupts[0];
+ return AE_OK;
+
+ case ACPI_RESOURCE_TYPE_IO:
+ fujitsu.io_base = res->data.io.minimum;
+ fujitsu.io_length = res->data.io.address_length;
+ return AE_OK;
+
+ case ACPI_RESOURCE_TYPE_END_TAG:
+ if (fujitsu.irq && fujitsu.io_base)
+ return AE_OK;
+ else
+ return AE_NOT_FOUND;
+
+ default:
+ return AE_ERROR;
+ }
+}
+
+static int __devinit acpi_fujitsu_add(struct acpi_device *adev)
+{
+ acpi_status status;
+ int error;
+
+ if (!adev)
+ return -EINVAL;
+
+ status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
+ fujitsu_walk_resources, NULL);
+ if (ACPI_FAILURE(status) || !fujitsu.irq || !fujitsu.io_base)
+ return -ENODEV;
+
+ sprintf(acpi_device_name(adev), "Fujitsu %s", acpi_device_hid(adev));
+ sprintf(acpi_device_class(adev), "%s", ACPI_FUJITSU_CLASS);
+
+ snprintf(fujitsu.phys, sizeof(fujitsu.phys),
+ "%s/input0", acpi_device_hid(adev));
+
+ error = input_fujitsu_setup(&adev->dev,
+ acpi_device_name(adev), fujitsu.phys);
+ if (error)
+ return error;
+
+ if (!request_region(fujitsu.io_base, fujitsu.io_length, MODULENAME)) {
+ input_fujitsu_remove();
+ return -EBUSY;
+ }
+
+ fujitsu_reset();
+
+ error = request_irq(fujitsu.irq, fujitsu_interrupt,
+ IRQF_SHARED, MODULENAME, fujitsu_interrupt);
+ if (error) {
+ release_region(fujitsu.io_base, fujitsu.io_length);
+ input_fujitsu_remove();
+ return error;
+ }
+
+ return 0;
+}
+
+static int __devexit acpi_fujitsu_remove(struct acpi_device *adev, int type)
+{
+ free_irq(fujitsu.irq, fujitsu_interrupt);
+ release_region(fujitsu.io_base, fujitsu.io_length);
+ input_fujitsu_remove();
+ return 0;
+}
+
+static int acpi_fujitsu_resume(struct acpi_device *adev)
+{
+ fujitsu_reset();
+ return 0;
+}
+
+static struct acpi_driver acpi_fujitsu_driver = {
+ .name = MODULENAME,
+ .class = "hotkey",
+ .ids = fujitsu_ids,
+ .ops = {
+ .add = acpi_fujitsu_add,
+ .remove = acpi_fujitsu_remove,
+ .resume = acpi_fujitsu_resume,
+ }
+};
+
+static int __init fujitsu_module_init(void)
+{
+ int error;
+
+ dmi_check_system(dmi_ids);
+
+ error = acpi_bus_register_driver(&acpi_fujitsu_driver);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static void __exit fujitsu_module_exit(void)
+{
+ acpi_bus_unregister_driver(&acpi_fujitsu_driver);
+}
+
+module_init(fujitsu_module_init);
+module_exit(fujitsu_module_exit);
+
+MODULE_AUTHOR("Robert Gerlach <khnz@gmx.de>");
+MODULE_DESCRIPTION("Fujitsu tablet pc extras driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("2.4");
+
+MODULE_DEVICE_TABLE(acpi, fujitsu_ids);
diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c
index 5a34973dc164..ba68d4e7a779 100644
--- a/drivers/platform/x86/hdaps.c
+++ b/drivers/platform/x86/hdaps.c
@@ -379,7 +379,7 @@ static ssize_t hdaps_temp1_show(struct device *dev,
int ret;
ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp);
- if (ret < 0)
+ if (ret)
return ret;
return sprintf(buf, "%u\n", temp);
@@ -392,7 +392,7 @@ static ssize_t hdaps_temp2_show(struct device *dev,
int ret;
ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp);
- if (ret < 0)
+ if (ret)
return ret;
return sprintf(buf, "%u\n", temp);
diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c
index f1ae5078b7ec..0903a883e9f4 100644
--- a/drivers/platform/x86/intel_mid_powerbtn.c
+++ b/drivers/platform/x86/intel_mid_powerbtn.c
@@ -23,21 +23,27 @@
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/input.h>
-
-#include <asm/intel_scu_ipc.h>
+#include <linux/mfd/intel_msic.h>
#define DRIVER_NAME "msic_power_btn"
-#define MSIC_PB_STATUS 0x3f
#define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */
+/*
+ * MSIC document ti_datasheet defines the 1st bit reg 0x21 is used to mask
+ * power button interrupt
+ */
+#define MSIC_PWRBTNM (1 << 0)
+
static irqreturn_t mfld_pb_isr(int irq, void *dev_id)
{
struct input_dev *input = dev_id;
int ret;
u8 pbstat;
- ret = intel_scu_ipc_ioread8(MSIC_PB_STATUS, &pbstat);
+ ret = intel_msic_reg_read(INTEL_MSIC_PBSTATUS, &pbstat);
+ dev_dbg(input->dev.parent, "PB_INT status= %d\n", pbstat);
+
if (ret < 0) {
dev_err(input->dev.parent, "Read error %d while reading"
" MSIC_PB_STATUS\n", ret);
@@ -88,6 +94,24 @@ static int __devinit mfld_pb_probe(struct platform_device *pdev)
}
platform_set_drvdata(pdev, input);
+
+ /*
+ * SCU firmware might send power button interrupts to IA core before
+ * kernel boots and doesn't get EOI from IA core. The first bit of
+ * MSIC reg 0x21 is kept masked, and SCU firmware doesn't send new
+ * power interrupt to Android kernel. Unmask the bit when probing
+ * power button in kernel.
+ * There is a very narrow race between irq handler and power button
+ * initialization. The race happens rarely. So we needn't worry
+ * about it.
+ */
+ error = intel_msic_reg_update(INTEL_MSIC_IRQLVL1MSK, 0, MSIC_PWRBTNM);
+ if (error) {
+ dev_err(&pdev->dev, "Unable to clear power button interrupt, "
+ "error: %d\n", error);
+ goto err_free_irq;
+ }
+
return 0;
err_free_irq:
diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c
index ccd7b1f83519..2ee9766737ea 100644
--- a/drivers/platform/x86/intel_mid_thermal.c
+++ b/drivers/platform/x86/intel_mid_thermal.c
@@ -33,18 +33,15 @@
#include <linux/slab.h>
#include <linux/pm.h>
#include <linux/thermal.h>
-
-#include <asm/intel_scu_ipc.h>
+#include <linux/mfd/intel_msic.h>
/* Number of thermal sensors */
#define MSIC_THERMAL_SENSORS 4
/* ADC1 - thermal registers */
-#define MSIC_THERM_ADC1CNTL1 0x1C0
#define MSIC_ADC_ENBL 0x10
#define MSIC_ADC_START 0x08
-#define MSIC_THERM_ADC1CNTL3 0x1C2
#define MSIC_ADCTHERM_ENBL 0x04
#define MSIC_ADCRRDATA_ENBL 0x05
#define MSIC_CHANL_MASK_VAL 0x0F
@@ -75,8 +72,8 @@
#define ADC_VAL60C 315
/* ADC base addresses */
-#define ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */
-#define ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */
+#define ADC_CHNL_START_ADDR INTEL_MSIC_ADC1ADDR0 /* increments by 1 */
+#define ADC_DATA_START_ADDR INTEL_MSIC_ADC1SNS0H /* increments by 2 */
/* MSIC die attributes */
#define MSIC_DIE_ADC_MIN 488
@@ -189,17 +186,17 @@ static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp)
addr = td_info->chnl_addr;
/* Enable the msic for conversion before reading */
- ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCRRDATA_ENBL);
+ ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, MSIC_ADCRRDATA_ENBL);
if (ret)
return ret;
/* Re-toggle the RRDATARD bit (temporary workaround) */
- ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCTHERM_ENBL);
+ ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, MSIC_ADCTHERM_ENBL);
if (ret)
return ret;
/* Read the higher bits of data */
- ret = intel_scu_ipc_ioread8(addr, &data);
+ ret = intel_msic_reg_read(addr, &data);
if (ret)
return ret;
@@ -207,7 +204,7 @@ static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp)
adc_val = (data << 2);
addr++;
- ret = intel_scu_ipc_ioread8(addr, &data);/* Read lower bits */
+ ret = intel_msic_reg_read(addr, &data);/* Read lower bits */
if (ret)
return ret;
@@ -235,7 +232,7 @@ static int configure_adc(int val)
int ret;
uint8_t data;
- ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data);
+ ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL1, &data);
if (ret)
return ret;
@@ -246,7 +243,7 @@ static int configure_adc(int val)
/* Just stop the ADC */
data &= (~MSIC_ADC_START);
}
- return intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL1, data);
+ return intel_msic_reg_write(INTEL_MSIC_ADC1CNTL1, data);
}
/**
@@ -262,21 +259,21 @@ static int set_up_therm_channel(u16 base_addr)
int ret;
/* Enable all the sensor channels */
- ret = intel_scu_ipc_iowrite8(base_addr, SKIN_SENSOR0_CODE);
+ ret = intel_msic_reg_write(base_addr, SKIN_SENSOR0_CODE);
if (ret)
return ret;
- ret = intel_scu_ipc_iowrite8(base_addr + 1, SKIN_SENSOR1_CODE);
+ ret = intel_msic_reg_write(base_addr + 1, SKIN_SENSOR1_CODE);
if (ret)
return ret;
- ret = intel_scu_ipc_iowrite8(base_addr + 2, SYS_SENSOR_CODE);
+ ret = intel_msic_reg_write(base_addr + 2, SYS_SENSOR_CODE);
if (ret)
return ret;
/* Since this is the last channel, set the stop bit
* to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
- ret = intel_scu_ipc_iowrite8(base_addr + 3,
+ ret = intel_msic_reg_write(base_addr + 3,
(MSIC_DIE_SENSOR_CODE | 0x10));
if (ret)
return ret;
@@ -295,11 +292,11 @@ static int reset_stopbit(uint16_t addr)
{
int ret;
uint8_t data;
- ret = intel_scu_ipc_ioread8(addr, &data);
+ ret = intel_msic_reg_read(addr, &data);
if (ret)
return ret;
/* Set the stop bit to zero */
- return intel_scu_ipc_iowrite8(addr, (data & 0xEF));
+ return intel_msic_reg_write(addr, (data & 0xEF));
}
/**
@@ -322,7 +319,7 @@ static int find_free_channel(void)
uint8_t data;
/* check whether ADC is enabled */
- ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data);
+ ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL1, &data);
if (ret)
return ret;
@@ -331,7 +328,7 @@ static int find_free_channel(void)
/* ADC is already enabled; Looking for an empty channel */
for (i = 0; i < ADC_CHANLS_MAX; i++) {
- ret = intel_scu_ipc_ioread8(ADC_CHNL_START_ADDR + i, &data);
+ ret = intel_msic_reg_read(ADC_CHNL_START_ADDR + i, &data);
if (ret)
return ret;
@@ -359,12 +356,14 @@ static int mid_initialize_adc(struct device *dev)
* Ensure that adctherm is disabled before we
* initialize the ADC
*/
- ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL3, &data);
+ ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL3, &data);
if (ret)
return ret;
- if (data & MSIC_ADCTHERM_MASK)
- dev_warn(dev, "ADCTHERM already set");
+ data &= ~MSIC_ADCTHERM_MASK;
+ ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, data);
+ if (ret)
+ return ret;
/* Index of the first channel in which the stop bit is set */
channel_index = find_free_channel();
@@ -546,7 +545,7 @@ static int mid_thermal_remove(struct platform_device *pdev)
return configure_adc(0);
}
-#define DRIVER_NAME "msic_sensor"
+#define DRIVER_NAME "msic_thermal"
static const struct platform_device_id therm_id_table[] = {
{ DRIVER_NAME, 1 },
diff --git a/drivers/platform/x86/intel_rar_register.c b/drivers/platform/x86/intel_rar_register.c
deleted file mode 100644
index c8a6aed45277..000000000000
--- a/drivers/platform/x86/intel_rar_register.c
+++ /dev/null
@@ -1,669 +0,0 @@
-/*
- * rar_register.c - An Intel Restricted Access Region register driver
- *
- * Copyright(c) 2009 Intel Corporation. All rights reserved.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- *
- * -------------------------------------------------------------------
- * 20091204 Mark Allyn <mark.a.allyn@intel.com>
- * Ossama Othman <ossama.othman@intel.com>
- * Cleanup per feedback from Alan Cox and Arjan Van De Ven
- *
- * 20090806 Ossama Othman <ossama.othman@intel.com>
- * Return zero high address if upper 22 bits is zero.
- * Cleaned up checkpatch errors.
- * Clarified that driver is dealing with bus addresses.
- *
- * 20090702 Ossama Othman <ossama.othman@intel.com>
- * Removed unnecessary include directives
- * Cleaned up spinlocks.
- * Cleaned up logging.
- * Improved invalid parameter checks.
- * Fixed and simplified RAR address retrieval and RAR locking
- * code.
- *
- * 20090626 Mark Allyn <mark.a.allyn@intel.com>
- * Initial publish
- */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/spinlock.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/rar_register.h>
-
-/* === Lincroft Message Bus Interface === */
-#define LNC_MCR_OFFSET 0xD0 /* Message Control Register */
-#define LNC_MDR_OFFSET 0xD4 /* Message Data Register */
-
-/* Message Opcodes */
-#define LNC_MESSAGE_READ_OPCODE 0xD0
-#define LNC_MESSAGE_WRITE_OPCODE 0xE0
-
-/* Message Write Byte Enables */
-#define LNC_MESSAGE_BYTE_WRITE_ENABLES 0xF
-
-/* B-unit Port */
-#define LNC_BUNIT_PORT 0x3
-
-/* === Lincroft B-Unit Registers - Programmed by IA32 firmware === */
-#define LNC_BRAR0L 0x10
-#define LNC_BRAR0H 0x11
-#define LNC_BRAR1L 0x12
-#define LNC_BRAR1H 0x13
-/* Reserved for SeP */
-#define LNC_BRAR2L 0x14
-#define LNC_BRAR2H 0x15
-
-/* Moorestown supports three restricted access regions. */
-#define MRST_NUM_RAR 3
-
-/* RAR Bus Address Range */
-struct rar_addr {
- dma_addr_t low;
- dma_addr_t high;
-};
-
-/*
- * We create one of these for each RAR
- */
-struct client {
- int (*callback)(unsigned long data);
- unsigned long driver_priv;
- bool busy;
-};
-
-static DEFINE_MUTEX(rar_mutex);
-static DEFINE_MUTEX(lnc_reg_mutex);
-
-/*
- * One per RAR device (currently only one device)
- */
-struct rar_device {
- struct rar_addr rar_addr[MRST_NUM_RAR];
- struct pci_dev *rar_dev;
- bool registered;
- bool allocated;
- struct client client[MRST_NUM_RAR];
-};
-
-/* Current platforms have only one rar_device for 3 rar regions */
-static struct rar_device my_rar_device;
-
-/*
- * Abstract out multiple device support. Current platforms only
- * have a single RAR device.
- */
-
-/**
- * alloc_rar_device - return a new RAR structure
- *
- * Return a new (but not yet ready) RAR device object
- */
-static struct rar_device *alloc_rar_device(void)
-{
- if (my_rar_device.allocated)
- return NULL;
- my_rar_device.allocated = 1;
- return &my_rar_device;
-}
-
-/**
- * free_rar_device - free a RAR object
- * @rar: the RAR device being freed
- *
- * Release a RAR object and any attached resources
- */
-static void free_rar_device(struct rar_device *rar)
-{
- pci_dev_put(rar->rar_dev);
- rar->allocated = 0;
-}
-
-/**
- * _rar_to_device - return the device handling this RAR
- * @rar: RAR number
- * @off: returned offset
- *
- * Internal helper for looking up RAR devices. This and alloc are the
- * two functions that need touching to go to multiple RAR devices.
- */
-static struct rar_device *_rar_to_device(int rar, int *off)
-{
- if (rar >= 0 && rar < MRST_NUM_RAR) {
- *off = rar;
- return &my_rar_device;
- }
- return NULL;
-}
-
-/**
- * rar_to_device - return the device handling this RAR
- * @rar: RAR number
- * @off: returned offset
- *
- * Return the device this RAR maps to if one is present, otherwise
- * returns NULL. Reports the offset relative to the base of this
- * RAR device in off.
- */
-static struct rar_device *rar_to_device(int rar, int *off)
-{
- struct rar_device *rar_dev = _rar_to_device(rar, off);
- if (rar_dev == NULL || !rar_dev->registered)
- return NULL;
- return rar_dev;
-}
-
-/**
- * rar_to_client - return the client handling this RAR
- * @rar: RAR number
- *
- * Return the client this RAR maps to if a mapping is known, otherwise
- * returns NULL.
- */
-static struct client *rar_to_client(int rar)
-{
- int idx;
- struct rar_device *r = _rar_to_device(rar, &idx);
- if (r != NULL)
- return &r->client[idx];
- return NULL;
-}
-
-/**
- * rar_read_addr - retrieve a RAR mapping
- * @pdev: PCI device for the RAR
- * @offset: offset for message
- * @addr: returned address
- *
- * Reads the address of a given RAR register. Returns 0 on success
- * or an error code on failure.
- */
-static int rar_read_addr(struct pci_dev *pdev, int offset, dma_addr_t *addr)
-{
- /*
- * ======== The Lincroft Message Bus Interface ========
- * Lincroft registers may be obtained via PCI from
- * the host bridge using the Lincroft Message Bus
- * Interface. That message bus interface is generally
- * comprised of two registers: a control register (MCR, 0xDO)
- * and a data register (MDR, 0xD4).
- *
- * The MCR (message control register) format is the following:
- * 1. [31:24]: Opcode
- * 2. [23:16]: Port
- * 3. [15:8]: Register Offset
- * 4. [7:4]: Byte Enables (use 0xF to set all of these bits
- * to 1)
- * 5. [3:0]: reserved
- *
- * Read (0xD0) and write (0xE0) opcodes are written to the
- * control register when reading and writing to Lincroft
- * registers, respectively.
- *
- * We're interested in registers found in the Lincroft
- * B-unit. The B-unit port is 0x3.
- *
- * The six B-unit RAR register offsets we use are listed
- * earlier in this file.
- *
- * Lastly writing to the MCR register requires the "Byte
- * enables" bits to be set to 1. This may be achieved by
- * writing 0xF at bit 4.
- *
- * The MDR (message data register) format is the following:
- * 1. [31:0]: Read/Write Data
- *
- * Data being read from this register is only available after
- * writing the appropriate control message to the MCR
- * register.
- *
- * Data being written to this register must be written before
- * writing the appropriate control message to the MCR
- * register.
- */
-
- int result;
- u32 addr32;
-
- /* Construct control message */
- u32 const message =
- (LNC_MESSAGE_READ_OPCODE << 24)
- | (LNC_BUNIT_PORT << 16)
- | (offset << 8)
- | (LNC_MESSAGE_BYTE_WRITE_ENABLES << 4);
-
- dev_dbg(&pdev->dev, "Offset for 'get' LNC MSG is %x\n", offset);
-
- /*
- * We synchronize access to the Lincroft MCR and MDR registers
- * until BOTH the command is issued through the MCR register
- * and the corresponding data is read from the MDR register.
- * Otherwise a race condition would exist between accesses to
- * both registers.
- */
-
- mutex_lock(&lnc_reg_mutex);
-
- /* Send the control message */
- result = pci_write_config_dword(pdev, LNC_MCR_OFFSET, message);
- if (!result) {
- /* Read back the address as a 32bit value */
- result = pci_read_config_dword(pdev, LNC_MDR_OFFSET, &addr32);
- *addr = (dma_addr_t)addr32;
- }
- mutex_unlock(&lnc_reg_mutex);
- return result;
-}
-
-/**
- * rar_set_addr - Set a RAR mapping
- * @pdev: PCI device for the RAR
- * @offset: offset for message
- * @addr: address to set
- *
- * Sets the address of a given RAR register. Returns 0 on success
- * or an error code on failure.
- */
-static int rar_set_addr(struct pci_dev *pdev,
- int offset,
- dma_addr_t addr)
-{
- /*
- * Data being written to this register must be written before
- * writing the appropriate control message to the MCR
- * register.
- * See rar_get_addrs() for a description of the
- * message bus interface being used here.
- */
-
- int result;
-
- /* Construct control message */
- u32 const message = (LNC_MESSAGE_WRITE_OPCODE << 24)
- | (LNC_BUNIT_PORT << 16)
- | (offset << 8)
- | (LNC_MESSAGE_BYTE_WRITE_ENABLES << 4);
-
- /*
- * We synchronize access to the Lincroft MCR and MDR registers
- * until BOTH the command is issued through the MCR register
- * and the corresponding data is read from the MDR register.
- * Otherwise a race condition would exist between accesses to
- * both registers.
- */
-
- mutex_lock(&lnc_reg_mutex);
-
- /* Send the control message */
- result = pci_write_config_dword(pdev, LNC_MDR_OFFSET, addr);
- if (!result)
- /* And address */
- result = pci_write_config_dword(pdev, LNC_MCR_OFFSET, message);
-
- mutex_unlock(&lnc_reg_mutex);
- return result;
-}
-
-/*
- * rar_init_params - Initialize RAR parameters
- * @rar: RAR device to initialise
- *
- * Initialize RAR parameters, such as bus addresses, etc. Returns 0
- * on success, or an error code on failure.
- */
-static int init_rar_params(struct rar_device *rar)
-{
- struct pci_dev *pdev = rar->rar_dev;
- unsigned int i;
- int result = 0;
- int offset = 0x10; /* RAR 0 to 2 in order low/high/low/high/... */
-
- /* Retrieve RAR start and end bus addresses.
- * Access the RAR registers through the Lincroft Message Bus
- * Interface on PCI device: 00:00.0 Host bridge.
- */
-
- for (i = 0; i < MRST_NUM_RAR; ++i) {
- struct rar_addr *addr = &rar->rar_addr[i];
-
- result = rar_read_addr(pdev, offset++, &addr->low);
- if (result != 0)
- return result;
-
- result = rar_read_addr(pdev, offset++, &addr->high);
- if (result != 0)
- return result;
-
-
- /*
- * Only the upper 22 bits of the RAR addresses are
- * stored in their corresponding RAR registers so we
- * must set the lower 10 bits accordingly.
-
- * The low address has its lower 10 bits cleared, and
- * the high address has all its lower 10 bits set,
- * e.g.:
- * low = 0x2ffffc00
- */
-
- addr->low &= (dma_addr_t)0xfffffc00u;
-
- /*
- * Set bits 9:0 on uppser address if bits 31:10 are non
- * zero; otherwize clear all bits
- */
-
- if ((addr->high & 0xfffffc00u) == 0)
- addr->high = 0;
- else
- addr->high |= 0x3ffu;
- }
- /* Done accessing the device. */
-
- if (result == 0) {
- for (i = 0; i != MRST_NUM_RAR; ++i) {
- /*
- * "BRAR" refers to the RAR registers in the
- * Lincroft B-unit.
- */
- dev_info(&pdev->dev, "BRAR[%u] bus address range = "
- "[%lx, %lx]\n", i,
- (unsigned long)rar->rar_addr[i].low,
- (unsigned long)rar->rar_addr[i].high);
- }
- }
- return result;
-}
-
-/**
- * rar_get_address - get the bus address in a RAR
- * @start: return value of start address of block
- * @end: return value of end address of block
- *
- * The rar_get_address function is used by other device drivers
- * to obtain RAR address information on a RAR. It takes three
- * parameters:
- *
- * The function returns a 0 upon success or an error if there is no RAR
- * facility on this system.
- */
-int rar_get_address(int rar_index, dma_addr_t *start, dma_addr_t *end)
-{
- int idx;
- struct rar_device *rar = rar_to_device(rar_index, &idx);
-
- if (rar == NULL) {
- WARN_ON(1);
- return -ENODEV;
- }
-
- *start = rar->rar_addr[idx].low;
- *end = rar->rar_addr[idx].high;
- return 0;
-}
-EXPORT_SYMBOL(rar_get_address);
-
-/**
- * rar_lock - lock a RAR register
- * @rar_index: RAR to lock (0-2)
- *
- * The rar_lock function is ued by other device drivers to lock an RAR.
- * once a RAR is locked, it stays locked until the next system reboot.
- *
- * The function returns a 0 upon success or an error if there is no RAR
- * facility on this system, or the locking fails
- */
-int rar_lock(int rar_index)
-{
- struct rar_device *rar;
- int result;
- int idx;
- dma_addr_t low, high;
-
- rar = rar_to_device(rar_index, &idx);
-
- if (rar == NULL) {
- WARN_ON(1);
- return -EINVAL;
- }
-
- low = rar->rar_addr[idx].low & 0xfffffc00u;
- high = rar->rar_addr[idx].high & 0xfffffc00u;
-
- /*
- * Only allow I/O from the graphics and Langwell;
- * not from the x86 processor
- */
-
- if (rar_index == RAR_TYPE_VIDEO) {
- low |= 0x00000009;
- high |= 0x00000015;
- } else if (rar_index == RAR_TYPE_AUDIO) {
- /* Only allow I/O from Langwell; nothing from x86 */
- low |= 0x00000008;
- high |= 0x00000018;
- } else
- /* Read-only from all agents */
- high |= 0x00000018;
-
- /*
- * Now program the register using the Lincroft message
- * bus interface.
- */
- result = rar_set_addr(rar->rar_dev,
- 2 * idx, low);
-
- if (result == 0)
- result = rar_set_addr(rar->rar_dev,
- 2 * idx + 1, high);
-
- return result;
-}
-EXPORT_SYMBOL(rar_lock);
-
-/**
- * register_rar - register a RAR handler
- * @num: RAR we wish to register for
- * @callback: function to call when RAR support is available
- * @data: data to pass to this function
- *
- * The register_rar function is to used by other device drivers
- * to ensure that this driver is ready. As we cannot be sure of
- * the compile/execute order of drivers in the kernel, it is
- * best to give this driver a callback function to call when
- * it is ready to give out addresses. The callback function
- * would have those steps that continue the initialization of
- * a driver that do require a valid RAR address. One of those
- * steps would be to call rar_get_address()
- *
- * This function return 0 on success or an error code on failure.
- */
-int register_rar(int num, int (*callback)(unsigned long data),
- unsigned long data)
-{
- /* For now we hardcode a single RAR device */
- struct rar_device *rar;
- struct client *c;
- int idx;
- int retval = 0;
-
- mutex_lock(&rar_mutex);
-
- /* Do we have a client mapping for this RAR number ? */
- c = rar_to_client(num);
- if (c == NULL) {
- retval = -ERANGE;
- goto done;
- }
- /* Is it claimed ? */
- if (c->busy) {
- retval = -EBUSY;
- goto done;
- }
- c->busy = 1;
-
- /* See if we have a handler for this RAR yet, if we do then fire it */
- rar = rar_to_device(num, &idx);
-
- if (rar) {
- /*
- * if the driver already registered, then we can simply
- * call the callback right now
- */
- (*callback)(data);
- goto done;
- }
-
- /* Arrange to be called back when the hardware is found */
- c->callback = callback;
- c->driver_priv = data;
-done:
- mutex_unlock(&rar_mutex);
- return retval;
-}
-EXPORT_SYMBOL(register_rar);
-
-/**
- * unregister_rar - release a RAR allocation
- * @num: RAR number
- *
- * Releases a RAR allocation, or pending allocation. If a callback is
- * pending then this function will either complete before the unregister
- * returns or not at all.
- */
-
-void unregister_rar(int num)
-{
- struct client *c;
-
- mutex_lock(&rar_mutex);
- c = rar_to_client(num);
- if (c == NULL || !c->busy)
- WARN_ON(1);
- else
- c->busy = 0;
- mutex_unlock(&rar_mutex);
-}
-EXPORT_SYMBOL(unregister_rar);
-
-/**
- * rar_callback - Process callbacks
- * @rar: new RAR device
- *
- * Process the callbacks for a newly found RAR device.
- */
-
-static void rar_callback(struct rar_device *rar)
-{
- struct client *c = &rar->client[0];
- int i;
-
- mutex_lock(&rar_mutex);
-
- rar->registered = 1; /* Ensure no more callbacks queue */
-
- for (i = 0; i < MRST_NUM_RAR; i++) {
- if (c->callback && c->busy) {
- c->callback(c->driver_priv);
- c->callback = NULL;
- }
- c++;
- }
- mutex_unlock(&rar_mutex);
-}
-
-/**
- * rar_probe - PCI probe callback
- * @dev: PCI device
- * @id: matching entry in the match table
- *
- * A RAR device has been discovered. Initialise it and if successful
- * process any pending callbacks that can now be completed.
- */
-static int rar_probe(struct pci_dev *dev, const struct pci_device_id *id)
-{
- int error;
- struct rar_device *rar;
-
- dev_dbg(&dev->dev, "PCI probe starting\n");
-
- rar = alloc_rar_device();
- if (rar == NULL)
- return -EBUSY;
-
- /* Enable the device */
- error = pci_enable_device(dev);
- if (error) {
- dev_err(&dev->dev,
- "Error enabling RAR register PCI device\n");
- goto end_function;
- }
-
- /* Fill in the rar_device structure */
- rar->rar_dev = pci_dev_get(dev);
- pci_set_drvdata(dev, rar);
-
- /*
- * Initialize the RAR parameters, which have to be retrieved
- * via the message bus interface.
- */
- error = init_rar_params(rar);
- if (error) {
- pci_disable_device(dev);
- dev_err(&dev->dev, "Error retrieving RAR addresses\n");
- goto end_function;
- }
- /* now call anyone who has registered (using callbacks) */
- rar_callback(rar);
- return 0;
-end_function:
- free_rar_device(rar);
- return error;
-}
-
-static DEFINE_PCI_DEVICE_TABLE(rar_pci_id_tbl) = {
- { PCI_VDEVICE(INTEL, 0x4110) },
- { 0 }
-};
-
-MODULE_DEVICE_TABLE(pci, rar_pci_id_tbl);
-
-/* field for registering driver to PCI device */
-static struct pci_driver rar_pci_driver = {
- .name = "rar_register_driver",
- .id_table = rar_pci_id_tbl,
- .probe = rar_probe,
- /* Cannot be unplugged - no remove */
-};
-
-static int __init rar_init_handler(void)
-{
- return pci_register_driver(&rar_pci_driver);
-}
-
-static void __exit rar_exit_handler(void)
-{
- pci_unregister_driver(&rar_pci_driver);
-}
-
-module_init(rar_init_handler);
-module_exit(rar_exit_handler);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Intel Restricted Access Region Register Driver");
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index f00d0d1e0653..9215ed72bece 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -159,7 +159,7 @@ static inline int busy_loop(void) /* Wait till scu status is busy */
/* 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)
{
- int i, nc, bytes, d;
+ int nc;
u32 offset = 0;
int err;
u8 cbuf[IPC_WWBUF_SIZE] = { };
@@ -174,55 +174,34 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
return -ENODEV;
}
- if (platform != MRST_CPU_CHIP_PENWELL) {
- bytes = 0;
- d = 0;
- for (i = 0; i < count; i++) {
- cbuf[bytes++] = addr[i];
- cbuf[bytes++] = addr[i] >> 8;
- if (id != IPC_CMD_PCNTRL_R)
- cbuf[bytes++] = data[d++];
- if (id == IPC_CMD_PCNTRL_M)
- cbuf[bytes++] = data[d++];
- }
- for (i = 0; i < bytes; i += 4)
- ipc_data_writel(wbuf[i/4], i);
- ipc_command(bytes << 16 | id << 12 | 0 << 8 | op);
- } else {
- for (nc = 0; nc < count; nc++, offset += 2) {
- cbuf[offset] = addr[nc];
- cbuf[offset + 1] = addr[nc] >> 8;
- }
+ for (nc = 0; nc < count; nc++, offset += 2) {
+ cbuf[offset] = addr[nc];
+ cbuf[offset + 1] = addr[nc] >> 8;
+ }
- 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);
- } 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);
- } 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);
- }
+ 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);
+ } 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);
+ } 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);
}
err = busy_loop();
if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */
/* Workaround: values are read as 0 without memcpy_fromio */
memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16);
- if (platform != MRST_CPU_CHIP_PENWELL) {
- for (nc = 0, offset = 2; nc < count; nc++, offset += 3)
- data[nc] = ipc_data_readb(offset);
- } else {
- for (nc = 0; nc < count; nc++)
- data[nc] = ipc_data_readb(nc);
- }
+ for (nc = 0; nc < count; nc++)
+ data[nc] = ipc_data_readb(nc);
}
mutex_unlock(&ipclock);
return err;
@@ -503,148 +482,6 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data)
}
EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl);
-#define IPC_FW_LOAD_ADDR 0xFFFC0000 /* Storage location for FW image */
-#define IPC_FW_UPDATE_MBOX_ADDR 0xFFFFDFF4 /* Mailbox between ipc and scu */
-#define IPC_MAX_FW_SIZE 262144 /* 256K storage size for loading the FW image */
-#define IPC_FW_MIP_HEADER_SIZE 2048 /* Firmware MIP header size */
-/* IPC inform SCU to get ready for update process */
-#define IPC_CMD_FW_UPDATE_READY 0x10FE
-/* IPC inform SCU to go for update process */
-#define IPC_CMD_FW_UPDATE_GO 0x20FE
-/* Status code for fw update */
-#define IPC_FW_UPDATE_SUCCESS 0x444f4e45 /* Status code 'DONE' */
-#define IPC_FW_UPDATE_BADN 0x4241444E /* Status code 'BADN' */
-#define IPC_FW_TXHIGH 0x54784849 /* Status code 'IPC_FW_TXHIGH' */
-#define IPC_FW_TXLOW 0x54784c4f /* Status code 'IPC_FW_TXLOW' */
-
-struct fw_update_mailbox {
- u32 status;
- u32 scu_flag;
- u32 driver_flag;
-};
-
-
-/**
- * intel_scu_ipc_fw_update - Firmware update utility
- * @buffer: firmware buffer
- * @length: size of firmware buffer
- *
- * This function provides an interface to load the firmware into
- * the SCU. Returns 0 on success or -1 on failure
- */
-int intel_scu_ipc_fw_update(u8 *buffer, u32 length)
-{
- void __iomem *fw_update_base;
- struct fw_update_mailbox __iomem *mailbox = NULL;
- int retry_cnt = 0;
- u32 status;
-
- mutex_lock(&ipclock);
- fw_update_base = ioremap_nocache(IPC_FW_LOAD_ADDR, (128*1024));
- if (fw_update_base == NULL) {
- mutex_unlock(&ipclock);
- return -ENOMEM;
- }
- mailbox = ioremap_nocache(IPC_FW_UPDATE_MBOX_ADDR,
- sizeof(struct fw_update_mailbox));
- if (mailbox == NULL) {
- iounmap(fw_update_base);
- mutex_unlock(&ipclock);
- return -ENOMEM;
- }
-
- ipc_command(IPC_CMD_FW_UPDATE_READY);
-
- /* Intitialize mailbox */
- writel(0, &mailbox->status);
- writel(0, &mailbox->scu_flag);
- writel(0, &mailbox->driver_flag);
-
- /* Driver copies the 2KB MIP header to SRAM at 0xFFFC0000*/
- memcpy_toio(fw_update_base, buffer, 0x800);
-
- /* Driver sends "FW Update" IPC command (CMD_ID 0xFE; MSG_ID 0x02).
- * Upon receiving this command, SCU will write the 2K MIP header
- * from 0xFFFC0000 into NAND.
- * SCU will write a status code into the Mailbox, and then set scu_flag.
- */
-
- ipc_command(IPC_CMD_FW_UPDATE_GO);
-
- /*Driver stalls until scu_flag is set */
- while (readl(&mailbox->scu_flag) != 1) {
- rmb();
- mdelay(1);
- }
-
- /* Driver checks Mailbox status.
- * If the status is 'BADN', then abort (bad NAND).
- * If the status is 'IPC_FW_TXLOW', then continue.
- */
- while (readl(&mailbox->status) != IPC_FW_TXLOW) {
- rmb();
- mdelay(10);
- }
- mdelay(10);
-
-update_retry:
- if (retry_cnt > 5)
- goto update_end;
-
- if (readl(&mailbox->status) != IPC_FW_TXLOW)
- goto update_end;
- buffer = buffer + 0x800;
- memcpy_toio(fw_update_base, buffer, 0x20000);
- writel(1, &mailbox->driver_flag);
- while (readl(&mailbox->scu_flag) == 1) {
- rmb();
- mdelay(1);
- }
-
- /* check for 'BADN' */
- if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN)
- goto update_end;
-
- while (readl(&mailbox->status) != IPC_FW_TXHIGH) {
- rmb();
- mdelay(10);
- }
- mdelay(10);
-
- if (readl(&mailbox->status) != IPC_FW_TXHIGH)
- goto update_end;
-
- buffer = buffer + 0x20000;
- memcpy_toio(fw_update_base, buffer, 0x20000);
- writel(0, &mailbox->driver_flag);
-
- while (mailbox->scu_flag == 0) {
- rmb();
- mdelay(1);
- }
-
- /* check for 'BADN' */
- if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN)
- goto update_end;
-
- if (readl(&mailbox->status) == IPC_FW_TXLOW) {
- ++retry_cnt;
- goto update_retry;
- }
-
-update_end:
- status = readl(&mailbox->status);
-
- iounmap(fw_update_base);
- iounmap(mailbox);
- mutex_unlock(&ipclock);
-
- if (status == IPC_FW_UPDATE_SUCCESS)
- return 0;
- return -EIO;
-}
-EXPORT_SYMBOL(intel_scu_ipc_fw_update);
-
/*
* Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1
* When ioc bit is set to 1, caller api must wait for interrupt handler called
@@ -727,7 +564,6 @@ static void ipc_remove(struct pci_dev *pdev)
}
static DEFINE_PCI_DEVICE_TABLE(pci_ids) = {
- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080e)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)},
{ 0,}
};
diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c
index 2d0f9136ea9a..02bc5a6343c3 100644
--- a/drivers/platform/x86/intel_scu_ipcutil.c
+++ b/drivers/platform/x86/intel_scu_ipcutil.c
@@ -26,13 +26,10 @@
static int major;
-#define MAX_FW_SIZE 264192
-
/* ioctl commnds */
#define INTE_SCU_IPC_REGISTER_READ 0
#define INTE_SCU_IPC_REGISTER_WRITE 1
#define INTE_SCU_IPC_REGISTER_UPDATE 2
-#define INTE_SCU_IPC_FW_UPDATE 0xA2
struct scu_ipc_data {
u32 count; /* No. of registers */
@@ -88,27 +85,14 @@ static long scu_ipc_ioctl(struct file *fp, unsigned int cmd,
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
- if (cmd == INTE_SCU_IPC_FW_UPDATE) {
- u8 *fwbuf = kmalloc(MAX_FW_SIZE, GFP_KERNEL);
- if (fwbuf == NULL)
- return -ENOMEM;
- if (copy_from_user(fwbuf, (u8 *)arg, MAX_FW_SIZE)) {
- kfree(fwbuf);
- return -EFAULT;
- }
- ret = intel_scu_ipc_fw_update(fwbuf, MAX_FW_SIZE);
- kfree(fwbuf);
- return ret;
- } else {
- if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data)))
- return -EFAULT;
- ret = scu_reg_access(cmd, &data);
- if (ret < 0)
- return ret;
- if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data)))
- return -EFAULT;
- return 0;
- }
+ if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data)))
+ return -EFAULT;
+ ret = scu_reg_access(cmd, &data);
+ if (ret < 0)
+ return ret;
+ if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data)))
+ return -EFAULT;
+ return 0;
}
static const struct file_operations scu_ipc_fops = {
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index 05be30ee158b..ffff8b4b4949 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -562,8 +562,8 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device)
num_sifr = acpi_pcc_get_sqty(device);
- if (num_sifr > 255) {
- ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr too large"));
+ if (num_sifr < 0 || num_sifr > 255) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "num_sifr out of range"));
return -ENODEV;
}