diff options
Diffstat (limited to 'drivers/platform')
31 files changed, 739 insertions, 355 deletions
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index d03df4a60d05..76bdae1a93bb 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -64,4 +64,14 @@ config CROS_EC_PROTO help ChromeOS EC communication protocol helpers. +config CROS_KBD_LED_BACKLIGHT + tristate "Backlight LED support for Chrome OS keyboards" + depends on LEDS_CLASS && ACPI + help + This option enables support for the keyboard backlight LEDs on + select Chrome OS systems. + + To compile this driver as a module, choose M here: the + module will be called cros_kbd_led_backlight. + endif # CHROMEOS_PLATFORMS diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index bc498bda8211..4f3462783a3c 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -1,8 +1,9 @@ -obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o -obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o -cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o \ - cros_ec_lightbar.o cros_ec_vbc.o -obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o -obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o -obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o +obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o +obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o +cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o \ + cros_ec_lightbar.o cros_ec_vbc.o +obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o +obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o +obj-$(CONFIG_CROS_EC_PROTO) += cros_ec_proto.o +obj-$(CONFIG_CROS_KBD_LED_BACKLIGHT) += cros_kbd_led_backlight.o diff --git a/drivers/platform/chrome/chromeos_laptop.c b/drivers/platform/chrome/chromeos_laptop.c index 2b441e9ae593..e8a44a9bc916 100644 --- a/drivers/platform/chrome/chromeos_laptop.c +++ b/drivers/platform/chrome/chromeos_laptop.c @@ -34,6 +34,7 @@ #define ATMEL_TS_I2C_ADDR 0x4a #define ATMEL_TS_I2C_BL_ADDR 0x26 #define CYAPA_TP_I2C_ADDR 0x67 +#define ELAN_TP_I2C_ADDR 0x15 #define ISL_ALS_I2C_ADDR 0x44 #define TAOS_ALS_I2C_ADDR 0x29 @@ -73,7 +74,7 @@ struct i2c_peripheral { int tries; }; -#define MAX_I2C_PERIPHERALS 3 +#define MAX_I2C_PERIPHERALS 4 struct chromeos_laptop { struct i2c_peripheral i2c_peripherals[MAX_I2C_PERIPHERALS]; @@ -86,6 +87,11 @@ static struct i2c_board_info cyapa_device = { .flags = I2C_CLIENT_WAKE, }; +static struct i2c_board_info elantech_device = { + I2C_BOARD_INFO("elan_i2c", ELAN_TP_I2C_ADDR), + .flags = I2C_CLIENT_WAKE, +}; + static struct i2c_board_info isl_als_device = { I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), }; @@ -306,6 +312,16 @@ static int setup_atmel_224s_tp(enum i2c_adapter_type type) return (!tp) ? -EAGAIN : 0; } +static int setup_elantech_tp(enum i2c_adapter_type type) +{ + if (tp) + return 0; + + /* add elantech touchpad */ + tp = add_i2c_device("trackpad", type, &elantech_device); + return (!tp) ? -EAGAIN : 0; +} + static int setup_atmel_1664s_ts(enum i2c_adapter_type type) { const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR, @@ -445,6 +461,8 @@ static struct chromeos_laptop dell_chromebook_11 = { .i2c_peripherals = { /* Touchpad. */ { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 }, + /* Elan Touchpad option. */ + { .add = setup_elantech_tp, I2C_ADAPTER_DESIGNWARE_0 }, }, }; @@ -475,6 +493,8 @@ static struct chromeos_laptop acer_c720 = { { .add = setup_atmel_1664s_ts, I2C_ADAPTER_DESIGNWARE_1 }, /* Touchpad. */ { .add = setup_cyapa_tp, I2C_ADAPTER_DESIGNWARE_0 }, + /* Elan Touchpad option. */ + { .add = setup_elantech_tp, I2C_ADAPTER_DESIGNWARE_0 }, /* Light Sensor. */ { .add = setup_isl29018_als, I2C_ADAPTER_DESIGNWARE_1 }, }, diff --git a/drivers/platform/chrome/chromeos_pstore.c b/drivers/platform/chrome/chromeos_pstore.c index 34749200e4ab..308a853ac4f1 100644 --- a/drivers/platform/chrome/chromeos_pstore.c +++ b/drivers/platform/chrome/chromeos_pstore.c @@ -8,6 +8,7 @@ * the Free Software Foundation, version 2 of the License. */ +#include <linux/acpi.h> #include <linux/dmi.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -58,7 +59,7 @@ MODULE_DEVICE_TABLE(dmi, chromeos_pstore_dmi_table); static struct ramoops_platform_data chromeos_ramoops_data = { .mem_size = 0x100000, .mem_address = 0xf00000, - .record_size = 0x20000, + .record_size = 0x40000, .console_size = 0x20000, .ftrace_size = 0x20000, .dump_oops = 1, @@ -71,9 +72,59 @@ static struct platform_device chromeos_ramoops = { }, }; +#ifdef CONFIG_ACPI +static const struct acpi_device_id cros_ramoops_acpi_match[] = { + { "GOOG9999", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cros_ramoops_acpi_match); + +static struct platform_driver chromeos_ramoops_acpi = { + .driver = { + .name = "chromeos_pstore", + .acpi_match_table = ACPI_PTR(cros_ramoops_acpi_match), + }, +}; + +static int __init chromeos_probe_acpi(struct platform_device *pdev) +{ + struct resource *res; + resource_size_t len; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOMEM; + + len = resource_size(res); + if (!res->start || !len) + return -ENOMEM; + + pr_info("chromeos ramoops using acpi device.\n"); + + chromeos_ramoops_data.mem_size = len; + chromeos_ramoops_data.mem_address = res->start; + + return 0; +} + +static bool __init chromeos_check_acpi(void) +{ + if (!platform_driver_probe(&chromeos_ramoops_acpi, chromeos_probe_acpi)) + return true; + return false; +} +#else +static inline bool chromeos_check_acpi(void) { return false; } +#endif + static int __init chromeos_pstore_init(void) { - if (dmi_check_system(chromeos_pstore_dmi_table)) + bool acpi_dev_found; + + /* First check ACPI for non-hardcoded values from firmware. */ + acpi_dev_found = chromeos_check_acpi(); + + if (acpi_dev_found || dmi_check_system(chromeos_pstore_dmi_table)) return platform_device_register(&chromeos_ramoops); return -ENODEV; diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c index d45cd254ed1c..6d8ee3b15872 100644 --- a/drivers/platform/chrome/cros_ec_dev.c +++ b/drivers/platform/chrome/cros_ec_dev.c @@ -137,6 +137,10 @@ static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg) if (copy_from_user(&u_cmd, arg, sizeof(u_cmd))) return -EFAULT; + if ((u_cmd.outsize > EC_MAX_MSG_BYTES) || + (u_cmd.insize > EC_MAX_MSG_BYTES)) + return -EINVAL; + s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize), GFP_KERNEL); if (!s_cmd) @@ -208,6 +212,9 @@ static const struct file_operations fops = { .release = ec_device_release, .read = ec_device_read, .unlocked_ioctl = ec_device_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ec_device_ioctl, +#endif }; static void __remove(struct device *dev) diff --git a/drivers/platform/chrome/cros_ec_lightbar.c b/drivers/platform/chrome/cros_ec_lightbar.c index ff7640575c75..8df3d447cacf 100644 --- a/drivers/platform/chrome/cros_ec_lightbar.c +++ b/drivers/platform/chrome/cros_ec_lightbar.c @@ -412,9 +412,13 @@ static umode_t cros_ec_lightbar_attrs_are_visible(struct kobject *kobj, struct device *dev = container_of(kobj, struct device, kobj); struct cros_ec_dev *ec = container_of(dev, struct cros_ec_dev, class_dev); - struct platform_device *pdev = container_of(ec->dev, - struct platform_device, dev); - if (pdev->id != 0) + struct platform_device *pdev = to_platform_device(ec->dev); + struct cros_ec_platform *pdata = pdev->dev.platform_data; + int is_cros_ec; + + is_cros_ec = strcmp(pdata->ec_name, CROS_EC_DEV_NAME); + + if (is_cros_ec != 0) return 0; /* Only instantiate this stuff if the EC has a lightbar */ diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index 990308ca384f..b6e161f71b26 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -298,8 +298,8 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev) ec_dev->max_response = EC_PROTO2_MAX_PARAM_SIZE; ec_dev->max_passthru = 0; ec_dev->pkt_xfer = NULL; - ec_dev->din_size = EC_MSG_BYTES; - ec_dev->dout_size = EC_MSG_BYTES; + ec_dev->din_size = EC_PROTO2_MSG_BYTES; + ec_dev->dout_size = EC_PROTO2_MSG_BYTES; } else { /* * It's possible for a test to occur too early when diff --git a/drivers/platform/chrome/cros_kbd_led_backlight.c b/drivers/platform/chrome/cros_kbd_led_backlight.c new file mode 100644 index 000000000000..ca3e4da852b4 --- /dev/null +++ b/drivers/platform/chrome/cros_kbd_led_backlight.c @@ -0,0 +1,122 @@ +/* + * Keyboard backlight LED driver for Chrome OS. + * + * Copyright (C) 2012 Google, Inc. + * + * 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. + */ + +#include <linux/acpi.h> +#include <linux/leds.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +/* Keyboard LED ACPI Device must be defined in firmware */ +#define ACPI_KEYBOARD_BACKLIGHT_DEVICE "\\_SB.KBLT" +#define ACPI_KEYBOARD_BACKLIGHT_READ ACPI_KEYBOARD_BACKLIGHT_DEVICE ".KBQC" +#define ACPI_KEYBOARD_BACKLIGHT_WRITE ACPI_KEYBOARD_BACKLIGHT_DEVICE ".KBCM" + +#define ACPI_KEYBOARD_BACKLIGHT_MAX 100 + +static void keyboard_led_set_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + union acpi_object param; + struct acpi_object_list input; + acpi_status status; + + param.type = ACPI_TYPE_INTEGER; + param.integer.value = brightness; + input.count = 1; + input.pointer = ¶m; + + status = acpi_evaluate_object(NULL, ACPI_KEYBOARD_BACKLIGHT_WRITE, + &input, NULL); + if (ACPI_FAILURE(status)) + dev_err(cdev->dev, "Error setting keyboard LED value: %d\n", + status); +} + +static enum led_brightness +keyboard_led_get_brightness(struct led_classdev *cdev) +{ + unsigned long long brightness; + acpi_status status; + + status = acpi_evaluate_integer(NULL, ACPI_KEYBOARD_BACKLIGHT_READ, + NULL, &brightness); + if (ACPI_FAILURE(status)) { + dev_err(cdev->dev, "Error getting keyboard LED value: %d\n", + status); + return -EIO; + } + + return brightness; +} + +static int keyboard_led_probe(struct platform_device *pdev) +{ + struct led_classdev *cdev; + acpi_handle handle; + acpi_status status; + int error; + + /* Look for the keyboard LED ACPI Device */ + status = acpi_get_handle(ACPI_ROOT_OBJECT, + ACPI_KEYBOARD_BACKLIGHT_DEVICE, + &handle); + if (ACPI_FAILURE(status)) { + dev_err(&pdev->dev, "Unable to find ACPI device %s: %d\n", + ACPI_KEYBOARD_BACKLIGHT_DEVICE, status); + return -ENXIO; + } + + cdev = devm_kzalloc(&pdev->dev, sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + cdev->name = "chromeos::kbd_backlight"; + cdev->max_brightness = ACPI_KEYBOARD_BACKLIGHT_MAX; + cdev->flags |= LED_CORE_SUSPENDRESUME; + cdev->brightness_set = keyboard_led_set_brightness; + cdev->brightness_get = keyboard_led_get_brightness; + + error = devm_led_classdev_register(&pdev->dev, cdev); + if (error) + return error; + + return 0; +} + +static const struct acpi_device_id keyboard_led_id[] = { + { "GOOG0002", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, keyboard_led_id); + +static struct platform_driver keyboard_led_driver = { + .driver = { + .name = "chromeos-keyboard-leds", + .acpi_match_table = ACPI_PTR(keyboard_led_id), + }, + .probe = keyboard_led_probe, +}; +module_platform_driver(keyboard_led_driver); + +MODULE_AUTHOR("Simon Que <sque@chromium.org>"); +MODULE_DESCRIPTION("ChromeOS Keyboard backlight LED Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:chromeos-keyboard-leds"); diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig index 125e569017be..b3ae30a4c67b 100644 --- a/drivers/platform/mips/Kconfig +++ b/drivers/platform/mips/Kconfig @@ -15,10 +15,6 @@ menuconfig MIPS_PLATFORM_DEVICES if MIPS_PLATFORM_DEVICES -config MIPS_ACPI - bool - default y if LOONGSON_MACH3X - config CPU_HWMON tristate "Loongson CPU HWMon Driver" depends on LOONGSON_MACH3X diff --git a/drivers/platform/mips/Makefile b/drivers/platform/mips/Makefile index 43412849b195..8dfd03924c37 100644 --- a/drivers/platform/mips/Makefile +++ b/drivers/platform/mips/Makefile @@ -1,2 +1 @@ -obj-$(CONFIG_MIPS_ACPI) += acpi_init.o obj-$(CONFIG_CPU_HWMON) += cpu_hwmon.o diff --git a/drivers/platform/mips/acpi_init.c b/drivers/platform/mips/acpi_init.c deleted file mode 100644 index dbdad79ead8f..000000000000 --- a/drivers/platform/mips/acpi_init.c +++ /dev/null @@ -1,150 +0,0 @@ -#include <linux/io.h> -#include <linux/init.h> -#include <linux/ioport.h> -#include <linux/export.h> - -#define SBX00_ACPI_IO_BASE 0x800 -#define SBX00_ACPI_IO_SIZE 0x100 - -#define ACPI_PM_EVT_BLK (SBX00_ACPI_IO_BASE + 0x00) /* 4 bytes */ -#define ACPI_PM_CNT_BLK (SBX00_ACPI_IO_BASE + 0x04) /* 2 bytes */ -#define ACPI_PMA_CNT_BLK (SBX00_ACPI_IO_BASE + 0x0F) /* 1 byte */ -#define ACPI_PM_TMR_BLK (SBX00_ACPI_IO_BASE + 0x18) /* 4 bytes */ -#define ACPI_GPE0_BLK (SBX00_ACPI_IO_BASE + 0x10) /* 8 bytes */ -#define ACPI_END (SBX00_ACPI_IO_BASE + 0x80) - -#define PM_INDEX 0xCD6 -#define PM_DATA 0xCD7 -#define PM2_INDEX 0xCD0 -#define PM2_DATA 0xCD1 - -/* - * SCI interrupt need acpi space, allocate here - */ - -static int __init register_acpi_resource(void) -{ - request_region(SBX00_ACPI_IO_BASE, SBX00_ACPI_IO_SIZE, "acpi"); - return 0; -} - -static void pmio_write_index(u16 index, u8 reg, u8 value) -{ - outb(reg, index); - outb(value, index + 1); -} - -static u8 pmio_read_index(u16 index, u8 reg) -{ - outb(reg, index); - return inb(index + 1); -} - -void pm_iowrite(u8 reg, u8 value) -{ - pmio_write_index(PM_INDEX, reg, value); -} -EXPORT_SYMBOL(pm_iowrite); - -u8 pm_ioread(u8 reg) -{ - return pmio_read_index(PM_INDEX, reg); -} -EXPORT_SYMBOL(pm_ioread); - -void pm2_iowrite(u8 reg, u8 value) -{ - pmio_write_index(PM2_INDEX, reg, value); -} -EXPORT_SYMBOL(pm2_iowrite); - -u8 pm2_ioread(u8 reg) -{ - return pmio_read_index(PM2_INDEX, reg); -} -EXPORT_SYMBOL(pm2_ioread); - -static void acpi_hw_clear_status(void) -{ - u16 value; - - /* PMStatus: Clear WakeStatus/PwrBtnStatus */ - value = inw(ACPI_PM_EVT_BLK); - value |= (1 << 8 | 1 << 15); - outw(value, ACPI_PM_EVT_BLK); - - /* GPEStatus: Clear all generated events */ - outl(inl(ACPI_GPE0_BLK), ACPI_GPE0_BLK); -} - -void acpi_registers_setup(void) -{ - u32 value; - - /* PM Status Base */ - pm_iowrite(0x20, ACPI_PM_EVT_BLK & 0xff); - pm_iowrite(0x21, ACPI_PM_EVT_BLK >> 8); - - /* PM Control Base */ - pm_iowrite(0x22, ACPI_PM_CNT_BLK & 0xff); - pm_iowrite(0x23, ACPI_PM_CNT_BLK >> 8); - - /* GPM Base */ - pm_iowrite(0x28, ACPI_GPE0_BLK & 0xff); - pm_iowrite(0x29, ACPI_GPE0_BLK >> 8); - - /* ACPI End */ - pm_iowrite(0x2e, ACPI_END & 0xff); - pm_iowrite(0x2f, ACPI_END >> 8); - - /* IO Decode: When AcpiDecodeEnable set, South-Bridge uses the contents - * of the PM registers at index 0x20~0x2B to decode ACPI I/O address. */ - pm_iowrite(0x0e, 1 << 3); - - /* SCI_EN set */ - outw(1, ACPI_PM_CNT_BLK); - - /* Enable to generate SCI */ - pm_iowrite(0x10, pm_ioread(0x10) | 1); - - /* GPM3/GPM9 enable */ - value = inl(ACPI_GPE0_BLK + 4); - outl(value | (1 << 14) | (1 << 22), ACPI_GPE0_BLK + 4); - - /* Set GPM9 as input */ - pm_iowrite(0x8d, pm_ioread(0x8d) & (~(1 << 1))); - - /* Set GPM9 as non-output */ - pm_iowrite(0x94, pm_ioread(0x94) | (1 << 3)); - - /* GPM3 config ACPI trigger SCIOUT */ - pm_iowrite(0x33, pm_ioread(0x33) & (~(3 << 4))); - - /* GPM9 config ACPI trigger SCIOUT */ - pm_iowrite(0x3d, pm_ioread(0x3d) & (~(3 << 2))); - - /* GPM3 config falling edge trigger */ - pm_iowrite(0x37, pm_ioread(0x37) & (~(1 << 6))); - - /* No wait for STPGNT# in ACPI Sx state */ - pm_iowrite(0x7c, pm_ioread(0x7c) | (1 << 6)); - - /* Set GPM3 pull-down enable */ - value = pm2_ioread(0xf6); - value |= ((1 << 7) | (1 << 3)); - pm2_iowrite(0xf6, value); - - /* Set GPM9 pull-down enable */ - value = pm2_ioread(0xf8); - value |= ((1 << 5) | (1 << 1)); - pm2_iowrite(0xf8, value); -} - -int __init sbx00_acpi_init(void) -{ - register_acpi_resource(); - acpi_registers_setup(); - acpi_hw_clear_status(); - - return 0; -} diff --git a/drivers/platform/mips/cpu_hwmon.c b/drivers/platform/mips/cpu_hwmon.c index 0f6c63e17049..4300a558d0f3 100644 --- a/drivers/platform/mips/cpu_hwmon.c +++ b/drivers/platform/mips/cpu_hwmon.c @@ -20,9 +20,9 @@ int loongson3_cpu_temp(int cpu) u32 reg; reg = LOONGSON_CHIPTEMP(cpu); - if (loongson_sysconf.cputype == Loongson_3A) + if ((read_c0_prid() & PRID_REV_MASK) == PRID_REV_LOONGSON3A_R1) reg = (reg >> 8) & 0xff; - else if (loongson_sysconf.cputype == Loongson_3B) + else reg = ((reg >> 8) & 0xff) - 100; return (int)reg * 1000; @@ -80,13 +80,13 @@ static const struct attribute *hwmon_cputemp2[] = { static ssize_t cpu0_temp_label(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "CPU 0 Temprature\n"); + return sprintf(buf, "CPU 0 Temperature\n"); } static ssize_t cpu1_temp_label(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "CPU 1 Temprature\n"); + return sprintf(buf, "CPU 1 Temperature\n"); } static ssize_t get_cpu0_temp(struct device *dev, @@ -169,7 +169,7 @@ static int __init loongson_hwmon_init(void) ret = create_sysfs_cputemp_files(&cpu_hwmon_dev->kobj); if (ret) { - pr_err("fail to create cpu temprature interface!\n"); + pr_err("fail to create cpu temperature interface!\n"); goto fail_create_sysfs_cputemp_files; } diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index ed2004be13cf..c06bb85c2839 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -846,6 +846,18 @@ config INTEL_IMR If you are running on a Galileo/Quark say Y here. +config INTEL_PMC_CORE + bool "Intel PMC Core driver" + depends on X86 && PCI + ---help--- + The Intel Platform Controller Hub for Intel Core SoCs provides access + to Power Management Controller registers via a PCI interface. This + driver can utilize debugging capabilities and supported features as + exposed by the Power Management Controller. + + Supported features: + - SLP_S0_RESIDENCY counter. + config IBM_RTL tristate "Device driver to enable PRTL support" depends on X86 && PCI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 448443c3baba..9b11b4073e03 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -69,3 +69,4 @@ obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ intel_telemetry_pltdrv.o \ intel_telemetry_debugfs.o +obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 1062fa42ff26..79d64ea00bfb 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -793,15 +793,6 @@ static acpi_status __init AMW0_find_mailled(void) return AE_OK; } -static int AMW0_set_cap_acpi_check_device_found __initdata; - -static acpi_status __init 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[] __initconst = { { "VPC2004", 0}, { "IBM0068", 0}, @@ -816,9 +807,10 @@ static int __init 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; + if (acpi_dev_found(id->id)) + return true; + + return false; } static acpi_status __init AMW0_set_capabilities(void) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index f2b5d0a8adf0..15f131146501 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -771,12 +771,14 @@ static int asus_read_brightness(struct backlight_device *bd) { struct asus_laptop *asus = bl_get_data(bd); unsigned long long value; - acpi_status rv = AE_OK; + acpi_status rv; rv = acpi_evaluate_integer(asus->handle, METHOD_BRIGHTNESS_GET, NULL, &value); - if (ACPI_FAILURE(rv)) + if (ACPI_FAILURE(rv)) { pr_warn("Error reading brightness\n"); + return 0; + } return value; } @@ -865,7 +867,7 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr, int len = 0; unsigned long long temp; char buf[16]; /* enough for all info */ - acpi_status rv = AE_OK; + acpi_status rv; /* * We use the easy way, we don't care of off and count, @@ -946,11 +948,10 @@ static ssize_t sysfs_acpi_set(struct asus_laptop *asus, const char *method) { int rv, value; - int out = 0; rv = parse_arg(buf, count, &value); - if (rv > 0) - out = value ? 1 : 0; + if (rv <= 0) + return rv; if (write_acpi_int(asus->handle, method, value)) return -ENODEV; @@ -1265,7 +1266,7 @@ static DEVICE_ATTR_RO(ls_value); static int asus_gps_status(struct asus_laptop *asus) { unsigned long long status; - acpi_status rv = AE_OK; + acpi_status rv; rv = acpi_evaluate_integer(asus->handle, METHOD_GPS_STATUS, NULL, &status); diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index a96630d52346..a26dca3640ea 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -114,6 +114,7 @@ MODULE_LICENSE("GPL"); #define ASUS_WMI_DEVID_LED6 0x00020016 /* Backlight and Brightness */ +#define ASUS_WMI_DEVID_ALS_ENABLE 0x00050001 /* Ambient Light Sensor */ #define ASUS_WMI_DEVID_BACKLIGHT 0x00050011 #define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 @@ -1730,6 +1731,7 @@ ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD); ASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA); ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER); ASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME); +ASUS_WMI_CREATE_DEVICE_ATTR(als_enable, 0644, ASUS_WMI_DEVID_ALS_ENABLE); static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1756,6 +1758,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_cardr.attr, &dev_attr_touchpad.attr, &dev_attr_lid_resume.attr, + &dev_attr_als_enable.attr, NULL }; @@ -1776,6 +1779,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_TOUCHPAD; else if (attr == &dev_attr_lid_resume.attr) devid = ASUS_WMI_DEVID_LID_RESUME; + else if (attr == &dev_attr_als_enable.attr) + devid = ASUS_WMI_DEVID_ALS_ENABLE; if (devid != -1) ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); diff --git a/drivers/platform/x86/dell-rbtn.c b/drivers/platform/x86/dell-rbtn.c index b51a2008d782..dcd9f40a4b18 100644 --- a/drivers/platform/x86/dell-rbtn.c +++ b/drivers/platform/x86/dell-rbtn.c @@ -28,6 +28,7 @@ struct rbtn_data { enum rbtn_type type; struct rfkill *rfkill; struct input_dev *input_dev; + bool suspended; }; @@ -235,9 +236,55 @@ static const struct acpi_device_id rbtn_ids[] = { { "", 0 }, }; +#ifdef CONFIG_PM_SLEEP +static void ACPI_SYSTEM_XFACE rbtn_clear_suspended_flag(void *context) +{ + struct rbtn_data *rbtn_data = context; + + rbtn_data->suspended = false; +} + +static int rbtn_suspend(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct rbtn_data *rbtn_data = acpi_driver_data(device); + + rbtn_data->suspended = true; + + return 0; +} + +static int rbtn_resume(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct rbtn_data *rbtn_data = acpi_driver_data(device); + acpi_status status; + + /* + * Upon resume, some BIOSes send an ACPI notification thet triggers + * an unwanted input event. In order to ignore it, we use a flag + * that we set at suspend and clear once we have received the extra + * ACPI notification. Since ACPI notifications are delivered + * asynchronously to drivers, we clear the flag from the workqueue + * used to deliver the notifications. This should be enough + * to have the flag cleared only after we received the extra + * notification, if any. + */ + status = acpi_os_execute(OSL_NOTIFY_HANDLER, + rbtn_clear_suspended_flag, rbtn_data); + if (ACPI_FAILURE(status)) + rbtn_clear_suspended_flag(rbtn_data); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(rbtn_pm_ops, rbtn_suspend, rbtn_resume); + static struct acpi_driver rbtn_driver = { .name = "dell-rbtn", .ids = rbtn_ids, + .drv.pm = &rbtn_pm_ops, .ops = { .add = rbtn_add, .remove = rbtn_remove, @@ -399,6 +446,15 @@ static void rbtn_notify(struct acpi_device *device, u32 event) { struct rbtn_data *rbtn_data = device->driver_data; + /* + * Some BIOSes send a notification at resume. + * Ignore it to prevent unwanted input events. + */ + if (rbtn_data->suspended) { + dev_dbg(&device->dev, "ACPI notification ignored\n"); + return; + } + if (event != 0x80) { dev_info(&device->dev, "Received unknown event (0x%x)\n", event); diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 14fd2ecb06a1..17b365f26f9d 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -204,30 +204,10 @@ static void eeepc_wmi_key_filter(struct asus_wmi_driver *asus_wmi, int *code, } } -static acpi_status eeepc_wmi_parse_device(acpi_handle handle, u32 level, - void *context, void **retval) -{ - pr_warn("Found legacy ATKD device (%s)\n", EEEPC_ACPI_HID); - *(bool *)context = true; - return AE_CTRL_TERMINATE; -} - -static int eeepc_wmi_check_atkd(void) -{ - acpi_status status; - bool found = false; - - status = acpi_get_devices(EEEPC_ACPI_HID, eeepc_wmi_parse_device, - &found, NULL); - - if (ACPI_FAILURE(status) || !found) - return 0; - return -1; -} - static int eeepc_wmi_probe(struct platform_device *pdev) { - if (eeepc_wmi_check_atkd()) { + if (acpi_dev_found(EEEPC_ACPI_HID)) { + pr_warn("Found legacy ATKD device (%s)\n", EEEPC_ACPI_HID); pr_warn("WMI device present, but legacy ATKD device is also " "present and enabled\n"); pr_warn("You probably booted with acpi_osi=\"Linux\" or " diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index ffc84cc7b1c7..ce41bc34288d 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -69,7 +69,7 @@ #include <linux/kfifo.h> #include <linux/platform_device.h> #include <linux/slab.h> -#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) +#if IS_ENABLED(CONFIG_LEDS_CLASS) #include <linux/leds.h> #endif #include <acpi/video.h> @@ -100,13 +100,14 @@ /* FUNC interface - responses */ #define UNSUPPORTED_CMD 0x80000000 -#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) +#if IS_ENABLED(CONFIG_LEDS_CLASS) /* FUNC interface - LED control */ #define FUNC_LED_OFF 0x1 #define FUNC_LED_ON 0x30001 #define KEYBOARD_LAMPS 0x100 #define LOGOLAMP_POWERON 0x2000 #define LOGOLAMP_ALWAYS 0x4000 +#define RADIO_LED_ON 0x20 #endif /* Hotkey details */ @@ -174,13 +175,14 @@ struct fujitsu_hotkey_t { int rfkill_state; int logolamp_registered; int kblamps_registered; + int radio_led_registered; }; static struct fujitsu_hotkey_t *fujitsu_hotkey; static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event); -#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) +#if IS_ENABLED(CONFIG_LEDS_CLASS) static enum led_brightness logolamp_get(struct led_classdev *cdev); static void logolamp_set(struct led_classdev *cdev, enum led_brightness brightness); @@ -200,6 +202,16 @@ static struct led_classdev kblamps_led = { .brightness_get = kblamps_get, .brightness_set = kblamps_set }; + +static enum led_brightness radio_led_get(struct led_classdev *cdev); +static void radio_led_set(struct led_classdev *cdev, + enum led_brightness brightness); + +static struct led_classdev radio_led = { + .name = "fujitsu::radio_led", + .brightness_get = radio_led_get, + .brightness_set = radio_led_set +}; #endif #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG @@ -249,7 +261,7 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2) return value; } -#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) +#if IS_ENABLED(CONFIG_LEDS_CLASS) /* LED class callbacks */ static void logolamp_set(struct led_classdev *cdev, @@ -275,6 +287,15 @@ static void kblamps_set(struct led_classdev *cdev, call_fext_func(FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF); } +static void radio_led_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + if (brightness >= LED_FULL) + call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, RADIO_LED_ON); + else + call_fext_func(FUNC_RFKILL, 0x5, RADIO_LED_ON, 0x0); +} + static enum led_brightness logolamp_get(struct led_classdev *cdev) { enum led_brightness brightness = LED_OFF; @@ -299,6 +320,16 @@ static enum led_brightness kblamps_get(struct led_classdev *cdev) return brightness; } + +static enum led_brightness radio_led_get(struct led_classdev *cdev) +{ + enum led_brightness brightness = LED_OFF; + + if (call_fext_func(FUNC_RFKILL, 0x4, 0x0, 0x0) & RADIO_LED_ON) + brightness = LED_FULL; + + return brightness; +} #endif /* Hardware access for LCD brightness control */ @@ -872,7 +903,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) /* Suspect this is a keymap of the application panel, print it */ pr_info("BTNI: [0x%x]\n", call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0)); -#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) +#if IS_ENABLED(CONFIG_LEDS_CLASS) if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { result = led_classdev_register(&fujitsu->pf_device->dev, &logolamp_led); @@ -895,6 +926,23 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) result); } } + + /* + * BTNI bit 24 seems to indicate the presence of a radio toggle + * button in place of a slide switch, and all such machines appear + * to also have an RF LED. Therefore use bit 24 as an indicator + * that an RF LED is present. + */ + if (call_fext_func(FUNC_BUTTONS, 0x0, 0x0, 0x0) & BIT(24)) { + result = led_classdev_register(&fujitsu->pf_device->dev, + &radio_led); + if (result == 0) { + fujitsu_hotkey->radio_led_registered = 1; + } else { + pr_err("Could not register LED handler for radio LED, error %i\n", + result); + } + } #endif return result; @@ -915,12 +963,15 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device) struct fujitsu_hotkey_t *fujitsu_hotkey = acpi_driver_data(device); struct input_dev *input = fujitsu_hotkey->input; -#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) +#if IS_ENABLED(CONFIG_LEDS_CLASS) if (fujitsu_hotkey->logolamp_registered) led_classdev_unregister(&logolamp_led); if (fujitsu_hotkey->kblamps_registered) led_classdev_unregister(&kblamps_led); + + if (fujitsu_hotkey->radio_led_registered) + led_classdev_unregister(&radio_led); #endif input_unregister_device(input); diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index be3bc2f4edd4..4a23fbc66b71 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -48,7 +48,10 @@ #define CFG_CAMERA_BIT (19) #if IS_ENABLED(CONFIG_ACPI_WMI) -static const char ideapad_wmi_fnesc_event[] = "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6"; +static const char *const ideapad_wmi_fnesc_events[] = { + "26CAB2E5-5CF1-46AE-AAC3-4A12B6BA50E6", /* Yoga 3 */ + "56322276-8493-4CE8-A783-98C991274F5E", /* Yoga 700 */ +}; #endif enum { @@ -93,6 +96,7 @@ struct ideapad_private { struct dentry *debug; unsigned long cfg; bool has_hw_rfkill_switch; + const char *fnesc_guid; }; static bool no_bt_rfkill; @@ -989,8 +993,16 @@ static int ideapad_acpi_add(struct platform_device *pdev) ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv); if (ret) goto notification_failed; + #if IS_ENABLED(CONFIG_ACPI_WMI) - ret = wmi_install_notify_handler(ideapad_wmi_fnesc_event, ideapad_wmi_notify, priv); + for (i = 0; i < ARRAY_SIZE(ideapad_wmi_fnesc_events); i++) { + ret = wmi_install_notify_handler(ideapad_wmi_fnesc_events[i], + ideapad_wmi_notify, priv); + if (ret == AE_OK) { + priv->fnesc_guid = ideapad_wmi_fnesc_events[i]; + break; + } + } if (ret != AE_OK && ret != AE_NOT_EXIST) goto notification_failed_wmi; #endif @@ -1020,7 +1032,8 @@ static int ideapad_acpi_remove(struct platform_device *pdev) int i; #if IS_ENABLED(CONFIG_ACPI_WMI) - wmi_remove_notify_handler(ideapad_wmi_fnesc_event); + if (priv->fnesc_guid) + wmi_remove_notify_handler(priv->fnesc_guid); #endif acpi_remove_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY, ideapad_acpi_notify); diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index 0a919d81662c..cbe01021c939 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c @@ -306,33 +306,32 @@ static int sensor_set_auxtrip(acpi_handle handle, int index, int value) #define to_intel_menlow_attr(_attr) \ container_of(_attr, struct intel_menlow_attribute, attr) -static ssize_t aux0_show(struct device *dev, - struct device_attribute *dev_attr, char *buf) +static ssize_t aux_show(struct device *dev, struct device_attribute *dev_attr, + char *buf, int idx) { struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); unsigned long long value; int result; - result = sensor_get_auxtrip(attr->handle, 0, &value); + result = sensor_get_auxtrip(attr->handle, idx, &value); return result ? result : sprintf(buf, "%lu", DECI_KELVIN_TO_CELSIUS(value)); } -static ssize_t aux1_show(struct device *dev, +static ssize_t aux0_show(struct device *dev, struct device_attribute *dev_attr, char *buf) { - struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); - unsigned long long value; - int result; - - result = sensor_get_auxtrip(attr->handle, 1, &value); + return aux_show(dev, dev_attr, buf, 0); +} - return result ? result : sprintf(buf, "%lu", DECI_KELVIN_TO_CELSIUS(value)); +static ssize_t aux1_show(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + return aux_show(dev, dev_attr, buf, 1); } -static ssize_t aux0_store(struct device *dev, - struct device_attribute *dev_attr, - const char *buf, size_t count) +static ssize_t aux_store(struct device *dev, struct device_attribute *dev_attr, + const char *buf, size_t count, int idx) { struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); int value; @@ -345,27 +344,23 @@ static ssize_t aux0_store(struct device *dev, if (value < 0) return -EINVAL; - result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_DECI_KELVIN(value)); + result = sensor_set_auxtrip(attr->handle, idx, + CELSIUS_TO_DECI_KELVIN(value)); return result ? result : count; } -static ssize_t aux1_store(struct device *dev, +static ssize_t aux0_store(struct device *dev, struct device_attribute *dev_attr, const char *buf, size_t count) { - struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); - int value; - int result; - - /*Sanity check; should be a positive integer */ - if (!sscanf(buf, "%d", &value)) - return -EINVAL; - - if (value < 0) - return -EINVAL; + return aux_store(dev, dev_attr, buf, count, 0); +} - result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_DECI_KELVIN(value)); - return result ? result : count; +static ssize_t aux1_store(struct device *dev, + struct device_attribute *dev_attr, + const char *buf, size_t count) +{ + return aux_store(dev, dev_attr, buf, count, 1); } /* BIOS can enable/disable the thermal user application in dabney platform */ diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c new file mode 100644 index 000000000000..2776bec89c88 --- /dev/null +++ b/drivers/platform/x86/intel_pmc_core.c @@ -0,0 +1,200 @@ +/* + * Intel Core SoC Power Management Controller Driver + * + * Copyright (c) 2016, Intel Corporation. + * All Rights Reserved. + * + * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com> + * Vishwanath Somayaji <vishwanath.somayaji@intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + */ + +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/pci.h> +#include <linux/seq_file.h> + +#include <asm/cpu_device_id.h> +#include <asm/pmc_core.h> + +#include "intel_pmc_core.h" + +static struct pmc_dev pmc; + +static const struct pci_device_id pmc_pci_ids[] = { + { PCI_VDEVICE(INTEL, SPT_PMC_PCI_DEVICE_ID), (kernel_ulong_t)NULL }, + { 0, }, +}; + +static inline u32 pmc_core_reg_read(struct pmc_dev *pmcdev, int reg_offset) +{ + return readl(pmcdev->regbase + reg_offset); +} + +static inline u32 pmc_core_adjust_slp_s0_step(u32 value) +{ + return value * SPT_PMC_SLP_S0_RES_COUNTER_STEP; +} + +/** + * intel_pmc_slp_s0_counter_read() - Read SLP_S0 residency. + * @data: Out param that contains current SLP_S0 count. + * + * This API currently supports Intel Skylake SoC and Sunrise + * Point Platform Controller Hub. Future platform support + * should be added for platforms that support low power modes + * beyond Package C10 state. + * + * SLP_S0_RESIDENCY counter counts in 100 us granularity per + * step hence function populates the multiplied value in out + * parameter @data. + * + * Return: an error code or 0 on success. + */ +int intel_pmc_slp_s0_counter_read(u32 *data) +{ + struct pmc_dev *pmcdev = &pmc; + u32 value; + + if (!pmcdev->has_slp_s0_res) + return -EACCES; + + value = pmc_core_reg_read(pmcdev, SPT_PMC_SLP_S0_RES_COUNTER_OFFSET); + *data = pmc_core_adjust_slp_s0_step(value); + + return 0; +} +EXPORT_SYMBOL_GPL(intel_pmc_slp_s0_counter_read); + +#if IS_ENABLED(CONFIG_DEBUG_FS) +static int pmc_core_dev_state_show(struct seq_file *s, void *unused) +{ + struct pmc_dev *pmcdev = s->private; + u32 counter_val; + + counter_val = pmc_core_reg_read(pmcdev, + SPT_PMC_SLP_S0_RES_COUNTER_OFFSET); + seq_printf(s, "%u\n", pmc_core_adjust_slp_s0_step(counter_val)); + + return 0; +} + +static int pmc_core_dev_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, pmc_core_dev_state_show, inode->i_private); +} + +static const struct file_operations pmc_core_dev_state_ops = { + .open = pmc_core_dev_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev) +{ + debugfs_remove_recursive(pmcdev->dbgfs_dir); +} + +static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev) +{ + struct dentry *dir, *file; + + dir = debugfs_create_dir("pmc_core", NULL); + if (!dir) + return -ENOMEM; + + pmcdev->dbgfs_dir = dir; + file = debugfs_create_file("slp_s0_residency_usec", S_IFREG | S_IRUGO, + dir, pmcdev, &pmc_core_dev_state_ops); + + if (!file) { + pmc_core_dbgfs_unregister(pmcdev); + return -ENODEV; + } + + return 0; +} +#else +static inline int pmc_core_dbgfs_register(struct pmc_dev *pmcdev) +{ + return 0; +} + +static inline void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev) +{ +} +#endif /* CONFIG_DEBUG_FS */ + +static const struct x86_cpu_id intel_pmc_core_ids[] = { + { X86_VENDOR_INTEL, 6, 0x4e, X86_FEATURE_MWAIT, + (kernel_ulong_t)NULL}, /* Skylake CPUID Signature */ + { X86_VENDOR_INTEL, 6, 0x5e, X86_FEATURE_MWAIT, + (kernel_ulong_t)NULL}, /* Skylake CPUID Signature */ + {} +}; + +static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct device *ptr_dev = &dev->dev; + struct pmc_dev *pmcdev = &pmc; + const struct x86_cpu_id *cpu_id; + int err; + + cpu_id = x86_match_cpu(intel_pmc_core_ids); + if (!cpu_id) { + dev_dbg(&dev->dev, "PMC Core: cpuid mismatch.\n"); + return -EINVAL; + } + + err = pcim_enable_device(dev); + if (err < 0) { + dev_dbg(&dev->dev, "PMC Core: failed to enable Power Management Controller.\n"); + return err; + } + + err = pci_read_config_dword(dev, + SPT_PMC_BASE_ADDR_OFFSET, + &pmcdev->base_addr); + if (err < 0) { + dev_dbg(&dev->dev, "PMC Core: failed to read PCI config space.\n"); + return err; + } + dev_dbg(&dev->dev, "PMC Core: PWRMBASE is %#x\n", pmcdev->base_addr); + + pmcdev->regbase = devm_ioremap_nocache(ptr_dev, + pmcdev->base_addr, + SPT_PMC_MMIO_REG_LEN); + if (!pmcdev->regbase) { + dev_dbg(&dev->dev, "PMC Core: ioremap failed.\n"); + return -ENOMEM; + } + + err = pmc_core_dbgfs_register(pmcdev); + if (err < 0) { + dev_err(&dev->dev, "PMC Core: debugfs register failed.\n"); + return err; + } + + pmc.has_slp_s0_res = true; + return 0; +} + +static struct pci_driver intel_pmc_core_driver = { + .name = "intel_pmc_core", + .id_table = pmc_pci_ids, + .probe = pmc_core_probe, +}; + +builtin_pci_driver(intel_pmc_core_driver); diff --git a/drivers/platform/x86/intel_pmc_core.h b/drivers/platform/x86/intel_pmc_core.h new file mode 100644 index 000000000000..a9dadaf787c1 --- /dev/null +++ b/drivers/platform/x86/intel_pmc_core.h @@ -0,0 +1,51 @@ +/* + * Intel Core SoC Power Management Controller Header File + * + * Copyright (c) 2016, Intel Corporation. + * All Rights Reserved. + * + * Authors: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com> + * Vishwanath Somayaji <vishwanath.somayaji@intel.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + */ + +#ifndef PMC_CORE_H +#define PMC_CORE_H + +/* Sunrise Point Power Management Controller PCI Device ID */ +#define SPT_PMC_PCI_DEVICE_ID 0x9d21 +#define SPT_PMC_BASE_ADDR_OFFSET 0x48 +#define SPT_PMC_SLP_S0_RES_COUNTER_OFFSET 0x13c +#define SPT_PMC_MMIO_REG_LEN 0x100 +#define SPT_PMC_SLP_S0_RES_COUNTER_STEP 0x64 + +/** + * struct pmc_dev - pmc device structure + * @base_addr: comtains pmc base address + * @regbase: pointer to io-remapped memory location + * @dbgfs_dir: path to debug fs interface + * @feature_available: flag to indicate whether + * the feature is available + * on a particular platform or not. + * + * pmc_dev contains info about power management controller device. + */ +struct pmc_dev { + u32 base_addr; + void __iomem *regbase; +#if IS_ENABLED(CONFIG_DEBUG_FS) + struct dentry *dbgfs_dir; +#endif /* CONFIG_DEBUG_FS */ + bool has_slp_s0_res; +}; + +#endif /* PMC_CORE_H */ diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index 0e73fd10ba72..63b371d6ee55 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -30,7 +30,7 @@ #include <linux/ioport.h> #include <linux/init.h> #include <linux/io.h> -#include <linux/gpio.h> +#include <linux/gpio/driver.h> #include <asm/intel_scu_ipc.h> #include <linux/device.h> #include <linux/intel_pmic_gpio.h> @@ -174,7 +174,7 @@ static int pmic_irq_type(struct irq_data *data, unsigned type) static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { - struct pmic_gpio *pg = container_of(chip, struct pmic_gpio, chip); + struct pmic_gpio *pg = gpiochip_get_data(chip); return pg->irq_base + offset; } @@ -279,7 +279,7 @@ static int platform_pmic_gpio_probe(struct platform_device *pdev) mutex_init(&pg->buslock); pg->chip.parent = dev; - retval = gpiochip_add(&pg->chip); + retval = gpiochip_add_data(&pg->chip, pg); if (retval) { pr_err("Can not add pmic gpio chip\n"); goto err; diff --git a/drivers/platform/x86/intel_telemetry_core.c b/drivers/platform/x86/intel_telemetry_core.c index a695a436a1c3..0d4c3808a6d8 100644 --- a/drivers/platform/x86/intel_telemetry_core.c +++ b/drivers/platform/x86/intel_telemetry_core.c @@ -25,7 +25,7 @@ struct telemetry_core_config { struct telemetry_plt_config *plt_config; - struct telemetry_core_ops *telem_ops; + const struct telemetry_core_ops *telem_ops; }; static struct telemetry_core_config telm_core_conf; @@ -95,7 +95,7 @@ static int telemetry_def_reset_events(void) return 0; } -static struct telemetry_core_ops telm_defpltops = { +static const struct telemetry_core_ops telm_defpltops = { .set_sampling_period = telemetry_def_set_sampling_period, .get_sampling_period = telemetry_def_get_sampling_period, .get_trace_verbosity = telemetry_def_get_trace_verbosity, @@ -332,7 +332,7 @@ EXPORT_SYMBOL_GPL(telemetry_set_trace_verbosity); * * Return: 0 success, < 0 for failure */ -int telemetry_set_pltdata(struct telemetry_core_ops *ops, +int telemetry_set_pltdata(const struct telemetry_core_ops *ops, struct telemetry_plt_config *pltconfig) { if (ops) diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c index 781bd10ca7ac..09c84a2b1c2c 100644 --- a/drivers/platform/x86/intel_telemetry_pltdrv.c +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c @@ -1081,7 +1081,7 @@ out: return ret; } -static struct telemetry_core_ops telm_pltops = { +static const struct telemetry_core_ops telm_pltops = { .get_trace_verbosity = telemetry_plt_get_trace_verbosity, .set_trace_verbosity = telemetry_plt_set_trace_verbosity, .set_sampling_period = telemetry_plt_set_sampling_period, diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index e9caa347a9bf..1dba3598cfcb 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1446,6 +1446,9 @@ static void sony_nc_function_cleanup(struct platform_device *pd) { unsigned int i, result, bitmask, handle; + if (!handles) + return; + /* get enabled events and disable them */ sony_nc_int_call(sony_nc_acpi_handle, "SN01", NULL, &bitmask); sony_nc_int_call(sony_nc_acpi_handle, "SN03", &bitmask, &result); diff --git a/drivers/platform/x86/surfacepro3_button.c b/drivers/platform/x86/surfacepro3_button.c index 700e0fa0eec2..6505c97705e1 100644 --- a/drivers/platform/x86/surfacepro3_button.c +++ b/drivers/platform/x86/surfacepro3_button.c @@ -24,6 +24,8 @@ #define SURFACE_BUTTON_OBJ_NAME "VGBI" #define SURFACE_BUTTON_DEVICE_NAME "Surface Pro 3/4 Buttons" +#define SURFACE_BUTTON_NOTIFY_TABLET_MODE 0xc8 + #define SURFACE_BUTTON_NOTIFY_PRESS_POWER 0xc6 #define SURFACE_BUTTON_NOTIFY_RELEASE_POWER 0xc7 @@ -33,7 +35,7 @@ #define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP 0xc0 #define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP 0xc1 -#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN 0xc2 +#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN 0xc2 #define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN 0xc3 ACPI_MODULE_NAME("surface pro 3 button"); @@ -105,9 +107,12 @@ static void surface_button_notify(struct acpi_device *device, u32 event) case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN: key_code = KEY_VOLUMEDOWN; break; + case SURFACE_BUTTON_NOTIFY_TABLET_MODE: + dev_warn_once(&device->dev, "Tablet mode is not supported\n"); + break; default: dev_info_ratelimited(&device->dev, - "Unsupported event [0x%x]\n", event); + "Unsupported event [0x%x]\n", event); break; } input = button->input; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 9255ff3ee81a..c3bfa1fe95bf 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -5001,6 +5001,8 @@ static int kbdlight_set_level(int level) return 0; } +static int kbdlight_set_level_and_update(int level); + static int kbdlight_get_level(void) { int status = 0; @@ -5068,7 +5070,7 @@ static void kbdlight_set_worker(struct work_struct *work) container_of(work, struct tpacpi_led_classdev, work); if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) - kbdlight_set_level(data->new_state); + kbdlight_set_level_and_update(data->new_state); } static void kbdlight_sysfs_set(struct led_classdev *led_cdev, @@ -5099,7 +5101,6 @@ static struct tpacpi_led_classdev tpacpi_led_kbdlight = { .max_brightness = 2, .brightness_set = &kbdlight_sysfs_set, .brightness_get = &kbdlight_sysfs_get, - .flags = LED_CORE_SUSPENDRESUME, } }; @@ -5137,6 +5138,20 @@ static void kbdlight_exit(void) flush_workqueue(tpacpi_wq); } +static int kbdlight_set_level_and_update(int level) +{ + int ret; + struct led_classdev *led_cdev; + + ret = kbdlight_set_level(level); + led_cdev = &tpacpi_led_kbdlight.led_classdev; + + if (ret == 0 && !(led_cdev->flags & LED_SUSPENDED)) + led_cdev->brightness = level; + + return ret; +} + static int kbdlight_read(struct seq_file *m) { int level; @@ -5177,13 +5192,35 @@ static int kbdlight_write(char *buf) if (level == -1) return -EINVAL; - return kbdlight_set_level(level); + return kbdlight_set_level_and_update(level); +} + +static void kbdlight_suspend(void) +{ + struct led_classdev *led_cdev; + + if (!tp_features.kbdlight) + return; + + led_cdev = &tpacpi_led_kbdlight.led_classdev; + led_update_brightness(led_cdev); + led_classdev_suspend(led_cdev); +} + +static void kbdlight_resume(void) +{ + if (!tp_features.kbdlight) + return; + + led_classdev_resume(&tpacpi_led_kbdlight.led_classdev); } static struct ibm_struct kbdlight_driver_data = { .name = "kbdlight", .read = kbdlight_read, .write = kbdlight_write, + .suspend = kbdlight_suspend, + .resume = kbdlight_resume, .exit = kbdlight_exit, }; diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index eb391a281833..ceeb8c188ef3 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -37,6 +37,7 @@ #include <linux/acpi.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/uuid.h> ACPI_MODULE_NAME("wmi"); MODULE_AUTHOR("Carlos Corbacho"); @@ -115,100 +116,21 @@ static struct acpi_driver acpi_wmi_driver = { * GUID parsing functions */ -/** - * wmi_parse_hexbyte - Convert a ASCII hex number to a byte - * @src: Pointer to at least 2 characters to convert. - * - * Convert a two character ASCII hex string to a number. - * - * Return: 0-255 Success, the byte was parsed correctly - * -1 Error, an invalid character was supplied - */ -static int wmi_parse_hexbyte(const u8 *src) -{ - int h; - int value; - - /* high part */ - h = value = hex_to_bin(src[0]); - if (value < 0) - return -1; - - /* low part */ - value = hex_to_bin(src[1]); - if (value >= 0) - return (h << 4) | value; - return -1; -} - -/** - * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary - * @src: Memory block holding binary GUID (16 bytes) - * @dest: Memory block to hold byte swapped binary GUID (16 bytes) - * - * Byte swap a binary GUID to match it's real GUID value - */ -static void wmi_swap_bytes(u8 *src, u8 *dest) -{ - int i; - - for (i = 0; i <= 3; i++) - memcpy(dest + i, src + (3 - i), 1); - - for (i = 0; i <= 1; i++) - memcpy(dest + 4 + i, src + (5 - i), 1); - - for (i = 0; i <= 1; i++) - memcpy(dest + 6 + i, src + (7 - i), 1); - - memcpy(dest + 8, src + 8, 8); -} - -/** - * wmi_parse_guid - Convert GUID from ASCII to binary - * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba - * @dest: Memory block to hold binary GUID (16 bytes) - * - * N.B. The GUID need not be NULL terminated. - * - * Return: 'true' @dest contains binary GUID - * 'false' @dest contents are undefined - */ -static bool wmi_parse_guid(const u8 *src, u8 *dest) -{ - static const int size[] = { 4, 2, 2, 2, 6 }; - int i, j, v; - - if (src[8] != '-' || src[13] != '-' || - src[18] != '-' || src[23] != '-') - return false; - - for (j = 0; j < 5; j++, src++) { - for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) { - v = wmi_parse_hexbyte(src); - if (v < 0) - return false; - } - } - - return true; -} - static bool find_guid(const char *guid_string, struct wmi_block **out) { - char tmp[16], guid_input[16]; + uuid_le guid_input; struct wmi_block *wblock; struct guid_block *block; struct list_head *p; - wmi_parse_guid(guid_string, tmp); - wmi_swap_bytes(tmp, guid_input); + if (uuid_le_to_bin(guid_string, &guid_input)) + return false; list_for_each(p, &wmi_block_list) { wblock = list_entry(p, struct wmi_block, list); block = &wblock->gblock; - if (memcmp(block->guid, guid_input, 16) == 0) { + if (memcmp(block->guid, &guid_input, 16) == 0) { if (out) *out = wblock; return true; @@ -498,20 +420,20 @@ wmi_notify_handler handler, void *data) { struct wmi_block *block; acpi_status status = AE_NOT_EXIST; - char tmp[16], guid_input[16]; + uuid_le guid_input; struct list_head *p; if (!guid || !handler) return AE_BAD_PARAMETER; - wmi_parse_guid(guid, tmp); - wmi_swap_bytes(tmp, guid_input); + if (uuid_le_to_bin(guid, &guid_input)) + return AE_BAD_PARAMETER; list_for_each(p, &wmi_block_list) { acpi_status wmi_status; block = list_entry(p, struct wmi_block, list); - if (memcmp(block->gblock.guid, guid_input, 16) == 0) { + if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { if (block->handler && block->handler != wmi_notify_debug) return AE_ALREADY_ACQUIRED; @@ -539,20 +461,20 @@ acpi_status wmi_remove_notify_handler(const char *guid) { struct wmi_block *block; acpi_status status = AE_NOT_EXIST; - char tmp[16], guid_input[16]; + uuid_le guid_input; struct list_head *p; if (!guid) return AE_BAD_PARAMETER; - wmi_parse_guid(guid, tmp); - wmi_swap_bytes(tmp, guid_input); + if (uuid_le_to_bin(guid, &guid_input)) + return AE_BAD_PARAMETER; list_for_each(p, &wmi_block_list) { acpi_status wmi_status; block = list_entry(p, struct wmi_block, list); - if (memcmp(block->gblock.guid, guid_input, 16) == 0) { + if (memcmp(block->gblock.guid, &guid_input, 16) == 0) { if (!block->handler || block->handler == wmi_notify_debug) return AE_NULL_ENTRY; |