summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-12-15 13:31:25 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2016-12-15 13:31:25 -0800
commite18bf801f1501e15830db5fa927a6e2832d49d7b (patch)
tree6880011bae0d372770c43fdbb7bcedf959d6d868 /drivers
parent8600b697cd4787ac3ce053d48ca7301836fd0c55 (diff)
parentcb2bf25145e0d2abef20f47dd2ae55bff97fd9cb (diff)
Merge tag 'platform-drivers-x86-v4.10-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86
Pull x86 platform driver updates from Darrent Hart: "Introduce one new driver for Mellanox platforms. Add support for various new models to existing drivers via quirks, hotkeys, etc. Significant updates to intel_pmc_core in support of Kabylake and Sunrise Point PCH power management debug. Some cleanup and refactoring across various drivers. Detailed summary: dell-laptop: - Use brightness_set_blocking for kbd_led_level_set thinkpad_acpi: - Initialize local in_tablet_mode and type - Fix old style declaration GCC warning - Adding new hotkey ID for Lenovo thinkpad - Add support for X1 Yoga (2016) Tablet Mode - Move tablet detection into separate function asus-nb-wmi: - Add X45U quirk - Make use of dmi->ident asus-wmi: - Set specified XUSB2PR value for X550LB intel_mid_thermal: - Fix suspend handlers unused warning intel-vbtn: - Switch to use devm_input_allocate_device dell-wmi: - Add events created by Dell Rugged 2-in-1s - Adjust wifi catcher to emit KEY_WLAN intel_pmc_core: - Add KBL CPUID support - Add LTR IGNORE debug feature - Add MPHY PLL clock gating status - ModPhy core lanes pg status - Add PCH IP Power Gating Status - Fix PWRMBASE mask and mmio reg len acer-wmi: - Only supports AMW0_GUID1 on acer family mlx-platform: - Introduce support for Mellanox hotplug driver platform/x86: - Use ACPI_FAILURE at appropriate places" * tag 'platform-drivers-x86-v4.10-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86: (22 commits) platform/x86: thinkpad_acpi: Initialize local in_tablet_mode and type platform/x86: dell-laptop: Use brightness_set_blocking for kbd_led_level_set platform/x86: thinkpad_acpi: Fix old style declaration GCC warning platform/x86: thinkpad_acpi: Adding new hotkey ID for Lenovo thinkpad platform/x86: thinkpad_acpi: Add support for X1 Yoga (2016) Tablet Mode platform/x86: thinkpad_acpi: Move tablet detection into separate function platform/x86: asus-nb-wmi.c: Add X45U quirk platform/x86: asus-nb-wmi: Make use of dmi->ident platform/x86: asus-wmi: Set specified XUSB2PR value for X550LB platform/x86: intel_mid_thermal: Fix suspend handlers unused warning platform/x86: intel-vbtn: Switch to use devm_input_allocate_device platform/x86: Use ACPI_FAILURE at appropriate places platform/x86: dell-wmi: Add events created by Dell Rugged 2-in-1s platform/x86: dell-wmi: Adjust wifi catcher to emit KEY_WLAN platform/x86: intel_pmc_core: Add KBL CPUID support platform/x86: intel_pmc_core: Add LTR IGNORE debug feature platform/x86: intel_pmc_core: Add MPHY PLL clock gating status platform/x86: intel_pmc_core: ModPhy core lanes pg status platform/x86: intel_pmc_core: Add PCH IP Power Gating Status platform/x86: intel_pmc_core: Fix PWRMBASE mask and mmio reg len ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/platform/x86/Kconfig11
-rw-r--r--drivers/platform/x86/Makefile1
-rw-r--r--drivers/platform/x86/acer-wmi.c56
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c23
-rw-r--r--drivers/platform/x86/asus-wmi.c29
-rw-r--r--drivers/platform/x86/asus-wmi.h1
-rw-r--r--drivers/platform/x86/dell-laptop.c26
-rw-r--r--drivers/platform/x86/dell-wmi.c12
-rw-r--r--drivers/platform/x86/intel-hid.c6
-rw-r--r--drivers/platform/x86/intel-smartconnect.c2
-rw-r--r--drivers/platform/x86/intel-vbtn.c35
-rw-r--r--drivers/platform/x86/intel_mid_thermal.c2
-rw-r--r--drivers/platform/x86/intel_pmc_core.c386
-rw-r--r--drivers/platform/x86/intel_pmc_core.h110
-rw-r--r--drivers/platform/x86/mlxcpld-hotplug.c515
-rw-r--r--drivers/platform/x86/panasonic-laptop.c2
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c95
17 files changed, 1239 insertions, 73 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index b8a21d7b25d4..185376901d9c 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1027,4 +1027,15 @@ config INTEL_TELEMETRY
used to get various SoC events and parameters
directly via debugfs files. Various tools may use
this interface for SoC state monitoring.
+
+config MLX_CPLD_PLATFORM
+ tristate "Mellanox platform hotplug driver support"
+ default n
+ depends on MLX_PLATFORM
+ select HWMON
+ select I2C
+ ---help---
+ This driver handles hot-plug events for the power suppliers, power
+ cables and fans on the wide range Mellanox IB and Ethernet systems.
+
endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 2efa86d2a1a7..1f06b6339cf7 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -71,3 +71,4 @@ 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
+obj-$(CONFIG_MLX_CPLD_PLATFORM) += mlxcpld-hotplug.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index 79d64ea00bfb..a66192f692e3 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -355,6 +355,32 @@ static const struct dmi_system_id acer_blacklist[] __initconst = {
{}
};
+static const struct dmi_system_id amw0_whitelist[] __initconst = {
+ {
+ .ident = "Acer",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ },
+ },
+ {
+ .ident = "Gateway",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Gateway"),
+ },
+ },
+ {
+ .ident = "Packard Bell",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Packard Bell"),
+ },
+ },
+ {}
+};
+
+/*
+ * This quirk table is only for Acer/Gateway/Packard Bell family
+ * that those machines are supported by acer-wmi driver.
+ */
static const struct dmi_system_id acer_quirks[] __initconst = {
{
.callback = dmi_matched,
@@ -464,6 +490,17 @@ static const struct dmi_system_id acer_quirks[] __initconst = {
},
.driver_data = &quirk_acer_travelmate_2490,
},
+ {}
+};
+
+/*
+ * This quirk list is for those non-acer machines that have AMW0_GUID1
+ * but supported by acer-wmi in past days. Keeping this quirk list here
+ * is only for backward compatible. Please do not add new machine to
+ * here anymore. Those non-acer machines should be supported by
+ * appropriate wmi drivers.
+ */
+static const struct dmi_system_id non_acer_quirks[] __initconst = {
{
.callback = dmi_matched,
.ident = "Fujitsu Siemens Amilo Li 1718",
@@ -598,6 +635,7 @@ static void __init find_quirks(void)
{
if (!force_series) {
dmi_check_system(acer_quirks);
+ dmi_check_system(non_acer_quirks);
} else if (force_series == 2490) {
quirks = &quirk_acer_travelmate_2490;
}
@@ -2108,6 +2146,24 @@ static int __init acer_wmi_init(void)
find_quirks();
/*
+ * The AMW0_GUID1 wmi is not only found on Acer family but also other
+ * machines like Lenovo, Fujitsu and Medion. In the past days,
+ * acer-wmi driver handled those non-Acer machines by quirks list.
+ * But actually acer-wmi driver was loaded on any machines that have
+ * AMW0_GUID1. This behavior is strange because those machines should
+ * be supported by appropriate wmi drivers. e.g. fujitsu-laptop,
+ * ideapad-laptop. So, here checks the machine that has AMW0_GUID1
+ * should be in Acer/Gateway/Packard Bell white list, or it's already
+ * in the past quirk list.
+ */
+ if (wmi_has_guid(AMW0_GUID1) &&
+ !dmi_check_system(amw0_whitelist) &&
+ quirks == &quirk_unknown) {
+ pr_err("Unsupported machine has AMW0_GUID1, unable to load\n");
+ return -ENODEV;
+ }
+
+ /*
* Detect which ACPI-WMI interface we're using.
*/
if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index 26e4cbc34db8..5be4783e40d4 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -116,8 +116,13 @@ static struct quirk_entry quirk_asus_ux303ub = {
.wmi_backlight_native = true,
};
+static struct quirk_entry quirk_asus_x550lb = {
+ .xusb2pr = 0x01D9,
+};
+
static int dmi_matched(const struct dmi_system_id *dmi)
{
+ pr_info("Identified laptop model '%s'\n", dmi->ident);
quirks = dmi->driver_data;
return 1;
}
@@ -175,6 +180,15 @@ static const struct dmi_system_id asus_quirks[] = {
},
{
.callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X45U",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X45U"),
+ },
+ .driver_data = &quirk_asus_wapf4,
+ },
+ {
+ .callback = dmi_matched,
.ident = "ASUSTeK COMPUTER INC. X456UA",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
@@ -398,6 +412,15 @@ static const struct dmi_system_id asus_quirks[] = {
},
.driver_data = &quirk_asus_ux303ub,
},
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X550LB",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X550LB"),
+ },
+ .driver_data = &quirk_asus_x550lb,
+ },
{},
};
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index ce6ca31a2d09..43cb680adbb4 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -156,6 +156,9 @@ MODULE_LICENSE("GPL");
#define ASUS_FAN_CTRL_MANUAL 1
#define ASUS_FAN_CTRL_AUTO 2
+#define USB_INTEL_XUSB2PR 0xD0
+#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
+
struct bios_args {
u32 arg0;
u32 arg1;
@@ -1080,6 +1083,29 @@ exit:
return result;
}
+static void asus_wmi_set_xusb2pr(struct asus_wmi *asus)
+{
+ struct pci_dev *xhci_pdev;
+ u32 orig_ports_available;
+ u32 ports_available = asus->driver->quirks->xusb2pr;
+
+ xhci_pdev = pci_get_device(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI,
+ NULL);
+
+ if (!xhci_pdev)
+ return;
+
+ pci_read_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
+ &orig_ports_available);
+
+ pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR,
+ cpu_to_le32(ports_available));
+
+ pr_info("set USB_INTEL_XUSB2PR old: 0x%04x, new: 0x%04x\n",
+ orig_ports_available, ports_available);
+}
+
/*
* Hwmon device
*/
@@ -2087,6 +2113,9 @@ static int asus_wmi_add(struct platform_device *pdev)
if (asus->driver->quirks->wmi_backlight_native)
acpi_video_set_dmi_backlight_type(acpi_backlight_native);
+ if (asus->driver->quirks->xusb2pr)
+ asus_wmi_set_xusb2pr(asus);
+
if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
err = asus_wmi_backlight_init(asus);
if (err && err != -ENODEV)
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index 0e19014e9f54..fdff626c3b51 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -53,6 +53,7 @@ struct quirk_entry {
* and let the ACPI interrupt to send out the key event.
*/
int no_display_toggle;
+ u32 xusb2pr;
bool (*i8042_filter)(unsigned char data, unsigned char str,
struct serio *serio);
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 2c2f02b2e08a..14392a01ab36 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -1904,38 +1904,40 @@ static enum led_brightness kbd_led_level_get(struct led_classdev *led_cdev)
return 0;
}
-static void kbd_led_level_set(struct led_classdev *led_cdev,
- enum led_brightness value)
+static int kbd_led_level_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
{
struct kbd_state state;
struct kbd_state new_state;
u16 num;
+ int ret;
if (kbd_get_max_level()) {
- if (kbd_get_state(&state))
- return;
+ ret = kbd_get_state(&state);
+ if (ret)
+ return ret;
new_state = state;
- if (kbd_set_level(&new_state, value))
- return;
- kbd_set_state_safe(&new_state, &state);
- return;
+ ret = kbd_set_level(&new_state, value);
+ if (ret)
+ return ret;
+ return kbd_set_state_safe(&new_state, &state);
}
if (kbd_get_valid_token_counts()) {
for (num = kbd_token_bits; num != 0 && value > 0; --value)
num &= num - 1; /* clear the first bit set */
if (num == 0)
- return;
- kbd_set_token_bit(ffs(num) - 1);
- return;
+ return 0;
+ return kbd_set_token_bit(ffs(num) - 1);
}
pr_warn("Keyboard brightness level control not supported\n");
+ return -ENXIO;
}
static struct led_classdev kbd_led = {
.name = "dell::kbd_backlight",
- .brightness_set = kbd_led_level_set,
+ .brightness_set_blocking = kbd_led_level_set,
.brightness_get = kbd_led_level_get,
.groups = kbd_led_groups,
};
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index da2fe18162e1..75e637047d36 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -114,7 +114,7 @@ static const struct key_entry dell_wmi_keymap_type_0000[] __initconst = {
{ KE_IGNORE, 0xe00e, { KEY_RESERVED } },
/* Wifi Catcher */
- { KE_KEY, 0xe011, { KEY_PROG2 } },
+ { KE_KEY, 0xe011, { KEY_WLAN } },
/* Ambient light sensor toggle */
{ KE_IGNORE, 0xe013, { KEY_RESERVED } },
@@ -274,6 +274,16 @@ static const struct key_entry dell_wmi_keymap_type_0010[] __initconst = {
/* Stealth mode toggle */
{ KE_IGNORE, 0x155, { KEY_RESERVED } },
+
+ /* Rugged magnetic dock attach/detach events */
+ { KE_IGNORE, 0x156, { KEY_RESERVED } },
+ { KE_IGNORE, 0x157, { KEY_RESERVED } },
+
+ /* Rugged programmable (P1/P2/P3 keys) */
+ { KE_KEY, 0x850, { KEY_PROG1 } },
+ { KE_KEY, 0x851, { KEY_PROG2 } },
+ { KE_KEY, 0x852, { KEY_PROG3 } },
+
};
/*
diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c
index 12dbb5063376..cb3ab2b212b1 100644
--- a/drivers/platform/x86/intel-hid.c
+++ b/drivers/platform/x86/intel-hid.c
@@ -69,7 +69,7 @@ static int intel_hid_set_enable(struct device *device, int enable)
arg0.integer.value = enable;
status = acpi_evaluate_object(ACPI_HANDLE(device), "HDSM", &args, NULL);
- if (!ACPI_SUCCESS(status)) {
+ if (ACPI_FAILURE(status)) {
dev_warn(device, "failed to %sable hotkeys\n",
enable ? "en" : "dis");
return -EIO;
@@ -148,7 +148,7 @@ static void notify_handler(acpi_handle handle, u32 event, void *context)
}
status = acpi_evaluate_integer(handle, "HDEM", NULL, &ev_index);
- if (!ACPI_SUCCESS(status)) {
+ if (ACPI_FAILURE(status)) {
dev_warn(&device->dev, "failed to get event index\n");
return;
}
@@ -167,7 +167,7 @@ static int intel_hid_probe(struct platform_device *device)
int err;
status = acpi_evaluate_integer(handle, "HDMM", NULL, &mode);
- if (!ACPI_SUCCESS(status)) {
+ if (ACPI_FAILURE(status)) {
dev_warn(&device->dev, "failed to read mode\n");
return -ENODEV;
}
diff --git a/drivers/platform/x86/intel-smartconnect.c b/drivers/platform/x86/intel-smartconnect.c
index 04cf5dffdfd9..bbe4c06c769f 100644
--- a/drivers/platform/x86/intel-smartconnect.c
+++ b/drivers/platform/x86/intel-smartconnect.c
@@ -29,7 +29,7 @@ static int smartconnect_acpi_init(struct acpi_device *acpi)
acpi_status status;
status = acpi_evaluate_integer(acpi->handle, "GAOS", NULL, &value);
- if (!ACPI_SUCCESS(status))
+ if (ACPI_FAILURE(status))
return -EINVAL;
if (value & 0x1) {
diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c
index 78080763df51..554e82ebe83c 100644
--- a/drivers/platform/x86/intel-vbtn.c
+++ b/drivers/platform/x86/intel-vbtn.c
@@ -49,34 +49,19 @@ static int intel_vbtn_input_setup(struct platform_device *device)
struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
int ret;
- priv->input_dev = input_allocate_device();
+ priv->input_dev = devm_input_allocate_device(&device->dev);
if (!priv->input_dev)
return -ENOMEM;
ret = sparse_keymap_setup(priv->input_dev, intel_vbtn_keymap, NULL);
if (ret)
- goto err_free_device;
+ return ret;
priv->input_dev->dev.parent = &device->dev;
priv->input_dev->name = "Intel Virtual Button driver";
priv->input_dev->id.bustype = BUS_HOST;
- ret = input_register_device(priv->input_dev);
- if (ret)
- goto err_free_device;
-
- return 0;
-
-err_free_device:
- input_free_device(priv->input_dev);
- return ret;
-}
-
-static void intel_vbtn_input_destroy(struct platform_device *device)
-{
- struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
-
- input_unregister_device(priv->input_dev);
+ return input_register_device(priv->input_dev);
}
static void notify_handler(acpi_handle handle, u32 event, void *context)
@@ -97,7 +82,7 @@ static int intel_vbtn_probe(struct platform_device *device)
int err;
status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
- if (!ACPI_SUCCESS(status)) {
+ if (ACPI_FAILURE(status)) {
dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n");
return -ENODEV;
}
@@ -117,24 +102,16 @@ static int intel_vbtn_probe(struct platform_device *device)
ACPI_DEVICE_NOTIFY,
notify_handler,
device);
- if (ACPI_FAILURE(status)) {
- err = -EBUSY;
- goto err_remove_input;
- }
+ if (ACPI_FAILURE(status))
+ return -EBUSY;
return 0;
-
-err_remove_input:
- intel_vbtn_input_destroy(device);
-
- return err;
}
static int intel_vbtn_remove(struct platform_device *device)
{
acpi_handle handle = ACPI_HANDLE(&device->dev);
- intel_vbtn_input_destroy(device);
acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
/*
diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c
index 9f713b832ba3..0df3c9d37509 100644
--- a/drivers/platform/x86/intel_mid_thermal.c
+++ b/drivers/platform/x86/intel_mid_thermal.c
@@ -415,6 +415,7 @@ static struct thermal_device_info *initialize_sensor(int index)
return td_info;
}
+#ifdef CONFIG_PM_SLEEP
/**
* mid_thermal_resume - resume routine
* @dev: device structure
@@ -442,6 +443,7 @@ static int mid_thermal_suspend(struct device *dev)
*/
return configure_adc(0);
}
+#endif
static SIMPLE_DEV_PM_OPS(mid_thermal_pm,
mid_thermal_suspend, mid_thermal_resume);
diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c
index e8b1b836ca2d..b130b8c9b9d7 100644
--- a/drivers/platform/x86/intel_pmc_core.c
+++ b/drivers/platform/x86/intel_pmc_core.c
@@ -19,10 +19,12 @@
*/
#include <linux/debugfs.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/pci.h>
+#include <linux/uaccess.h>
#include <asm/cpu_device_id.h>
#include <asm/intel-family.h>
@@ -32,16 +34,106 @@
static struct pmc_dev pmc;
+static const struct pmc_bit_map spt_pll_map[] = {
+ {"MIPI PLL", SPT_PMC_BIT_MPHY_CMN_LANE0},
+ {"GEN2 USB2PCIE2 PLL", SPT_PMC_BIT_MPHY_CMN_LANE1},
+ {"DMIPCIE3 PLL", SPT_PMC_BIT_MPHY_CMN_LANE2},
+ {"SATA PLL", SPT_PMC_BIT_MPHY_CMN_LANE3},
+ {},
+};
+
+static const struct pmc_bit_map spt_mphy_map[] = {
+ {"MPHY CORE LANE 0", SPT_PMC_BIT_MPHY_LANE0},
+ {"MPHY CORE LANE 1", SPT_PMC_BIT_MPHY_LANE1},
+ {"MPHY CORE LANE 2", SPT_PMC_BIT_MPHY_LANE2},
+ {"MPHY CORE LANE 3", SPT_PMC_BIT_MPHY_LANE3},
+ {"MPHY CORE LANE 4", SPT_PMC_BIT_MPHY_LANE4},
+ {"MPHY CORE LANE 5", SPT_PMC_BIT_MPHY_LANE5},
+ {"MPHY CORE LANE 6", SPT_PMC_BIT_MPHY_LANE6},
+ {"MPHY CORE LANE 7", SPT_PMC_BIT_MPHY_LANE7},
+ {"MPHY CORE LANE 8", SPT_PMC_BIT_MPHY_LANE8},
+ {"MPHY CORE LANE 9", SPT_PMC_BIT_MPHY_LANE9},
+ {"MPHY CORE LANE 10", SPT_PMC_BIT_MPHY_LANE10},
+ {"MPHY CORE LANE 11", SPT_PMC_BIT_MPHY_LANE11},
+ {"MPHY CORE LANE 12", SPT_PMC_BIT_MPHY_LANE12},
+ {"MPHY CORE LANE 13", SPT_PMC_BIT_MPHY_LANE13},
+ {"MPHY CORE LANE 14", SPT_PMC_BIT_MPHY_LANE14},
+ {"MPHY CORE LANE 15", SPT_PMC_BIT_MPHY_LANE15},
+ {},
+};
+
+static const struct pmc_bit_map spt_pfear_map[] = {
+ {"PMC", SPT_PMC_BIT_PMC},
+ {"OPI-DMI", SPT_PMC_BIT_OPI},
+ {"SPI / eSPI", SPT_PMC_BIT_SPI},
+ {"XHCI", SPT_PMC_BIT_XHCI},
+ {"SPA", SPT_PMC_BIT_SPA},
+ {"SPB", SPT_PMC_BIT_SPB},
+ {"SPC", SPT_PMC_BIT_SPC},
+ {"GBE", SPT_PMC_BIT_GBE},
+ {"SATA", SPT_PMC_BIT_SATA},
+ {"HDA-PGD0", SPT_PMC_BIT_HDA_PGD0},
+ {"HDA-PGD1", SPT_PMC_BIT_HDA_PGD1},
+ {"HDA-PGD2", SPT_PMC_BIT_HDA_PGD2},
+ {"HDA-PGD3", SPT_PMC_BIT_HDA_PGD3},
+ {"RSVD", SPT_PMC_BIT_RSVD_0B},
+ {"LPSS", SPT_PMC_BIT_LPSS},
+ {"LPC", SPT_PMC_BIT_LPC},
+ {"SMB", SPT_PMC_BIT_SMB},
+ {"ISH", SPT_PMC_BIT_ISH},
+ {"P2SB", SPT_PMC_BIT_P2SB},
+ {"DFX", SPT_PMC_BIT_DFX},
+ {"SCC", SPT_PMC_BIT_SCC},
+ {"RSVD", SPT_PMC_BIT_RSVD_0C},
+ {"FUSE", SPT_PMC_BIT_FUSE},
+ {"CAMERA", SPT_PMC_BIT_CAMREA},
+ {"RSVD", SPT_PMC_BIT_RSVD_0D},
+ {"USB3-OTG", SPT_PMC_BIT_USB3_OTG},
+ {"EXI", SPT_PMC_BIT_EXI},
+ {"CSE", SPT_PMC_BIT_CSE},
+ {"CSME_KVM", SPT_PMC_BIT_CSME_KVM},
+ {"CSME_PMT", SPT_PMC_BIT_CSME_PMT},
+ {"CSME_CLINK", SPT_PMC_BIT_CSME_CLINK},
+ {"CSME_PTIO", SPT_PMC_BIT_CSME_PTIO},
+ {"CSME_USBR", SPT_PMC_BIT_CSME_USBR},
+ {"CSME_SUSRAM", SPT_PMC_BIT_CSME_SUSRAM},
+ {"CSME_SMT", SPT_PMC_BIT_CSME_SMT},
+ {"RSVD", SPT_PMC_BIT_RSVD_1A},
+ {"CSME_SMS2", SPT_PMC_BIT_CSME_SMS2},
+ {"CSME_SMS1", SPT_PMC_BIT_CSME_SMS1},
+ {"CSME_RTC", SPT_PMC_BIT_CSME_RTC},
+ {"CSME_PSF", SPT_PMC_BIT_CSME_PSF},
+ {},
+};
+
+static const struct pmc_reg_map spt_reg_map = {
+ .pfear_sts = spt_pfear_map,
+ .mphy_sts = spt_mphy_map,
+ .pll_sts = spt_pll_map,
+};
+
static const struct pci_device_id pmc_pci_ids[] = {
- { PCI_VDEVICE(INTEL, SPT_PMC_PCI_DEVICE_ID), (kernel_ulong_t)NULL },
+ { PCI_VDEVICE(INTEL, SPT_PMC_PCI_DEVICE_ID),
+ (kernel_ulong_t)&spt_reg_map },
{ 0, },
};
+static inline u8 pmc_core_reg_read_byte(struct pmc_dev *pmcdev, int offset)
+{
+ return readb(pmcdev->regbase + offset);
+}
+
static inline u32 pmc_core_reg_read(struct pmc_dev *pmcdev, int reg_offset)
{
return readl(pmcdev->regbase + reg_offset);
}
+static inline void pmc_core_reg_write(struct pmc_dev *pmcdev, int
+ reg_offset, u32 val)
+{
+ writel(val, pmcdev->regbase + reg_offset);
+}
+
static inline u32 pmc_core_adjust_slp_s0_step(u32 value)
{
return value * SPT_PMC_SLP_S0_RES_COUNTER_STEP;
@@ -90,6 +182,245 @@ static int pmc_core_dev_state_get(void *data, u64 *val)
DEFINE_DEBUGFS_ATTRIBUTE(pmc_core_dev_state, pmc_core_dev_state_get, NULL, "%llu\n");
+static int pmc_core_check_read_lock_bit(void)
+{
+ struct pmc_dev *pmcdev = &pmc;
+ u32 value;
+
+ value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_CFG_OFFSET);
+ return test_bit(SPT_PMC_READ_DISABLE_BIT,
+ (unsigned long *)&value);
+}
+
+#if IS_ENABLED(CONFIG_DEBUG_FS)
+static void pmc_core_display_map(struct seq_file *s, int index,
+ u8 pf_reg, const struct pmc_bit_map *pf_map)
+{
+ seq_printf(s, "PCH IP: %-2d - %-32s\tState: %s\n",
+ index, pf_map[index].name,
+ pf_map[index].bit_mask & pf_reg ? "Off" : "On");
+}
+
+static int pmc_core_ppfear_sts_show(struct seq_file *s, void *unused)
+{
+ struct pmc_dev *pmcdev = s->private;
+ const struct pmc_bit_map *map = pmcdev->map->pfear_sts;
+ u8 pf_regs[NUM_ENTRIES];
+ int index, iter;
+
+ iter = SPT_PMC_XRAM_PPFEAR0A;
+
+ for (index = 0; index < NUM_ENTRIES; index++, iter++)
+ pf_regs[index] = pmc_core_reg_read_byte(pmcdev, iter);
+
+ for (index = 0; map[index].name; index++)
+ pmc_core_display_map(s, index, pf_regs[index / 8], map);
+
+ return 0;
+}
+
+static int pmc_core_ppfear_sts_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pmc_core_ppfear_sts_show, inode->i_private);
+}
+
+static const struct file_operations pmc_core_ppfear_ops = {
+ .open = pmc_core_ppfear_sts_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* This function should return link status, 0 means ready */
+static int pmc_core_mtpmc_link_status(void)
+{
+ struct pmc_dev *pmcdev = &pmc;
+ u32 value;
+
+ value = pmc_core_reg_read(pmcdev, SPT_PMC_PM_STS_OFFSET);
+ return test_bit(SPT_PMC_MSG_FULL_STS_BIT,
+ (unsigned long *)&value);
+}
+
+static int pmc_core_send_msg(u32 *addr_xram)
+{
+ struct pmc_dev *pmcdev = &pmc;
+ u32 dest;
+ int timeout;
+
+ for (timeout = NUM_RETRIES; timeout > 0; timeout--) {
+ if (pmc_core_mtpmc_link_status() == 0)
+ break;
+ msleep(5);
+ }
+
+ if (timeout <= 0 && pmc_core_mtpmc_link_status())
+ return -EBUSY;
+
+ dest = (*addr_xram & MTPMC_MASK) | (1U << 1);
+ pmc_core_reg_write(pmcdev, SPT_PMC_MTPMC_OFFSET, dest);
+ return 0;
+}
+
+static int pmc_core_mphy_pg_sts_show(struct seq_file *s, void *unused)
+{
+ struct pmc_dev *pmcdev = s->private;
+ const struct pmc_bit_map *map = pmcdev->map->mphy_sts;
+ u32 mphy_core_reg_low, mphy_core_reg_high;
+ u32 val_low, val_high;
+ int index, err = 0;
+
+ if (pmcdev->pmc_xram_read_bit) {
+ seq_puts(s, "Access denied: please disable PMC_READ_DISABLE setting in BIOS.");
+ return 0;
+ }
+
+ mphy_core_reg_low = (SPT_PMC_MPHY_CORE_STS_0 << 16);
+ mphy_core_reg_high = (SPT_PMC_MPHY_CORE_STS_1 << 16);
+
+ mutex_lock(&pmcdev->lock);
+
+ if (pmc_core_send_msg(&mphy_core_reg_low) != 0) {
+ err = -EBUSY;
+ goto out_unlock;
+ }
+
+ msleep(10);
+ val_low = pmc_core_reg_read(pmcdev, SPT_PMC_MFPMC_OFFSET);
+
+ if (pmc_core_send_msg(&mphy_core_reg_high) != 0) {
+ err = -EBUSY;
+ goto out_unlock;
+ }
+
+ msleep(10);
+ val_high = pmc_core_reg_read(pmcdev, SPT_PMC_MFPMC_OFFSET);
+
+ for (index = 0; map[index].name && index < 8; index++) {
+ seq_printf(s, "%-32s\tState: %s\n",
+ map[index].name,
+ map[index].bit_mask & val_low ? "Not power gated" :
+ "Power gated");
+ }
+
+ for (index = 8; map[index].name; index++) {
+ seq_printf(s, "%-32s\tState: %s\n",
+ map[index].name,
+ map[index].bit_mask & val_high ? "Not power gated" :
+ "Power gated");
+ }
+
+out_unlock:
+ mutex_unlock(&pmcdev->lock);
+ return err;
+}
+
+static int pmc_core_mphy_pg_sts_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pmc_core_mphy_pg_sts_show, inode->i_private);
+}
+
+static const struct file_operations pmc_core_mphy_pg_ops = {
+ .open = pmc_core_mphy_pg_sts_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int pmc_core_pll_show(struct seq_file *s, void *unused)
+{
+ struct pmc_dev *pmcdev = s->private;
+ const struct pmc_bit_map *map = pmcdev->map->pll_sts;
+ u32 mphy_common_reg, val;
+ int index, err = 0;
+
+ if (pmcdev->pmc_xram_read_bit) {
+ seq_puts(s, "Access denied: please disable PMC_READ_DISABLE setting in BIOS.");
+ return 0;
+ }
+
+ mphy_common_reg = (SPT_PMC_MPHY_COM_STS_0 << 16);
+ mutex_lock(&pmcdev->lock);
+
+ if (pmc_core_send_msg(&mphy_common_reg) != 0) {
+ err = -EBUSY;
+ goto out_unlock;
+ }
+
+ /* Observed PMC HW response latency for MTPMC-MFPMC is ~10 ms */
+ msleep(10);
+ val = pmc_core_reg_read(pmcdev, SPT_PMC_MFPMC_OFFSET);
+
+ for (index = 0; map[index].name ; index++) {
+ seq_printf(s, "%-32s\tState: %s\n",
+ map[index].name,
+ map[index].bit_mask & val ? "Active" : "Idle");
+ }
+
+out_unlock:
+ mutex_unlock(&pmcdev->lock);
+ return err;
+}
+
+static int pmc_core_pll_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pmc_core_pll_show, inode->i_private);
+}
+
+static const struct file_operations pmc_core_pll_ops = {
+ .open = pmc_core_pll_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static ssize_t pmc_core_ltr_ignore_write(struct file *file, const char __user
+*userbuf, size_t count, loff_t *ppos)
+{
+ struct pmc_dev *pmcdev = &pmc;
+ u32 val, buf_size, fd;
+ int err = 0;
+
+ buf_size = count < 64 ? count : 64;
+ mutex_lock(&pmcdev->lock);
+
+ if (kstrtou32_from_user(userbuf, buf_size, 10, &val)) {
+ err = -EFAULT;
+ goto out_unlock;
+ }
+
+ if (val > NUM_IP_IGN_ALLOWED) {
+ err = -EINVAL;
+ goto out_unlock;
+ }
+
+ fd = pmc_core_reg_read(pmcdev, SPT_PMC_LTR_IGNORE_OFFSET);
+ fd |= (1U << val);
+ pmc_core_reg_write(pmcdev, SPT_PMC_LTR_IGNORE_OFFSET, fd);
+
+out_unlock:
+ mutex_unlock(&pmcdev->lock);
+ return err == 0 ? count : err;
+}
+
+static int pmc_core_ltr_ignore_show(struct seq_file *s, void *unused)
+{
+ return 0;
+}
+
+static int pmc_core_ltr_ignore_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pmc_core_ltr_ignore_show, inode->i_private);
+}
+
+static const struct file_operations pmc_core_ltr_ignore_ops = {
+ .open = pmc_core_ltr_ignore_open,
+ .read = seq_read,
+ .write = pmc_core_ltr_ignore_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static void pmc_core_dbgfs_unregister(struct pmc_dev *pmcdev)
{
debugfs_remove_recursive(pmcdev->dbgfs_dir);
@@ -106,20 +437,59 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
pmcdev->dbgfs_dir = dir;
file = debugfs_create_file("slp_s0_residency_usec", S_IFREG | S_IRUGO,
dir, pmcdev, &pmc_core_dev_state);
+ if (!file)
+ goto err;
- if (!file) {
- pmc_core_dbgfs_unregister(pmcdev);
- return -ENODEV;
- }
+ file = debugfs_create_file("pch_ip_power_gating_status",
+ S_IFREG | S_IRUGO, dir, pmcdev,
+ &pmc_core_ppfear_ops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("mphy_core_lanes_power_gating_status",
+ S_IFREG | S_IRUGO, dir, pmcdev,
+ &pmc_core_mphy_pg_ops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("pll_status",
+ S_IFREG | S_IRUGO, dir, pmcdev,
+ &pmc_core_pll_ops);
+ if (!file)
+ goto err;
+
+ file = debugfs_create_file("ltr_ignore",
+ S_IFREG | S_IRUGO, dir, pmcdev,
+ &pmc_core_ltr_ignore_ops);
+
+ if (!file)
+ goto err;
return 0;
+err:
+ pmc_core_dbgfs_unregister(pmcdev);
+ return -ENODEV;
}
+#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, INTEL_FAM6_SKYLAKE_MOBILE, X86_FEATURE_MWAIT,
(kernel_ulong_t)NULL},
{ X86_VENDOR_INTEL, 6, INTEL_FAM6_SKYLAKE_DESKTOP, X86_FEATURE_MWAIT,
(kernel_ulong_t)NULL},
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_KABYLAKE_MOBILE, X86_FEATURE_MWAIT,
+ (kernel_ulong_t)NULL},
+ { X86_VENDOR_INTEL, 6, INTEL_FAM6_KABYLAKE_DESKTOP, X86_FEATURE_MWAIT,
+ (kernel_ulong_t)NULL},
{}
};
@@ -128,6 +498,7 @@ static int pmc_core_probe(struct pci_dev *dev, const struct pci_device_id *id)
struct device *p