summaryrefslogtreecommitdiff
path: root/drivers/platform/x86/intel
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/intel')
-rw-r--r--drivers/platform/x86/intel/Kconfig47
-rw-r--r--drivers/platform/x86/intel/Makefile9
-rw-r--r--drivers/platform/x86/intel/chtwc_int33fe.c (renamed from drivers/platform/x86/intel/int33fe/intel_cht_int33fe_typec.c)65
-rw-r--r--drivers/platform/x86/intel/hid.c7
-rw-r--r--drivers/platform/x86/intel/int33fe/Kconfig24
-rw-r--r--drivers/platform/x86/intel/int33fe/Makefile5
-rw-r--r--drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.c118
-rw-r--r--drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.h41
-rw-r--r--drivers/platform/x86/intel/int33fe/intel_cht_int33fe_microb.c61
-rw-r--r--drivers/platform/x86/intel/int3472/discrete.c5
-rw-r--r--drivers/platform/x86/intel/sdsi.c574
-rw-r--r--drivers/platform/x86/intel/uncore-frequency.c452
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/Kconfig21
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/Makefile9
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c252
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h62
-rw-r--r--drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c272
-rw-r--r--drivers/platform/x86/intel/vbtn.c7
-rw-r--r--drivers/platform/x86/intel/vsec.c12
19 files changed, 1308 insertions, 735 deletions
diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig
index 8e65086bb6c8..1f01a8a23c57 100644
--- a/drivers/platform/x86/intel/Kconfig
+++ b/drivers/platform/x86/intel/Kconfig
@@ -5,13 +5,14 @@
source "drivers/platform/x86/intel/atomisp2/Kconfig"
source "drivers/platform/x86/intel/int1092/Kconfig"
-source "drivers/platform/x86/intel/int33fe/Kconfig"
source "drivers/platform/x86/intel/int3472/Kconfig"
source "drivers/platform/x86/intel/pmc/Kconfig"
source "drivers/platform/x86/intel/pmt/Kconfig"
source "drivers/platform/x86/intel/speed_select_if/Kconfig"
source "drivers/platform/x86/intel/telemetry/Kconfig"
source "drivers/platform/x86/intel/wmi/Kconfig"
+source "drivers/platform/x86/intel/uncore-frequency/Kconfig"
+
config INTEL_HID_EVENT
tristate "Intel HID Event"
@@ -89,6 +90,26 @@ config INTEL_CHTDC_TI_PWRBTN
To compile this driver as a module, choose M here: the module
will be called intel_chtdc_ti_pwrbtn.
+config INTEL_CHTWC_INT33FE
+ tristate "Intel Cherry Trail Whiskey Cove ACPI INT33FE Driver"
+ depends on X86 && ACPI && I2C && REGULATOR
+ depends on CHARGER_BQ24190=y || (CHARGER_BQ24190=m && m)
+ depends on USB_ROLES_INTEL_XHCI=y || (USB_ROLES_INTEL_XHCI=m && m)
+ depends on TYPEC_MUX_PI3USB30532=y || (TYPEC_MUX_PI3USB30532=m && m)
+ help
+ This driver add support for the Intel Cherry Trail Whiskey Cove
+ INT33FE ACPI device found on the GPD win and the GPD pocket.
+
+ The INT33FE ACPI device on these mini laptops contains I2cSerialBusV2
+ resources for a MAX17042 Fuel Gauge, FUSB302 USB Type-C Controller
+ and PI3USB30532 USB switch.
+ This driver instantiates i2c-clients for these, so that standard
+ i2c drivers for these chips can bind to the them.
+
+ If you enable this driver it is advised to also select
+ CONFIG_TYPEC_FUSB302=m, CONFIG_TYPEC_MUX_PI3USB30532=m and
+ CONFIG_BATTERY_MAX17042=m.
+
config INTEL_ISHTP_ECLITE
tristate "Intel ISHTP eclite controller Driver"
depends on INTEL_ISH_HID
@@ -134,6 +155,18 @@ config INTEL_RST
firmware will copy the memory contents back to RAM and resume the OS
as usual.
+config INTEL_SDSI
+ tristate "Intel Software Defined Silicon Driver"
+ depends on INTEL_VSEC
+ depends on X86_64
+ help
+ This driver enables access to the Intel Software Defined Silicon
+ interface used to provision silicon features with an authentication
+ certificate and capability license.
+
+ To compile this driver as a module, choose M here: the module will
+ be called intel_sdsi.
+
config INTEL_SMARTCONNECT
tristate "Intel Smart Connect disabling driver"
depends on ACPI
@@ -159,18 +192,6 @@ config INTEL_TURBO_MAX_3
This driver is only required when the system is not using Hardware
P-States (HWP). In HWP mode, priority can be read from ACPI tables.
-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.
-
config INTEL_VSEC
tristate "Intel Vendor Specific Extended Capabilities Driver"
depends on PCI
diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile
index 35f2066578b2..c61bc3e97121 100644
--- a/drivers/platform/x86/intel/Makefile
+++ b/drivers/platform/x86/intel/Makefile
@@ -6,13 +6,14 @@
obj-$(CONFIG_INTEL_ATOMISP2_PDX86) += atomisp2/
obj-$(CONFIG_INTEL_SAR_INT1092) += int1092/
-obj-$(CONFIG_INTEL_CHT_INT33FE) += int33fe/
obj-$(CONFIG_INTEL_SKL_INT3472) += int3472/
obj-$(CONFIG_INTEL_PMC_CORE) += pmc/
obj-$(CONFIG_INTEL_PMT_CLASS) += pmt/
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += speed_select_if/
obj-$(CONFIG_INTEL_TELEMETRY) += telemetry/
obj-$(CONFIG_INTEL_WMI) += wmi/
+obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL) += uncore-frequency/
+
# Intel input drivers
intel-hid-y := hid.o
@@ -26,6 +27,8 @@ intel_int0002_vgpio-y := int0002_vgpio.o
obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o
intel_oaktrail-y := oaktrail.o
obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
+intel_sdsi-y := sdsi.o
+obj-$(CONFIG_INTEL_SDSI) += intel_sdsi.o
intel_vsec-y := vsec.o
obj-$(CONFIG_INTEL_VSEC) += intel_vsec.o
@@ -36,6 +39,8 @@ intel_crystal_cove_charger-y := crystal_cove_charger.o
obj-$(CONFIG_X86_ANDROID_TABLETS) += intel_crystal_cove_charger.o
intel_chtdc_ti_pwrbtn-y := chtdc_ti_pwrbtn.o
obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o
+intel_chtwc_int33fe-y := chtwc_int33fe.o
+obj-$(CONFIG_INTEL_CHTWC_INT33FE) += intel_chtwc_int33fe.o
intel_mrfld_pwrbtn-y := mrfld_pwrbtn.o
obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o
intel_punit_ipc-y := punit_ipc.o
@@ -48,5 +53,3 @@ intel-smartconnect-y := smartconnect.o
obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o
intel_turbo_max_3-y := turbo_max_3.o
obj-$(CONFIG_INTEL_TURBO_MAX_3) += intel_turbo_max_3.o
-intel-uncore-frequency-y := uncore-frequency.o
-obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL) += intel-uncore-frequency.o
diff --git a/drivers/platform/x86/intel/int33fe/intel_cht_int33fe_typec.c b/drivers/platform/x86/intel/chtwc_int33fe.c
index d59544167430..0de509fbf020 100644
--- a/drivers/platform/x86/intel/int33fe/intel_cht_int33fe_typec.c
+++ b/drivers/platform/x86/intel/chtwc_int33fe.c
@@ -17,6 +17,7 @@
* for these chips can bind to the them.
*/
+#include <linux/dmi.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
@@ -26,7 +27,12 @@
#include <linux/slab.h>
#include <linux/usb/pd.h>
-#include "intel_cht_int33fe_common.h"
+struct cht_int33fe_data {
+ struct i2c_client *battery_fg;
+ struct i2c_client *fusb302;
+ struct i2c_client *pi3usb30532;
+ struct fwnode_handle *dp;
+};
/*
* Grrr, I severely dislike buggy BIOS-es. At least one BIOS enumerates
@@ -272,15 +278,44 @@ cht_int33fe_register_max17047(struct device *dev, struct cht_int33fe_data *data)
return PTR_ERR_OR_ZERO(data->battery_fg);
}
-int cht_int33fe_typec_probe(struct cht_int33fe_data *data)
+static const struct dmi_system_id cht_int33fe_typec_ids[] = {
+ {
+ /*
+ * GPD win / GPD pocket mini laptops
+ *
+ * This DMI match may not seem unique, but it is. In the 67000+
+ * DMI decode dumps from linux-hardware.org only 116 have
+ * board_vendor set to "AMI Corporation" and of those 116 only
+ * the GPD win's and pocket's board_name is "Default string".
+ */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Default string"),
+ DMI_EXACT_MATCH(DMI_BOARD_SERIAL, "Default string"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Default string"),
+ },
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(dmi, cht_int33fe_typec_ids);
+
+static int cht_int33fe_typec_probe(struct platform_device *pdev)
{
- struct device *dev = data->dev;
struct i2c_board_info board_info;
+ struct device *dev = &pdev->dev;
+ struct cht_int33fe_data *data;
struct fwnode_handle *fwnode;
struct regulator *regulator;
int fusb302_irq;
int ret;
+ if (!dmi_check_system(cht_int33fe_typec_ids))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
/*
* We expect the WC PMIC to be paired with a TI bq24292i charger-IC.
* We check for the bq24292i vbus regulator here, this has 2 purposes:
@@ -368,8 +403,10 @@ out_remove_nodes:
return ret;
}
-int cht_int33fe_typec_remove(struct cht_int33fe_data *data)
+static int cht_int33fe_typec_remove(struct platform_device *pdev)
{
+ struct cht_int33fe_data *data = platform_get_drvdata(pdev);
+
i2c_unregister_device(data->pi3usb30532);
i2c_unregister_device(data->fusb302);
i2c_unregister_device(data->battery_fg);
@@ -378,3 +415,23 @@ int cht_int33fe_typec_remove(struct cht_int33fe_data *data)
return 0;
}
+
+static const struct acpi_device_id cht_int33fe_acpi_ids[] = {
+ { "INT33FE", },
+ { }
+};
+
+static struct platform_driver cht_int33fe_typec_driver = {
+ .driver = {
+ .name = "Intel Cherry Trail ACPI INT33FE Type-C driver",
+ .acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids),
+ },
+ .probe = cht_int33fe_typec_probe,
+ .remove = cht_int33fe_typec_remove,
+};
+
+module_platform_driver(cht_int33fe_typec_driver);
+
+MODULE_DESCRIPTION("Intel Cherry Trail ACPI INT33FE Type-C pseudo device driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c
index 13f8cf70b9ae..2def562c6e1d 100644
--- a/drivers/platform/x86/intel/hid.c
+++ b/drivers/platform/x86/intel/hid.c
@@ -726,12 +726,9 @@ static acpi_status __init
check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv)
{
const struct acpi_device_id *ids = context;
- struct acpi_device *dev;
+ struct acpi_device *dev = acpi_fetch_acpi_dev(handle);
- if (acpi_bus_get_device(handle, &dev) != 0)
- return AE_OK;
-
- if (acpi_match_device_ids(dev, ids) == 0)
+ if (dev && acpi_match_device_ids(dev, ids) == 0)
if (!IS_ERR_OR_NULL(acpi_create_platform_device(dev, NULL)))
dev_info(&dev->dev,
"intel-hid: created platform device\n");
diff --git a/drivers/platform/x86/intel/int33fe/Kconfig b/drivers/platform/x86/intel/int33fe/Kconfig
deleted file mode 100644
index 2f7329a2e399..000000000000
--- a/drivers/platform/x86/intel/int33fe/Kconfig
+++ /dev/null
@@ -1,24 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-config INTEL_CHT_INT33FE
- tristate "Intel Cherry Trail ACPI INT33FE Driver"
- depends on X86 && ACPI && I2C && REGULATOR
- depends on CHARGER_BQ24190=y || (CHARGER_BQ24190=m && m)
- depends on USB_ROLES_INTEL_XHCI=y || (USB_ROLES_INTEL_XHCI=m && m)
- depends on TYPEC_MUX_PI3USB30532=y || (TYPEC_MUX_PI3USB30532=m && m)
- help
- This driver add support for the INT33FE ACPI device found on
- some Intel Cherry Trail devices.
-
- There are two kinds of INT33FE ACPI device possible: for hardware
- with USB Type-C and Micro-B connectors. This driver supports both.
-
- The INT33FE ACPI device has a CRS table with I2cSerialBusV2
- resources for Fuel Gauge Controller and (in the Type-C variant)
- FUSB302 USB Type-C Controller and PI3USB30532 USB switch.
- This driver instantiates i2c-clients for these, so that standard
- i2c drivers for these chips can bind to the them.
-
- If you enable this driver it is advised to also select
- CONFIG_BATTERY_BQ27XXX=m or CONFIG_BATTERY_BQ27XXX_I2C=m for Micro-B
- device and CONFIG_TYPEC_FUSB302=m and CONFIG_BATTERY_MAX17042=m
- for Type-C device.
diff --git a/drivers/platform/x86/intel/int33fe/Makefile b/drivers/platform/x86/intel/int33fe/Makefile
deleted file mode 100644
index 9456e3b37f6f..000000000000
--- a/drivers/platform/x86/intel/int33fe/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_INTEL_CHT_INT33FE) += intel_cht_int33fe.o
-intel_cht_int33fe-y := intel_cht_int33fe_common.o \
- intel_cht_int33fe_typec.o \
- intel_cht_int33fe_microb.o
diff --git a/drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.c b/drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.c
deleted file mode 100644
index 463222521e61..000000000000
--- a/drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.c
+++ /dev/null
@@ -1,118 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Common code for Intel Cherry Trail ACPI INT33FE pseudo device drivers
- * (USB Micro-B and Type-C connector variants).
- *
- * Copyright (c) 2019 Yauhen Kharuzhy <jekhor@gmail.com>
- */
-
-#include <linux/acpi.h>
-#include <linux/i2c.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include "intel_cht_int33fe_common.h"
-
-#define EXPECTED_PTYPE 4
-
-static int cht_int33fe_check_hw_type(struct device *dev)
-{
- unsigned long long ptyp;
- acpi_status status;
- int ret;
-
- status = acpi_evaluate_integer(ACPI_HANDLE(dev), "PTYP", NULL, &ptyp);
- if (ACPI_FAILURE(status)) {
- dev_err(dev, "Error getting PTYPE\n");
- return -ENODEV;
- }
-
- /*
- * The same ACPI HID is used for different configurations check PTYP
- * to ensure that we are dealing with the expected config.
- */
- if (ptyp != EXPECTED_PTYPE)
- return -ENODEV;
-
- /* Check presence of INT34D3 (hardware-rev 3) expected for ptype == 4 */
- if (!acpi_dev_present("INT34D3", "1", 3)) {
- dev_err(dev, "Error PTYPE == %d, but no INT34D3 device\n",
- EXPECTED_PTYPE);
- return -ENODEV;
- }
-
- ret = i2c_acpi_client_count(ACPI_COMPANION(dev));
- if (ret < 0)
- return ret;
-
- switch (ret) {
- case 2:
- return INT33FE_HW_MICROB;
- case 4:
- return INT33FE_HW_TYPEC;
- default:
- return -ENODEV;
- }
-}
-
-static int cht_int33fe_probe(struct platform_device *pdev)
-{
- struct cht_int33fe_data *data;
- struct device *dev = &pdev->dev;
- int ret;
-
- ret = cht_int33fe_check_hw_type(dev);
- if (ret < 0)
- return ret;
-
- data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- data->dev = dev;
-
- switch (ret) {
- case INT33FE_HW_MICROB:
- data->probe = cht_int33fe_microb_probe;
- data->remove = cht_int33fe_microb_remove;
- break;
-
- case INT33FE_HW_TYPEC:
- data->probe = cht_int33fe_typec_probe;
- data->remove = cht_int33fe_typec_remove;
- break;
- }
-
- platform_set_drvdata(pdev, data);
-
- return data->probe(data);
-}
-
-static int cht_int33fe_remove(struct platform_device *pdev)
-{
- struct cht_int33fe_data *data = platform_get_drvdata(pdev);
-
- return data->remove(data);
-}
-
-static const struct acpi_device_id cht_int33fe_acpi_ids[] = {
- { "INT33FE", },
- { }
-};
-MODULE_DEVICE_TABLE(acpi, cht_int33fe_acpi_ids);
-
-static struct platform_driver cht_int33fe_driver = {
- .driver = {
- .name = "Intel Cherry Trail ACPI INT33FE driver",
- .acpi_match_table = ACPI_PTR(cht_int33fe_acpi_ids),
- },
- .probe = cht_int33fe_probe,
- .remove = cht_int33fe_remove,
-};
-
-module_platform_driver(cht_int33fe_driver);
-
-MODULE_DESCRIPTION("Intel Cherry Trail ACPI INT33FE pseudo device driver");
-MODULE_AUTHOR("Yauhen Kharuzhy <jekhor@gmail.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.h b/drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.h
deleted file mode 100644
index 03cd45f4e8cb..000000000000
--- a/drivers/platform/x86/intel/int33fe/intel_cht_int33fe_common.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Common code for Intel Cherry Trail ACPI INT33FE pseudo device drivers
- * (USB Micro-B and Type-C connector variants), header file
- *
- * Copyright (c) 2019 Yauhen Kharuzhy <jekhor@gmail.com>
- */
-
-#ifndef _INTEL_CHT_INT33FE_COMMON_H
-#define _INTEL_CHT_INT33FE_COMMON_H
-
-#include <linux/device.h>
-#include <linux/fwnode.h>
-#include <linux/i2c.h>
-
-enum int33fe_hw_type {
- INT33FE_HW_MICROB,
- INT33FE_HW_TYPEC,
-};
-
-struct cht_int33fe_data {
- struct device *dev;
-
- int (*probe)(struct cht_int33fe_data *data);
- int (*remove)(struct cht_int33fe_data *data);
-
- struct i2c_client *battery_fg;
-
- /* Type-C only */
- struct i2c_client *fusb302;
- struct i2c_client *pi3usb30532;
-
- struct fwnode_handle *dp;
-};
-
-int cht_int33fe_microb_probe(struct cht_int33fe_data *data);
-int cht_int33fe_microb_remove(struct cht_int33fe_data *data);
-int cht_int33fe_typec_probe(struct cht_int33fe_data *data);
-int cht_int33fe_typec_remove(struct cht_int33fe_data *data);
-
-#endif /* _INTEL_CHT_INT33FE_COMMON_H */
diff --git a/drivers/platform/x86/intel/int33fe/intel_cht_int33fe_microb.c b/drivers/platform/x86/intel/int33fe/intel_cht_int33fe_microb.c
deleted file mode 100644
index 673f41cd14b5..000000000000
--- a/drivers/platform/x86/intel/int33fe/intel_cht_int33fe_microb.c
+++ /dev/null
@@ -1,61 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Intel Cherry Trail ACPI INT33FE pseudo device driver for devices with
- * USB Micro-B connector (e.g. without of FUSB302 USB Type-C controller)
- *
- * Copyright (C) 2019 Yauhen Kharuzhy <jekhor@gmail.com>
- *
- * At least one Intel Cherry Trail based device which ship with Windows 10
- * (Lenovo YogaBook YB1-X91L/F tablet), have this weird INT33FE ACPI device
- * with a CRS table with 2 I2cSerialBusV2 resources, for 2 different chips
- * attached to various i2c busses:
- * 1. The Whiskey Cove PMIC, which is also described by the INT34D3 ACPI device
- * 2. TI BQ27542 Fuel Gauge Controller
- *
- * So this driver is a stub / pseudo driver whose only purpose is to
- * instantiate i2c-client for battery fuel gauge, so that standard i2c driver
- * for these chip can bind to the it.
- */
-
-#include <linux/acpi.h>
-#include <linux/i2c.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/slab.h>
-#include <linux/usb/pd.h>
-
-#include "intel_cht_int33fe_common.h"
-
-static const char * const bq27xxx_suppliers[] = { "bq25890-charger" };
-
-static const struct property_entry bq27xxx_props[] = {
- PROPERTY_ENTRY_STRING_ARRAY("supplied-from", bq27xxx_suppliers),
- { }
-};
-
-static const struct software_node bq27xxx_node = {
- .properties = bq27xxx_props,
-};
-
-int cht_int33fe_microb_probe(struct cht_int33fe_data *data)
-{
- struct device *dev = data->dev;
- struct i2c_board_info board_info;
-
- memset(&board_info, 0, sizeof(board_info));
- strscpy(board_info.type, "bq27542", ARRAY_SIZE(board_info.type));
- board_info.dev_name = "bq27542";
- board_info.swnode = &bq27xxx_node;
- data->battery_fg = i2c_acpi_new_device(dev, 1, &board_info);
-
- return PTR_ERR_OR_ZERO(data->battery_fg);
-}
-
-int cht_int33fe_microb_remove(struct cht_int33fe_data *data)
-{
- i2c_unregister_device(data->battery_fg);
-
- return 0;
-}
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
index 5b514fa01a97..ed4c9d760757 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
@@ -112,7 +112,6 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
struct acpi_device *adev;
acpi_handle handle;
acpi_status status;
- int ret;
if (int3472->n_sensor_gpios >= INT3472_MAX_SENSOR_GPIOS) {
dev_warn(int3472->dev, "Too many GPIOs mapped\n");
@@ -139,8 +138,8 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347
if (ACPI_FAILURE(status))
return -EINVAL;
- ret = acpi_bus_get_device(handle, &adev);
- if (ret)
+ adev = acpi_fetch_acpi_dev(handle);
+ if (!adev)
return -ENODEV;
table_entry = &int3472->gpios.table[int3472->n_sensor_gpios];
diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c
new file mode 100644
index 000000000000..11d14cc0ff0a
--- /dev/null
+++ b/drivers/platform/x86/intel/sdsi.c
@@ -0,0 +1,574 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Software Defined Silicon driver
+ *
+ * Copyright (c) 2022, Intel Corporation.
+ * All Rights Reserved.
+ *
+ * Author: "David E. Box" <david.e.box@linux.intel.com>
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+
+#include "vsec.h"
+
+#define ACCESS_TYPE_BARID 2
+#define ACCESS_TYPE_LOCAL 3
+
+#define SDSI_MIN_SIZE_DWORDS 276
+#define SDSI_SIZE_CONTROL 8
+#define SDSI_SIZE_MAILBOX 1024
+#define SDSI_SIZE_REGS 72
+#define SDSI_SIZE_CMD sizeof(u64)
+
+/*
+ * Write messages are currently up to the size of the mailbox
+ * while read messages are up to 4 times the size of the
+ * mailbox, sent in packets
+ */
+#define SDSI_SIZE_WRITE_MSG SDSI_SIZE_MAILBOX
+#define SDSI_SIZE_READ_MSG (SDSI_SIZE_MAILBOX * 4)
+
+#define SDSI_ENABLED_FEATURES_OFFSET 16
+#define SDSI_ENABLED BIT(3)
+#define SDSI_SOCKET_ID_OFFSET 64
+#define SDSI_SOCKET_ID GENMASK(3, 0)
+
+#define SDSI_MBOX_CMD_SUCCESS 0x40
+#define SDSI_MBOX_CMD_TIMEOUT 0x80
+
+#define MBOX_TIMEOUT_US 2000
+#define MBOX_TIMEOUT_ACQUIRE_US 1000
+#define MBOX_POLLING_PERIOD_US 100
+#define MBOX_MAX_PACKETS 4
+
+#define MBOX_OWNER_NONE 0x00
+#define MBOX_OWNER_INBAND 0x01
+
+#define CTRL_RUN_BUSY BIT(0)
+#define CTRL_READ_WRITE BIT(1)
+#define CTRL_SOM BIT(2)
+#define CTRL_EOM BIT(3)
+#define CTRL_OWNER GENMASK(5, 4)
+#define CTRL_COMPLETE BIT(6)
+#define CTRL_READY BIT(7)
+#define CTRL_STATUS GENMASK(15, 8)
+#define CTRL_PACKET_SIZE GENMASK(31, 16)
+#define CTRL_MSG_SIZE GENMASK(63, 48)
+
+#define DISC_TABLE_SIZE 12
+#define DT_ACCESS_TYPE GENMASK(3, 0)
+#define DT_SIZE GENMASK(27, 12)
+#define DT_TBIR GENMASK(2, 0)
+#define DT_OFFSET(v) ((v) & GENMASK(31, 3))
+
+enum sdsi_command {
+ SDSI_CMD_PROVISION_AKC = 0x04,
+ SDSI_CMD_PROVISION_CAP = 0x08,
+ SDSI_CMD_READ_STATE = 0x10,
+};
+
+struct sdsi_mbox_info {
+ u64 *payload;
+ u64 *buffer;
+ int size;
+};
+
+struct disc_table {
+ u32 access_info;
+ u32 guid;
+ u32 offset;
+};
+
+struct sdsi_priv {
+ struct mutex mb_lock; /* Mailbox access lock */
+ struct device *dev;
+ void __iomem *control_addr;
+ void __iomem *mbox_addr;
+ void __iomem *regs_addr;
+ u32 guid;
+ bool sdsi_enabled;
+};
+
+/* SDSi mailbox operations must be performed using 64bit mov instructions */
+static __always_inline void
+sdsi_memcpy64_toio(u64 __iomem *to, const u64 *from, size_t count_bytes)
+{
+ size_t count = count_bytes / sizeof(*to);
+ int i;
+
+ for (i = 0; i < count; i++)
+ writeq(from[i], &to[i]);
+}
+
+static __always_inline void
+sdsi_memcpy64_fromio(u64 *to, const u64 __iomem *from, size_t count_bytes)
+{
+ size_t count = count_bytes / sizeof(*to);
+ int i;
+
+ for (i = 0; i < count; i++)
+ to[i] = readq(&from[i]);
+}
+
+static inline void sdsi_complete_transaction(struct sdsi_priv *priv)
+{
+ u64 control = FIELD_PREP(CTRL_COMPLETE, 1);
+
+ lockdep_assert_held(&priv->mb_lock);
+ writeq(control, priv->control_addr);
+}
+
+static int sdsi_status_to_errno(u32 status)
+{
+ switch (status) {
+ case SDSI_MBOX_CMD_SUCCESS:
+ return 0;
+ case SDSI_MBOX_CMD_TIMEOUT:
+ return -ETIMEDOUT;
+ default:
+ return -EIO;
+ }
+}
+
+static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info,
+ size_t *data_size)
+{
+ struct device *dev = priv->dev;
+ u32 total, loop, eom, status, message_size;
+ u64 control;
+ int ret;
+
+ lockdep_assert_held(&priv->mb_lock);
+
+ /* Format and send the read command */
+ control = FIELD_PREP(CTRL_EOM, 1) |
+ FIELD_PREP(CTRL_SOM, 1) |
+ FIELD_PREP(CTRL_RUN_BUSY, 1) |
+ FIELD_PREP(CTRL_PACKET_SIZE, info->size);
+ writeq(control, priv->control_addr);
+
+ /* For reads, data sizes that are larger than the mailbox size are read in packets. */
+ total = 0;
+ loop = 0;
+ do {
+ int offset = SDSI_SIZE_MAILBOX * loop;
+ void __iomem *addr = priv->mbox_addr + offset;
+ u64 *buf = info->buffer + offset / SDSI_SIZE_CMD;
+ u32 packet_size;
+
+ /* Poll on ready bit */
+ ret = readq_poll_timeout(priv->control_addr, control, control & CTRL_READY,
+ MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_US);
+ if (ret)
+ break;
+
+ eom = FIELD_GET(CTRL_EOM, control);
+ status = FIELD_GET(CTRL_STATUS, control);
+ packet_size = FIELD_GET(CTRL_PACKET_SIZE, control);
+ message_size = FIELD_GET(CTRL_MSG_SIZE, control);
+
+ ret = sdsi_status_to_errno(status);
+ if (ret)
+ break;
+
+ /* Only the last packet can be less than the mailbox size. */
+ if (!eom && packet_size != SDSI_SIZE_MAILBOX) {
+ dev_err(dev, "Invalid packet size\n");
+ ret = -EPROTO;
+ break;
+ }
+
+ if (packet_size > SDSI_SIZE_MAILBOX) {
+ dev_err(dev, "Packet size too large\n");
+ ret = -EPROTO;
+ break;
+ }
+
+ sdsi_memcpy64_fromio(buf, addr, round_up(packet_size, SDSI_SIZE_CMD));
+
+ total += packet_size;
+
+ sdsi_complete_transaction(priv);
+ } while (!eom && ++loop < MBOX_MAX_PACKETS);
+
+ if (ret) {
+ sdsi_complete_transaction(priv);
+ return ret;
+ }
+
+ if (!eom) {
+ dev_err(dev, "Exceeded read attempts\n");
+ return -EPROTO;
+ }
+
+ /* Message size check is only valid for multi-packet transfers */
+ if (loop && total != message_size)
+ dev_warn(dev, "Read count %u differs from expected count %u\n",
+ total, message_size);
+
+ *data_size = total;
+
+ return 0;
+}
+
+static int sdsi_mbox_cmd_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
+{
+ u64 control;
+ u32 status;
+ int ret;
+
+ lockdep_assert_held(&priv->mb_lock);
+
+ /* Write rest of the payload */
+ sdsi_memcpy64_toio(priv->mbox_addr + SDSI_SIZE_CMD, info->payload + 1,
+ info->size - SDSI_SIZE_CMD);
+
+ /* Format and send the write command */
+ control = FIELD_PREP(CTRL_EOM, 1) |
+ FIELD_PREP(CTRL_SOM, 1) |
+ FIELD_PREP(CTRL_RUN_BUSY, 1) |
+ FIELD_PREP(CTRL_READ_WRITE, 1) |
+ FIELD_PREP(CTRL_PACKET_SIZE, info->size);
+ writeq(control, priv->control_addr);
+
+ /* Poll on run_busy bit */
+ ret = readq_poll_timeout(priv->control_addr, control, !(control & CTRL_RUN_BUSY),
+ MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_US);
+
+ if (ret)
+ goto release_mbox;
+
+ status = FIELD_GET(CTRL_STATUS, control);
+ ret = sdsi_status_to_errno(status);
+
+release_mbox:
+ sdsi_complete_transaction(priv);
+
+ return ret;
+}
+
+static int sdsi_mbox_acquire(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
+{
+ u64 control;
+ u32 owner;
+ int ret;
+
+ lockdep_assert_held(&priv->mb_lock);
+
+ /* Check mailbox is available */
+ control = readq(priv->control_addr);
+ owner = FIELD_GET(CTRL_OWNER, control);
+ if (owner != MBOX_OWNER_NONE)
+ return -EBUSY;
+
+ /* Write first qword of payload */
+ writeq(info->payload[0], priv->mbox_addr);
+
+ /* Check for ownership */
+ ret = readq_poll_timeout(priv->control_addr, control,
+ FIELD_GET(CTRL_OWNER, control) & MBOX_OWNER_INBAND,
+ MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_ACQUIRE_US);
+
+ return ret;
+}
+
+static int sdsi_mbox_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
+{
+ int ret;
+
+ lockdep_assert_held(&priv->mb_lock);
+
+ ret = sdsi_mbox_acquire(priv, info);
+ if (ret)
+ return ret;
+
+ return sdsi_mbox_cmd_write(priv, info);
+}
+
+static int sdsi_mbox_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info, size_t *data_size)
+{
+ int ret;
+
+ lockdep_assert_held(&priv->mb_lock);
+
+ ret = sdsi_mbox_acquire(priv, info);
+ if (ret)
+ return ret;
+
+ return sdsi_mbox_cmd_read(priv, info, data_size);
+}
+
+static ssize_t sdsi_provision(struct sdsi_priv *priv, char *buf, size_t count,
+ enum sdsi_command command)
+{
+ struct sdsi_mbox_info info;
+ int ret;
+
+ if (!priv->sdsi_enabled)
+ return -EPERM;
+
+ if (count > (SDSI_SIZE_WRITE_MSG - SDSI_SIZE_CMD))
+ return -EOVERFLOW;
+
+ /* Qword aligned message + command qword */
+ info.size = round_up(count, SDSI_SIZE_CMD) + SDSI_SIZE_CMD;
+
+ info.payload = kzalloc(info.size, GFP_KERNEL);
+ if (!info.payload)
+ return -ENOMEM;
+
+ /* Copy message to payload buffer */
+ memcpy(info.payload, buf, count);
+
+ /* Command is last qword of payload buffer */
+ info.payload[(info.size - SDSI_SIZE_CMD) / SDSI_SIZE_CMD] = command;
+
+ ret = mutex_lock_interruptible(&priv->mb_lock);
+ if (ret)
+ goto free_payload;
+ ret = sdsi_mbox_write(priv, &info);
+ mutex_unlock(&priv->mb_lock);
+
+free_payload:
+ kfree(info.payload);
+
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t provision_akc_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off,
+ size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct sdsi_priv *priv = dev_get_drvdata(dev);
+
+ if (off)
+ return -ESPIPE;
+
+ return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_AKC);
+}
+static BIN_ATTR_WO(provision_akc, SDSI_SIZE_WRITE_MSG);
+
+static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off,
+ size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct sdsi_priv *priv = dev_get_drvdata(dev);
+
+ if (off)
+ return -ESPIPE;
+
+ return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_CAP);
+}
+static BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG);
+
+static long state_certificate_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off,
+ size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct sdsi_priv *priv = dev_get_drvdata(dev);
+ u64 command = SDSI_CMD_READ_STATE;
+ struct sdsi_mbox_info info;
+ size_t size;
+ int ret;
+
+ if (!priv->sdsi_enabled)
+ return -EPERM;
+
+ if (off)
+ return 0;
+
+ /* Buffer for return data */
+ info.buffer = kmalloc(SDSI_SIZE_READ_MSG, GFP_KERNEL);
+ if (!info.buffer)
+ return -ENOMEM;
+
+ info.payload = &command;
+ info.size = sizeof(command);
+
+ ret = mutex_lock_interruptible(&priv->mb_lock);
+ if (ret)
+ goto free_buffer;
+ ret = sdsi_mbox_read(priv, &info, &size);
+ mutex_unlock(&priv->mb_lock);
+ if (ret < 0)
+ goto free_buffer;
+
+ if (size > count)
+ size = count;
+
+ memcpy(buf, info.buffer, size);
+
+free_buffer:
+ kfree(info.buffer);
+
+ if (ret)
+ return ret;
+
+ return size;
+}
+static BIN_ATTR(state_certificate, 0400, state_certificate_read, NULL, SDSI_SIZE_READ_MSG);
+
+static ssize_t registers_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf, loff_t off,
+ size_t count)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct sdsi_priv *priv = dev_get_drvdata(dev);
+ void __iomem *addr = priv->regs_addr;
+
+ memcpy_fromio(buf, addr + off, count);
+
+ return count;
+}
+static BIN_ATTR(registers, 0400, registers_read, NULL, SDSI_SIZE_REGS);
+
+static struct bin_attribute *sdsi_bin_attrs[] = {
+ &bin_attr_registers,
+ &bin_attr_state_certificate,
+ &bin_attr_provision_akc,
+ &bin_attr_provision_cap,
+ NULL
+};
+
+static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct sdsi_priv *priv = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "0x%x\n", priv->guid);
+}
+static DEVICE_ATTR_RO(guid);
+
+static struct attribute *sdsi_attrs[] = {
+ &dev_attr_guid.attr,
+ NULL
+};
+
+static const struct attribute_group sdsi_group = {
+ .attrs = sdsi_attrs,
+ .bin_attrs = sdsi_bin_attrs,
+};
+__ATTRIBUTE_GROUPS(sdsi);
+
+static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct pci_dev *parent,
+ struct disc_table *disc_table, struct resource *disc_res)
+{
+ u32 access_type = FIELD_GET(DT_ACCESS_TYPE, disc_table->access_info);
+ u32 size = FIELD_GET(DT_SIZE, disc_table->access_info);
+ u32 tbir = FIELD_GET(DT_TBIR, disc_table->offset);
+ u32 offset = DT_OFFSET(disc_table->offset);
+ u32 features_offset;
+ struct resource res = {};
+
+ /* Starting location of SDSi MMIO region based on access type */
+ switch (access_type) {
+ case ACCESS_TYPE_LOCAL:
+ if (tbir) {
+ dev_err(priv->dev, "Unsupported BAR index %u for access type %u\n",
+ tbir, access_type);
+ return -EINVAL;
+ }
+
+ /*
+ * For access_type LOCAL, the base address is as follows:
+ * base address = end of discovery region + base offset + 1
+ */
+ res.start = disc_res->end + offset + 1;
+ break;
+
+ case ACCESS_TYPE_BARID:
+ res.start = pci_resource_start(parent, tbir) + offset;
+ break;
+
+ default:
+ dev_err(priv->dev, "Unrecognized access_type %u\n", access_type);
+ return -EINVAL;
+ }
+
+ res.end = res.start + size * sizeof(u32) - 1;
+ res.flags = IORESOURCE_MEM;
+
+ priv->control_addr = devm_ioremap_resource(priv->dev, &res);
+ if (IS_ERR(priv->control_addr))
+ return PTR_ERR(priv->control_addr);
+
+ priv->mbox_addr = priv->control_addr + SDSI_SIZE_CONTROL;
+ priv->regs_addr = priv->mbox_addr + SDSI_SIZE_MAILBOX;
+
+ features_offset = readq(priv->regs_addr + SDSI_ENABLED_FEATURES_OFFSET);
+ priv->sdsi_enabled = !!(features_offset & SDSI_ENABLED);
+
+ return 0;
+}
+
+static int sdsi_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
+{
+ struct intel_vsec_device *intel_cap_dev = auxdev_to_ivdev(auxdev);
+ struct disc_table disc_table;
+ struct resource *disc_res;
+ void __iomem *disc_addr;
+ struct sdsi_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &auxdev->dev;
+ mutex_init(&priv->mb_lock);
+ auxiliary_set_drvdata(auxdev, priv);
+
+ /* Get the SDSi discovery table */
+ disc_res = &intel_cap_dev->resource[0];
+ disc_addr = devm_ioremap_resource(&auxdev->dev, disc_res);
+ if (IS_ERR(disc_addr))
+ return PTR_ERR(disc_addr);
+
+ memcpy_fromio(&disc_table, disc_addr, DISC_TABLE_SIZE);
+
+ priv->guid = disc_table.guid;
+
+ /* Map the SDSi mailbox registers */
+ ret = sdsi_map_mbox_registers(priv, intel_cap_dev->pcidev, &disc_table, disc_res);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct auxiliary_device_id sdsi_aux_id_table[] = {
+ { .name = "intel_vsec.sdsi" },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, sdsi_aux_id_table);
+
+static struct auxiliary_driver sdsi_aux_driver = {
+ .driver = {
+ .dev_groups = sdsi_groups,
+ },
+ .id_table = sdsi_aux_id_table,
+ .probe = sdsi_probe,
+ /* No remove. All resources are handled under devm */
+};
+module_auxiliary_driver(sdsi_aux_driver);
+
+MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
+MODULE_DESCRIPTION("Intel Software Defined Silicon driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/intel/uncore-frequency.c b/drivers/platform/x86/intel/uncore-frequency.c
deleted file mode 100644
index 4cd8254f2e40..000000000000
--- a/drivers/platform/x86/intel/uncore-frequency.c
+++ /dev/null
@@ -1,452 +0,0 @@
-// 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;
- struct completion kobj_unregister;
- 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 */
-static 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;
-
- if (data->control_cpu < 0)
- return -ENXIO;
-
- 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);
-
- if (data->control_cpu < 0) {
- ret = -ENXIO;
- goto finish_write;
- }
-
- 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
-};
-ATTRIBUTE_GROUPS(uncore);
-
-static void uncore_sysfs_entry_release(struct kobject *kobj)
-{
- struct uncore_data *data = to_uncore_data(kobj);
-
- complete(&data->kobj_unregister);
-}
-
-static struct kobj_type uncore_ktype = {
- .release = uncore_sysfs_entry_release,
- .sysfs_ops = &kobj_sysfs_ops,
- .default_groups = uncore_groups,
-};
-
-/* 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);
-
- init_completion(&data->kobj_unregister);
-
- 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, make control cpu invalid */
-static void uncore_remove_die_entry(int cpu)
-{
- struct uncore_data *data;
-
- mutex_lock(&uncore_lock);
- data = uncore_get_instance(cpu);
- if (data)
- data->control_cpu = -1;
- 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,
-};
-
-static const struct x86_cpu_id intel_uncore_cpu_ids[] = {
- X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_G, NULL),
- X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_X, NULL),
- X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_D, NULL),
- X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, NULL),
- X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X, NULL),
- X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, NULL),
- X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, NULL),
- {}
-};
-
-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;
-
- uncore_root_kobj = kobject_create_and_add("intel_uncore_frequency",
- &cpu_subsys.dev_root->kobj);
- if (!uncore_root_kobj) {
- ret = -ENOMEM;
- 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);
- wait_for_completion(&uncore_instances[i].kobj_unregister);
- }
- }
- 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/uncore-frequency/Kconfig b/drivers/platform/x86/intel/uncore-frequency/Kconfig
new file mode 100644
index 000000000000..21b209124916
--- /dev/null
+++ b/drivers/platform/x86/intel/uncore-frequency/Kconfig
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Uncore Frquency control drivers
+#
+
+menu "Intel Uncore Frequency Control"
+ depends on X86_64 || COMPILE_TEST
+
+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.
+
+endmenu
diff --git a/drivers/platform/x86/intel/uncore-frequency/Makefile b/drivers/platform/x86/intel/uncore-frequency/Makefile
new file mode 100644
index 000000000000..e0f7968e8285
--- /dev/null
+++ b/drivers/platform/x86/intel/uncore-frequency/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for linux/drivers/platform/x86/intel/uncore-frequency
+#
+
+obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL) += intel-uncore-frequency.o
+intel-uncore-frequency-y := uncore-frequency.o
+obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL) += intel-uncore-frequency-common.o
+intel-uncore-frequency-common-y := uncore-frequency-common.o
diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c
new file mode 100644
index 000000000000..84eabd6156bb
--- /dev/null
+++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Uncore Frequency Control: Common code implementation
+ * Copyright (c) 2022, Intel Corporation.
+ * All rights reserved.
+ *
+ */
+#include <linux/cpu.h>
+#include <linux/module.h>
+#include "uncore-frequency-common.h"
+
+/* Mutex to control all mutual exclusions */
+static DEFINE_MUTEX(uncore_lock);
+/* Root of the all uncore sysfs kobjs */
+static struct kobject *uncore_root_kobj;
+/* uncore instance count */
+static int uncore_instance_count;
+
+/* callbacks for actual HW read/write */
+static int (*uncore_read)(struct uncore_data *data, unsigned int *min, unsigned int *max);
+static int (*uncore_write)(struct uncore_data *data, unsigned int input, unsigned int min_max);
+static int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq);
+
+static ssize_t show_min_max_freq_khz(struct uncore_data *data,
+ char *buf, int min_max)
+{
+ unsigned int min, max;
+ int ret;
+
+ mutex_lock(&uncore_lock);
+ ret = uncore_read(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);
+}
+
+static ssize_t store_min_max_freq_khz(struct uncore_data *data,
+ const char *buf, ssize_t count,
+ int min_max)
+{
+ unsigned int input;
+
+ if (kstrtouint(buf, 10, &input))
+ return -EINVAL;
+
+ mutex_lock(&uncore_lock);
+ uncore_write(data, input, min_max);
+ mutex_unlock(&uncore_lock);
+
+ return count;
+}
+
+static ssize_t show_perf_status_freq_khz(struct uncore_data *data, char *buf)
+{
+ unsigned int freq;
+ int ret;
+
+ mutex_lock(&uncore_lock);
+ ret = uncore_read_freq(data, &freq);
+ mutex_unlock(&uncore_lock);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%u\n", freq);
+}
+
+#define store_uncore_min_max(name, min_max) \
+ static ssize_t store_##name(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+ { \
+ struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\
+ \
+ return store_min_max_freq_khz(data, buf, count, \
+ min_max); \
+ }
+
+#define show_uncore_min_max(name, min_max) \
+ static ssize_t show_##name(struct device *dev, \
+ struct device_attribute *attr, char *buf)\
+ { \
+ struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\
+ \
+ return show_min_max_freq_khz(data, buf, min_max); \
+ }
+
+#define show_uncore_perf_status(name) \
+ static ssize_t show_##name(struct device *dev, \
+ struct device_attribute *attr, char *buf)\
+ { \
+ struct uncore_data *data = container_of(attr, struct uncore_data, name##_dev_attr);\
+ \
+ return show_perf_status_freq_khz(data, buf); \
+ }
+
+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);
+
+show_uncore_perf_status(current_freq_khz);
+
+#define show_uncore_data(member_name) \
+ static ssize_t show_##member_name(struct device *dev, \
+ struct device_attribute *attr, char *buf)\
+ { \
+ struct uncore_data *data = container_of(attr, struct uncore_data,\
+ member_name##_dev_attr);\
+ \
+ return scnprintf(buf, PAGE_SIZE, "%u\n", \
+ data->member_name); \
+ } \
+
+show_uncore_data(initial_min_freq_khz);
+show_uncore_data(initial_max_freq_khz);
+
+#define init_attribute_rw(_name) \
+ do { \
+ sysfs_attr_init(&data->_name##_dev_attr.attr); \
+ data->_name##_dev_attr.show = show_##_name; \
+ data->_name##_dev_attr.store = store_##_name; \
+ data->_name##_dev_attr.attr.name = #_name; \
+ data->_name##_dev_attr.attr.mode = 0644; \
+ } while (0)
+
+#define init_attribute_ro(_name) \
+ do { \
+ sysfs_attr_init(&data->_name##_dev_attr.attr); \
+ data->_name##_dev_attr.show = show_##_name; \
+ data->_name##_dev_attr.store = NULL; \
+ data->_name##_dev_attr.attr.name = #_name; \
+ data->_name##_dev_attr.attr.mode = 0444; \
+ } while (0)
+
+#define init_attribute_root_ro(_name) \
+ do { \
+ sysfs_attr_init(&data->_name##_dev_attr.attr); \
+ data->_name##_dev_attr.show = show_##_name; \
+ data->_name##_dev_attr.store = NULL; \
+ data->_name##_dev_attr.attr.name = #_name; \
+ data->_name##_dev_attr.attr.mode = 0400; \
+ } while (0)
+
+static int create_attr_group(struct uncore_data *data, char *name)
+{
+ int ret, index = 0;
+
+ init_attribute_rw(max_freq_khz);
+ init_attribute_rw(min_freq_khz);
+ init_attribute_ro(initial_min_freq_khz);
+ init_attribute_ro(initial_max_freq_khz);
+ init_attribute_root_ro(current_freq_khz);
+
+ data->uncore_attrs[index++] = &data->max_freq_khz_dev_attr.attr;
+ data->uncore_attrs[index++] = &data->min_freq_khz_dev_attr.attr;
+ data->uncore_attrs[index++] = &data->initial_min_freq_khz_dev_attr.attr;
+ data->uncore_attrs[index++] = &data->initial_max_freq_khz_dev_attr.attr;
+ data->uncore_attrs[index++] = &data->current_freq_khz_dev_attr.attr;
+ data->uncore_attrs[index] = NULL;
+
+ data->uncore_attr_group.name = name;
+ data->uncore_attr_group.attrs = data->uncore_attrs;
+ ret = sysfs_create_group(uncore_root_kobj, &data->uncore_attr_group);
+
+ return ret;
+}
+
+static void delete_attr_group(struct uncore_data *data, char *name)
+{
+ sysfs_remove_group(uncore_root_kobj, &data->uncore_attr_group);
+}
+
+int uncore_freq_add_entry(struct uncore_data *data, int cpu)
+{
+ int ret = 0;
+
+ mutex_lock(&uncore_lock);
+ if (data->valid) {
+ /* control cpu changed */
+ data->control_cpu = cpu;
+ goto uncore_unlock;
+ }
+
+ sprintf(data->name, "package_%02d_die_%02d", data->package_id, data->die_id);
+
+ uncore_read(data, &data->initial_min_freq_khz, &data->initial_max_freq_khz);
+
+ ret = create_attr_group(data, data->name);
+ if (!ret) {
+ data->control_cpu = cpu;
+ data->valid = true;
+ }
+
+uncore_unlock:
+ mutex_unlock(&uncore_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(uncore_freq_add_entry, INTEL_UNCORE_FREQUENCY);
+
+void uncore_freq_remove_die_entry(struct uncore_data *data)
+{
+ mutex_lock(&uncore_lock);
+ delete_attr_group(data, data->name);
+ data->control_cpu = -1;
+ data->valid = false;
+ mutex_unlock(&uncore_lock);
+}
+EXPORT_SYMBOL_NS_GPL(uncore_freq_remove_die_entry, INTEL_UNCORE_FREQUENCY);
+
+int uncore_freq_common_init(int (*read_control_freq)(struct uncore_data *data, unsigned int *min, unsigned int *max),
+ int (*write_control_freq)(struct uncore_data *data, unsigned int input, unsigned int set_max),
+ int (*read_freq)(struct uncore_data *data, unsigned int *freq))
+{
+ mutex_lock(&uncore_lock);
+
+ uncore_read = read_control_freq;
+ uncore_write = write_control_freq;
+ uncore_read_freq = read_freq;
+
+ if (!uncore_root_kobj)
+ uncore_root_kobj = kobject_create_and_add("intel_uncore_frequency",
+ &cpu_subsys.dev_root->kobj);
+ if (uncore_root_kobj)
+ ++uncore_instance_count;
+ mutex_unlock(&uncore_lock);
+
+ return uncore_root_kobj ? 0 : -ENOMEM;
+}
+EXPORT_SYMBOL_NS_GPL(uncore_freq_common_init, INTEL_UNCORE_FREQUENCY);
+
+void uncore_freq_common_exit(void)
+{
+ mutex_lock(&uncore_lock);
+ --uncore_instance_count;
+ if (!uncore_instance_count) {
+ kobject_put(uncore_root_kobj);
+ uncore_root_kobj = NULL;
+ }
+ mutex_unlock(&uncore_lock);
+}
+EXPORT_SYMBOL_NS_GPL(uncore_freq_common_exit, INTEL_UNCORE_FREQUENCY);
+
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Uncore Frequency Common Module");
diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h
new file mode 100644
index 000000000000..f5dcfa2fb285
--- /dev/null
+++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Intel Uncore Frequency Control: Common defines and prototypes
+ * Copyright (c) 2022, Intel Corporation.
+ * All rights reserved.
+ *
+ */
+
+#ifndef __INTEL_UNCORE_FREQ_COMMON_H
+#define __INTEL_UNCORE_FREQ_COMMON_H
+
+#include <linux/device.h>
+
+/**
+ * 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
+ * @package_id: Package id for this instance
+ * @die_id: Die id for this instance
+ * @name: Sysfs entry name for this instance
+ * @uncore_attr_group: Attribute group storage
+ * @max_freq_khz_dev_attr: Storage for device attribute max_freq_khz
+ * @mix_freq_khz_dev_attr: Storage for device attribute min_freq_khz
+ * @initial_max_freq_khz_dev_attr: Storage for device attribute initial_max_freq_khz
+ * @initial_min_freq_khz_dev_attr: Storage for device attribute initial_min_freq_khz
+ * @current_freq_khz_dev_attr: Storage for device attribute current_freq_khz
+ * @uncore_attrs: Attribute storage for group creation
+ *
+ * This structure is used to encapsulate all data related to uncore sysfs
+ * settings for a die/package.
+ */
+struct uncore_data {
+ u64 stored_uncore_data;
+ u32 initial_min_freq_khz;
+ u32 initial_max_freq_khz;
+ int control_cpu;
+ bool valid;
+ int package_id;
+ int die_id;
+ char name[32];
+
+ struct attribute_group uncore_attr_group;
+ struct device_attribute max_freq_khz_dev_attr;
+ struct device_attribute min_freq_khz_dev_attr;
+ struct device_attribute initial_max_freq_khz_dev_attr;
+ struct device_attribute initial_min_freq_khz_dev_attr;
+ struct device_attribute current_freq_khz_dev_attr;
+ struct attribute *uncore_attrs[6];
+};
+
+int uncore_freq_common_init(int (*read_control_freq)(struct uncore_data *data, unsigned int *min, unsigned int *max),
+ int (*write_control_freq)(struct uncore_data *data, unsigned int input, unsigned int min_max),
+ int (*uncore_read_freq)(struct uncore_data *data, unsigned int *freq));
+void uncore_freq_common_exit(void);
+int uncore_freq_add_entry(struct uncore_data *data, int cpu);
+void uncore_freq_remove_die_entry(struct uncore_data *data);
+
+#endif
diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
new file mode 100644
index 000000000000..c61f804dd44e
--- /dev/null
+++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Uncore Frequency Setting
+ * Copyright (c) 2022, 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>
+
+#include "uncore-frequency-common.h"
+
+/* 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;
+/* 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;
+
+#define MSR_UNCORE_RATIO_LIMIT 0x620
+#define MSR_UNCORE_PERF_STATUS 0x621
+#define UNCORE_FREQ_KHZ_MULTIPLIER 100000
+
+static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min,
+ unsigned int *max)
+{
+ u64 cap;
+ int ret;
+
+ if (data->control_cpu < 0)
+ return -ENXIO;
+
+ 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;
+}
+
+static int uncore_write_control_freq(struct uncore_data *data, unsigned int input,
+ unsigned int min_max)
+{
+ int ret;
+ u64 cap;
+
+ input /= UNCORE_FREQ_KHZ_MULTIPLIER;
+ if (!input || input > 0x7F)
+ return -EINVAL;
+
+ if (data->control_cpu < 0)
+ return -ENXIO;
+
+ ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, &cap);
+ if (ret)
+ return ret;
+
+ if (min_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)
+ return ret;
+
+ data->stored_uncore_data = cap;
+
+ return 0;
+}
+
+static int uncore_read_freq(struct uncore_data *data, unsigned int *freq)
+{
+ u64 ratio;
+ int ret;
+
+ if (data->control_cpu < 0)
+ return -ENXIO;
+
+ ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_PERF_STATUS, &ratio);
+ if (ret)
+ return ret;
+
+ *freq = (ratio & 0x7F) * UNCORE_FREQ_KHZ_MULTIPLIER;
+
+ return 0;
+}
+
+/* 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 int uncore_event_cpu_online(unsigned int cpu)
+{
+ struct uncore_data *data;
+ 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);
+
+ data = uncore_get_instance(cpu);
+ if (!data)
+ return 0;
+
+ data->package_id = topology_physical_package_id(cpu);
+ data->die_id = topology_die_id(cpu);
+
+ return uncore_freq_add_entry(data, cpu);
+}
+
+static int uncore_event_cpu_offline(unsigned int cpu)
+{
+ struct uncore_data *data;
+ int target;
+
+ data = uncore_get_instance(cpu);
+ if (!data)
+ return 0;
+
+ /* 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_freq_add_entry(data, target);
+ } else {
+ uncore_freq_remove_die_entry(data);
+ }
+
+ return 0;
+}
+
+static int uncore_pm_notify(struct notifier_block *nb, unsigned long mode,
+ void *_unused)
+{
+ int i;
+
+ switch (mode) {
+ case PM_POST_HIBERNATION:
+ case PM_POST_RESTORE:
+ case PM_POST_SUSPEND:
+ for (i = 0; i < uncore_max_entries; ++i) {
+ struct uncore_data *data = &uncore_instances[i];
+
+ if (!data || !data->valid || !data->stored_uncore_data)
+ return 0;
+
+ wrmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT,
+ data->stored_uncore_data);
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block uncore_pm_nb = {
+ .notifier_call = uncore_pm_notify,
+};
+
+static const struct x86_cpu_id intel_uncore_cpu_ids[] = {
+ X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_G, NULL),
+ X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_X, NULL),
+ X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_D, NULL),
+ X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X, NULL),
+ X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X, NULL),
+ X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, NULL),
+ X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, NULL),
+ {}
+};
+MODULE_DEVICE_TABLE(x86cpu, intel_uncore_cpu_ids);
+
+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 = uncore_freq_common_init(uncore_read_control_freq, uncore_write_control_freq,
+ uncore_read_freq);
+ 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:
+ uncore_freq_common_exit();
+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)
+ uncore_freq_remove_die_entry(&uncore_instances[i]);
+ uncore_freq_common_exit();
+ kfree(uncore_instances);
+}
+module_exit(intel_uncore_exit)
+
+MODULE_IMPORT_NS(INTEL_UNCORE_FREQUENCY);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel Uncore Frequency Limits Driver");
diff --git a/drivers/platform/x86/intel/vbtn.c b/drivers/platform/x86/intel/vbtn.c
index 15f013af9e62..c5e4e35c8d20 100644
--- a/drivers/platform/x86/intel/vbtn.c
+++ b/drivers/platform/x86/intel/vbtn.c
@@ -384,12 +384,9 @@ static acpi_status __init
check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv)
{
const struct acpi_device_id *ids = context;
- struct acpi_device *dev;
+ struct acpi_device *dev = acpi_fetch_acpi_dev(handle);
- if (acpi_bus_get_device(handle, &dev) != 0)
- return AE_OK;
-
- if (acpi_match_device_ids(dev, ids) == 0)
+ if (dev && acpi_match_device_ids(dev, ids) == 0)
if (!IS_ERR_OR_NULL(acpi_create_platform_device(dev, NULL)))
dev_info(&dev->dev,
"intel-vbtn: created platform device\n");
diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c
index c3bdd75ed690..bed436bf181f 100644
--- a/drivers/platform/x86/intel/vsec.c
+++ b/drivers/platform/x86/intel/vsec.c
@@ -32,6 +32,7 @@
#define TABLE_OFFSET_SHIFT 3
static DEFINE_IDA(intel_vsec_ida);
+static DEFINE_IDA(intel_vsec_sdsi_ida);
/**
* struct intel_vsec_header - Common fields of Intel VSEC and DVSEC registers.
@@ -63,12 +64,14 @@ enum intel_vsec_id {
VSEC_ID_TELEMETRY = 2,
VSEC_ID_WATCHER = 3,
VSEC_ID_CRASHLOG = 4,
+ VSEC_ID_SDSI = 65,
};
static enum intel_vsec_id intel_vsec_allow_list[] = {
VSEC_ID_TELEMETRY,
VSEC_ID_WATCHER,
VSEC_ID_CRASHLOG,
+ VSEC_ID_SDSI,
};
static const char *intel_vsec_name(enum intel_vsec_id id)
@@ -83,6 +86,9 @@ static const char *intel_vsec_name(enum intel_vsec_id id)
case VSEC_ID_CRASHLOG:
return "crashlog";
+ case VSEC_ID_SDSI:
+ return "sdsi";
+
default:
return NULL;
}
@@ -211,7 +217,11 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he
intel_vsec_dev->resource = res;
intel_vsec_dev->num_resources = header->num_entries;
intel_vsec_dev->quirks = quirks;
- intel_vsec_dev->ida = &intel_vsec_ida;
+
+ if (header->id == VSEC_ID_SDSI)
+ intel_vsec_dev->ida = &intel_vsec_sdsi_ida;
+ else
+ intel_vsec_dev->ida = &intel_vsec_ida;
return intel_vsec_add_aux(pdev, intel_vsec_dev, intel_vsec_name(header->id));
}