Commit 1e8143db authored by Linus Torvalds's avatar Linus Torvalds

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

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

Pull x86 platform driver updates from Darren Hart:
 "Mostly minor updates and cleanups.  One new power management
  controller driver for Intel Core SoCs.

  platform/x86:
   - Add PMC Driver for Intel Core SoC

  dell-rbtn:
   - Ignore ACPI notifications if device is suspended

  thinkpad_acpi:
   - save kbdlight state on suspend and restore it on resume

  intel_menlow:
   - reduce code duplication

  asus-wmi:
   - provide access to ALS control

  ideapad-laptop:
   - add a new WMI string for ESC key

  surfacepro3_button:
   - Add a warning when switching to tablet mode

  sony-laptop:
   - Avoid oops on module unload for older laptops

  intel_telemetry:
   - Constify telemetry_core_ops structures

  fujitsu-laptop:
   - Use IS_ENABLED() instead of checking for built-in or module

  asus-laptop:
   - correct error handling in sysfs_acpi_set
   - remove redundant initializers
   - correct error handling in asus_read_brightness()

  fujitsu-laptop:
   - Support radio LED"

* tag 'platform-drivers-x86-v4.7-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86:
  platform/x86: Add PMC Driver for Intel Core SoC
  dell-rbtn: Ignore ACPI notifications if device is suspended
  thinkpad_acpi: save kbdlight state on suspend and restore it on resume
  intel_menlow: reduce code duplication
  asus-wmi: provide access to ALS control
  ideapad-laptop: add a new WMI string for ESC key
  surfacepro3_button: Add a warning when switching to tablet mode
  sony-laptop: Avoid oops on module unload for older laptops
  intel_telemetry: Constify telemetry_core_ops structures
  fujitsu-laptop: Use IS_ENABLED() instead of checking for built-in or module
  asus-laptop: correct error handling in sysfs_acpi_set
  asus-laptop: remove redundant initializers
  asus-laptop: correct error handling in asus_read_brightness()
  fujitsu-laptop: Support radio LED
