diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-15 13:31:25 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-15 13:31:25 -0800 |
commit | e18bf801f1501e15830db5fa927a6e2832d49d7b (patch) | |
tree | 6880011bae0d372770c43fdbb7bcedf959d6d868 /drivers | |
parent | 8600b697cd4787ac3ce053d48ca7301836fd0c55 (diff) | |
parent | cb2bf25145e0d2abef20f47dd2ae55bff97fd9cb (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/Kconfig | 11 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/acer-wmi.c | 56 | ||||
-rw-r--r-- | drivers/platform/x86/asus-nb-wmi.c | 23 | ||||
-rw-r--r-- | drivers/platform/x86/asus-wmi.c | 29 | ||||
-rw-r--r-- | drivers/platform/x86/asus-wmi.h | 1 | ||||
-rw-r--r-- | drivers/platform/x86/dell-laptop.c | 26 | ||||
-rw-r--r-- | drivers/platform/x86/dell-wmi.c | 12 | ||||
-rw-r--r-- | drivers/platform/x86/intel-hid.c | 6 | ||||
-rw-r--r-- | drivers/platform/x86/intel-smartconnect.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/intel-vbtn.c | 35 | ||||
-rw-r--r-- | drivers/platform/x86/intel_mid_thermal.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_core.c | 386 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_core.h | 110 | ||||
-rw-r--r-- | drivers/platform/x86/mlxcpld-hotplug.c | 515 | ||||
-rw-r--r-- | drivers/platform/x86/panasonic-laptop.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 95 |
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 |