diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-27 10:42:35 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-01-27 10:42:35 -0800 |
commit | 08c49dc135225ab29569df955114912544df5c53 (patch) | |
tree | db0a0f4f041397acbb881f6070a6927dc25ff9c7 /drivers | |
parent | 9e1af7567b266dc6c3c8fd434ea807b3206bfdc1 (diff) | |
parent | cf85e7c7f437cb4e378bddbdb366477096714819 (diff) |
Merge tag 'platform-drivers-x86-v5.6-1' of git://git.infradead.org/linux-platform-drivers-x86
Pull x86 platform driver updates from Andy Shevchenko:
- Enable thermal policy for ASUS TUF FX705DY/FX505DY
- Support left round button on ASUS N56VB
- Support new Mellanox platforms of basic class VMOD0009 and VMOD0010
- Intel Comet Lake, Tiger Lake and Elkhart Lake support in the PMC
driver
- Big clean-up to Intel PMC core, PMC IPC and SCU IPC drivers
- Touchscreen support for the PiPO W11 tablet
* tag 'platform-drivers-x86-v5.6-1' of git://git.infradead.org/linux-platform-drivers-x86: (64 commits)
platform/x86: intel_pmc_ipc: Switch to use driver->dev_groups
platform/x86: intel_pmc_ipc: Propagate error from kstrtoul()
platform/x86: intel_pmc_ipc: Use octal permissions in sysfs attributes
platform/x86: intel_pmc_ipc: Get rid of unnecessary includes
platform/x86: intel_pmc_ipc: Drop ipc_data_readb()
platform/x86: intel_pmc_ipc: Drop intel_pmc_gcr_read() and intel_pmc_gcr_write()
platform/x86: intel_pmc_ipc: Make intel_pmc_ipc_raw_cmd() static
platform/x86: intel_pmc_ipc: Make intel_pmc_ipc_simple_command() static
platform/x86: intel_pmc_ipc: Make intel_pmc_gcr_update() static
platform/x86: intel_scu_ipc: Reformat kernel-doc comments of exported functions
platform/x86: intel_scu_ipc: Drop intel_scu_ipc_raw_command()
platform/x86: intel_scu_ipc: Drop intel_scu_ipc_io[read|write][8|16]()
platform/x86: intel_scu_ipc: Drop unused macros
platform/x86: intel_scu_ipc: Drop unused prototype intel_scu_ipc_fw_update()
platform/x86: intel_scu_ipc: Sleeping is fine when polling
platform/x86: intel_scu_ipc: Drop intel_scu_ipc_i2c_cntrl()
platform/x86: intel_scu_ipc: Remove Lincroft support
platform/x86: intel_scu_ipc: Add constants for register offsets
platform/x86: intel_scu_ipc: Fix interrupt support
platform/x86: intel_scu_ipcutil: Remove default y from Kconfig
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/platform/mellanox/mlxreg-hotplug.c | 14 | ||||
-rw-r--r-- | drivers/platform/x86/Kconfig | 18 | ||||
-rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/x86/asus-nb-wmi.c | 1 | ||||
-rw-r--r-- | drivers/platform/x86/asus-wmi.c | 124 | ||||
-rw-r--r-- | drivers/platform/x86/intel-uncore-frequency.c | 437 | ||||
-rw-r--r-- | drivers/platform/x86/intel_atomisp2_pm.c | 25 | ||||
-rw-r--r-- | drivers/platform/x86/intel_mid_powerbtn.c | 5 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_core.c | 141 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_core.h | 4 | ||||
-rw-r--r-- | drivers/platform/x86/intel_pmc_ipc.c | 114 | ||||
-rw-r--r-- | drivers/platform/x86/intel_scu_ipc.c | 414 | ||||
-rw-r--r-- | drivers/platform/x86/intel_speed_select_if/isst_if_common.c | 3 | ||||
-rw-r--r-- | drivers/platform/x86/intel_telemetry_debugfs.c | 14 | ||||
-rw-r--r-- | drivers/platform/x86/intel_telemetry_pltdrv.c | 64 | ||||
-rw-r--r-- | drivers/platform/x86/mlx-platform.c | 564 | ||||
-rw-r--r-- | drivers/platform/x86/touchscreen_dmi.c | 82 |
17 files changed, 1430 insertions, 595 deletions
diff --git a/drivers/platform/mellanox/mlxreg-hotplug.c b/drivers/platform/mellanox/mlxreg-hotplug.c index 706207d192ae..77be37a1fbcf 100644 --- a/drivers/platform/mellanox/mlxreg-hotplug.c +++ b/drivers/platform/mellanox/mlxreg-hotplug.c @@ -504,6 +504,20 @@ static int mlxreg_hotplug_set_irq(struct mlxreg_hotplug_priv_data *priv) item = pdata->items; for (i = 0; i < pdata->counter; i++, item++) { + if (item->capability) { + /* + * Read group capability register to get actual number + * of interrupt capable components and set group mask + * accordingly. + */ + ret = regmap_read(priv->regmap, item->capability, + ®val); + if (ret) + goto out; + + item->mask = GENMASK((regval & item->mask) - 1, 0); + } + /* Clear group presense event. */ ret = regmap_write(priv->regmap, item->reg + MLXREG_HOTPLUG_EVENT_OFF, 0); diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 27d5b40fb717..587403c44598 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -997,7 +997,6 @@ config INTEL_SCU_IPC config INTEL_SCU_IPC_UTIL tristate "Intel SCU IPC utility driver" depends on INTEL_SCU_IPC - default y ---help--- The IPC Util driver provides an interface with the SCU enabling low level access for debug work and updating the firmware. Say @@ -1299,9 +1298,9 @@ config INTEL_ATOMISP2_PM depends on PCI && IOSF_MBI && PM help Power-management driver for Intel's Image Signal Processor found on - Bay and Cherry Trail devices. This dummy driver's sole purpose is to - turn the ISP off (put it in D3) to save power and to allow entering - of S0ix modes. + Bay Trail and Cherry Trail devices. This dummy driver's sole purpose + is to turn the ISP off (put it in D3) to save power and to allow + entering of S0ix modes. To compile this driver as a module, choose M here: the module will be called intel_atomisp2_pm. @@ -1337,6 +1336,17 @@ config PCENGINES_APU2 To compile this driver as a module, choose M here: the module will be called pcengines-apuv2. +config INTEL_UNCORE_FREQ_CONTROL + tristate "Intel Uncore frequency control driver" + depends on X86_64 + help + This driver allows control of uncore frequency limits on + supported server platforms. + Uncore frequency controls RING/LLC (last-level cache) clocks. + + To compile this driver as a module, choose M here: the module + will be called intel-uncore-frequency. + source "drivers/platform/x86/intel_speed_select_if/Kconfig" config SYSTEM76_ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 42d85a00be4e..3747b1f07cf1 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -105,3 +105,4 @@ obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += intel_speed_select_if/ obj-$(CONFIG_SYSTEM76_ACPI) += system76_acpi.o +obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL) += intel-uncore-frequency.o diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index b361c73636a4..6f12747a359a 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -471,6 +471,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */ { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, { KE_IGNORE, 0x6E, }, /* Low Battery notification */ + { KE_KEY, 0x71, { KEY_F13 } }, /* General-purpose button */ { KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */ { KE_KEY, 0x7c, { KEY_MICMUTE } }, { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 982f0cc8270c..43bb15e05529 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -61,6 +61,7 @@ MODULE_LICENSE("GPL"); #define NOTIFY_KBD_BRTDWN 0xc5 #define NOTIFY_KBD_BRTTOGGLE 0xc7 #define NOTIFY_KBD_FBM 0x99 +#define NOTIFY_KBD_TTP 0xae #define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0) @@ -81,6 +82,10 @@ MODULE_LICENSE("GPL"); #define ASUS_FAN_BOOST_MODE_SILENT_MASK 0x02 #define ASUS_FAN_BOOST_MODES_MASK 0x03 +#define ASUS_THROTTLE_THERMAL_POLICY_DEFAULT 0 +#define ASUS_THROTTLE_THERMAL_POLICY_OVERBOOST 1 +#define ASUS_THROTTLE_THERMAL_POLICY_SILENT 2 + #define USB_INTEL_XUSB2PR 0xD0 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31 @@ -198,6 +203,9 @@ struct asus_wmi { u8 fan_boost_mode_mask; u8 fan_boost_mode; + bool throttle_thermal_policy_available; + u8 throttle_thermal_policy_mode; + // The RSOC controls the maximum charging percentage. bool battery_rsoc_available; @@ -1718,6 +1726,107 @@ static ssize_t fan_boost_mode_store(struct device *dev, // Fan boost mode: 0 - normal, 1 - overboost, 2 - silent static DEVICE_ATTR_RW(fan_boost_mode); +/* Throttle thermal policy ****************************************************/ + +static int throttle_thermal_policy_check_present(struct asus_wmi *asus) +{ + u32 result; + int err; + + asus->throttle_thermal_policy_available = false; + + err = asus_wmi_get_devstate(asus, + ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY, + &result); + if (err) { + if (err == -ENODEV) + return 0; + return err; + } + + if (result & ASUS_WMI_DSTS_PRESENCE_BIT) + asus->throttle_thermal_policy_available = true; + + return 0; +} + +static int throttle_thermal_policy_write(struct asus_wmi *asus) +{ + int err; + u8 value; + u32 retval; + + value = asus->throttle_thermal_policy_mode; + + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY, + value, &retval); + if (err) { + pr_warn("Failed to set throttle thermal policy: %d\n", err); + return err; + } + + if (retval != 1) { + pr_warn("Failed to set throttle thermal policy (retval): 0x%x\n", + retval); + return -EIO; + } + + return 0; +} + +static int throttle_thermal_policy_set_default(struct asus_wmi *asus) +{ + if (!asus->throttle_thermal_policy_available) + return 0; + + asus->throttle_thermal_policy_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT; + return throttle_thermal_policy_write(asus); +} + +static int throttle_thermal_policy_switch_next(struct asus_wmi *asus) +{ + u8 new_mode = asus->throttle_thermal_policy_mode + 1; + + if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT) + new_mode = ASUS_THROTTLE_THERMAL_POLICY_DEFAULT; + + asus->throttle_thermal_policy_mode = new_mode; + return throttle_thermal_policy_write(asus); +} + +static ssize_t throttle_thermal_policy_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + u8 mode = asus->throttle_thermal_policy_mode; + + return scnprintf(buf, PAGE_SIZE, "%d\n", mode); +} + +static ssize_t throttle_thermal_policy_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int result; + u8 new_mode; + struct asus_wmi *asus = dev_get_drvdata(dev); + + result = kstrtou8(buf, 10, &new_mode); + if (result < 0) + return result; + + if (new_mode > ASUS_THROTTLE_THERMAL_POLICY_SILENT) + return -EINVAL; + + asus->throttle_thermal_policy_mode = new_mode; + throttle_thermal_policy_write(asus); + + return count; +} + +// Throttle thermal policy: 0 - default, 1 - overboost, 2 - silent +static DEVICE_ATTR_RW(throttle_thermal_policy); + /* Backlight ******************************************************************/ static int read_backlight_power(struct asus_wmi *asus) @@ -1999,6 +2108,11 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) return; } + if (asus->throttle_thermal_policy_available && code == NOTIFY_KBD_TTP) { + throttle_thermal_policy_switch_next(asus); + return; + } + if (is_display_toggle(code) && asus->driver->quirks->no_display_toggle) return; @@ -2149,6 +2263,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_lid_resume.attr, &dev_attr_als_enable.attr, &dev_attr_fan_boost_mode.attr, + &dev_attr_throttle_thermal_policy.attr, NULL }; @@ -2172,6 +2287,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_ALS_ENABLE; else if (attr == &dev_attr_fan_boost_mode.attr) ok = asus->fan_boost_mode_available; + else if (attr == &dev_attr_throttle_thermal_policy.attr) + ok = asus->throttle_thermal_policy_available; if (devid != -1) ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); @@ -2431,6 +2548,12 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_fan_boost_mode; + err = throttle_thermal_policy_check_present(asus); + if (err) + goto fail_throttle_thermal_policy; + else + throttle_thermal_policy_set_default(asus); + err = asus_wmi_sysfs_init(asus->platform_device); if (err) goto fail_sysfs; @@ -2515,6 +2638,7 @@ fail_hwmon: fail_input: asus_wmi_sysfs_exit(asus->platform_device); fail_sysfs: +fail_throttle_thermal_policy: fail_fan_boost_mode: fail_platform: kfree(asus); diff --git a/drivers/platform/x86/intel-uncore-frequency.c b/drivers/platform/x86/intel-uncore-frequency.c new file mode 100644 index 000000000000..2b1a0734c3f8 --- /dev/null +++ b/drivers/platform/x86/intel-uncore-frequency.c @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Uncore Frequency Setting + * Copyright (c) 2019, Intel Corporation. + * All rights reserved. + * + * Provide interface to set MSR 620 at a granularity of per die. On CPU online, + * one control CPU is identified per die to read/write limit. This control CPU + * is changed, if the CPU state is changed to offline. When the last CPU is + * offline in a die then remove the sysfs object for that die. + * The majority of actual code is related to sysfs create and read/write + * attributes. + * + * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> + */ + +#include <linux/cpu.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/suspend.h> +#include <asm/cpu_device_id.h> +#include <asm/intel-family.h> + +#define MSR_UNCORE_RATIO_LIMIT 0x620 +#define UNCORE_FREQ_KHZ_MULTIPLIER 100000 + +/** + * struct uncore_data - Encapsulate all uncore data + * @stored_uncore_data: Last user changed MSR 620 value, which will be restored + * on system resume. + * @initial_min_freq_khz: Sampled minimum uncore frequency at driver init + * @initial_max_freq_khz: Sampled maximum uncore frequency at driver init + * @control_cpu: Designated CPU for a die to read/write + * @valid: Mark the data valid/invalid + * + * This structure is used to encapsulate all data related to uncore sysfs + * settings for a die/package. + */ +struct uncore_data { + struct kobject kobj; + u64 stored_uncore_data; + u32 initial_min_freq_khz; + u32 initial_max_freq_khz; + int control_cpu; + bool valid; +}; + +#define to_uncore_data(a) container_of(a, struct uncore_data, kobj) + +/* Max instances for uncore data, one for each die */ +static int uncore_max_entries __read_mostly; +/* Storage for uncore data for all instances */ +static struct uncore_data *uncore_instances; +/* Root of the all uncore sysfs kobjs */ +struct kobject uncore_root_kobj; +/* Stores the CPU mask of the target CPUs to use during uncore read/write */ +static cpumask_t uncore_cpu_mask; +/* CPU online callback register instance */ +static enum cpuhp_state uncore_hp_state __read_mostly; +/* Mutex to control all mutual exclusions */ +static DEFINE_MUTEX(uncore_lock); + +struct uncore_attr { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, + struct attribute *attr, char *buf); + ssize_t (*store)(struct kobject *kobj, + struct attribute *attr, const char *c, ssize_t count); +}; + +#define define_one_uncore_ro(_name) \ +static struct uncore_attr _name = \ +__ATTR(_name, 0444, show_##_name, NULL) + +#define define_one_uncore_rw(_name) \ +static struct uncore_attr _name = \ +__ATTR(_name, 0644, show_##_name, store_##_name) + +#define show_uncore_data(member_name) \ + static ssize_t show_##member_name(struct kobject *kobj, \ + struct attribute *attr, \ + char *buf) \ + { \ + struct uncore_data *data = to_uncore_data(kobj); \ + return scnprintf(buf, PAGE_SIZE, "%u\n", \ + data->member_name); \ + } \ + define_one_uncore_ro(member_name) + +show_uncore_data(initial_min_freq_khz); +show_uncore_data(initial_max_freq_khz); + +/* Common function to read MSR 0x620 and read min/max */ +static int uncore_read_ratio(struct uncore_data *data, unsigned int *min, + unsigned int *max) +{ + u64 cap; + int ret; + + ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, &cap); + if (ret) + return ret; + + *max = (cap & 0x7F) * UNCORE_FREQ_KHZ_MULTIPLIER; + *min = ((cap & GENMASK(14, 8)) >> 8) * UNCORE_FREQ_KHZ_MULTIPLIER; + + return 0; +} + +/* Common function to set min/max ratios to be used by sysfs callbacks */ +static int uncore_write_ratio(struct uncore_data *data, unsigned int input, + int set_max) +{ + int ret; + u64 cap; + + mutex_lock(&uncore_lock); + + input /= UNCORE_FREQ_KHZ_MULTIPLIER; + if (!input || input > 0x7F) { + ret = -EINVAL; + goto finish_write; + } + + ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, &cap); + if (ret) + goto finish_write; + + if (set_max) { + cap &= ~0x7F; + cap |= input; + } else { + cap &= ~GENMASK(14, 8); + cap |= (input << 8); + } + + ret = wrmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, cap); + if (ret) + goto finish_write; + + data->stored_uncore_data = cap; + +finish_write: + mutex_unlock(&uncore_lock); + + return ret; +} + +static ssize_t store_min_max_freq_khz(struct kobject *kobj, + struct attribute *attr, + const char *buf, ssize_t count, + int min_max) +{ + struct uncore_data *data = to_uncore_data(kobj); + unsigned int input; + + if (kstrtouint(buf, 10, &input)) + return -EINVAL; + + uncore_write_ratio(data, input, min_max); + + return count; +} + +static ssize_t show_min_max_freq_khz(struct kobject *kobj, + struct attribute *attr, + char *buf, int min_max) +{ + struct uncore_data *data = to_uncore_data(kobj); + unsigned int min, max; + int ret; + + mutex_lock(&uncore_lock); + ret = uncore_read_ratio(data, &min, &max); + mutex_unlock(&uncore_lock); + if (ret) + return ret; + + if (min_max) + return sprintf(buf, "%u\n", max); + + return sprintf(buf, "%u\n", min); +} + +#define store_uncore_min_max(name, min_max) \ + static ssize_t store_##name(struct kobject *kobj, \ + struct attribute *attr, \ + const char *buf, ssize_t count) \ + { \ + \ + return store_min_max_freq_khz(kobj, attr, buf, count, \ + min_max); \ + } + +#define show_uncore_min_max(name, min_max) \ + static ssize_t show_##name(struct kobject *kobj, \ + struct attribute *attr, char *buf) \ + { \ + \ + return show_min_max_freq_khz(kobj, attr, buf, min_max); \ + } + +store_uncore_min_max(min_freq_khz, 0); +store_uncore_min_max(max_freq_khz, 1); + +show_uncore_min_max(min_freq_khz, 0); +show_uncore_min_max(max_freq_khz, 1); + +define_one_uncore_rw(min_freq_khz); +define_one_uncore_rw(max_freq_khz); + +static struct attribute *uncore_attrs[] = { + &initial_min_freq_khz.attr, + &initial_max_freq_khz.attr, + &max_freq_khz.attr, + &min_freq_khz.attr, + NULL +}; + +static struct kobj_type uncore_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .default_attrs = uncore_attrs, +}; + +static struct kobj_type uncore_root_ktype = { + .sysfs_ops = &kobj_sysfs_ops, +}; + +/* Caller provides protection */ +static struct uncore_data *uncore_get_instance(unsigned int cpu) +{ + int id = topology_logical_die_id(cpu); + + if (id >= 0 && id < uncore_max_entries) + return &uncore_instances[id]; + + return NULL; +} + +static void uncore_add_die_entry(int cpu) +{ + struct uncore_data *data; + + mutex_lock(&uncore_lock); + data = uncore_get_instance(cpu); + if (!data) { + mutex_unlock(&uncore_lock); + return; + } + + if (data->valid) { + /* control cpu changed */ + data->control_cpu = cpu; + } else { + char str[64]; + int ret; + + memset(data, 0, sizeof(*data)); + sprintf(str, "package_%02d_die_%02d", + topology_physical_package_id(cpu), + topology_die_id(cpu)); + + uncore_read_ratio(data, &data->initial_min_freq_khz, + &data->initial_max_freq_khz); + + ret = kobject_init_and_add(&data->kobj, &uncore_ktype, + &uncore_root_kobj, str); + if (!ret) { + data->control_cpu = cpu; + data->valid = true; + } + } + mutex_unlock(&uncore_lock); +} + +/* Last CPU in this die is offline, so remove sysfs entries */ +static void uncore_remove_die_entry(int cpu) +{ + struct uncore_data *data; + + mutex_lock(&uncore_lock); + data = uncore_get_instance(cpu); + if (data) { + kobject_put(&data->kobj); + data->control_cpu = -1; + data->valid = false; + } + mutex_unlock(&uncore_lock); +} + +static int uncore_event_cpu_online(unsigned int cpu) +{ + int target; + + /* Check if there is an online cpu in the package for uncore MSR */ + target = cpumask_any_and(&uncore_cpu_mask, topology_die_cpumask(cpu)); + if (target < nr_cpu_ids) + return 0; + + /* Use this CPU on this die as a control CPU */ + cpumask_set_cpu(cpu, &uncore_cpu_mask); + uncore_add_die_entry(cpu); + + return 0; +} + +static int uncore_event_cpu_offline(unsigned int cpu) +{ + int target; + + /* Check if existing cpu is used for uncore MSRs */ + if (!cpumask_test_and_clear_cpu(cpu, &uncore_cpu_mask)) + return 0; + + /* Find a new cpu to set uncore MSR */ + target = cpumask_any_but(topology_die_cpumask(cpu), cpu); + + if (target < nr_cpu_ids) { + cpumask_set_cpu(target, &uncore_cpu_mask); + uncore_add_die_entry(target); + } else { + uncore_remove_die_entry(cpu); + } + + return 0; +} + +static int uncore_pm_notify(struct notifier_block *nb, unsigned long mode, + void *_unused) +{ + int cpu; + + switch (mode) { + case PM_POST_HIBERNATION: + case PM_POST_RESTORE: + case PM_POST_SUSPEND: + for_each_cpu(cpu, &uncore_cpu_mask) { + struct uncore_data *data; + int ret; + + data = uncore_get_instance(cpu); + if (!data || !data->valid || !data->stored_uncore_data) + continue; + + ret = wrmsrl_on_cpu(cpu, MSR_UNCORE_RATIO_LIMIT, + data->stored_uncore_data); + if (ret) + return ret; + } + break; + default: + break; + } + return 0; +} + +static struct notifier_block uncore_pm_nb = { + .notifier_call = uncore_pm_notify, +}; + +#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, } + +static const struct x86_cpu_id intel_uncore_cpu_ids[] = { + ICPU(INTEL_FAM6_BROADWELL_G), + ICPU(INTEL_FAM6_BROADWELL_X), + ICPU(INTEL_FAM6_BROADWELL_D), + ICPU(INTEL_FAM6_SKYLAKE_X), + ICPU(INTEL_FAM6_ICELAKE_X), + ICPU(INTEL_FAM6_ICELAKE_D), + {} +}; + +static int __init intel_uncore_init(void) +{ + const struct x86_cpu_id *id; + int ret; + + id = x86_match_cpu(intel_uncore_cpu_ids); + if (!id) + return -ENODEV; + + uncore_max_entries = topology_max_packages() * + topology_max_die_per_package(); + uncore_instances = kcalloc(uncore_max_entries, + sizeof(*uncore_instances), GFP_KERNEL); + if (!uncore_instances) + return -ENOMEM; + + ret = kobject_init_and_add(&uncore_root_kobj, &uncore_root_ktype, + &cpu_subsys.dev_root->kobj, + "intel_uncore_frequency"); + if (ret) + goto err_free; + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, + "platform/x86/uncore-freq:online", + uncore_event_cpu_online, + uncore_event_cpu_offline); + if (ret < 0) + goto err_rem_kobj; + + uncore_hp_state = ret; + + ret = register_pm_notifier(&uncore_pm_nb); + if (ret) + goto err_rem_state; + + return 0; + +err_rem_state: + cpuhp_remove_state(uncore_hp_state); +err_rem_kobj: + kobject_put(&uncore_root_kobj); +err_free: + kfree(uncore_instances); + + return ret; +} +module_init(intel_uncore_init) + +static void __exit intel_uncore_exit(void) +{ + int i; + + unregister_pm_notifier(&uncore_pm_nb); + cpuhp_remove_state(uncore_hp_state); + for (i = 0; i < uncore_max_entries; ++i) { + if (uncore_instances[i].valid) + kobject_put(&uncore_instances[i].kobj); + } + kobject_put(&uncore_root_kobj); + kfree(uncore_instances); +} +module_exit(intel_uncore_exit) + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Uncore Frequency Limits Driver"); diff --git a/drivers/platform/x86/intel_atomisp2_pm.c b/drivers/platform/x86/intel_atomisp2_pm.c index b0f421fea2a5..805fc0d8515c 100644 --- a/drivers/platform/x86/intel_atomisp2_pm.c +++ b/drivers/platform/x86/intel_atomisp2_pm.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Dummy driver for Intel's Image Signal Processor found on Bay and Cherry - * Trail devices. The sole purpose of this driver is to allow the ISP to - * be put in D3. + * Dummy driver for Intel's Image Signal Processor found on Bay Trail + * and Cherry Trail devices. The sole purpose of this driver is to allow + * the ISP to be put in D3. * * Copyright (C) 2018 Hans de Goede <hdegoede@redhat.com> * @@ -36,8 +36,7 @@ static int isp_set_power(struct pci_dev *dev, bool enable) { unsigned long timeout; - u32 val = enable ? ISPSSPM0_IUNIT_POWER_ON : - ISPSSPM0_IUNIT_POWER_OFF; + u32 val = enable ? ISPSSPM0_IUNIT_POWER_ON : ISPSSPM0_IUNIT_POWER_OFF; /* Write to ISPSSPM0 bit[1:0] to power on/off the IUNIT */ iosf_mbi_modify(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM0, @@ -45,29 +44,25 @@ static int isp_set_power(struct pci_dev *dev, bool enable) /* * There should be no IUNIT access while power-down is - * in progress HW sighting: 4567865 + * in progress. HW sighting: 4567865. * Wait up to 50 ms for the IUNIT to shut down. * And we do the same for power on. */ timeout = jiffies + msecs_to_jiffies(50); - while (1) { + do { u32 tmp; /* Wait until ISPSSPM0 bit[25:24] shows the right value */ iosf_mbi_read(BT_MBI_UNIT_PMC, MBI_REG_READ, ISPSSPM0, &tmp); tmp = (tmp & ISPSSPM0_ISPSSS_MASK) >> ISPSSPM0_ISPSSS_OFFSET; if (tmp == val) - break; + return 0; - if (time_after(jiffies, timeout)) { - dev_err(&dev->dev, "IUNIT power-%s timeout.\n", - enable ? "on" : "off"); - return -EBUSY; - } usleep_range(1000, 2000); - } + } while (time_before(jiffies, timeout)); - return 0; + dev_err(&dev->dev, "IUNIT power-%s timeout.\n", enable ? "on" : "off"); + return -EBUSY; } static int isp_probe(struct pci_dev *dev, const struct pci_device_id *id) diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index 292bace83f1e..6f436836fe50 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -146,9 +146,10 @@ static int mid_pb_probe(struct platform_device *pdev) input_set_capability(input, EV_KEY, KEY_POWER); - ddata = (struct mid_pb_ddata *)id->driver_data; + ddata = devm_kmemdup(&pdev->dev, (void *)id->driver_data, + sizeof(*ddata), GFP_KERNEL); if (!ddata) - return -ENODATA; + return -ENOMEM; ddata->dev = &pdev->dev; ddata->irq = irq; diff --git a/drivers/platform/x86/intel_pmc_core.c b/drivers/platform/x86/intel_pmc_core.c index 571b4754477c..144faa8bad3d 100644 --- a/drivers/platform/x86/intel_pmc_core.c +++ b/drivers/platform/x86/intel_pmc_core.c @@ -49,7 +49,7 @@ static const struct pmc_bit_map spt_pll_map[] = { {"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[] = { @@ -69,7 +69,7 @@ static const struct pmc_bit_map spt_mphy_map[] = { {"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[] = { @@ -113,7 +113,12 @@ static const struct pmc_bit_map spt_pfear_map[] = { {"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_bit_map *ext_spt_pfear_map[] = { + spt_pfear_map, + NULL }; static const struct pmc_bit_map spt_ltr_show_map[] = { @@ -142,7 +147,7 @@ static const struct pmc_bit_map spt_ltr_show_map[] = { }; static const struct pmc_reg_map spt_reg_map = { - .pfear_sts = spt_pfear_map, + .pfear_sts = ext_spt_pfear_map, .mphy_sts = spt_mphy_map, .pll_sts = spt_pll_map, .ltr_show_sts = spt_ltr_show_map, @@ -186,7 +191,10 @@ static const struct pmc_bit_map cnp_pfear_map[] = { {"SDX", BIT(4)}, {"SPE", BIT(5)}, {"Fuse", BIT(6)}, - /* Reserved for Cannon Lake but valid for Ice Lake and Comet Lake */ + /* + * Reserved for Cannon Lake but valid for Ice Lake, Comet Lake, + * Tiger Lake and Elkhart Lake. + */ {"SBR8", BIT(7)}, {"CSME_FSC", BIT(0)}, @@ -230,11 +238,22 @@ static const struct pmc_bit_map cnp_pfear_map[] = { {"HDA_PGD4", BIT(2)}, {"HDA_PGD5", BIT(3)}, {"HDA_PGD6", BIT(4)}, - /* Reserved for Cannon Lake but valid for Ice Lake and Comet Lake */ + /* + * Reserved for Cannon Lake but valid for Ice Lake, Comet Lake, + * Tiger Lake and ELkhart Lake. + */ {"PSF6", BIT(5)}, {"PSF7", BIT(6)}, {"PSF8", BIT(7)}, + {} +}; + +static const struct pmc_bit_map *ext_cnp_pfear_map[] = { + cnp_pfear_map, + NULL +}; +static const struct pmc_bit_map icl_pfear_map[] = { /* Ice Lake generation onwards only */ {"RES_65", BIT(0)}, {"RES_66", BIT(1)}, @@ -247,6 +266,30 @@ static const struct pmc_bit_map cnp_pfear_map[] = { {} }; +static const struct pmc_bit_map *ext_icl_pfear_map[] = { + cnp_pfear_map, + icl_pfear_map, + NULL +}; + +static const struct pmc_bit_map tgl_pfear_map[] = { + /* Tiger Lake and Elkhart Lake generation onwards only */ + {"PSF9", BIT(0)}, + {"RES_66", BIT(1)}, + {"RES_67", BIT(2)}, + {"RES_68", BIT(3)}, + {"RES_69", BIT(4)}, + {"RES_70", BIT(5)}, + {"TBTLSX", BIT(6)}, + {} +}; + +static const struct pmc_bit_map *ext_tgl_pfear_map[] = { + cnp_pfear_map, + tgl_pfear_map, + NULL +}; + static const struct pmc_bit_map cnp_slps0_dbg0_map[] = { {"AUDIO_D3", BIT(0)}, {"OTG_D3", BIT(1)}, @@ -300,7 +343,7 @@ static const struct pmc_bit_map *cnp_slps0_dbg_maps[] = { cnp_slps0_dbg0_map, cnp_slps0_dbg1_map, cnp_slps0_dbg2_map, - NULL, + NULL }; static const struct pmc_bit_map cnp_ltr_show_map[] = { @@ -334,7 +377,7 @@ static const struct pmc_bit_map cnp_ltr_show_map[] = { }; static const struct pmc_reg_map cnp_reg_map = { - .pfear_sts = cnp_pfear_map, + .pfear_sts = ext_cnp_pfear_map, .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, .slps0_dbg_maps = cnp_slps0_dbg_maps, .ltr_show_sts = cnp_ltr_show_map, @@ -350,7 +393,7 @@ static const struct pmc_reg_map cnp_reg_map = { }; static const struct pmc_reg_map icl_reg_map = { - .pfear_sts = cnp_pfear_map, + .pfear_sts = ext_icl_pfear_map, .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, .slps0_dbg_maps = cnp_slps0_dbg_maps, .ltr_show_sts = cnp_ltr_show_map, @@ -365,18 +408,29 @@ static const struct pmc_reg_map icl_reg_map = { .ltr_ignore_max = ICL_NUM_IP_IGN_ALLO |