summaryrefslogtreecommitdiff
path: root/drivers/thermal/intel
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thermal/intel')
-rw-r--r--drivers/thermal/intel/Kconfig14
-rw-r--r--drivers/thermal/intel/Makefile1
-rw-r--r--drivers/thermal/intel/int340x_thermal/Kconfig2
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3400_thermal.c58
-rw-r--r--drivers/thermal/intel/int340x_thermal/int3403_thermal.c2
-rw-r--r--drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c336
-rw-r--r--drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h16
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device.c129
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c53
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c4
-rw-r--r--drivers/thermal/intel/intel_hfi.c3
-rw-r--r--drivers/thermal/intel/intel_menlow.c12
-rw-r--r--drivers/thermal/intel/intel_pch_thermal.c410
-rw-r--r--drivers/thermal/intel/intel_powerclamp.c545
-rw-r--r--drivers/thermal/intel/intel_quark_dts_thermal.c67
-rw-r--r--drivers/thermal/intel/intel_soc_dts_iosf.c35
-rw-r--r--drivers/thermal/intel/intel_tcc.c139
-rw-r--r--drivers/thermal/intel/intel_tcc_cooling.c37
-rw-r--r--drivers/thermal/intel/x86_pkg_temp_thermal.c176
19 files changed, 1000 insertions, 1039 deletions
diff --git a/drivers/thermal/intel/Kconfig b/drivers/thermal/intel/Kconfig
index f0c845679250..cb7e7697cf1e 100644
--- a/drivers/thermal/intel/Kconfig
+++ b/drivers/thermal/intel/Kconfig
@@ -3,6 +3,9 @@ config INTEL_POWERCLAMP
tristate "Intel PowerClamp idle injection driver"
depends on X86
depends on CPU_SUP_INTEL
+ depends on CPU_IDLE
+ select POWERCAP
+ select IDLE_INJECT
help
Enable this to enable Intel PowerClamp idle injection driver. This
enforce idle time which results in more package C-state residency. The
@@ -12,11 +15,16 @@ config X86_THERMAL_VECTOR
def_bool y
depends on X86 && CPU_SUP_INTEL && X86_LOCAL_APIC
+config INTEL_TCC
+ bool
+ depends on X86
+
config X86_PKG_TEMP_THERMAL
tristate "X86 package temperature thermal driver"
depends on X86_THERMAL_VECTOR
select THERMAL_GOV_USER_SPACE
select THERMAL_WRITABLE_TRIPS
+ select INTEL_TCC
default m
help
Enable this to register CPU digital sensor for package temperature as
@@ -28,6 +36,7 @@ config INTEL_SOC_DTS_IOSF_CORE
tristate
depends on X86 && PCI
select IOSF_MBI
+ select INTEL_TCC
help
This is becoming a common feature for Intel SoCs to expose the additional
digital temperature sensors (DTSs) using side band interface (IOSF). This
@@ -64,7 +73,8 @@ endmenu
config INTEL_BXT_PMIC_THERMAL
tristate "Intel Broxton PMIC thermal driver"
- depends on X86 && INTEL_SOC_PMIC_BXTWC && REGMAP
+ depends on X86 && INTEL_SOC_PMIC_BXTWC
+ select REGMAP
help
Select this driver for Intel Broxton PMIC with ADC channels monitoring
system temperature measurements and alerts.
@@ -75,6 +85,7 @@ config INTEL_BXT_PMIC_THERMAL
config INTEL_PCH_THERMAL
tristate "Intel PCH Thermal Reporting Driver"
depends on X86 && PCI
+ select THERMAL_ACPI if ACPI
help
Enable this to support thermal reporting on certain intel PCHs.
Thermal reporting device will provide temperature reading,
@@ -83,6 +94,7 @@ config INTEL_PCH_THERMAL
config INTEL_TCC_COOLING
tristate "Intel TCC offset cooling Driver"
depends on X86
+ select INTEL_TCC
help
Enable this to support system cooling by adjusting the effective TCC
activation temperature via the TCC Offset register, which is widely
diff --git a/drivers/thermal/intel/Makefile b/drivers/thermal/intel/Makefile
index 9a8d8054f316..5d8833c82ab6 100644
--- a/drivers/thermal/intel/Makefile
+++ b/drivers/thermal/intel/Makefile
@@ -2,6 +2,7 @@
#
# Makefile for various Intel thermal drivers.
+obj-$(CONFIG_INTEL_TCC) += intel_tcc.o
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
obj-$(CONFIG_INTEL_SOC_DTS_IOSF_CORE) += intel_soc_dts_iosf.o
diff --git a/drivers/thermal/intel/int340x_thermal/Kconfig b/drivers/thermal/intel/int340x_thermal/Kconfig
index 5d046de96a5d..300ea53e9b33 100644
--- a/drivers/thermal/intel/int340x_thermal/Kconfig
+++ b/drivers/thermal/intel/int340x_thermal/Kconfig
@@ -9,7 +9,9 @@ config INT340X_THERMAL
select THERMAL_GOV_USER_SPACE
select ACPI_THERMAL_REL
select ACPI_FAN
+ select THERMAL_ACPI
select INTEL_SOC_DTS_IOSF_CORE
+ select INTEL_TCC
select PROC_THERMAL_MMIO_RAPL if POWERCAP
help
Newer laptops and tablets that use ACPI may have thermal sensors and
diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
index db8a6f63657d..d0295123cc3e 100644
--- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
+++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c
@@ -60,6 +60,7 @@ struct int3400_thermal_priv {
int odvp_count;
int *odvp;
u32 os_uuid_mask;
+ int production_mode;
struct odvp_attr *odvp_attrs;
};
@@ -130,10 +131,7 @@ static ssize_t available_uuids_show(struct device *dev,
for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {
if (priv->uuid_bitmap & (1 << i))
- length += scnprintf(&buf[length],
- PAGE_SIZE - length,
- "%s\n",
- int3400_thermal_uuids[i]);
+ length += sysfs_emit_at(buf, length, int3400_thermal_uuids[i]);
}
return length;
@@ -151,10 +149,7 @@ static ssize_t current_uuid_show(struct device *dev,
for (i = 0; i <= INT3400_THERMAL_CRITICAL; i++) {
if (priv->os_uuid_mask & BIT(i))
- length += scnprintf(&buf[length],
- PAGE_SIZE - length,
- "%s\n",
- int3400_thermal_uuids[i]);
+ length += sysfs_emit_at(buf, length, int3400_thermal_uuids[i]);
}
if (length)
@@ -315,6 +310,44 @@ end:
return result;
}
+static ssize_t production_mode_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", priv->production_mode);
+}
+
+static DEVICE_ATTR_RO(production_mode);
+
+static int production_mode_init(struct int3400_thermal_priv *priv)
+{
+ unsigned long long mode;
+ acpi_status status;
+ int ret;
+
+ priv->production_mode = -1;
+
+ status = acpi_evaluate_integer(priv->adev->handle, "DCFG", NULL, &mode);
+ /* If the method is not present, this is not an error */
+ if (ACPI_FAILURE(status))
+ return 0;
+
+ ret = sysfs_create_file(&priv->pdev->dev.kobj, &dev_attr_production_mode.attr);
+ if (ret)
+ return ret;
+
+ priv->production_mode = mode;
+
+ return 0;
+}
+
+static void production_mode_exit(struct int3400_thermal_priv *priv)
+{
+ if (priv->production_mode >= 0)
+ sysfs_remove_file(&priv->pdev->dev.kobj, &dev_attr_production_mode.attr);
+}
+
static ssize_t odvp_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -610,8 +643,15 @@ static int int3400_thermal_probe(struct platform_device *pdev)
if (result)
goto free_sysfs;
+ result = production_mode_init(priv);
+ if (result)
+ goto free_notify;
+
return 0;
+free_notify:
+ acpi_remove_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY,
+ int3400_notify);
free_sysfs:
cleanup_odvp(priv);
if (!ZERO_OR_NULL_PTR(priv->data_vault)) {
@@ -638,6 +678,8 @@ static int int3400_thermal_remove(struct platform_device *pdev)
{
struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
+ production_mode_exit(priv);
+
acpi_remove_notify_handler(
priv->adev->handle, ACPI_DEVICE_NOTIFY,
int3400_notify);
diff --git a/drivers/thermal/intel/int340x_thermal/int3403_thermal.c b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c
index 71d084c4c456..e418d270bc76 100644
--- a/drivers/thermal/intel/int340x_thermal/int3403_thermal.c
+++ b/drivers/thermal/intel/int340x_thermal/int3403_thermal.c
@@ -69,7 +69,7 @@ static void int3403_notify(acpi_handle handle,
THERMAL_TRIP_VIOLATED);
break;
case INT3403_PERF_TRIP_POINT_CHANGED:
- int340x_thermal_read_trips(obj->int340x_zone);
+ int340x_thermal_update_trips(obj->int340x_zone);
int340x_thermal_zone_device_update(obj->int340x_zone,
THERMAL_TRIP_CHANGED);
break;
diff --git a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
index 62c0aa5d0783..00665967ca52 100644
--- a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
+++ b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
@@ -18,9 +18,6 @@ static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
unsigned long long tmp;
acpi_status status;
- if (d->override_ops && d->override_ops->get_temp)
- return d->override_ops->get_temp(zone, temp);
-
status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);
if (ACPI_FAILURE(status))
return -EIO;
@@ -32,117 +29,30 @@ static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
if (conv_temp < 0)
return conv_temp;
- *temp = (unsigned long)conv_temp * 10;
- } else
+ *temp = conv_temp * 10;
+ } else {
/* _TMP returns the temperature in tenths of degrees Kelvin */
*temp = deci_kelvin_to_millicelsius(tmp);
-
- return 0;
-}
-
-static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone,
- int trip, int *temp)
-{
- struct int34x_thermal_zone *d = zone->devdata;
- int i;
-
- if (d->override_ops && d->override_ops->get_trip_temp)
- return d->override_ops->get_trip_temp(zone, trip, temp);
-
- if (trip < d->aux_trip_nr)
- *temp = d->aux_trips[trip];
- else if (trip == d->crt_trip_id)
- *temp = d->crt_temp;
- else if (trip == d->psv_trip_id)
- *temp = d->psv_temp;
- else if (trip == d->hot_trip_id)
- *temp = d->hot_temp;
- else {
- for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
- if (d->act_trips[i].valid &&
- d->act_trips[i].id == trip) {
- *temp = d->act_trips[i].temp;
- break;
- }
- }
- if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
- int trip,
- enum thermal_trip_type *type)
-{
- struct int34x_thermal_zone *d = zone->devdata;
- int i;
-
- if (d->override_ops && d->override_ops->get_trip_type)
- return d->override_ops->get_trip_type(zone, trip, type);
-
- if (trip < d->aux_trip_nr)
- *type = THERMAL_TRIP_PASSIVE;
- else if (trip == d->crt_trip_id)
- *type = THERMAL_TRIP_CRITICAL;
- else if (trip == d->hot_trip_id)
- *type = THERMAL_TRIP_HOT;
- else if (trip == d->psv_trip_id)
- *type = THERMAL_TRIP_PASSIVE;
- else {
- for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
- if (d->act_trips[i].valid &&
- d->act_trips[i].id == trip) {
- *type = THERMAL_TRIP_ACTIVE;
- break;
- }
- }
- if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
- return -EINVAL;
}
return 0;
}
static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
- int trip, int temp)
+ int trip, int temp)
{
struct int34x_thermal_zone *d = zone->devdata;
+ char name[] = {'P', 'A', 'T', '0' + trip, '\0'};
acpi_status status;
- char name[10];
- if (d->override_ops && d->override_ops->set_trip_temp)
- return d->override_ops->set_trip_temp(zone, trip, temp);
+ if (trip > 9)
+ return -EINVAL;
- snprintf(name, sizeof(name), "PAT%d", trip);
status = acpi_execute_simple_method(d->adev->handle, name,
- millicelsius_to_deci_kelvin(temp));
+ millicelsius_to_deci_kelvin(temp));
if (ACPI_FAILURE(status))
return -EIO;
- d->aux_trips[trip] = temp;
-
- return 0;
-}
-
-
-static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone,
- int trip, int *temp)
-{
- struct int34x_thermal_zone *d = zone->devdata;
- acpi_status status;
- unsigned long long hyst;
-
- if (d->override_ops && d->override_ops->get_trip_hyst)
- return d->override_ops->get_trip_hyst(zone, trip, temp);
-
- status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst);
- if (ACPI_FAILURE(status))
- *temp = 0;
- else
- *temp = hyst * 100;
-
return 0;
}
@@ -153,63 +63,49 @@ static void int340x_thermal_critical(struct thermal_zone_device *zone)
static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
.get_temp = int340x_thermal_get_zone_temp,
- .get_trip_temp = int340x_thermal_get_trip_temp,
- .get_trip_type = int340x_thermal_get_trip_type,
.set_trip_temp = int340x_thermal_set_trip_temp,
- .get_trip_hyst = int340x_thermal_get_trip_hyst,
.critical = int340x_thermal_critical,
};
-static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
- int *temp)
+static int int340x_thermal_read_trips(struct acpi_device *zone_adev,
+ struct thermal_trip *zone_trips,
+ int trip_cnt)
{
- unsigned long long r;
- acpi_status status;
-
- status = acpi_evaluate_integer(handle, name, NULL, &r);
- if (ACPI_FAILURE(status))
- return -EIO;
-
- *temp = deci_kelvin_to_millicelsius(r);
+ int i, ret;
- return 0;
-}
-
-int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone)
-{
- int trip_cnt = int34x_zone->aux_trip_nr;
- int i;
-
- int34x_zone->crt_trip_id = -1;
- if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_CRT",
- &int34x_zone->crt_temp))
- int34x_zone->crt_trip_id = trip_cnt++;
+ ret = thermal_acpi_critical_trip_temp(zone_adev,
+ &zone_trips[trip_cnt].temperature);
+ if (!ret) {
+ zone_trips[trip_cnt].type = THERMAL_TRIP_CRITICAL;
+ trip_cnt++;
+ }
- int34x_zone->hot_trip_id = -1;
- if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_HOT",
- &int34x_zone->hot_temp))
- int34x_zone->hot_trip_id = trip_cnt++;
+ ret = thermal_acpi_hot_trip_temp(zone_adev,
+ &zone_trips[trip_cnt].temperature);
+ if (!ret) {
+ zone_trips[trip_cnt].type = THERMAL_TRIP_HOT;
+ trip_cnt++;
+ }
- int34x_zone->psv_trip_id = -1;
- if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_PSV",
- &int34x_zone->psv_temp))
- int34x_zone->psv_trip_id = trip_cnt++;
+ ret = thermal_acpi_passive_trip_temp(zone_adev,
+ &zone_trips[trip_cnt].temperature);
+ if (!ret) {
+ zone_trips[trip_cnt].type = THERMAL_TRIP_PASSIVE;
+ trip_cnt++;
+ }
for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
- char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
-
- if (int340x_thermal_get_trip_config(int34x_zone->adev->handle,
- name,
- &int34x_zone->act_trips[i].temp))
+ ret = thermal_acpi_active_trip_temp(zone_adev, i,
+ &zone_trips[trip_cnt].temperature);
+ if (ret)
break;
- int34x_zone->act_trips[i].id = trip_cnt++;
- int34x_zone->act_trips[i].valid = true;
+ zone_trips[trip_cnt].type = THERMAL_TRIP_ACTIVE;
+ trip_cnt++;
}
return trip_cnt;
}
-EXPORT_SYMBOL_GPL(int340x_thermal_read_trips);
static struct thermal_zone_params int340x_thermal_params = {
.governor_name = "user_space",
@@ -217,85 +113,147 @@ static struct thermal_zone_params int340x_thermal_params = {
};
struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
- struct thermal_zone_device_ops *override_ops)
+ int (*get_temp) (struct thermal_zone_device *, int *))
{
- struct int34x_thermal_zone *int34x_thermal_zone;
- acpi_status status;
- unsigned long long trip_cnt;
+ struct int34x_thermal_zone *int34x_zone;
+ struct thermal_trip *zone_trips;
+ unsigned long long trip_cnt = 0;
+ unsigned long long hyst;
int trip_mask = 0;
- int ret;
+ acpi_status status;
+ int i, ret;
- int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone),
- GFP_KERNEL);
- if (!int34x_thermal_zone)
+ int34x_zone = kzalloc(sizeof(*int34x_zone), GFP_KERNEL);
+ if (!int34x_zone)
return ERR_PTR(-ENOMEM);
- int34x_thermal_zone->adev = adev;
- int34x_thermal_zone->override_ops = override_ops;
+ int34x_zone->adev = adev;
+
+ int34x_zone->ops = kmemdup(&int340x_thermal_zone_ops,
+ sizeof(int340x_thermal_zone_ops), GFP_KERNEL);
+ if (!int34x_zone->ops) {
+ ret = -ENOMEM;
+ goto err_ops_alloc;
+ }
+
+ if (get_temp)
+ int34x_zone->ops->get_temp = get_temp;
status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
- if (ACPI_FAILURE(status))
- trip_cnt = 0;
- else {
- int i;
-
- int34x_thermal_zone->aux_trips =
- kcalloc(trip_cnt,
- sizeof(*int34x_thermal_zone->aux_trips),
- GFP_KERNEL);
- if (!int34x_thermal_zone->aux_trips) {
- ret = -ENOMEM;
- goto err_trip_alloc;
- }
+ if (ACPI_SUCCESS(status)) {
+ int34x_zone->aux_trip_nr = trip_cnt;
trip_mask = BIT(trip_cnt) - 1;
- int34x_thermal_zone->aux_trip_nr = trip_cnt;
- for (i = 0; i < trip_cnt; ++i)
- int34x_thermal_zone->aux_trips[i] = THERMAL_TEMP_INVALID;
}
- trip_cnt = int340x_thermal_read_trips(int34x_thermal_zone);
+ zone_trips = kzalloc(sizeof(*zone_trips) * (trip_cnt + INT340X_THERMAL_MAX_TRIP_COUNT),
+ GFP_KERNEL);
+ if (!zone_trips) {
+ ret = -ENOMEM;
+ goto err_trips_alloc;
+ }
+
+ for (i = 0; i < trip_cnt; i++) {
+ zone_trips[i].type = THERMAL_TRIP_PASSIVE;
+ zone_trips[i].temperature = THERMAL_TEMP_INVALID;
+ }
+
+ trip_cnt = int340x_thermal_read_trips(adev, zone_trips, trip_cnt);
- int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table(
- adev->handle);
+ status = acpi_evaluate_integer(adev->handle, "GTSH", NULL, &hyst);
+ if (ACPI_SUCCESS(status))
+ hyst *= 100;
+ else
+ hyst = 0;
+
+ for (i = 0; i < trip_cnt; ++i)
+ zone_trips[i].hysteresis = hyst;
+
+ int34x_zone->trips = zone_trips;
- int34x_thermal_zone->zone = thermal_zone_device_register(
- acpi_device_bid(adev),
- trip_cnt,
- trip_mask, int34x_thermal_zone,
- &int340x_thermal_zone_ops,
- &int340x_thermal_params,
- 0, 0);
- if (IS_ERR(int34x_thermal_zone->zone)) {
- ret = PTR_ERR(int34x_thermal_zone->zone);
+ int34x_zone->lpat_table = acpi_lpat_get_conversion_table(adev->handle);
+
+ int34x_zone->zone = thermal_zone_device_register_with_trips(
+ acpi_device_bid(adev),
+ zone_trips, trip_cnt,
+ trip_mask, int34x_zone,
+ int34x_zone->ops,
+ &int340x_thermal_params,
+ 0, 0);
+ if (IS_ERR(int34x_zone->zone)) {
+ ret = PTR_ERR(int34x_zone->zone);
goto err_thermal_zone;
}
- ret = thermal_zone_device_enable(int34x_thermal_zone->zone);
+ ret = thermal_zone_device_enable(int34x_zone->zone);
if (ret)
goto err_enable;
- return int34x_thermal_zone;
+ return int34x_zone;
err_enable:
- thermal_zone_device_unregister(int34x_thermal_zone->zone);
+ thermal_zone_device_unregister(int34x_zone->zone);
err_thermal_zone:
- acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
- kfree(int34x_thermal_zone->aux_trips);
-err_trip_alloc:
- kfree(int34x_thermal_zone);
+ kfree(int34x_zone->trips);
+ acpi_lpat_free_conversion_table(int34x_zone->lpat_table);
+err_trips_alloc:
+ kfree(int34x_zone->ops);
+err_ops_alloc:
+ kfree(int34x_zone);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(int340x_thermal_zone_add);
-void int340x_thermal_zone_remove(struct int34x_thermal_zone
- *int34x_thermal_zone)
+void int340x_thermal_zone_remove(struct int34x_thermal_zone *int34x_zone)
{
- thermal_zone_device_unregister(int34x_thermal_zone->zone);
- acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
- kfree(int34x_thermal_zone->aux_trips);
- kfree(int34x_thermal_zone);
+ thermal_zone_device_unregister(int34x_zone->zone);
+ acpi_lpat_free_conversion_table(int34x_zone->lpat_table);
+ kfree(int34x_zone->trips);
+ kfree(int34x_zone->ops);
+ kfree(int34x_zone);
}
EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
+void int340x_thermal_update_trips(struct int34x_thermal_zone *int34x_zone)
+{
+ struct acpi_device *zone_adev = int34x_zone->adev;
+ struct thermal_trip *zone_trips = int34x_zone->trips;
+ int trip_cnt = int34x_zone->zone->num_trips;
+ int act_trip_nr = 0;
+ int i;
+
+ mutex_lock(&int34x_zone->zone->lock);
+
+ for (i = int34x_zone->aux_trip_nr; i < trip_cnt; i++) {
+ int temp, err;
+
+ switch (zone_trips[i].type) {
+ case THERMAL_TRIP_CRITICAL:
+ err = thermal_acpi_critical_trip_temp(zone_adev, &temp);
+ break;
+ case THERMAL_TRIP_HOT:
+ err = thermal_acpi_hot_trip_temp(zone_adev, &temp);
+ break;
+ case THERMAL_TRIP_PASSIVE:
+ err = thermal_acpi_passive_trip_temp(zone_adev, &temp);
+ break;
+ case THERMAL_TRIP_ACTIVE:
+ err = thermal_acpi_active_trip_temp(zone_adev, act_trip_nr++,
+ &temp);
+ break;
+ default:
+ err = -ENODEV;
+ }
+ if (err) {
+ zone_trips[i].temperature = THERMAL_TEMP_INVALID;
+ continue;
+ }
+
+ zone_trips[i].temperature = temp;
+ }
+
+ mutex_unlock(&int34x_zone->zone->lock);
+}
+EXPORT_SYMBOL_GPL(int340x_thermal_update_trips);
+
MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");
diff --git a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
index 3b4971df1b33..e0df6271facc 100644
--- a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
+++ b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
@@ -10,6 +10,7 @@
#include <acpi/acpi_lpat.h>
#define INT340X_THERMAL_MAX_ACT_TRIP_COUNT 10
+#define INT340X_THERMAL_MAX_TRIP_COUNT INT340X_THERMAL_MAX_ACT_TRIP_COUNT + 3
struct active_trip {
int temp;
@@ -19,25 +20,18 @@ struct active_trip {
struct int34x_thermal_zone {
struct acpi_device *adev;
- struct active_trip act_trips[INT340X_THERMAL_MAX_ACT_TRIP_COUNT];
- unsigned long *aux_trips;
+ struct thermal_trip *trips;
int aux_trip_nr;
- int psv_temp;
- int psv_trip_id;
- int crt_temp;
- int crt_trip_id;
- int hot_temp;
- int hot_trip_id;
struct thermal_zone_device *zone;
- struct thermal_zone_device_ops *override_ops;
+ struct thermal_zone_device_ops *ops;
void *priv_data;
struct acpi_lpat_conversion_table *lpat_table;
};
struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *,
- struct thermal_zone_device_ops *override_ops);
+ int (*get_temp) (struct thermal_zone_device *, int *));
void int340x_thermal_zone_remove(struct int34x_thermal_zone *);
-int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone);
+void int340x_thermal_update_trips(struct int34x_thermal_zone *int34x_zone);
static inline void int340x_thermal_zone_set_priv_data(
struct int34x_thermal_zone *tzone, void *priv_data)
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
index a8d98f1bd6c6..a1dc18be7609 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
@@ -4,6 +4,7 @@
* Copyright (c) 2014, Intel Corporation.
*/
#include <linux/acpi.h>
+#include <linux/intel_tcc.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -68,54 +69,17 @@ static const struct attribute_group power_limit_attribute_group = {
.name = "power_limits"
};
-static int tcc_get_offset(void)
-{
- u64 val;
- int err;
-
- err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
- if (err)
- return err;
-
- return (val >> 24) & 0x3f;
-}
-
static ssize_t tcc_offset_degree_celsius_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- int tcc;
-
- tcc = tcc_get_offset();
- if (tcc < 0)
- return tcc;
-
- return sprintf(buf, "%d\n", tcc);
-}
-
-static int tcc_offset_update(unsigned int tcc)
-{
- u64 val;
- int err;
+ int offset;
- if (tcc > 63)
- return -EINVAL;
-
- err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
- if (err)
- return err;
+ offset = intel_tcc_get_offset(-1);
+ if (offset < 0)
+ return offset;
- if (val & BIT(31))
- return -EPERM;
-
- val &= ~GENMASK_ULL(29, 24);
- val |= (tcc & 0x3f) << 24;
-
- err = wrmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, val);
- if (err)
- return err;
-
- return 0;
+ return sprintf(buf, "%d\n", offset);
}
static ssize_t tcc_offset_degree_celsius_store(struct device *dev,
@@ -136,7 +100,7 @@ static ssize_t tcc_offset_degree_celsius_store(struct device *dev,
if (kstrtouint(buf, 0, &tcc))
return -EINVAL;
- err = tcc_offset_update(tcc);
+ err = intel_tcc_set_offset(-1, tcc);
if (err)
return err;
@@ -145,72 +109,27 @@ static ssize_t tcc_offset_degree_celsius_store(struct device *dev,
static DEVICE_ATTR_RW(tcc_offset_degree_celsius);
-static int stored_tjmax; /* since it is fixed, we can have local storage */
-
-static int get_tjmax(void)
-{
- u32 eax, edx;
- u32 val;
- int err;
-
- err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
- if (err)
- return err;
-
- val = (eax >> 16) & 0xff;
- if (val)
- return val;
-
- return -EINVAL;
-}
-
-static int read_temp_msr(int *temp)
+static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
+ int *temp)
{
int cpu;
- u32 eax, edx;
- int err;
- unsigned long curr_temp_off = 0;
+ int curr_temp;
*temp = 0;
for_each_online_cpu(cpu) {
- err = rdmsr_safe_on_cpu(cpu, MSR_IA32_THERM_STATUS, &eax,
- &edx);
- if (err)
- goto err_ret;
- else {
- if (eax & 0x80000000) {
- curr_temp_off = (eax >> 16) & 0x7f;
- if (!*temp || curr_temp_off < *temp)
- *temp = curr_temp_off;
- } else {
- err = -EINVAL;
- goto err_ret;
- }
- }
+ curr_temp = intel_tcc_get_temp(cpu, false);
+ if (curr_temp < 0)
+ return curr_temp;
+ if (!*temp || curr_temp > *temp)
+ *temp = curr_temp;
}
- return 0;
-err_ret:
- return err;
-}
+ *temp *= 1000;
-static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
- int *temp)
-{
- int ret;
-
- ret = read_temp_msr(temp);
- if (!ret)
- *temp = (stored_tjmax - *temp) * 1000;
-
- return ret;
+ return 0;
}
-static struct thermal_zone_device_ops proc_thermal_local_ops = {
- .get_temp = proc_thermal_get_zone_temp,
-};
-
static int proc_thermal_read_ppcc(struct proc_thermal_device *proc_priv)
{
int i;
@@ -285,7 +204,7 @@ int proc_thermal_add(struct device *dev, struct proc_thermal_device *proc_priv)
struct acpi_device *adev;
acpi_status status;
unsigned long long tmp;
- struct thermal_zone_device_ops *ops = NULL;
+ int (*get_temp) (struct thermal_zone_device *, int *) = NULL;
int ret;
adev = ACPI_COMPANION(dev);
@@ -302,12 +221,11 @@ int proc_thermal_add(struct device *dev, struct proc_thermal_device *proc_priv)
status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp);
if (ACPI_FAILURE(status)) {
/* there is no _TMP method, add local method */
- stored_tjmax = get_tjmax();
- if (stored_tjmax > 0)
- ops = &proc_thermal_local_ops;
+ if (intel_tcc_get_tjmax(-1) > 0)
+ get_temp = proc_thermal_get_zone_temp;
}
- proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops);
+ proc_priv->int340x_zone = int340x_thermal_zone_add(adev, get_temp);
if (IS_ERR(proc_priv->int340x_zone)) {
return PTR_ERR(proc_priv->int340x_zone);
} else
@@ -356,7 +274,7 @@ static int tcc_offset_save = -1;
int proc_thermal_suspend(struct device *dev)
{
- tcc_offset_save = tcc_get_offset();
+ tcc_offset_save = intel_tcc_get_offset(-1);
if (tcc_offset_save < 0)
dev_warn(dev, "failed to save offset (%d)\n", tcc_offset_save);
@@ -373,7 +291,7 @@ int proc_thermal_resume(struct device *dev)
/* Do not update if saving failed */
if (tcc_offset_save >= 0)
- tcc_offset_update(tcc_offset_save);
+ intel_tcc_set_offset(-1, tcc_offset_save);
return 0;
}
@@ -460,6 +378,7 @@ void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *
}
EXPORT_SYMBOL_GPL(proc_thermal_mmio_remove);
+MODULE_IMPORT_NS(INTEL_TCC);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
index bf1b1cdfade4..40725cbc6eb0 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c
@@ -144,34 +144,6 @@ static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp)
return 0;
}
-static int sys_get_trip_temp(struct thermal_zone_device *tzd,
- int trip, int *temp)
-{
- struct proc_thermal_pci *pci_info = tzd->devdata;
- u32 _temp;
-
- proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_THRES_0, &_temp);
- if (!_temp) {
- *temp = THERMAL_TEMP_INVALID;
- } else {
- int tjmax;
-
- proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_TJMAX, &tjmax);
- _temp = tjmax - _temp;
- *temp = (unsigned long)_temp * 1000;
- }
-
- return 0;
-}
-
-static int sys_get_trip_type(struct thermal_zone_device *tzd, int trip,
- enum thermal_trip_type *type)
-{
- *type = THERMAL_TRIP_PASSIVE;
-
- return 0;
-}
-
static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
{
struct proc_thermal_pci *pci_info = tzd->devdata;
@@ -200,10 +172,26 @@ static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp
return 0;
}
+static int get_trip_temp(struct proc_thermal_pci *pci_info)
+{
+ int temp, tjmax;
+
+ proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_THRES_0, &temp);
+ if (!temp)
+ return THERMAL_TEMP_INVALID;
+
+ proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_TJMAX, &tjmax);
+ temp = (tjmax - temp) * 1000;
+
+ return temp;
+}
+
+static struct thermal_trip psv_trip = {
+ .type = THERMAL_TRIP_PASSIVE,
+};
+
static struct thermal_zone_device_ops tzone_ops = {
.get_temp = sys_get_curr_temp,
- .get_trip_temp = sys_get_trip_temp,
- .get_trip_type = sys_get_trip_type,
.set_trip_temp = sys_set_trip_temp,
};
@@ -251,7 +239,10 @@ static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_
if (ret)
goto err_ret_thermal;
- pci_info->tzone = thermal_zone_device_register("TCPU_PCI", 1, 1, pci_info,
+ psv_trip.temperature = get_trip_temp(pci_info);
+
+ pci_info->tzone = thermal_zone_device_register_with_trips("TCPU_PCI", &psv_trip,
+ 1, 1, pci_info,
&tzone_ops,
&tzone_params, 0, 0);
if (IS_ERR(pci_info->tzone)) {
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c
index 8c42e7662033..92ed1213fe37 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c
@@ -172,6 +172,7 @@ static const struct attribute_group fivr_attribute_group = {
RFIM_SHOW(rfi_restriction_run_busy, 1)
RFIM_SHOW(rfi_restriction_err_code, 1)
RFIM_SHOW(rfi_restriction_data_rate, 1)
+RFIM_SHOW(rfi_restriction_data_rate_base, 1)
RFIM_SHOW(ddr_data_rate_point_0, 1)
RFIM_SHOW(ddr_data_rate_point_1, 1)
RFIM_SHOW(ddr_data_rate_point_2, 1)
@@ -181,11 +182,13 @@ RFIM_SHOW(rfi_disable, 1)
RFIM_STORE(rfi_restriction_run_busy, 1)
RFIM_STORE(rfi_restriction_err_code, 1)
RFIM_STORE(rfi_restriction_data_rate, 1)
+RFIM_STORE(rfi_restriction_data_rate_base, 1)
RFIM_STORE(rfi_disable, 1)
static DEVICE_ATTR_RW(rfi_restriction_run_busy);
static DEVICE_ATTR_RW(rfi_restriction_err_code);
static DEVICE_ATTR_RW(rfi_restriction_data_rate);
+static DEVICE_ATTR_RW(rfi_restriction_data_rate_base);
static DEVICE_ATTR_RO(ddr_data_rate_point_0);
static DEVICE_ATTR_RO(ddr_data_rate_point_1);
static DEVICE_ATTR_RO(ddr_data_rate_point_2);
@@ -248,6 +251,7 @@ static struct attribute *dvfs_attrs[] = {
&dev_attr_rfi_restriction_run_busy.attr,
&dev_attr_rfi_restriction_err_code.attr,
&dev_attr_rfi_restriction_data_rate.attr,
+ &dev_attr_rfi_restriction_data_rate_base.attr,
&dev_attr_ddr_data_rate_point_0.attr,
&dev_attr_ddr_data_rate_point_1.attr,
&dev_attr_ddr_data_rate_point_2.attr,
diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c
index 6e604bda2b93..c69db6c90869 100644
--- a/drivers/thermal/intel/intel_hfi.c
+++ b/drivers/thermal/intel/intel_hfi.c
@@ -40,10 +40,11 @@
#include <asm/msr.h>
-#include "../thermal_core.h"
#include "intel_hfi.h"
#include "thermal_interrupt.h"
+#include "../thermal_netlink.h"
+
/* Hardware Feedback Interface MSR configuration bits */
#define HW_FEEDBACK_PTR_VALID_BIT BIT(0)
#define HW_FEEDBACK_CONFIG_HFI_ENABLE_BIT BIT(0)
diff --git a/drivers/thermal/intel/intel_menlow.c b/drivers/thermal/intel/intel_menlow.c
index 3f885b08a490..5a6ad0552311 100644
--- a/drivers/thermal/intel/intel_menlow.c
+++ b/drivers/thermal/intel/intel_menlow.c
@@ -232,9 +232,9 @@ static DEFINE_MUTEX(intel_menlow_attr_lock);
/*
* sensor_get_auxtrip - get the current auxtrip value from sensor
- * @name: Thermalzone name
- * @auxtype : AUX0/AUX1
- * @buf: syfs buffer
+ * @handle: Object handle
+ * @index : GET_AUX1/GET_AUX0
+ * @value : The address will be fill by the value
*/
static int sensor_get_auxtrip(acpi_handle handle, int index,
unsigned long long *value)
@@ -254,9 +254,9 @@ static int sensor_get_auxtrip(acpi_handle handle, int index,
/*
* sensor_set_auxtrip - set the new auxtrip value to sensor
- * @name: Thermalzone name
- * @auxtype : AUX0/AUX1
- * @buf: syfs buffer
+ * @handle: Object handle
+ * @index : GET_AUX1/GET_AUX0
+ * @value : The value will be set
*/
static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
{
diff --git a/drivers/thermal/intel/intel_pch_thermal.c b/drivers/thermal/intel/intel_pch_thermal.c
index dabf11a687a1..b855d031a855 100644
--- a/drivers/thermal/intel/intel_pch_thermal.c
+++ b/drivers/thermal/intel/intel_pch_thermal.c
@@ -29,6 +29,7 @@
#define PCH_THERMAL_DID_CNL_LP 0x02F9 /* CNL-LP PCH */
#define PCH_THERMAL_DID_CML_H 0X06F9 /* CML-H PCH */
#define PCH_THERMAL_DID_LWB 0xA1B1 /* Lewisburg PCH */
+#define PCH_THERMAL_DID_WBG 0x8D24 /* Wellsburg PCH */
/* Wildcat Point-LP PCH Thermal registers */
#define WPT_TEMP 0x0000 /* Temperature */
@@ -65,6 +66,8 @@
#define WPT_TEMP_OFFSET (PCH_TEMP_OFFSET * MILLIDEGREE_PER_DEGREE)
#define GET_PCH_TEMP(x) (((x) / 2) + PCH_TEMP_OFFSET)
+#define PCH_MAX_TRIPS 3 /* critical, hot, passive */
+
/* Amount of time for each cooling delay, 100ms by default for now */
static unsigned int delay_timeout = 100;
module_param(delay_timeout, int, 0644);
@@ -79,66 +82,114 @@ static char driver_name[] = "Intel PCH thermal driver";
struct pch_thermal_device {
void __iomem *hw_base;
- const struct pch_dev_ops *ops;
struct pci_dev *pdev;
struct thermal_zone_device *tzd;
- int crt_trip_id;
- unsigned long crt_temp;
- int hot_trip_id;
- unsigned long hot_temp;
- int psv_trip_id;
- unsigned long psv_temp;
+ struct thermal_trip trips[PCH_MAX_TRIPS];
bool bios_enabled;
};
#ifdef CONFIG_ACPI
-
/*
* On some platforms, there is a companion ACPI device, which adds
* passive trip temperature using _PSV method. There is no specific
* passive temperature setting in MMIO interface of this PCI device.
*/
-static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
- int *nr_trips)
+static int pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd, int trip)
{
struct acpi_device *adev;
-
- ptd->psv_trip_id = -1;
+ int temp;
adev = ACPI_COMPANION(&ptd->pdev->dev);
- if (adev) {
- unsigned long long r;
- acpi_status status;
-
- status = acpi_evaluate_integer(adev->handle, "_PSV", NULL,
- &r);
- if (ACPI_SUCCESS(status)) {
- unsigned long trip_temp;
-
- trip_temp = deci_kelvin_to_millicelsius(r);
- if (trip_temp) {
- ptd->psv_temp = trip_temp;
- ptd->psv_trip_id = *nr_trips;
- ++(*nr_trips);
- }
- }
- }
+ if (!adev)
+ return 0;
+
+ if (thermal_acpi_passive_trip_temp(adev, &temp) || temp <= 0)
+ return 0;
+
+ ptd->trips[trip].type = THERMAL_TRIP_PASSIVE;
+ ptd->trips[trip].temperature = temp;
+ return 1;
}
#else
-static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
- int *nr_trips)
+static int pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd, int trip)
{
- ptd->psv_trip_id = -1;
-
+ return 0;
}
#endif
-static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips)
+static int pch_thermal_get_temp(struct thermal_zone_device *tzd, int *temp)
{
- u8 tsel;
+ struct pch_thermal_device *ptd = tzd->devdata;
+
+ *temp = GET_WPT_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP));
+ return 0;
+}
+
+static void pch_critical(struct thermal_zone_device *tzd)
+{
+ dev_dbg(&tzd->device, "%s: critical temperature reached\n", tzd->type);
+}
+
+static struct thermal_zone_device_ops tzd_ops = {
+ .get_temp = pch_thermal_get_temp,
+ .critical = pch_critical,
+};
+
+enum pch_board_ids {
+ PCH_BOARD_HSW = 0,
+ PCH_BOARD_WPT,
+ PCH_BOARD_SKL,
+ PCH_BOARD_CNL,
+ PCH_BOARD_CML,
+ PCH_BOARD_LWB,
+ PCH_BOARD_WBG,
+};
+
+static const char *board_names[] = {
+ [PCH_BOARD_HSW] = "pch_haswell",
+ [PCH_BOARD_WPT] = "pch_wildcat_point",
+ [PCH_BOARD_SKL] = "pch_skylake",
+ [PCH_BOARD_CNL] = "pch_cannonlake",
+ [PCH_BOARD_CML] = "pch_cometlake",
+ [PCH_BOARD_LWB] = "pch_lewisburg",
+ [PCH_BOARD_WBG] = "pch_wellsburg",
+};
+
+static int intel_pch_thermal_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ enum pch_board_ids board_id = id->driver_data;
+ struct pch_thermal_device *ptd;
+ int nr_trips = 0;
u16 trip_temp;
+ u8 tsel;
+ int err;
+
+ ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL);
+ if (!ptd)
+ return -ENOMEM;
+
+ pci_set_drvdata(pdev, ptd);
+ ptd->pdev = pdev;
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "failed to enable pci device\n");
+ return err;
+ }
+
+ err = pci_request_regions(pdev, driver_name);
+ if (err) {
+ dev_err(&pdev->dev, "failed to request pci region\n");
+ goto error_disable;
+ }
- *nr_trips = 0;
+ ptd->hw_base = pci_ioremap_bar(pdev, 0);
+ if (!ptd->hw_base) {
+ err = -ENOMEM;
+ dev_err(&pdev->dev, "failed to map mem base\n");
+ goto error_release;
+ }
/* Check if BIOS has already enabled thermal sensor */
if (WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL)) {
@@ -153,52 +204,79 @@ static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips)
*/
if (tsel & WPT_TSEL_PLDB) {
dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
- return -ENODEV;
+ err = -ENODEV;
+ goto error_cleanup;
}
writeb(tsel|WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
if (!(WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL))) {
dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
- return -ENODEV;
+ err = -ENODEV;
+ goto error_cleanup;
}
read_trips:
- ptd->crt_trip_id = -1;
trip_temp = readw(ptd->hw_base + WPT_CTT);
trip_temp &= 0x1FF;
if (trip_temp) {
- ptd->crt_temp = GET_WPT_TEMP(trip_temp);
- ptd->crt_trip_id = 0;
- ++(*nr_trips);
+ ptd->trips[nr_trips].temperature = GET_WPT_TEMP(trip_temp);
+ ptd->trips[nr_trips++].type = THERMAL_TRIP_CRITICAL;
}
- ptd->hot_trip_id = -1;
trip_temp = readw(ptd->hw_base + WPT_PHL);
trip_temp &= 0x1FF;
if (trip_temp) {
- ptd->hot_temp = GET_WPT_TEMP(trip_temp);
- ptd->hot_trip_id = *nr_trips;
- ++(*nr_trips);
+ ptd->trips[nr_trips].temperature = GET_WPT_TEMP(trip_temp);
+ ptd->trips[nr_trips++].type = THERMAL_TRIP_HOT;
}
- pch_wpt_add_acpi_psv_trip(ptd, nr_trips);
+ nr_trips += pch_wpt_add_acpi_psv_trip(ptd, nr_trips);
+
+ ptd->tzd = thermal_zone_device_register_with_trips(board_names[board_id],
+ ptd->trips, nr_trips,
+ 0, ptd, &tzd_ops,
+ NULL, 0, 0);
+ if (IS_ERR(ptd->tzd)) {
+ dev_err(&pdev->dev, "Failed to register thermal zone %s\n",
+ board_names[board_id]);
+ err = PTR_ERR(ptd->tzd);
+ goto error_cleanup;
+ }
+ err = thermal_zone_device_enable(ptd->tzd);
+ if (err)
+ goto err_unregister;
return 0;
+
+err_unregister:
+ thermal_zone_device_unregister(ptd->tzd);
+error_cleanup:
+ iounmap(ptd->hw_base);
+error_release:
+ pci_release_regions(pdev);
+error_disable:
+ pci_disable_device(pdev);
+ dev_err(&pdev->dev, "pci device failed to probe\n");
+ return err;
}
-static int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp)
+static void intel_pch_thermal_remove(struct pci_dev *pdev)
{
- *temp = GET_WPT_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP));
+ struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
- return 0;
+ thermal_zone_device_unregister(ptd->tzd);
+ iounmap(ptd->hw_base);
+ pci_set_drvdata(pdev, NULL);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
}
-/* Cool the PCH when it's overheat in .suspend_noirq phase */
-static int pch_wpt_suspend(struct pch_thermal_device *ptd)
+static int intel_pch_thermal_suspend_noirq(struct device *device)
{
- u8 tsel;
- int pch_delay_cnt = 0;
+ struct pch_thermal_device *ptd = dev_get_drvdata(device);
u16 pch_thr_temp, pch_cur_temp;
+ int pch_delay_cnt = 0;
+ u8 tsel;
/* Shutdown the thermal sensor if it is not enabled by BIOS */
if (!ptd->bios_enabled) {
@@ -261,8 +339,9 @@ static int pch_wpt_suspend(struct pch_thermal_device *ptd)
return 0;
}
-static int pch_wpt_resume(struct pch_thermal_device *ptd)
+static int intel_pch_thermal_resume(struct device *device)
{
+ struct pch_thermal_device *ptd = dev_get_drvdata(device);
u8 tsel;
if (ptd->bios_enabled)
@@ -275,226 +354,29 @@ static int pch_wpt_resume(struct pch_thermal_device *ptd)
return 0;
}
-struct pch_dev_ops {
- int (*hw_init)(struct pch_thermal_device *ptd, int *nr_trips);
- int (*get_temp)(struct pch_thermal_device *ptd, int *temp);
- int (*suspend)(struct pch_thermal_device *ptd);
- int (*resume)(struct pch_thermal_device *ptd);
-};
-
-
-/* dev ops for Wildcat Point */
-static const struct pch_dev_ops pch_dev_ops_wpt = {
- .hw_init = pch_wpt_init,
- .get_temp = pch_wpt_get_temp,
- .suspend = pch_wpt_suspend,
- .resume = pch_wpt_resume,
-};
-
-static int pch_thermal_get_temp(struct thermal_zone_device *tzd, int *temp)
-{
- struct pch_thermal_device *ptd = tzd->devdata;
-
- return ptd->ops->get_temp(ptd, temp);
-}
-
-static int pch_get_trip_type(struct thermal_zone_device *tzd, int trip,
- enum thermal_trip_type *type)
-{
- struct pch_thermal_device *ptd = tzd->devdata;
-
- if (ptd->crt_trip_id == trip)
- *type = THERMAL_TRIP_CRITICAL;
- else if (ptd->hot_trip_id == trip)
- *type = THERMAL_TRIP_HOT;
- else if (ptd->psv_trip_id == trip)
- *type = THERMAL_TRIP_PASSIVE;
- else
- return -EINVAL;
-
- return 0;
-}
-
-static int pch_get_trip_temp(struct thermal_zone_device *tzd, int trip, int *temp)
-{
- struct pch_thermal_device *ptd = tzd->devdata;
-
- if (ptd->crt_trip_id == trip)
- *temp = ptd->crt_temp;
- else if (ptd->hot_trip_id == trip)
- *temp = ptd->hot_temp;
- else if (ptd->psv_trip_id == trip)
- *temp = ptd->psv_temp;
- else
- return -EINVAL;
-
- return 0;
-}
-
-static void pch_critical(struct thermal_zone_device *tzd)
-{
- dev_dbg(&tzd->device, "%s: critical temperature reached\n", tzd->type);
-}
-
-static struct thermal_zone_device_ops tzd_ops = {
- .get_temp = pch_thermal_get_temp,
- .get_trip_type = pch_get_trip_type,
- .get_trip_temp = pch_get_trip_temp,
- .critical = pch_critical,
-};
-
-enum board_ids {
- board_hsw,
- board_wpt,
- board_skl,
- board_cnl,
- board_cml,
- board_lwb,
-};
-
-static const struct board_info {
- const char *name;
- const struct pch_dev_ops *ops;
-} board_info[] = {
- [board_hsw] = {
- .name = "pch_haswell",
- .ops = &pch_dev_ops_wpt,
- },
- [board_wpt] = {
- .name = "pch_wildcat_point",
- .ops = &pch_dev_ops_wpt,
- },
- [board_skl] = {
- .name = "pch_skylake",
- .ops = &pch_dev_ops_wpt,
- },
- [board_cnl] = {
- .name = "pch_cannonlake",
- .ops = &pch_dev_ops_wpt,
- },
- [board_cml] = {
- .name = "pch_cometlake",
- .ops = &pch_dev_ops_wpt,
- },
- [board_lwb] = {
- .name = "pch_lewisburg",
- .ops = &pch_dev_ops_wpt,
- },
-};
-
-static int intel_pch_thermal_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
-{
- enum board_ids board_id = id->driver_data;
- const struct board_info *bi = &board_info[board_id];
- struct pch_thermal_device *ptd;
- int err;
- int nr_trips;
-
- ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL);
- if (!ptd)
- return -ENOMEM;
-
- ptd->ops = bi->ops;
-
- pci_set_drvdata(pdev, ptd);
- ptd->pdev = pdev;
-
- err = pci_enable_device(pdev);
- if (err) {
- dev_err(&pdev->dev, "failed to enable pci device\n");
- return err;
- }
-
- err = pci_request_regions(pdev, driver_name);
- if (err) {
- dev_err(&pdev->dev, "failed to request pci region\n");
- goto error_disable;
- }
-
- ptd->hw_base = pci_ioremap_bar(pdev, 0);
- if (!ptd->hw_base) {
- err = -ENOMEM;
- dev_err(&pdev->dev, "failed to map mem base\n");
- goto error_release;
- }
-
- err = ptd->ops->hw_init(ptd, &nr_trips);
- if (err)
- goto error_cleanup;
-
- ptd->tzd = thermal_zone_device_register(bi->name, nr_trips, 0, ptd,
- &tzd_ops, NULL, 0, 0);
- if (IS_ERR(ptd->tzd)) {
- dev_err(&pdev->dev, "Failed to register thermal zone %s\n",
- bi->name);
- err = PTR_ERR(ptd->tzd);
- goto error_cleanup;
- }
- err = thermal_zone_device_enable(ptd->tzd);
- if (err)
- goto err_unregister;
-
- return 0;
-
-err_unregister:
- thermal_zone_device_unregister(ptd->tzd);
-error_cleanup:
- iounmap(ptd->hw_base);
-error_release:
- pci_release_regions(pdev);
-error_disable:
- pci_disable_device(pdev);
- dev_err(&pdev->dev, "pci device failed to probe\n");
- return err;
-}
-
-static void intel_pch_thermal_remove(struct pci_dev *pdev)
-{
- struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
-
- thermal_zone_device_unregister(ptd->tzd);
- iounmap(ptd->hw_base);
- pci_set_drvdata(pdev, NULL);
- pci_release_regions(pdev);
- pci_disable_device(pdev);
-}
-
-static int intel_pch_thermal_suspend_noirq(struct device *device)
-{
- struct pch_thermal_device *ptd = dev_get_drvdata(device);
-
- return ptd->ops->suspend(ptd);
-}
-
-static int intel_pch_thermal_resume(struct device *device)
-{
- struct pch_thermal_device *ptd = dev_get_drvdata(device);
-
- return ptd->ops->resume(ptd);
-}
-
static const struct pci_device_id intel_pch_thermal_id[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_1),
- .driver_data = board_hsw, },
+ .driver_data = PCH_BOARD_HSW, },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_2),
- .driver_data = board_hsw, },
+ .driver_data = PCH_BOARD_HSW, },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT),
- .driver_data = board_wpt, },
+ .driver_data = PCH_BOARD_WPT, },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL),
- .driver_data = board_skl, },
+ .driver_data = PCH_BOARD_SKL, },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL_H),
- .driver_data = board_skl, },
+ .driver_data = PCH_BOARD_SKL, },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL),
- .driver_data = board_cnl, },
+ .driver_data = PCH_BOARD_CNL, },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_H),
- .driver_data = board_cnl, },
+ .driver_data = PCH_BOARD_CNL, },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_LP),
- .driver_data = board_cnl, },
+ .driver_data = PCH_BOARD_CNL, },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CML_H),
- .driver_data = board_cml, },
+ .driver_data = PCH_BOARD_CML, },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_LWB),
- .driver_data = board_lwb, },
+ .driver_data = PCH_BOARD_LWB, },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WBG),
+ .driver_data = PCH_BOARD_WBG, },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id);
diff --git a/drivers/thermal/intel/intel_powerclamp.c b/drivers/thermal/intel/intel_powerclamp.c
index b80e25ec1261..c7ba5680cd48 100644
--- a/drivers/thermal/intel/intel_powerclamp.c
+++ b/drivers/thermal/intel/intel_powerclamp.c
@@ -2,7 +2,7 @@
/*
* intel_powerclamp.c - package c-state idle injection
*
- * Copyright (c) 2012, Intel Corporation.
+ * Copyright (c) 2012-2023, Intel Corporation.
*
* Authors:
* Arjan van de Ven <arjan@linux.intel.com>
@@ -27,23 +27,17 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
-#include <linux/kthread.h>
#include <linux/cpu.h>
#include <linux/thermal.h>
-#include <linux/slab.h>
-#include <linux/tick.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
-#include <linux/sched/rt.h>
-#include <uapi/linux/sched/types.h>
+#include <linux/idle_inject.h>
-#include <asm/nmi.h>
#include <asm/msr.h>
#include <asm/mwait.h>
#include <asm/cpu_device_id.h>
-#include <asm/hardirq.h>
-#define MAX_TARGET_RATIO (50U)
+#define MAX_TARGET_RATIO (100U)
/* For each undisturbed clamping period (no extra wake ups during idle time),
* we increment the confidence counter for the given target ratio.
* CONFIDENCE_OK defines the level where runtime calibration results are
@@ -57,37 +51,30 @@
static unsigned int target_mwait;
static struct dentry *debug_dir;
+static bool poll_pkg_cstate_enable;
-/* user selected target */
-static unsigned int set_target_ratio;
+/* Idle ratio observed using package C-state counters */
static unsigned int current_ratio;
-static bool should_skip;
-static unsigned int control_cpu; /* The cpu assigned to collect stat and update
- * control parameters. default to BSP but BSP
- * can be offlined.
- */
-static bool clamping;
+/* Skip the idle injection till set to true */
+static bool should_skip;
-struct powerclamp_worker_data {
- struct kthread_worker *worker;
- struct kthread_work balancing_work;
- struct kthread_delayed_work idle_injection_work;
+struct powerclamp_data {
unsigned int cpu;
unsigned int count;
unsigned int guard;
unsigned int window_size_now;
unsigned int target_ratio;
- unsigned int duration_jiffies;
bool clamping;
};
-static struct powerclamp_worker_data __percpu *worker_data;
+static struct powerclamp_data powerclamp_data;
+
static struct thermal_cooling_device *cooling_dev;
-static unsigned long *cpu_clamping_mask; /* bit map for tracking per cpu
- * clamping kthread worker
- */
+static DEFINE_MUTEX(powerclamp_lock);
+
+/* This duration is in microseconds */
static unsigned int duration;
static unsigned int pkg_cstate_ratio_cur;
static unsigned int window_size;
@@ -104,25 +91,171 @@ static int duration_set(const char *arg, const struct kernel_param *kp)
pr_err("Out of recommended range %lu, between 6-25ms\n",
new_duration);
ret = -EINVAL;
+ goto exit;
}
- duration = clamp(new_duration, 6ul, 25ul);
- smp_mb();
-
+ mutex_lock(&powerclamp_lock);
+ duration = clamp(new_duration, 6ul, 25ul) * 1000;
+ mutex_unlock(&powerclamp_lock);
exit:
return ret;
}
+static int duration_get(char *buf, const struct kernel_param *kp)
+{
+ int ret;
+
+ mutex_lock(&powerclamp_lock);
+ ret = sysfs_emit(buf, "%d\n", duration / 1000);
+ mutex_unlock(&powerclamp_lock);
+
+ return ret;
+}
+
static const struct kernel_param_ops duration_ops = {
.set = duration_set,
- .get = param_get_int,
+ .get = duration_get,
};
-
-module_param_cb(duration, &duration_ops, &duration, 0644);
+module_param_cb(duration, &duration_ops, NULL, 0644);
MODULE_PARM_DESC(duration, "forced idle time for each attempt in msec.");
+#define DEFAULT_MAX_IDLE 50
+#define MAX_ALL_CPU_IDLE 75
+
+static u8 max_idle = DEFAULT_MAX_IDLE;
+
+static cpumask_var_t idle_injection_cpu_mask;
+
+static int allocate_copy_idle_injection_mask(const struct cpumask *copy_mask)
+{
+ if (cpumask_available(idle_injection_cpu_mask))
+ goto copy_mask;
+
+ /* This mask is allocated only one time and freed during module exit */
+ if (!alloc_cpumask_var(&idle_injection_cpu_mask, GFP_KERNEL))
+ return -ENOMEM;
+
+copy_mask:
+ cpumask_copy(idle_injection_cpu_mask, copy_mask);
+
+ return 0;
+}
+
+/* Return true if the cpumask and idle percent combination is invalid */
+static bool check_invalid(cpumask_var_t mask, u8 idle)
+{
+ if (cpumask_equal(cpu_present_mask, mask) && idle > MAX_ALL_CPU_IDLE)
+ return true;
+
+ return false;
+}
+
+static int cpumask_set(const char *arg, const struct kernel_param *kp)
+{
+ cpumask_var_t new_mask;
+ int ret;
+
+ mutex_lock(&powerclamp_lock);
+
+ /* Can't set mask when cooling device is in use */
+ if (powerclamp_data.clamping) {
+ ret = -EAGAIN;
+ goto skip_cpumask_set;
+ }
+
+ ret = alloc_cpumask_var(&new_mask, GFP_KERNEL);
+ if (!ret)
+ goto skip_cpumask_set;
+
+ ret = bitmap_parse(arg, strlen(arg), cpumask_bits(new_mask),
+ nr_cpumask_bits);
+ if (ret)
+ goto free_cpumask_set;
+
+ if (cpumask_empty(new_mask) || check_invalid(new_mask, max_idle)) {
+ ret = -EINVAL;
+ goto free_cpumask_set;
+ }
+
+ /*
+ * When module parameters are passed from kernel command line
+ * during insmod, the module parameter callback is called
+ * before powerclamp_init(), so we can't assume that some
+ * cpumask can be allocated and copied before here. Also
+ * in this case this cpumask is used as the default mask.
+ */
+ ret = allocate_copy_idle_injection_mask(new_mask);
+
+free_cpumask_set:
+ free_cpumask_var(new_mask);
+skip_cpumask_set:
+ mutex_unlock(&powerclamp_lock);
+
+ return ret;
+}
+
+static int cpumask_get(char *buf, const struct kernel_param *kp)
+{
+ if (!cpumask_available(idle_injection_cpu_mask))
+ return -ENODEV;
+
+ return bitmap_print_to_pagebuf(false, buf, cpumask_bits(idle_injection_cpu_mask),
+ nr_cpumask_bits);
+}
+
+static const struct kernel_param_ops cpumask_ops = {
+ .set = cpumask_set,
+ .get = cpumask_get,
+};
+
+module_param_cb(cpumask, &cpumask_ops, NULL, 0644);
+MODULE_PARM_DESC(cpumask, "Mask of CPUs to use for idle injection.");
+
+static int max_idle_set(const char *arg, const struct kernel_param *kp)
+{
+ u8 new_max_idle;
+ int ret = 0;
+
+ mutex_lock(&powerclamp_lock);
+
+ /* Can't set mask when cooling device is in use */
+ if (powerclamp_data.clamping) {
+ ret = -EAGAIN;
+ goto skip_limit_set;
+ }
+
+ ret = kstrtou8(arg, 10, &new_max_idle);
+ if (ret)
+ goto skip_limit_set;
+
+ if (new_max_idle > MAX_TARGET_RATIO) {
+ ret = -EINVAL;
+ goto skip_limit_set;
+ }
+
+ if (check_invalid(idle_injection_cpu_mask, new_max_idle)) {
+ ret = -EINVAL;
+ goto skip_limit_set;
+ }
+
+ max_idle = new_max_idle;
+
+skip_limit_set:
+ mutex_unlock(&powerclamp_lock);
+
+ return ret;
+}
+
+static const struct kernel_param_ops max_idle_ops = {
+ .set = max_idle_set,
+ .get = param_get_int,
+};
+
+module_param_cb(max_idle, &max_idle_ops, &max_idle, 0644);
+MODULE_PARM_DESC(max_idle, "maximum injected idle time to the total CPU time ratio in percent range:1-100");
+
struct powerclamp_calibration_data {
unsigned long confidence; /* used for calibration, basically a counter
* gets incremented each time a clamping
@@ -261,6 +394,9 @@ static unsigned int get_compensation(int ratio)
{
unsigned int comp = 0;
+ if (!poll_pkg_cstate_enable)
+ return 0;
+
/* we only use compensation if all adjacent ones are good */
if (ratio == 1 &&
cal_data[ratio].confidence >= CONFIDENCE_OK &&
@@ -302,7 +438,7 @@ static void adjust_compensation(int target_ratio, unsigned int win)
if (d->confidence >= CONFIDENCE_OK)
return;
- delta = set_target_ratio - current_ratio;
+ delta = powerclamp_data.target_ratio - current_ratio;
/* filter out bad data */
if (delta >= 0 && delta <= (1+target_ratio/10)) {
if (d->steady_comp)
@@ -341,82 +477,39 @@ static bool powerclamp_adjust_controls(unsigned int target_ratio,
adjust_compensation(target_ratio, win);
/* if we are above target+guard, skip */
- return set_target_ratio + guard <= current_ratio;
+ return powerclamp_data.target_ratio + guard <= current_ratio;
}
-static void clamp_balancing_func(struct kthread_work *work)
+/*
+ * This function calculates runtime from the current target ratio.
+ * This function gets called under powerclamp_lock.
+ */
+static unsigned int get_run_time(void)
{
- struct powerclamp_worker_data *w_data;
- int sleeptime;
- unsigned long target_jiffies;
unsigned int compensated_ratio;
- int interval; /* jiffies to sleep for each attempt */
-
- w_data = container_of(work, struct powerclamp_worker_data,
- balancing_work);
+ unsigned int runtime;
/*
* make sure user selected ratio does not take effect until
* the next round. adjust target_ratio if user has changed
* target such that we can converge quickly.
*/
- w_data->target_ratio = READ_ONCE(set_target_ratio);
- w_data->guard = 1 + w_data->target_ratio / 20;
- w_data->window_size_now = window_size;
- w_data->duration_jiffies = msecs_to_jiffies(duration);
- w_data->count++;
+ powerclamp_data.guard = 1 + powerclamp_data.target_ratio / 20;
+ powerclamp_data.window_size_now = window_size;
/*
* systems may have different ability to enter package level
* c-states, thus we need to compensate the injected idle ratio
* to achieve the actual target reported by the HW.
*/
- compensated_ratio = w_data->target_ratio +
- get_compensation(w_data->target_ratio);
+ compensated_ratio = powerclamp_data.target_ratio +
+ get_compensation(powerclamp_data.target_ratio);
if (compensated_ratio <= 0)
compensated_ratio = 1;
- interval = w_data->duration_jiffies * 100 / compensated_ratio;
-
- /* align idle time */
- target_jiffies = roundup(jiffies, interval);
- sleeptime = target_jiffies - jiffies;
- if (sleeptime <= 0)
- sleeptime = 1;
-
- if (clamping && w_data->clamping && cpu_online(w_data->cpu))
- kthread_queue_delayed_work(w_data->worker,
- &w_data->idle_injection_work,
- sleeptime);
-}
-static void clamp_idle_injection_func(struct kthread_work *work)
-{
- struct powerclamp_worker_data *w_data;
-
- w_data = container_of(work, struct powerclamp_worker_data,
- idle_injection_work.work);
-
- /*
- * only elected controlling cpu can collect stats and update
- * control parameters.
- */
- if (w_data->cpu == control_cpu &&
- !(w_data->count % w_data->window_size_now)) {
- should_skip =
- powerclamp_adjust_controls(w_data->target_ratio,
- w_data->guard,
- w_data->window_size_now);
- smp_mb();
- }
+ runtime = duration * 100 / compensated_ratio - duration;
- if (should_skip)
- goto balance;
-
- play_idle(jiffies_to_usecs(w_data->duration_jiffies));
-
-balance:
- if (clamping && w_data->clamping && cpu_online(w_data->cpu))
- kthread_queue_work(w_data->worker, &w_data->balancing_work);
+ return runtime;
}
/*
@@ -452,126 +545,129 @@ static void poll_pkg_cstate(struct work_struct *dummy)
msr_last = msr_now;
tsc_last = tsc_now;
- if (true == clamping)
+ mutex_lock(&powerclamp_lock);
+ if (powerclamp_data.clamping)
schedule_delayed_work(&poll_pkg_cstate_work, HZ);
+ mutex_unlock(&powerclamp_lock);
}
-static void start_power_clamp_worker(unsigned long cpu)
+static struct idle_inject_device *ii_dev;
+
+/*
+ * This function is called from idle injection core on timer expiry
+ * for the run duration. This allows powerclamp to readjust or skip
+ * injecting idle for this cycle.
+ */
+static bool idle_inject_update(void)
{
- struct powerclamp_worker_data *w_data = per_cpu_ptr(worker_data, cpu);
- struct kthread_worker *worker;
+ bool update = false;
- worker = kthread_create_worker_on_cpu(cpu, 0, "kidle_inj/%ld", cpu);
- if (IS_ERR(worker))
- return;
+ /* We can't sleep in this callback */
+ if (!mutex_trylock(&powerclamp_lock))
+ return true;
- w_data->worker = worker;
- w_data->count = 0;
- w_data->cpu = cpu;
- w_data->clamping = true;
- set_bit(cpu, cpu_clamping_mask);
- sched_set_fifo(worker->task);
- kthread_init_work(&w_data->balancing_work, clamp_balancing_func);
- kthread_init_delayed_work(&w_data->idle_injection_work,
- clamp_idle_injection_func);
- kthread_queue_work(w_data->worker, &w_data->balancing_work);
-}
+ if (!(powerclamp_data.count % powerclamp_data.window_size_now)) {
-static void stop_power_clamp_worker(unsigned long cpu)
-{
- struct powerclamp_worker_data *w_data = per_cpu_ptr(worker_data, cpu);
+ should_skip = powerclamp_adjust_controls(powerclamp_data.target_ratio,
+ powerclamp_data.guard,
+ powerclamp_data.window_size_now);
+ update = true;
+ }
- if (!w_data->worker)
- return;
+ if (update) {
+ unsigned int runtime = get_run_time();
- w_data->clamping = false;
- /*
- * Make sure that all works that get queued after this point see
- * the clamping disabled. The counter part is not needed because
- * there is an implicit memory barrier when the queued work
- * is proceed.
- */
- smp_wmb();
- kthread_cancel_work_sync(&w_data->balancing_work);
- kthread_cancel_delayed_work_sync(&w_data->idle_injection_work);
- /*
- * The balancing work still might be queued here because
- * the handling of the "clapming" variable, cancel, and queue
- * operations are not synchronized via a lock. But it is not
- * a big deal. The balancing work is fast and destroy kthread
- * will wait for it.
- */
- clear_bit(w_data->cpu, cpu_clamping_mask);
- kthread_destroy_worker(w_data->worker);
+ idle_inject_set_duration(ii_dev, runtime, duration);
+ }
+
+ powerclamp_data.count++;
+
+ mutex_unlock(&powerclamp_lock);
+
+ if (should_skip)
+ return false;
- w_data->worker = NULL;
+ return true;
}
-static int start_power_clamp(void)
+/* This function starts idle injection by calling idle_inject_start() */
+static void trigger_idle_injection(void)
{
- unsigned long cpu;
-
- set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO - 1);
- /* prevent cpu hotplug */
- cpus_read_lock();
+ unsigned int runtime = get_run_time();
- /* prefer BSP */
- control_cpu = cpumask_first(cpu_online_mask);
+ idle_inject_set_duration(ii_dev, runtime, duration);
+ idle_inject_start(ii_dev);
+ powerclamp_data.clamping = true;
+}
- clamping = true;
- schedule_delayed_work(&poll_pkg_cstate_work, 0);
+/*
+ * This function is called from start_power_clamp() to register
+ * CPUS with powercap idle injection register and set default
+ * idle duration and latency.
+ */
+static int powerclamp_idle_injection_register(void)
+{
+ poll_pkg_cstate_enable = false;
+ if (cpumask_equal(cpu_present_mask, idle_injection_cpu_mask)) {
+ ii_dev = idle_inject_register_full(idle_injection_cpu_mask, idle_inject_update);
+ if (topology_max_packages() == 1 && topology_max_die_per_package() == 1)
+ poll_pkg_cstate_enable = true;
+ } else {
+ ii_dev = idle_inject_register(idle_injection_cpu_mask);
+ }
- /* start one kthread worker per online cpu */
- for_each_online_cpu(cpu) {
- start_power_clamp_worker(cpu);
+ if (!ii_dev) {
+ pr_err("powerclamp: idle_inject_register failed\n");
+ return -EAGAIN;
}
- cpus_read_unlock();
+
+ idle_inject_set_duration(ii_dev, TICK_USEC, duration);
+ idle_inject_set_latency(ii_dev, UINT_MAX);
return 0;
}
-static void end_power_clamp(void)
+/*
+ * This function is called from end_power_clamp() to stop idle injection
+ * and unregister CPUS from powercap idle injection core.
+ */
+static void remove_idle_injection(void)
{
- int i;
+ if (!powerclamp_data.clamping)
+ return;
- /*
- * Block requeuing in all the kthread workers. They will flush and
- * stop faster.
- */
- clamping = false;
- for_each_set_bit(i, cpu_clamping_mask, num_possible_cpus()) {
- pr_debug("clamping worker for cpu %d alive, destroy\n", i);
- stop_power_clamp_worker(i);
- }
+ powerclamp_data.clamping = false;
+ idle_inject_stop(ii_dev);
}
-static int powerclamp_cpu_online(unsigned int cpu)
+/*
+ * This function is called when user change the cooling device
+ * state from zero to some other value.
+ */
+static int start_power_clamp(void)
{
- if (clamping == false)
- return 0;
- start_power_clamp_worker(cpu);
- /* prefer BSP as controlling CPU */
- if (cpu == 0) {
- control_cpu = 0;
- smp_mb();
+ int ret;
+
+ ret = powerclamp_idle_injection_register();
+ if (!ret) {
+ trigger_idle_injection();
+ if (poll_pkg_cstate_enable)
+ schedule_delayed_work(&poll_pkg_cstate_work, 0);
}
- return 0;
+
+ return ret;
}
-static int powerclamp_cpu_predown(unsigned int cpu)
+/*
+ * This function is called when user change the cooling device
+ * state from non zero value zero.
+ */
+static void end_power_clamp(void)
{
- if (clamping == false)
- return 0;
-
- stop_power_clamp_worker(cpu);
- if (cpu != control_cpu)
- return 0;
-
- control_cpu = cpumask_first(cpu_online_mask);
- if (control_cpu == cpu)
- control_cpu = cpumask_next(cpu, cpu_online_mask);
- smp_mb();
- return 0;
+ if (powerclamp_data.clamping) {
+ remove_idle_injection();
+ idle_inject_unregister(ii_dev);
+ }
}
static int powerclamp_get_max_state(struct thermal_cooling_device *cdev,
@@ -585,11 +681,9 @@ static int powerclamp_get_max_state(struct thermal_cooling_device *cdev,
static int powerclamp_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
- if (true == clamping)
- *state = pkg_cstate_ratio_cur;
- else
- /* to save power, do not poll idle ratio while not clamping */
- *state = -1; /* indicates invalid state */
+ mutex_lock(&powerclamp_lock);
+ *state = powerclamp_data.target_ratio;
+ mutex_unlock(&powerclamp_lock);
return 0;
}
@@ -599,24 +693,32 @@ static int powerclamp_set_cur_state(struct thermal_cooling_device *cdev,
{
int ret = 0;
+ mutex_lock(&powerclamp_lock);
+
new_target_ratio = clamp(new_target_ratio, 0UL,
- (unsigned long) (MAX_TARGET_RATIO-1));
- if (set_target_ratio == 0 && new_target_ratio > 0) {
+ (unsigned long) (max_idle - 1));
+ if (!powerclamp_data.target_ratio && new_target_ratio > 0) {
pr_info("Start idle injection to reduce power\n");
- set_target_ratio = new_target_ratio;
+ powerclamp_data.target_ratio = new_target_ratio;
ret = start_power_clamp();
+ if (ret)
+ powerclamp_data.target_ratio = 0;
goto exit_set;
- } else if (set_target_ratio > 0 && new_target_ratio == 0) {
+ } else if (powerclamp_data.target_ratio > 0 && new_target_ratio == 0) {
pr_info("Stop forced idle injection\n");
end_power_clamp();
- set_target_ratio = 0;
+ powerclamp_data.target_ratio = 0;
} else /* adjust currently running */ {
- set_target_ratio = new_target_ratio;
- /* make new set_target_ratio visible to other cpus */
- smp_mb();
+ unsigned int runtime;
+
+ powerclamp_data.target_ratio = new_target_ratio;
+ runtime = get_run_time();
+ idle_inject_set_duration(ii_dev, runtime, duration);
}
exit_set:
+ mutex_unlock(&powerclamp_lock);
+
return ret;
}
@@ -657,7 +759,6 @@ static int powerclamp_debug_show(struct seq_file *m, void *unused)
{
int i = 0;
- seq_printf(m, "controlling cpu: %d\n", control_cpu);
seq_printf(m, "pct confidence steady dynamic (compensation)\n");
for (i = 0; i < MAX_TARGET_RATIO; i++) {
seq_printf(m, "%d\t%lu\t%lu\t%lu\n",
@@ -680,75 +781,57 @@ static inline void powerclamp_create_debug_files(void)
&powerclamp_debug_fops);
}
-static enum cpuhp_state hp_state;
-
static int __init powerclamp_init(void)
{
int retval;
- cpu_clamping_mask = bitmap_zalloc(num_possible_cpus(), GFP_KERNEL);
- if (!cpu_clamping_mask)
- return -ENOMEM;
-
/* probe cpu features and ids here */
retval = powerclamp_probe();
if (retval)
- goto exit_free;
+ return retval;
+
+ mutex_lock(&powerclamp_lock);
+ retval = allocate_copy_idle_injection_mask(cpu_present_mask);
+ mutex_unlock(&powerclamp_lock);
+
+ if (retval)
+ return retval;
/* set default limit, maybe adjusted during runtime based on feedback */
window_size = 2;
- retval = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
- "thermal/intel_powerclamp:online",
- powerclamp_cpu_online,
- powerclamp_cpu_predown);
- if (retval < 0)
- goto exit_free;
-
- hp_state = retval;
-
- worker_data = alloc_percpu(struct powerclamp_worker_data);
- if (!worker_data) {
- retval = -ENOMEM;
- goto exit_unregister;
- }
cooling_dev = thermal_cooling_device_register("intel_powerclamp", NULL,
- &powerclamp_cooling_ops);
- if (IS_ERR(cooling_dev)) {
- retval = -ENODEV;
- goto exit_free_thread;
- }
+ &powerclamp_cooling_ops);
+ if (IS_ERR(cooling_dev))
+ return -ENODEV;
if (!duration)
- duration = jiffies_to_msecs(DEFAULT_DURATION_JIFFIES);
+ duration = jiffies_to_usecs(DEFAULT_DURATION_JIFFIES);
powerclamp_create_debug_files();
return 0;
-
-exit_free_thread:
- free_percpu(worker_data);
-exit_unregister:
- cpuhp_remove_state_nocalls(hp_state);
-exit_free:
- bitmap_free(cpu_clamping_mask);
- return retval;
}
module_init(powerclamp_init);
static void __exit powerclamp_exit(void)
{
+ mutex_lock(&powerclamp_lock);
end_power_clamp();
- cpuhp_remove_state_nocalls(hp_state);
- free_percpu(worker_data);
+ mutex_unlock(&powerclamp_lock);
+
thermal_cooling_device_unregister(cooling_dev);
- bitmap_free(cpu_clamping_mask);
cancel_delayed_work_sync(&poll_pkg_cstate_work);
debugfs_remove_recursive(debug_dir);
+
+ if (cpumask_available(idle_injection_cpu_mask))
+ free_cpumask_var(idle_injection_cpu_mask);
}
module_exit(powerclamp_exit);
+MODULE_IMPORT_NS(IDLE_INJECT);
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arjan van de Ven <arjan@linux.intel.com>");
MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
diff --git a/drivers/thermal/intel/intel_quark_dts_thermal.c b/drivers/thermal/intel/intel_quark_dts_thermal.c
index 3eafc6b0e6c3..ffdc95047838 100644
--- a/drivers/thermal/intel/intel_quark_dts_thermal.c
+++ b/drivers/thermal/intel/intel_quark_dts_thermal.c
@@ -84,6 +84,7 @@
#define QRK_DTS_MASK_TP_THRES 0xFF
#define QRK_DTS_SHIFT_TP 8
#define QRK_DTS_ID_TP_CRITICAL 0
+#define QRK_DTS_ID_TP_HOT 1
#define QRK_DTS_SAFE_TP_THRES 105
/* Thermal Sensor Register Lock */
@@ -104,6 +105,7 @@ struct soc_sensor_entry {
u32 store_ptps;
u32 store_dts_enable;
struct thermal_zone_device *tzone;
+ struct thermal_trip trips[QRK_MAX_DTS_TRIPS];
};
static struct soc_sensor_entry *soc_dts;
@@ -172,9 +174,9 @@ static int soc_dts_disable(struct thermal_zone_device *tzd)
return ret;
}
-static int _get_trip_temp(int trip, int *temp)
+static int get_trip_temp(int trip)
{
- int status;
+ int status, temp;
u32 out;
mutex_lock(&dts_update_mutex);
@@ -183,7 +185,7 @@ static int _get_trip_temp(int trip, int *temp)
mutex_unlock(&dts_update_mutex);
if (status)
- return status;
+ return THERMAL_TEMP_INVALID;
/*
* Thermal Sensor Programmable Trip Point Register has 8-bit
@@ -191,21 +193,10 @@ static int _get_trip_temp(int trip, int *temp)
* thresholds. The threshold value is always offset by its
* temperature base (50 degree Celsius).
*/
- *temp = (out >> (trip * QRK_DTS_SHIFT_TP)) & QRK_DTS_MASK_TP_THRES;
- *temp -= QRK_DTS_TEMP_BASE;
+ temp = (out >> (trip * QRK_DTS_SHIFT_TP)) & QRK_DTS_MASK_TP_THRES;
+ temp -= QRK_DTS_TEMP_BASE;
- return 0;
-}
-
-static inline int sys_get_trip_temp(struct thermal_zone_device *tzd,
- int trip, int *temp)
-{
- return _get_trip_temp(trip, temp);
-}
-
-static inline int sys_get_crit_temp(struct thermal_zone_device *tzd, int *temp)
-{
- return _get_trip_temp(QRK_DTS_ID_TP_CRITICAL, temp);
+ return temp;
}
static int update_trip_temp(struct soc_sensor_entry *aux_entry,
@@ -262,17 +253,6 @@ static inline int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
return update_trip_temp(tzd->devdata, trip, temp);
}
-static int sys_get_trip_type(struct thermal_zone_device *thermal,
- int trip, enum thermal_trip_type *type)
-{
- if (trip)
- *type = THERMAL_TRIP_HOT;
- else
- *type = THERMAL_TRIP_CRITICAL;
-
- return 0;
-}
-
static int sys_get_curr_temp(struct thermal_zone_device *tzd,
int *temp)
{
@@ -315,10 +295,7 @@ static int sys_change_mode(struct thermal_zone_device *tzd,
static struct thermal_zone_device_ops tzone_ops = {
.get_temp = sys_get_curr_temp,
- .get_trip_temp = sys_get_trip_temp,
- .get_trip_type = sys_get_trip_type,
.set_trip_temp = sys_set_trip_temp,
- .get_crit_temp = sys_get_crit_temp,
.change_mode = sys_change_mode,
};
@@ -385,10 +362,18 @@ static struct soc_sensor_entry *alloc_soc_dts(void)
goto err_ret;
}
- aux_entry->tzone = thermal_zone_device_register("quark_dts",
- QRK_MAX_DTS_TRIPS,
- wr_mask,
- aux_entry, &tzone_ops, NULL, 0, polling_delay);
+ aux_entry->trips[QRK_DTS_ID_TP_CRITICAL].temperature = get_trip_temp(QRK_DTS_ID_TP_CRITICAL);
+ aux_entry->trips[QRK_DTS_ID_TP_CRITICAL].type = THERMAL_TRIP_CRITICAL;
+
+ aux_entry->trips[QRK_DTS_ID_TP_HOT].temperature = get_trip_temp(QRK_DTS_ID_TP_HOT);
+ aux_entry->trips[QRK_DTS_ID_TP_HOT].type = THERMAL_TRIP_HOT;
+
+ aux_entry->tzone = thermal_zone_device_register_with_trips("quark_dts",
+ aux_entry->trips,
+ QRK_MAX_DTS_TRIPS,
+ wr_mask,
+ aux_entry, &tzone_ops,
+ NULL, 0, polling_delay);
if (IS_ERR(aux_entry->tzone)) {
err = PTR_ERR(aux_entry->tzone);
goto err_ret;
@@ -415,22 +400,14 @@ MODULE_DEVICE_TABLE(x86cpu, qrk_thermal_ids);
static int __init intel_quark_thermal_init(void)
{
- int err = 0;
-
if (!x86_match_cpu(qrk_thermal_ids) || !iosf_mbi_available())
return -ENODEV;
soc_dts = alloc_soc_dts();
- if (IS_ERR(soc_dts)) {
- err = PTR_ERR(soc_dts);
- goto err_free;
- }
+ if (IS_ERR(soc_dts))
+ return PTR_ERR(soc_dts);
return 0;
-
-err_free:
- free_soc_dts(soc_dts);
- return err;
}
static void __exit intel_quark_thermal_exit(void)
diff --git a/drivers/thermal/intel/intel_soc_dts_iosf.c b/drivers/thermal/intel/intel_soc_dts_iosf.c
index 342b0bb5a56d..8c26f7b2316b 100644
--- a/drivers/thermal/intel/intel_soc_dts_iosf.c
+++ b/drivers/thermal/intel/intel_soc_dts_iosf.c
@@ -7,6 +7,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitops.h>
+#include <linux/intel_tcc.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
@@ -45,32 +46,6 @@
/* DTS0 and DTS 1 */
#define SOC_MAX_DTS_SENSORS 2
-static int get_tj_max(u32 *tj_max)
-{
- u32 eax, edx;
- u32 val;
- int err;
-
- err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
- if (err)
- goto err_ret;
- else {
- val = (eax >> 16) & 0xff;
- if (val)
- *tj_max = val * 1000;
- else {
- err = -EINVAL;
- goto err_ret;
- }
- }
-
- return 0;
-err_ret:
- *tj_max = 0;
-
- return err;
-}
-
static int sys_get_trip_temp(struct thermal_zone_device *tzd, int trip,
int *temp)
{
@@ -405,7 +380,7 @@ struct intel_soc_dts_sensors *intel_soc_dts_iosf_init(
{
struct intel_soc_dts_sensors *sensors;
bool notification;
- u32 tj_max;
+ int tj_max;
int ret;
int i;
@@ -415,8 +390,9 @@ struct intel_soc_dts_sensors *intel_soc_dts_iosf_init(
if (!trip_count || read_only_trip_count > trip_count)
return ERR_PTR(-EINVAL);
- if (get_tj_max(&tj_max))
- return ERR_PTR(-EINVAL);
+ tj_max = intel_tcc_get_tjmax(-1);
+ if (tj_max < 0)
+ return ERR_PTR(tj_max);
sensors = kzalloc(sizeof(*sensors), GFP_KERNEL);
if (!sensors)
@@ -475,4 +451,5 @@ void intel_soc_dts_iosf_exit(struct intel_soc_dts_sensors *sensors)
}
EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_exit);
+MODULE_IMPORT_NS(INTEL_TCC);
MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/intel_tcc.c b/drivers/thermal/intel/intel_tcc.c
new file mode 100644
index 000000000000..2e5c741c41ca
--- /dev/null
+++ b/drivers/thermal/intel/intel_tcc.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * intel_tcc.c - Library for Intel TCC (thermal control circuitry) MSR access
+ * Copyright (c) 2022, Intel Corporation.
+ */
+
+#include <linux/errno.h>
+#include <linux/intel_tcc.h>
+#include <asm/msr.h>
+
+/**
+ * intel_tcc_get_tjmax() - returns the default TCC activation Temperature
+ * @cpu: cpu that the MSR should be run on, nagative value means any cpu.
+ *
+ * Get the TjMax value, which is the default thermal throttling or TCC
+ * activation temperature in degrees C.
+ *
+ * Return: Tjmax value in degrees C on success, negative error code otherwise.
+ */
+int intel_tcc_get_tjmax(int cpu)
+{
+ u32 low, high;
+ int val, err;
+
+ if (cpu < 0)
+ err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high);
+ else
+ err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high);
+ if (err)
+ return err;
+
+ val = (low >> 16) & 0xff;
+
+ return val ? val : -ENODATA;
+}
+EXPORT_SYMBOL_NS_GPL(intel_tcc_get_tjmax, INTEL_TCC);
+
+/**
+ * intel_tcc_get_offset() - returns the TCC Offset value to Tjmax
+ * @cpu: cpu that the MSR should be run on, nagative value means any cpu.
+ *
+ * Get the TCC offset value to Tjmax. The effective thermal throttling or TCC
+ * activation temperature equals "Tjmax" - "TCC Offset", in degrees C.
+ *
+ * Return: Tcc offset value in degrees C on success, negative error code otherwise.
+ */
+int intel_tcc_get_offset(int cpu)
+{
+ u32 low, high;
+ int err;
+
+ if (cpu < 0)
+ err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high);
+ else
+ err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high);
+ if (err)
+ return err;
+
+ return (low >> 24) & 0x3f;
+}
+EXPORT_SYMBOL_NS_GPL(intel_tcc_get_offset, INTEL_TCC);
+
+/**
+ * intel_tcc_set_offset() - set the TCC offset value to Tjmax
+ * @cpu: cpu that the MSR should be run on, nagative value means any cpu.
+ * @offset: TCC offset value in degree C
+ *
+ * Set the TCC Offset value to Tjmax. The effective thermal throttling or TCC
+ * activation temperature equals "Tjmax" - "TCC Offset", in degree C.
+ *
+ * Return: On success returns 0, negative error code otherwise.
+ */
+
+int intel_tcc_set_offset(int cpu, int offset)
+{
+ u32 low, high;
+ int err;
+
+ if (offset < 0 || offset > 0x3f)
+ return -EINVAL;
+
+ if (cpu < 0)
+ err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &low, &high);
+ else
+ err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &low, &high);
+ if (err)
+ return err;
+
+ /* MSR Locked */
+ if (low & BIT(31))
+ return -EPERM;
+
+ low &= ~(0x3f << 24);
+ low |= offset << 24;
+
+ if (cpu < 0)
+ return wrmsr_safe(MSR_IA32_TEMPERATURE_TARGET, low, high);
+ else
+ return wrmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, low, high);
+}
+EXPORT_SYMBOL_NS_GPL(intel_tcc_set_offset, INTEL_TCC);
+
+/**
+ * intel_tcc_get_temp() - returns the current temperature
+ * @cpu: cpu that the MSR should be run on, nagative value means any cpu.
+ * @pkg: true: Package Thermal Sensor. false: Core Thermal Sensor.
+ *
+ * Get the current temperature returned by the CPU core/package level
+ * thermal sensor, in degrees C.
+ *
+ * Return: Temperature in degrees C on success, negative error code otherwise.
+ */
+int intel_tcc_get_temp(int cpu, bool pkg)
+{
+ u32 low, high;
+ u32 msr = pkg ? MSR_IA32_PACKAGE_THERM_STATUS : MSR_IA32_THERM_STATUS;
+ int tjmax, temp, err;
+
+ tjmax = intel_tcc_get_tjmax(cpu);
+ if (tjmax < 0)
+ return tjmax;
+
+ if (cpu < 0)
+ err = rdmsr_safe(msr, &low, &high);
+ else
+ err = rdmsr_safe_on_cpu(cpu, msr, &low, &high);
+ if (err)
+ return err;
+
+ /* Temperature is beyond the valid thermal sensor range */
+ if (!(low & BIT(31)))
+ return -ENODATA;
+
+ temp = tjmax - ((low >> 16) & 0x7f);
+
+ /* Do not allow negative CPU temperature */
+ return temp >= 0 ? temp : -ENODATA;
+}
+EXPORT_SYMBOL_NS_GPL(intel_tcc_get_temp, INTEL_TCC);
diff --git a/drivers/thermal/intel/intel_tcc_cooling.c b/drivers/thermal/intel/intel_tcc_cooling.c
index a89e7e1890e4..e95f799454fe 100644
--- a/drivers/thermal/intel/intel_tcc_cooling.c
+++ b/drivers/thermal/intel/intel_tcc_cooling.c
@@ -7,12 +7,11 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.h>
+#include <linux/intel_tcc.h>
#include <linux/module.h>
#include <linux/thermal.h>
#include <asm/cpu_device_id.h>
-#define TCC_SHIFT 24
-#define TCC_MASK (0x3fULL<<24)
#define TCC_PROGRAMMABLE BIT(30)
#define TCC_LOCKED BIT(31)
@@ -21,47 +20,26 @@ static struct thermal_cooling_device *tcc_cdev;
static int tcc_get_max_state(struct thermal_cooling_device *cdev, unsigned long
*state)
{
- *state = TCC_MASK >> TCC_SHIFT;
- return 0;
-}
-
-static int tcc_offset_update(int tcc)
-{
- u64 val;
- int err;
-
- err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
- if (err)
- return err;
-
- val &= ~TCC_MASK;
- val |= tcc << TCC_SHIFT;
-
- err = wrmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, val);
- if (err)
- return err;
-
+ *state = 0x3f;
return 0;
}
static int tcc_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
*state)
{
- u64 val;
- int err;
+ int offset = intel_tcc_get_offset(-1);
- err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
- if (err)
- return err;
+ if (offset < 0)
+ return offset;
- *state = (val & TCC_MASK) >> TCC_SHIFT;
+ *state = offset;
return 0;
}
static int tcc_set_cur_state(struct thermal_cooling_device *cdev, unsigned long
state)
{
- return tcc_offset_update(state);
+ return intel_tcc_set_offset(-1, (int)state);
}
static const struct thermal_cooling_device_ops tcc_cooling_ops = {
@@ -140,6 +118,7 @@ static void __exit tcc_cooling_exit(void)
module_exit(tcc_cooling_exit)
+MODULE_IMPORT_NS(INTEL_TCC);
MODULE_DESCRIPTION("TCC offset cooling device Driver");
MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/intel/x86_pkg_temp_thermal.c b/drivers/thermal/intel/x86_pkg_temp_thermal.c
index 84c3a116ed04..1c2de84742df 100644
--- a/drivers/thermal/intel/x86_pkg_temp_thermal.c
+++ b/drivers/thermal/intel/x86_pkg_temp_thermal.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/intel_tcc.h>
#include <linux/err.h>
#include <linux/param.h>
#include <linux/device.h>
@@ -48,11 +49,11 @@ MODULE_PARM_DESC(notify_delay_ms,
struct zone_device {
int cpu;
bool work_scheduled;
- u32 tj_max;
u32 msr_pkg_therm_low;
u32 msr_pkg_therm_high;
struct delayed_work work;
struct thermal_zone_device *tzone;
+ struct thermal_trip *trips;
struct cpumask cpumask;
};
@@ -104,71 +105,17 @@ static struct zone_device *pkg_temp_thermal_get_dev(unsigned int cpu)
return NULL;
}
-/*
-* tj-max is interesting because threshold is set relative to this
-* temperature.
-*/
-static int get_tj_max(int cpu, u32 *tj_max)
-{
- u32 eax, edx, val;
- int err;
-
- err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
- if (err)
- return err;
-
- val = (eax >> 16) & 0xff;
- *tj_max = val * 1000;
-
- return val ? 0 : -EINVAL;
-}
-
static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp)
{
struct zone_device *zonedev = tzd->devdata;
- u32 eax, edx;
-
- rdmsr_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_STATUS,
- &eax, &edx);
- if (eax & 0x80000000) {
- *temp = zonedev->tj_max - ((eax >> 16) & 0x7f) * 1000;
- pr_debug("sys_get_curr_temp %d\n", *temp);
- return 0;
- }
- return -EINVAL;
-}
-
-static int sys_get_trip_temp(struct thermal_zone_device *tzd,
- int trip, int *temp)
-{
- struct zone_device *zonedev = tzd->devdata;
- unsigned long thres_reg_value;
- u32 mask, shift, eax, edx;
- int ret;
-
- if (trip >= MAX_NUMBER_OF_TRIPS)
- return -EINVAL;
-
- if (trip) {
- mask = THERM_MASK_THRESHOLD1;
- shift = THERM_SHIFT_THRESHOLD1;
- } else {
- mask = THERM_MASK_THRESHOLD0;
- shift = THERM_SHIFT_THRESHOLD0;
- }
-
- ret = rdmsr_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
- &eax, &edx);
- if (ret < 0)
- return ret;
+ int val;
- thres_reg_value = (eax & mask) >> shift;
- if (thres_reg_value)
- *temp = zonedev->tj_max - thres_reg_value * 1000;
- else
- *temp = THERMAL_TEMP_INVALID;
- pr_debug("sys_get_trip_temp %d\n", *temp);
+ val = intel_tcc_get_temp(zonedev->cpu, true);
+ if (val < 0)
+ return val;
+ *temp = val * 1000;
+ pr_debug("sys_get_curr_temp %d\n", *temp);
return 0;
}
@@ -177,9 +124,14 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
{
struct zone_device *zonedev = tzd->devdata;
u32 l, h, mask, shift, intr;
- int ret;
+ int tj_max, ret;
+
+ tj_max = intel_tcc_get_tjmax(zonedev->cpu);
+ if (tj_max < 0)
+ return tj_max;
+ tj_max *= 1000;
- if (trip >= MAX_NUMBER_OF_TRIPS || temp >= zonedev->tj_max)
+ if (trip >= MAX_NUMBER_OF_TRIPS || temp >= tj_max)
return -EINVAL;
ret = rdmsr_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
@@ -204,7 +156,7 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
if (!temp) {
l &= ~intr;
} else {
- l |= (zonedev->tj_max - temp)/1000 << shift;
+ l |= (tj_max - temp)/1000 << shift;
l |= intr;
}
@@ -212,18 +164,9 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
l, h);
}
-static int sys_get_trip_type(struct thermal_zone_device *thermal, int trip,
- enum thermal_trip_type *type)
-{
- *type = THERMAL_TRIP_PASSIVE;
- return 0;
-}
-
/* Thermal zone callback registry */
static struct thermal_zone_device_ops tzone_ops = {
.get_temp = sys_get_curr_temp,
- .get_trip_temp = sys_get_trip_temp,
- .get_trip_type = sys_get_trip_type,
.set_trip_temp = sys_set_trip_temp,
};
@@ -323,12 +266,55 @@ static int pkg_thermal_notify(u64 msr_val)
return 0;
}
+static struct thermal_trip *pkg_temp_thermal_trips_init(int cpu, int tj_max, int num_trips)
+{
+ struct thermal_trip *trips;
+ unsigned long thres_reg_value;
+ u32 mask, shift, eax, edx;
+ int ret, i;
+
+ trips = kzalloc(sizeof(*trips) * num_trips, GFP_KERNEL);
+ if (!trips)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < num_trips; i++) {
+
+ if (i) {
+ mask = THERM_MASK_THRESHOLD1;
+ shift = THERM_SHIFT_THRESHOLD1;
+ } else {
+ mask = THERM_MASK_THRESHOLD0;
+ shift = THERM_SHIFT_THRESHOLD0;
+ }
+
+ ret = rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
+ &eax, &edx);
+ if (ret < 0) {
+ kfree(trips);
+ return ERR_PTR(ret);
+ }
+
+ thres_reg_value = (eax & mask) >> shift;
+
+ trips[i].temperature = thres_reg_value ?
+ tj_max - thres_reg_value * 1000 : THERMAL_TEMP_INVALID;
+
+ trips[i].type = THERMAL_TRIP_PASSIVE;
+
+ pr_debug("%s: cpu=%d, trip=%d, temp=%d\n",
+ __func__, cpu, i, trips[i].temperature);
+ }
+
+ return trips;
+}
+
static int pkg_temp_thermal_device_add(unsigned int cpu)
{
int id = topology_logical_die_id(cpu);
- u32 tj_max, eax, ebx, ecx, edx;
+ u32 eax, ebx, ecx, edx;
struct zone_device *zonedev;
int thres_count, err;
+ int tj_max;
if (id >= max_id)
return -ENOMEM;
@@ -340,32 +326,34 @@ static int pkg_temp_thermal_device_add(unsigned int cpu)
thres_count = clamp_val(thres_count, 0, MAX_NUMBER_OF_TRIPS);
- err = get_tj_max(cpu, &tj_max);
- if (err)
- return err;
+ tj_max = intel_tcc_get_tjmax(cpu);
+ if (tj_max < 0)
+ return tj_max;
zonedev = kzalloc(sizeof(*zonedev), GFP_KERNEL);
if (!zonedev)
return -ENOMEM;
+ zonedev->trips = pkg_temp_thermal_trips_init(cpu, tj_max, thres_count);
+ if (IS_ERR(zonedev->trips)) {
+ err = PTR_ERR(zonedev->trips);
+ goto out_kfree_zonedev;
+ }
+
INIT_DELAYED_WORK(&zonedev->work, pkg_temp_thermal_threshold_work_fn);
zonedev->cpu = cpu;
- zonedev->tj_max = tj_max;
- zonedev->tzone = thermal_zone_device_register("x86_pkg_temp",
- thres_count,
+ zonedev->tzone = thermal_zone_device_register_with_trips("x86_pkg_temp",
+ zonedev->trips, thres_count,
(thres_count == MAX_NUMBER_OF_TRIPS) ? 0x03 : 0x01,
zonedev, &tzone_ops, &pkg_temp_tz_params, 0, 0);
if (IS_ERR(zonedev->tzone)) {
err = PTR_ERR(zonedev->tzone);
- kfree(zonedev);
- return err;
+ goto out_kfree_trips;
}
err = thermal_zone_device_enable(zonedev->tzone);
- if (err) {
- thermal_zone_device_unregister(zonedev->tzone);
- kfree(zonedev);
- return err;
- }
+ if (err)
+ goto out_unregister_tz;
+
/* Store MSR value for package thermal interrupt, to restore at exit */
rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, zonedev->msr_pkg_therm_low,
zonedev->msr_pkg_therm_high);
@@ -374,7 +362,16 @@ static int pkg_temp_thermal_device_add(unsigned int cpu)
raw_spin_lock_irq(&pkg_temp_lock);
zones[id] = zonedev;
raw_spin_unlock_irq(&pkg_temp_lock);
+
return 0;
+
+out_unregister_tz:
+ thermal_zone_device_unregister(zonedev->tzone);
+out_kfree_trips:
+ kfree(zonedev->trips);
+out_kfree_zonedev:
+ kfree(zonedev);
+ return err;
}
static int pkg_thermal_cpu_offline(unsigned int cpu)
@@ -458,8 +455,10 @@ static int pkg_thermal_cpu_offline(unsigned int cpu)
raw_spin_unlock_irq(&pkg_temp_lock);
/* Final cleanup if this is the last cpu */
- if (lastcpu)
+ if (lastcpu) {
+ kfree(zonedev->trips);
kfree(zonedev);
+ }
return 0;
}
@@ -531,6 +530,7 @@ static void __exit pkg_temp_thermal_exit(void)
}
module_exit(pkg_temp_thermal_exit)
+MODULE_IMPORT_NS(INTEL_TCC);
MODULE_DESCRIPTION("X86 PKG TEMP Thermal Driver");
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_LICENSE("GPL v2");