parents 25662785 b740d2e9
...@@ -6096,6 +6096,14 @@ S: Maintained ...@@ -6096,6 +6096,14 @@ S: Maintained
F: arch/x86/include/asm/intel_telemetry.h F: arch/x86/include/asm/intel_telemetry.h
F: drivers/platform/x86/intel_telemetry* F: drivers/platform/x86/intel_telemetry*
INTEL PMC CORE DRIVER
M: Rajneesh Bhardwaj <rajneesh.bhardwaj@intel.com>
M: Vishwanath Somayaji <vishwanath.somayaji@intel.com>
L: platform-driver-x86@vger.kernel.org
S: Maintained
F: arch/x86/include/asm/pmc_core.h
F: drivers/platform/x86/intel_pmc_core*
IOC3 ETHERNET DRIVER IOC3 ETHERNET DRIVER
M: Ralf Baechle <ralf@linux-mips.org> M: Ralf Baechle <ralf@linux-mips.org>
L: linux-mips@linux-mips.org L: linux-mips@linux-mips.org
......
...@@ -99,7 +99,7 @@ struct telemetry_core_ops { ...@@ -99,7 +99,7 @@ struct telemetry_core_ops {
int (*reset_events)(void); int (*reset_events)(void);
}; };
int telemetry_set_pltdata(struct telemetry_core_ops *ops, int telemetry_set_pltdata(const struct telemetry_core_ops *ops,
struct telemetry_plt_config *pltconfig); struct telemetry_plt_config *pltconfig);
int telemetry_clear_pltdata(void); int telemetry_clear_pltdata(void);
......
/*
* 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 _ASM_PMC_CORE_H
#define _ASM_PMC_CORE_H
/* API to read SLP_S0_RESIDENCY counter */
int intel_pmc_slp_s0_counter_read(u32 *data);
#endif /* _ASM_PMC_CORE_H */
...@@ -846,6 +846,18 @@ config INTEL_IMR ...@@ -846,6 +846,18 @@ config INTEL_IMR
If you are running on a Galileo/Quark say Y here. 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 config IBM_RTL
tristate "Device driver to enable PRTL support" tristate "Device driver to enable PRTL support"
depends on X86 && PCI depends on X86 && PCI
......
...@@ -69,3 +69,4 @@ obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o ...@@ -69,3 +69,4 @@ obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o
obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \ obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \
intel_telemetry_pltdrv.o \ intel_telemetry_pltdrv.o \
intel_telemetry_debugfs.o intel_telemetry_debugfs.o
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o
...@@ -771,12 +771,14 @@ static int asus_read_brightness(struct backlight_device *bd) ...@@ -771,12 +771,14 @@ static int asus_read_brightness(struct backlight_device *bd)
{ {
struct asus_laptop *asus = bl_get_data(bd); struct asus_laptop *asus = bl_get_data(bd);
unsigned long long value; unsigned long long value;
acpi_status rv = AE_OK; acpi_status rv;
rv = acpi_evaluate_integer(asus->handle, METHOD_BRIGHTNESS_GET, rv = acpi_evaluate_integer(asus->handle, METHOD_BRIGHTNESS_GET,
NULL, &value); NULL, &value);
if (ACPI_FAILURE(rv)) if (ACPI_FAILURE(rv)) {
pr_warn("Error reading brightness\n"); pr_warn("Error reading brightness\n");
return 0;
}
return value; return value;
} }
...@@ -865,7 +867,7 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr, ...@@ -865,7 +867,7 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr,
int len = 0; int len = 0;
unsigned long long temp; unsigned long long temp;
char buf[16]; /* enough for all info */ 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, * 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, ...@@ -946,11 +948,10 @@ static ssize_t sysfs_acpi_set(struct asus_laptop *asus,
const char *method) const char *method)
{ {
int rv, value; int rv, value;
int out = 0;
rv = parse_arg(buf, count, &value); rv = parse_arg(buf, count, &value);
if (rv > 0) if (rv <= 0)
out = value ? 1 : 0; return rv;
if (write_acpi_int(asus->handle, method, value)) if (write_acpi_int(asus->handle, method, value))
return -ENODEV; return -ENODEV;
...@@ -1265,7 +1266,7 @@ static DEVICE_ATTR_RO(ls_value); ...@@ -1265,7 +1266,7 @@ static DEVICE_ATTR_RO(ls_value);
static int asus_gps_status(struct asus_laptop *asus) static int asus_gps_status(struct asus_laptop *asus)
{ {
unsigned long long status; unsigned long long status;
acpi_status rv = AE_OK; acpi_status rv;
rv = acpi_evaluate_integer(asus->handle, METHOD_GPS_STATUS, rv = acpi_evaluate_integer(asus->handle, METHOD_GPS_STATUS,
NULL, &status); NULL, &status);
......
...@@ -114,6 +114,7 @@ MODULE_LICENSE("GPL"); ...@@ -114,6 +114,7 @@ MODULE_LICENSE("GPL");
#define ASUS_WMI_DEVID_LED6 0x00020016 #define ASUS_WMI_DEVID_LED6 0x00020016
/* Backlight and Brightness */ /* Backlight and Brightness */
#define ASUS_WMI_DEVID_ALS_ENABLE 0x00050001 /* Ambient Light Sensor */
#define ASUS_WMI_DEVID_BACKLIGHT 0x00050011 #define ASUS_WMI_DEVID_BACKLIGHT 0x00050011
#define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012 #define ASUS_WMI_DEVID_BRIGHTNESS 0x00050012
#define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021 #define ASUS_WMI_DEVID_KBD_BACKLIGHT 0x00050021
...@@ -1730,6 +1731,7 @@ ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD); ...@@ -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(camera, 0644, ASUS_WMI_DEVID_CAMERA);
ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER); 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(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, static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
...@@ -1756,6 +1758,7 @@ static struct attribute *platform_attributes[] = { ...@@ -1756,6 +1758,7 @@ static struct attribute *platform_attributes[] = {
&dev_attr_cardr.attr, &dev_attr_cardr.attr,
&dev_attr_touchpad.attr, &dev_attr_touchpad.attr,
&dev_attr_lid_resume.attr, &dev_attr_lid_resume.attr,
&dev_attr_als_enable.attr,
NULL NULL
}; };
...@@ -1776,6 +1779,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, ...@@ -1776,6 +1779,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
devid = ASUS_WMI_DEVID_TOUCHPAD; devid = ASUS_WMI_DEVID_TOUCHPAD;
else if (attr == &dev_attr_lid_resume.attr) else if (attr == &dev_attr_lid_resume.attr)
devid = ASUS_WMI_DEVID_LID_RESUME; devid = ASUS_WMI_DEVID_LID_RESUME;
else if (attr == &dev_attr_als_enable.attr)
devid = ASUS_WMI_DEVID_ALS_ENABLE;
if (devid != -1) if (devid != -1)
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
......
...@@ -28,6 +28,7 @@ struct rbtn_data { ...@@ -28,6 +28,7 @@ struct rbtn_data {
enum rbtn_type type; enum rbtn_type type;
struct rfkill *rfkill; struct rfkill *rfkill;
struct input_dev *input_dev; struct input_dev *input_dev;
bool suspended;
}; };
...@@ -235,9 +236,55 @@ static const struct acpi_device_id rbtn_ids[] = { ...@@ -235,9 +236,55 @@ static const struct acpi_device_id rbtn_ids[] = {
{ "", 0 }, { "", 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 = { static struct acpi_driver rbtn_driver = {
.name = "dell-rbtn", .name = "dell-rbtn",
.ids = rbtn_ids, .ids = rbtn_ids,
.drv.pm = &rbtn_pm_ops,
.ops = { .ops = {
.add = rbtn_add, .add = rbtn_add,
.remove = rbtn_remove, .remove = rbtn_remove,
...@@ -399,6 +446,15 @@ static void rbtn_notify(struct acpi_device *device, u32 event) ...@@ -399,6 +446,15 @@ static void rbtn_notify(struct acpi_device *device, u32 event)
{ {
struct rbtn_data *rbtn_data = device->driver_data; 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) { if (event != 0x80) {
dev_info(&device->dev, "Received unknown event (0x%x)\n", dev_info(&device->dev, "Received unknown event (0x%x)\n",
event); event);
......
...@@ -69,7 +69,7 @@ ...@@ -69,7 +69,7 @@
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.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> #include <linux/leds.h>
#endif #endif
#include <acpi/video.h> #include <acpi/video.h>
...@@ -100,13 +100,14 @@ ...@@ -100,13 +100,14 @@
/* FUNC interface - responses */ /* FUNC interface - responses */
#define UNSUPPORTED_CMD 0x80000000 #define UNSUPPORTED_CMD 0x80000000
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) #if IS_ENABLED(CONFIG_LEDS_CLASS)
/* FUNC interface - LED control */ /* FUNC interface - LED control */
#define FUNC_LED_OFF 0x1 #define FUNC_LED_OFF 0x1
#define FUNC_LED_ON 0x30001 #define FUNC_LED_ON 0x30001
#define KEYBOARD_LAMPS 0x100 #define KEYBOARD_LAMPS 0x100
#define LOGOLAMP_POWERON 0x2000 #define LOGOLAMP_POWERON 0x2000
#define LOGOLAMP_ALWAYS 0x4000 #define LOGOLAMP_ALWAYS 0x4000
#define RADIO_LED_ON 0x20
#endif #endif
/* Hotkey details */ /* Hotkey details */
...@@ -174,13 +175,14 @@ struct fujitsu_hotkey_t { ...@@ -174,13 +175,14 @@ struct fujitsu_hotkey_t {
int rfkill_state; int rfkill_state;
int logolamp_registered; int logolamp_registered;
int kblamps_registered; int kblamps_registered;
int radio_led_registered;
}; };
static struct fujitsu_hotkey_t *fujitsu_hotkey; static struct fujitsu_hotkey_t *fujitsu_hotkey;
static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event); 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 enum led_brightness logolamp_get(struct led_classdev *cdev);
static void logolamp_set(struct led_classdev *cdev, static void logolamp_set(struct led_classdev *cdev,
enum led_brightness brightness); enum led_brightness brightness);
...@@ -200,6 +202,16 @@ static struct led_classdev kblamps_led = { ...@@ -200,6 +202,16 @@ static struct led_classdev kblamps_led = {
.brightness_get = kblamps_get, .brightness_get = kblamps_get,
.brightness_set = kblamps_set .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 #endif
#ifdef CONFIG_FUJITSU_LAPTOP_DEBUG #ifdef CONFIG_FUJITSU_LAPTOP_DEBUG
...@@ -249,7 +261,7 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2) ...@@ -249,7 +261,7 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2)
return value; return value;
} }
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) #if IS_ENABLED(CONFIG_LEDS_CLASS)
/* LED class callbacks */ /* LED class callbacks */
static void logolamp_set(struct led_classdev *cdev, static void logolamp_set(struct led_classdev *cdev,
...@@ -275,6 +287,15 @@ static void kblamps_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); 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) static enum led_brightness logolamp_get(struct led_classdev *cdev)
{ {
enum led_brightness brightness = LED_OFF; enum led_brightness brightness = LED_OFF;
...@@ -299,6 +320,16 @@ static enum led_brightness kblamps_get(struct led_classdev *cdev) ...@@ -299,6 +320,16 @@ static enum led_brightness kblamps_get(struct led_classdev *cdev)
return brightness; 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 #endif
/* Hardware access for LCD brightness control */ /* Hardware access for LCD brightness control */
...@@ -872,7 +903,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) ...@@ -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 */ /* 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)); 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) { if (call_fext_func(FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) {
result = led_classdev_register(&fujitsu->pf_device->dev, result = led_classdev_register(&fujitsu->pf_device->dev,
&logolamp_led); &logolamp_led);
...@@ -895,6 +926,23 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device) ...@@ -895,6 +926,23 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
result); 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 #endif
return result; return result;
...@@ -915,12 +963,15 @@ static int acpi_fujitsu_hotkey_remove(struct acpi_device *device) ...@@ -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 fujitsu_hotkey_t *fujitsu_hotkey = acpi_driver_data(device);
struct input_dev *input = fujitsu_hotkey->input; 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) if (fujitsu_hotkey->logolamp_registered)
led_classdev_unregister(&logolamp_led); led_classdev_unregister(&logolamp_led);
if (fujitsu_hotkey->kblamps_registered) if (fujitsu_hotkey->kblamps_registered)
led_classdev_unregister(&kblamps_led); led_classdev_unregister(&kblamps_led);
if (fujitsu_hotkey->radio_led_registered)
led_classdev_unregister(&radio_led);
#endif #endif
input_unregister_device(input); input_unregister_device(input);
......
...@@ -48,7 +48,10 @@ ...@@ -48,7 +48,10 @@
#define CFG_CAMERA_BIT (19) #define CFG_CAMERA_BIT (19)
#if IS_ENABLED(CONFIG_ACPI_WMI) #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 #endif
enum { enum {
...@@ -93,6 +96,7 @@ struct ideapad_private { ...@@ -93,6 +96,7 @@ struct ideapad_private {
struct dentry *debug; struct dentry *debug;
unsigned long cfg; unsigned long cfg;
bool has_hw_rfkill_switch; bool has_hw_rfkill_switch;
const char *fnesc_guid;
}; };
static bool no_bt_rfkill; static bool no_bt_rfkill;
...@@ -989,8 +993,16 @@ static int ideapad_acpi_add(struct platform_device *pdev) ...@@ -989,8 +993,16 @@ static int ideapad_acpi_add(struct platform_device *pdev)
ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv); ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv);
if (ret) if (ret)
goto notification_failed; goto notification_failed;
#if IS_ENABLED(CONFIG_ACPI_WMI) #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) if (ret != AE_OK && ret != AE_NOT_EXIST)
goto notification_failed_wmi; goto notification_failed_wmi;
#endif #endif
...@@ -1020,7 +1032,8 @@ static int ideapad_acpi_remove(struct platform_device *pdev) ...@@ -1020,7 +1032,8 @@ static int ideapad_acpi_remove(struct platform_device *pdev)
int i; int i;
#if IS_ENABLED(CONFIG_ACPI_WMI) #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 #endif
acpi_remove_notify_handler(priv->adev->handle, acpi_remove_notify_handler(priv->adev->handle,
ACPI_DEVICE_NOTIFY, ideapad_acpi_notify); ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
......
...@@ -306,33 +306,32 @@ static int sensor_set_auxtrip(acpi_handle handle, int index, int value) ...@@ -306,33 +306,32 @@ static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
#define to_intel_menlow_attr(_attr) \ #define to_intel_menlow_attr(_attr) \
container_of(_attr, struct intel_menlow_attribute, attr) container_of(_attr, struct intel_menlow_attribute, attr)
static ssize_t aux0_show(struct device *dev, static ssize_t aux_show(struct device *dev, struct device_attribute *dev_attr,
struct device_attribute *dev_attr, char *buf) char *buf, int idx)
{ {
struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
unsigned long long value; unsigned long long value;
int result; 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)); 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 device_attribute *dev_attr, char *buf)
{ {
struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); return aux_show(dev, dev_attr, buf, 0);
unsigned long long value; }
int result;
result = sensor_get_auxtrip(attr->handle, 1, &value);
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, static ssize_t aux_store(struct device *dev, struct device_attribute *dev_attr,
struct device_attribute *dev_attr, const char *buf, size_t count, int idx)
const char *buf, size_t count)
{ {
struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
int value; int value;
...@@ -345,27 +344,23 @@ static ssize_t aux0_store(struct device *dev, ...@@ -345,27 +344,23 @@ static ssize_t aux0_store(struct device *dev,
if (value < 0) if (value < 0)
return -EINVAL; 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; 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, struct device_attribute *dev_attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr); return aux_store(dev, dev_attr, buf, count, 0);
int value; }
int result;
/*Sanity check; should be a positive integer */
if (!sscanf(buf, "%d", &value))
return -EINVAL;
if (value < 0)
return -EINVAL;
result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_DECI_KELVIN(value)); static ssize_t aux1_store(struct device *dev,
return result ? result : count; 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 */ /* BIOS can enable/disable the thermal user application in dabney platform */
......
/*
* 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);
/*
* 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 */
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
struct telemetry_core_config { struct telemetry_core_config {
struct telemetry_plt_config *plt_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; static struct telemetry_core_config telm_core_conf;
...@@ -95,7 +95,7 @@ static int telemetry_def_reset_events(void) ...@@ -95,7 +95,7 @@ static int telemetry_def_reset_events(void)
return 0; 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, .set_sampling_period = telemetry_def_set_sampling_period,
.get_sampling_period = telemetry_def_get_sampling_period, .get_sampling_period = telemetry_def_get_sampling_period,
.get_trace_verbosity = telemetry_def_get_trace_verbosity, .get_trace_verbosity = telemetry_def_get_trace_verbosity,
...@@ -332,7 +332,7 @@ EXPORT_SYMBOL_GPL(telemetry_set_trace_verbosity); ...@@ -332,7 +332,7 @@ EXPORT_SYMBOL_GPL(telemetry_set_trace_verbosity);
* *
* Return: 0 success, < 0 for failure * 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) struct telemetry_plt_config *pltconfig)
{ {
if (ops) if (ops)
......
...@@ -1081,7 +1081,7 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit, ...@@ -1081,7 +1081,7 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
return ret; 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, .get_trace_verbosity = telemetry_plt_get_trace_verbosity,
.set_trace_verbosity = telemetry_plt_set_trace_verbosity, .set_trace_verbosity = telemetry_plt_set_trace_verbosity,
.set_sampling_period = telemetry_plt_set_sampling_period, .set_sampling_period = telemetry_plt_set_sampling_period,
......
...@@ -1446,6 +1446,9 @@ static void sony_nc_function_cleanup(struct platform_device *pd) ...@@ -1446,6 +1446,9 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
{ {
unsigned int i, result, bitmask, handle; unsigned int i, result, bitmask, handle;
if (!handles)
return;
/* get enabled events and disable them */ /* 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, "SN01", NULL, &bitmask);
sony_nc_int_call(sony_nc_acpi_handle, "SN03", &bitmask, &result); sony_nc_int_call(sony_nc_acpi_handle, "SN03", &bitmask, &result);
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
#define SURFACE_BUTTON_OBJ_NAME "VGBI" #define SURFACE_BUTTON_OBJ_NAME "VGBI"
#define SURFACE_BUTTON_DEVICE_NAME "Surface Pro 3/4 Buttons" #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_PRESS_POWER 0xc6
#define SURFACE_BUTTON_NOTIFY_RELEASE_POWER 0xc7 #define SURFACE_BUTTON_NOTIFY_RELEASE_POWER 0xc7
...@@ -33,7 +35,7 @@ ...@@ -33,7 +35,7 @@
#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP 0xc0 #define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP 0xc0
#define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP 0xc1 #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 #define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN 0xc3
ACPI_MODULE_NAME("surface pro 3 button"); ACPI_MODULE_NAME("surface pro 3 button");
...@@ -105,9 +107,12 @@ static void surface_button_notify(struct acpi_device *device, u32 event) ...@@ -105,9 +107,12 @@ static void surface_button_notify(struct acpi_device *device, u32 event)
case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN: case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN:
key_code = KEY_VOLUMEDOWN; key_code = KEY_VOLUMEDOWN;
break; break;
case SURFACE_BUTTON_NOTIFY_TABLET_MODE:
dev_warn_once(&device->dev, "Tablet mode is not supported\n");
break;
default: default:
dev_info_ratelimited(&device->dev, dev_info_ratelimited(&device->dev,
"Unsupported event [0x%x]\n", event); "Unsupported event [0x%x]\n", event);
break; break;
} }
input = button->input; input = button->input;
......
...@@ -5001,6 +5001,8 @@ static int kbdlight_set_level(int level) ...@@ -5001,6 +5001,8 @@ static int kbdlight_set_level(int level)
return 0; return 0;
} }
static int kbdlight_set_level_and_update(int level);
static int kbdlight_get_level(void) static int kbdlight_get_level(void)
{ {
int status = 0; int status = 0;
...@@ -5068,7 +5070,7 @@ static void kbdlight_set_worker(struct work_struct *work) ...@@ -5068,7 +5070,7 @@ static void kbdlight_set_worker(struct work_struct *work)
container_of(work, struct tpacpi_led_classdev, work); container_of(work, struct tpacpi_led_classdev, work);
if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) 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, static void kbdlight_sysfs_set(struct led_classdev *led_cdev,
...@@ -5099,7 +5101,6 @@ static struct tpacpi_led_classdev tpacpi_led_kbdlight = { ...@@ -5099,7 +5101,6 @@ static struct tpacpi_led_classdev tpacpi_led_kbdlight = {
.max_brightness = 2, .max_brightness = 2,
.brightness_set = &kbdlight_sysfs_set, .brightness_set = &kbdlight_sysfs_set,
.brightness_get = &kbdlight_sysfs_get, .brightness_get = &kbdlight_sysfs_get,
.flags = LED_CORE_SUSPENDRESUME,
} }
}; };
...@@ -5137,6 +5138,20 @@ static void kbdlight_exit(void) ...@@ -5137,6 +5138,20 @@ static void kbdlight_exit(void)
flush_workqueue(tpacpi_wq); 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) static int kbdlight_read(struct seq_file *m)
{ {
int level; int level;
...@@ -5177,13 +5192,35 @@ static int kbdlight_write(char *buf) ...@@ -5177,13 +5192,35 @@ static int kbdlight_write(char *buf)
if (level == -1) if (level == -1)
return -EINVAL; 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 = { static struct ibm_struct kbdlight_driver_data = {
.name = "kbdlight", .name = "kbdlight",
.read = kbdlight_read, .read = kbdlight_read,
.write = kbdlight_write, .write = kbdlight_write,
.suspend = kbdlight_suspend,
.resume = kbdlight_resume,
.exit = kbdlight_exit, .exit = kbdlight_exit,
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment