Merge branch 'thermal-intel'

Merge changes affecting thermal control on Intel platforms for 6.3-rc1:

 - Consolidate code accessing the Intel TCC (Thermal Control Circuitry)
   MSRs by introducing library functions for that and making the
   TCC-related code in thermal drivers use them (Zhang Rui).

 - Enhance the x86_pkg_temp_thermal driver to support dynamic tjmax
   changes (Zhang Rui).

 - Address an "unsigned expression compared with zero" warning in the
   intel_soc_dts_iosf thermal driver (Yang Li).

 - Update comments regarding two functions in the Intel Menlow thermal
   driver (Deming Wang).

 - Use sysfs_emit_at() instead of scnprintf() in the int340x thermal
   driver (ye xingchen).

 - Make the intel_pch thermal driver support the Wellsburg PCH (Tim
   Zimmermann).

 - Add trip point initialization helper functions for ACPI-defined trip
   points and modify two thermal drivers to use them (Rafael Wysocki,
   Daniel Lezcano).

 - Modify the intel_pch and processor_thermal_device_pci thermal drivers
   use generic trip point tables instead of thermal zone trip point
   callbacks (Daniel Lezcano).

 - Add production mode attribute sysfs attribute to the int340x thermal
   driver (Srinivas Pandruvada).

 - Rework dynamic trip point updates handling and locking in the int340x
   thermal driver (Rafael Wysocki).

 - Make the int340x thermal driver use a generic trip points table
   instead of thermal zone trip point callbacks (Rafael Wysocki, Daniel
   Lezcano).

* thermal-intel:
  thermal: intel: int340x: Use generic trip points table
  thermal: intel: int340x: Use zone lock for synchronization
  thermal: intel: int340x: Rework updating trip points
  thermal: ACPI: Initialize trips if temperature is out of range
  thermal: intel: processor_thermal_device_pci: Use generic trip point
  thermal: intel: int340x: Add production mode attribute
  thermal: intel: intel_pch: Use generic trip points
  thermal: ACPI: Add ACPI trip point routines
  thermal: intel: intel_pch: Add support for Wellsburg PCH
  thermal: int340x_thermal: Use sysfs_emit_at() instead of scnprintf()
  thermal: intel: menlow: Update function descriptions
  thermal: intel: Fix unsigned comparison with less than zero
  thermal/x86_pkg_temp_thermal: Add support for handling dynamic tjmax
  thermal/x86_pkg_temp_thermal: Use Intel TCC library
  thermal/intel/intel_tcc_cooling: Use Intel TCC library
  thermal/intel/intel_soc_dts_iosf: Use Intel TCC library
  thermal/int340x/processor_thermal: Use Intel TCC library
  thermal/intel: Introduce Intel TCC library
This commit is contained in:
Rafael J. Wysocki 2023-01-30 14:13:26 +01:00
commit f364beb5b6
21 changed files with 595 additions and 475 deletions

View File

@ -84,6 +84,9 @@ DPTF ACPI Drivers interface
https:/github.com/intel/thermal_daemon for decoding https:/github.com/intel/thermal_daemon for decoding
thermal table. thermal table.
``production_mode`` (RO)
When different from zero, manufacturer locked thermal configuration
from further changes.
ACPI Thermal Relationship table interface ACPI Thermal Relationship table interface
------------------------------------------ ------------------------------------------

View File

@ -76,6 +76,10 @@ config THERMAL_OF
Say 'Y' here if you need to build thermal infrastructure Say 'Y' here if you need to build thermal infrastructure
based on device tree. based on device tree.
config THERMAL_ACPI
depends on ACPI
bool
config THERMAL_WRITABLE_TRIPS config THERMAL_WRITABLE_TRIPS
bool "Enable writable trip points" bool "Enable writable trip points"
help help

View File

@ -13,6 +13,7 @@ thermal_sys-$(CONFIG_THERMAL_NETLINK) += thermal_netlink.o
# interface to/from other layers providing sensors # interface to/from other layers providing sensors
thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o
thermal_sys-$(CONFIG_THERMAL_OF) += thermal_of.o thermal_sys-$(CONFIG_THERMAL_OF) += thermal_of.o
thermal_sys-$(CONFIG_THERMAL_ACPI) += thermal_acpi.o
# governors # governors
thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += gov_fair_share.o thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += gov_fair_share.o

View File

@ -12,11 +12,16 @@ config X86_THERMAL_VECTOR
def_bool y def_bool y
depends on X86 && CPU_SUP_INTEL && X86_LOCAL_APIC depends on X86 && CPU_SUP_INTEL && X86_LOCAL_APIC
config INTEL_TCC
bool
depends on X86
config X86_PKG_TEMP_THERMAL config X86_PKG_TEMP_THERMAL
tristate "X86 package temperature thermal driver" tristate "X86 package temperature thermal driver"
depends on X86_THERMAL_VECTOR depends on X86_THERMAL_VECTOR
select THERMAL_GOV_USER_SPACE select THERMAL_GOV_USER_SPACE
select THERMAL_WRITABLE_TRIPS select THERMAL_WRITABLE_TRIPS
select INTEL_TCC
default m default m
help help
Enable this to register CPU digital sensor for package temperature as Enable this to register CPU digital sensor for package temperature as
@ -28,6 +33,7 @@ config INTEL_SOC_DTS_IOSF_CORE
tristate tristate
depends on X86 && PCI depends on X86 && PCI
select IOSF_MBI select IOSF_MBI
select INTEL_TCC
help help
This is becoming a common feature for Intel SoCs to expose the additional This is becoming a common feature for Intel SoCs to expose the additional
digital temperature sensors (DTSs) using side band interface (IOSF). This digital temperature sensors (DTSs) using side band interface (IOSF). This
@ -75,6 +81,7 @@ config INTEL_BXT_PMIC_THERMAL
config INTEL_PCH_THERMAL config INTEL_PCH_THERMAL
tristate "Intel PCH Thermal Reporting Driver" tristate "Intel PCH Thermal Reporting Driver"
depends on X86 && PCI depends on X86 && PCI
select THERMAL_ACPI if ACPI
help help
Enable this to support thermal reporting on certain intel PCHs. Enable this to support thermal reporting on certain intel PCHs.
Thermal reporting device will provide temperature reading, Thermal reporting device will provide temperature reading,
@ -83,6 +90,7 @@ config INTEL_PCH_THERMAL
config INTEL_TCC_COOLING config INTEL_TCC_COOLING
tristate "Intel TCC offset cooling Driver" tristate "Intel TCC offset cooling Driver"
depends on X86 depends on X86
select INTEL_TCC
help help
Enable this to support system cooling by adjusting the effective TCC Enable this to support system cooling by adjusting the effective TCC
activation temperature via the TCC Offset register, which is widely activation temperature via the TCC Offset register, which is widely

View File

@ -2,6 +2,7 @@
# #
# Makefile for various Intel thermal drivers. # Makefile for various Intel thermal drivers.
obj-$(CONFIG_INTEL_TCC) += intel_tcc.o
obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
obj-$(CONFIG_INTEL_SOC_DTS_IOSF_CORE) += intel_soc_dts_iosf.o obj-$(CONFIG_INTEL_SOC_DTS_IOSF_CORE) += intel_soc_dts_iosf.o

View File

@ -9,7 +9,9 @@ config INT340X_THERMAL
select THERMAL_GOV_USER_SPACE select THERMAL_GOV_USER_SPACE
select ACPI_THERMAL_REL select ACPI_THERMAL_REL
select ACPI_FAN select ACPI_FAN
select THERMAL_ACPI
select INTEL_SOC_DTS_IOSF_CORE select INTEL_SOC_DTS_IOSF_CORE
select INTEL_TCC
select PROC_THERMAL_MMIO_RAPL if POWERCAP select PROC_THERMAL_MMIO_RAPL if POWERCAP
help help
Newer laptops and tablets that use ACPI may have thermal sensors and Newer laptops and tablets that use ACPI may have thermal sensors and

View File

@ -60,6 +60,7 @@ struct int3400_thermal_priv {
int odvp_count; int odvp_count;
int *odvp; int *odvp;
u32 os_uuid_mask; u32 os_uuid_mask;
int production_mode;
struct odvp_attr *odvp_attrs; 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++) { for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {
if (priv->uuid_bitmap & (1 << i)) if (priv->uuid_bitmap & (1 << i))
length += scnprintf(&buf[length], length += sysfs_emit_at(buf, length, int3400_thermal_uuids[i]);
PAGE_SIZE - length,
"%s\n",
int3400_thermal_uuids[i]);
} }
return length; return length;
@ -151,10 +149,7 @@ static ssize_t current_uuid_show(struct device *dev,
for (i = 0; i <= INT3400_THERMAL_CRITICAL; i++) { for (i = 0; i <= INT3400_THERMAL_CRITICAL; i++) {
if (priv->os_uuid_mask & BIT(i)) if (priv->os_uuid_mask & BIT(i))
length += scnprintf(&buf[length], length += sysfs_emit_at(buf, length, int3400_thermal_uuids[i]);
PAGE_SIZE - length,
"%s\n",
int3400_thermal_uuids[i]);
} }
if (length) if (length)
@ -315,6 +310,44 @@ static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv)
return result; 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, static ssize_t odvp_show(struct device *dev, struct device_attribute *attr,
char *buf) char *buf)
{ {
@ -610,8 +643,15 @@ static int int3400_thermal_probe(struct platform_device *pdev)
if (result) if (result)
goto free_sysfs; goto free_sysfs;
result = production_mode_init(priv);
if (result)
goto free_notify;
return 0; return 0;
free_notify:
acpi_remove_notify_handler(priv->adev->handle, ACPI_DEVICE_NOTIFY,
int3400_notify);
free_sysfs: free_sysfs:
cleanup_odvp(priv); cleanup_odvp(priv);
if (!ZERO_OR_NULL_PTR(priv->data_vault)) { 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); struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
production_mode_exit(priv);
acpi_remove_notify_handler( acpi_remove_notify_handler(
priv->adev->handle, ACPI_DEVICE_NOTIFY, priv->adev->handle, ACPI_DEVICE_NOTIFY,
int3400_notify); int3400_notify);

View File

@ -69,7 +69,7 @@ static void int3403_notify(acpi_handle handle,
THERMAL_TRIP_VIOLATED); THERMAL_TRIP_VIOLATED);
break; break;
case INT3403_PERF_TRIP_POINT_CHANGED: 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, int340x_thermal_zone_device_update(obj->int340x_zone,
THERMAL_TRIP_CHANGED); THERMAL_TRIP_CHANGED);
break; break;

View File

@ -37,73 +37,6 @@ static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
return 0; 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, ret = 0;
mutex_lock(&d->trip_mutex);
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)
ret = -EINVAL;
}
mutex_unlock(&d->trip_mutex);
return ret;
}
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, ret = 0;
mutex_lock(&d->trip_mutex);
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)
ret = -EINVAL;
}
mutex_unlock(&d->trip_mutex);
return ret;
}
static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone, static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
int trip, int temp) int trip, int temp)
{ {
@ -117,25 +50,6 @@ static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return -EIO; 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;
status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst);
if (ACPI_FAILURE(status))
*temp = 0;
else
*temp = hyst * 100;
return 0; return 0;
} }
@ -146,67 +60,39 @@ static void int340x_thermal_critical(struct thermal_zone_device *zone)
static struct thermal_zone_device_ops int340x_thermal_zone_ops = { static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
.get_temp = int340x_thermal_get_zone_temp, .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, .set_trip_temp = int340x_thermal_set_trip_temp,
.get_trip_hyst = int340x_thermal_get_trip_hyst,
.critical = int340x_thermal_critical, .critical = int340x_thermal_critical,
}; };
static int int340x_thermal_get_trip_config(acpi_handle handle, char *name, static int int340x_thermal_read_trips(struct acpi_device *zone_adev,
int *temp) struct thermal_trip *zone_trips,
int trip_cnt)
{ {
unsigned long long r; int i, ret;
acpi_status status;
status = acpi_evaluate_integer(handle, name, NULL, &r); ret = thermal_acpi_trip_critical(zone_adev, &zone_trips[trip_cnt]);
if (ACPI_FAILURE(status)) if (!ret)
return -EIO; trip_cnt++;
*temp = deci_kelvin_to_millicelsius(r); ret = thermal_acpi_trip_hot(zone_adev, &zone_trips[trip_cnt]);
if (!ret)
trip_cnt++;
return 0; ret = thermal_acpi_trip_passive(zone_adev, &zone_trips[trip_cnt]);
} if (!ret)
trip_cnt++;
int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone)
{
int trip_cnt = int34x_zone->aux_trip_nr;
int i;
mutex_lock(&int34x_zone->trip_mutex);
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++;
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++;
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++;
for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) { 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, ret = thermal_acpi_trip_active(zone_adev, i, &zone_trips[trip_cnt]);
name, if (ret)
&int34x_zone->act_trips[i].temp))
break; break;
int34x_zone->act_trips[i].id = trip_cnt++; trip_cnt++;
int34x_zone->act_trips[i].valid = true;
} }
mutex_unlock(&int34x_zone->trip_mutex);
return trip_cnt; return trip_cnt;
} }
EXPORT_SYMBOL_GPL(int340x_thermal_read_trips);
static struct thermal_zone_params int340x_thermal_params = { static struct thermal_zone_params int340x_thermal_params = {
.governor_name = "user_space", .governor_name = "user_space",
@ -217,18 +103,18 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
int (*get_temp) (struct thermal_zone_device *, int *)) int (*get_temp) (struct thermal_zone_device *, int *))
{ {
struct int34x_thermal_zone *int34x_thermal_zone; struct int34x_thermal_zone *int34x_thermal_zone;
acpi_status status; struct thermal_trip *zone_trips;
unsigned long long trip_cnt; unsigned long long trip_cnt = 0;
unsigned long long hyst;
int trip_mask = 0; int trip_mask = 0;
int ret; acpi_status status;
int i, ret;
int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone), int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone),
GFP_KERNEL); GFP_KERNEL);
if (!int34x_thermal_zone) if (!int34x_thermal_zone)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
mutex_init(&int34x_thermal_zone->trip_mutex);
int34x_thermal_zone->adev = adev; int34x_thermal_zone->adev = adev;
int34x_thermal_zone->ops = kmemdup(&int340x_thermal_zone_ops, int34x_thermal_zone->ops = kmemdup(&int340x_thermal_zone_ops,
@ -242,33 +128,42 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
int34x_thermal_zone->ops->get_temp = get_temp; int34x_thermal_zone->ops->get_temp = get_temp;
status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt); status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
if (ACPI_FAILURE(status)) 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;
}
trip_mask = BIT(trip_cnt) - 1;
int34x_thermal_zone->aux_trip_nr = trip_cnt; int34x_thermal_zone->aux_trip_nr = trip_cnt;
for (i = 0; i < trip_cnt; ++i) trip_mask = BIT(trip_cnt) - 1;
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);
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_thermal_zone->trips = zone_trips;
int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table( int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table(
adev->handle); adev->handle);
int34x_thermal_zone->zone = thermal_zone_device_register( int34x_thermal_zone->zone = thermal_zone_device_register_with_trips(
acpi_device_bid(adev), acpi_device_bid(adev),
trip_cnt, zone_trips, trip_cnt,
trip_mask, int34x_thermal_zone, trip_mask, int34x_thermal_zone,
int34x_thermal_zone->ops, int34x_thermal_zone->ops,
&int340x_thermal_params, &int340x_thermal_params,
@ -286,12 +181,11 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
err_enable: err_enable:
thermal_zone_device_unregister(int34x_thermal_zone->zone); thermal_zone_device_unregister(int34x_thermal_zone->zone);
err_thermal_zone: err_thermal_zone:
kfree(int34x_thermal_zone->trips);
acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table); acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
kfree(int34x_thermal_zone->aux_trips); err_trips_alloc:
err_trip_alloc:
kfree(int34x_thermal_zone->ops); kfree(int34x_thermal_zone->ops);
err_ops_alloc: err_ops_alloc:
mutex_destroy(&int34x_thermal_zone->trip_mutex);
kfree(int34x_thermal_zone); kfree(int34x_thermal_zone);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
@ -302,13 +196,55 @@ void int340x_thermal_zone_remove(struct int34x_thermal_zone
{ {
thermal_zone_device_unregister(int34x_thermal_zone->zone); thermal_zone_device_unregister(int34x_thermal_zone->zone);
acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table); acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
kfree(int34x_thermal_zone->aux_trips); kfree(int34x_thermal_zone->trips);
kfree(int34x_thermal_zone->ops); kfree(int34x_thermal_zone->ops);
mutex_destroy(&int34x_thermal_zone->trip_mutex);
kfree(int34x_thermal_zone); kfree(int34x_thermal_zone);
} }
EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove); 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++) {
struct thermal_trip trip;
int err;
switch (zone_trips[i].type) {
case THERMAL_TRIP_CRITICAL:
err = thermal_acpi_trip_critical(zone_adev, &trip);
break;
case THERMAL_TRIP_HOT:
err = thermal_acpi_trip_hot(zone_adev, &trip);
break;
case THERMAL_TRIP_PASSIVE:
err = thermal_acpi_trip_passive(zone_adev, &trip);
break;
case THERMAL_TRIP_ACTIVE:
err = thermal_acpi_trip_active(zone_adev, act_trip_nr++,
&trip);
break;
default:
err = -ENODEV;
}
if (err) {
zone_trips[i].temperature = THERMAL_TEMP_INVALID;
continue;
}
zone_trips[i].temperature = trip.temperature;
}
mutex_unlock(&int34x_zone->zone->lock);
}
EXPORT_SYMBOL_GPL(int340x_thermal_update_trips);
MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>"); MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_DESCRIPTION("Intel INT340x common thermal zone handler"); MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");

View File

@ -10,6 +10,7 @@
#include <acpi/acpi_lpat.h> #include <acpi/acpi_lpat.h>
#define INT340X_THERMAL_MAX_ACT_TRIP_COUNT 10 #define INT340X_THERMAL_MAX_ACT_TRIP_COUNT 10
#define INT340X_THERMAL_MAX_TRIP_COUNT INT340X_THERMAL_MAX_ACT_TRIP_COUNT + 3
struct active_trip { struct active_trip {
int temp; int temp;
@ -19,26 +20,18 @@ struct active_trip {
struct int34x_thermal_zone { struct int34x_thermal_zone {
struct acpi_device *adev; struct acpi_device *adev;
struct active_trip act_trips[INT340X_THERMAL_MAX_ACT_TRIP_COUNT]; struct thermal_trip *trips;
unsigned long *aux_trips;
int aux_trip_nr; 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 *zone;
struct thermal_zone_device_ops *ops; struct thermal_zone_device_ops *ops;
void *priv_data; void *priv_data;
struct acpi_lpat_conversion_table *lpat_table; struct acpi_lpat_conversion_table *lpat_table;
struct mutex trip_mutex;
}; };
struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *, struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *,
int (*get_temp) (struct thermal_zone_device *, int *)); int (*get_temp) (struct thermal_zone_device *, int *));
void int340x_thermal_zone_remove(struct int34x_thermal_zone *); 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( static inline void int340x_thermal_zone_set_priv_data(
struct int34x_thermal_zone *tzone, void *priv_data) struct int34x_thermal_zone *tzone, void *priv_data)

View File

@ -4,6 +4,7 @@
* Copyright (c) 2014, Intel Corporation. * Copyright (c) 2014, Intel Corporation.
*/ */
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/intel_tcc.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
@ -68,54 +69,17 @@ static const struct attribute_group power_limit_attribute_group = {
.name = "power_limits" .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, static ssize_t tcc_offset_degree_celsius_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
int tcc; int offset;
tcc = tcc_get_offset(); offset = intel_tcc_get_offset(-1);
if (tcc < 0) if (offset < 0)
return tcc; return offset;
return sprintf(buf, "%d\n", tcc); return sprintf(buf, "%d\n", offset);
}
static int tcc_offset_update(unsigned int tcc)
{
u64 val;
int err;
if (tcc > 63)
return -EINVAL;
err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val);
if (err)
return err;
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;
} }
static ssize_t tcc_offset_degree_celsius_store(struct device *dev, 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)) if (kstrtouint(buf, 0, &tcc))
return -EINVAL; return -EINVAL;
err = tcc_offset_update(tcc); err = intel_tcc_set_offset(-1, tcc);
if (err) if (err)
return err; return err;
@ -145,66 +109,25 @@ static ssize_t tcc_offset_degree_celsius_store(struct device *dev,
static DEVICE_ATTR_RW(tcc_offset_degree_celsius); static DEVICE_ATTR_RW(tcc_offset_degree_celsius);
static int stored_tjmax; /* since it is fixed, we can have local storage */ static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
int *temp)
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)
{ {
int cpu; int cpu;
u32 eax, edx; int curr_temp;
int err;
unsigned long curr_temp_off = 0;
*temp = 0; *temp = 0;
for_each_online_cpu(cpu) { for_each_online_cpu(cpu) {
err = rdmsr_safe_on_cpu(cpu, MSR_IA32_THERM_STATUS, &eax, curr_temp = intel_tcc_get_temp(cpu, false);
&edx); if (curr_temp < 0)
if (err) return curr_temp;
goto err_ret; if (!*temp || curr_temp > *temp)
else { *temp = curr_temp;
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;
}
}
} }
*temp *= 1000;
return 0; return 0;
err_ret:
return err;
}
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;
} }
static int proc_thermal_read_ppcc(struct proc_thermal_device *proc_priv) static int proc_thermal_read_ppcc(struct proc_thermal_device *proc_priv)
@ -298,8 +221,7 @@ int proc_thermal_add(struct device *dev, struct proc_thermal_device *proc_priv)
status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp); status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
/* there is no _TMP method, add local method */ /* there is no _TMP method, add local method */
stored_tjmax = get_tjmax(); if (intel_tcc_get_tjmax(-1) > 0)
if (stored_tjmax > 0)
get_temp = proc_thermal_get_zone_temp; get_temp = proc_thermal_get_zone_temp;
} }
@ -352,7 +274,7 @@ static int tcc_offset_save = -1;
int proc_thermal_suspend(struct device *dev) 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) if (tcc_offset_save < 0)
dev_warn(dev, "failed to save offset (%d)\n", tcc_offset_save); dev_warn(dev, "failed to save offset (%d)\n", tcc_offset_save);
@ -369,7 +291,7 @@ int proc_thermal_resume(struct device *dev)
/* Do not update if saving failed */ /* Do not update if saving failed */
if (tcc_offset_save >= 0) if (tcc_offset_save >= 0)
tcc_offset_update(tcc_offset_save); intel_tcc_set_offset(-1, tcc_offset_save);
return 0; return 0;
} }
@ -456,6 +378,7 @@ void proc_thermal_mmio_remove(struct pci_dev *pdev, struct proc_thermal_device *
} }
EXPORT_SYMBOL_GPL(proc_thermal_mmio_remove); EXPORT_SYMBOL_GPL(proc_thermal_mmio_remove);
MODULE_IMPORT_NS(INTEL_TCC);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver"); MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -144,34 +144,6 @@ static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp)
return 0; 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) static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
{ {
struct proc_thermal_pci *pci_info = tzd->devdata; 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; 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 = { static struct thermal_zone_device_ops tzone_ops = {
.get_temp = sys_get_curr_temp, .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, .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) if (ret)
goto err_ret_thermal; 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_ops,
&tzone_params, 0, 0); &tzone_params, 0, 0);
if (IS_ERR(pci_info->tzone)) { if (IS_ERR(pci_info->tzone)) {

View File

@ -232,9 +232,9 @@ static DEFINE_MUTEX(intel_menlow_attr_lock);
/* /*
* sensor_get_auxtrip - get the current auxtrip value from sensor * sensor_get_auxtrip - get the current auxtrip value from sensor
* @name: Thermalzone name * @handle: Object handle
* @auxtype : AUX0/AUX1 * @index : GET_AUX1/GET_AUX0
* @buf: syfs buffer * @value : The address will be fill by the value
*/ */
static int sensor_get_auxtrip(acpi_handle handle, int index, static int sensor_get_auxtrip(acpi_handle handle, int index,
unsigned long long *value) 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 * sensor_set_auxtrip - set the new auxtrip value to sensor
* @name: Thermalzone name * @handle: Object handle
* @auxtype : AUX0/AUX1 * @index : GET_AUX1/GET_AUX0
* @buf: syfs buffer * @value : The value will be set
*/ */
static int sensor_set_auxtrip(acpi_handle handle, int index, int value) static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
{ {

View File

@ -29,6 +29,7 @@
#define PCH_THERMAL_DID_CNL_LP 0x02F9 /* CNL-LP PCH */ #define PCH_THERMAL_DID_CNL_LP 0x02F9 /* CNL-LP PCH */
#define PCH_THERMAL_DID_CML_H 0X06F9 /* CML-H PCH */ #define PCH_THERMAL_DID_CML_H 0X06F9 /* CML-H PCH */
#define PCH_THERMAL_DID_LWB 0xA1B1 /* Lewisburg PCH */ #define PCH_THERMAL_DID_LWB 0xA1B1 /* Lewisburg PCH */
#define PCH_THERMAL_DID_WBG 0x8D24 /* Wellsburg PCH */
/* Wildcat Point-LP PCH Thermal registers */ /* Wildcat Point-LP PCH Thermal registers */
#define WPT_TEMP 0x0000 /* Temperature */ #define WPT_TEMP 0x0000 /* Temperature */
@ -65,6 +66,8 @@
#define WPT_TEMP_OFFSET (PCH_TEMP_OFFSET * MILLIDEGREE_PER_DEGREE) #define WPT_TEMP_OFFSET (PCH_TEMP_OFFSET * MILLIDEGREE_PER_DEGREE)
#define GET_PCH_TEMP(x) (((x) / 2) + PCH_TEMP_OFFSET) #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 */ /* Amount of time for each cooling delay, 100ms by default for now */
static unsigned int delay_timeout = 100; static unsigned int delay_timeout = 100;
module_param(delay_timeout, int, 0644); module_param(delay_timeout, int, 0644);
@ -82,12 +85,7 @@ struct pch_thermal_device {
const struct pch_dev_ops *ops; const struct pch_dev_ops *ops;
struct pci_dev *pdev; struct pci_dev *pdev;
struct thermal_zone_device *tzd; struct thermal_zone_device *tzd;
int crt_trip_id; struct thermal_trip trips[PCH_MAX_TRIPS];
unsigned long crt_temp;
int hot_trip_id;
unsigned long hot_temp;
int psv_trip_id;
unsigned long psv_temp;
bool bios_enabled; bool bios_enabled;
}; };
@ -102,33 +100,22 @@ static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
int *nr_trips) int *nr_trips)
{ {
struct acpi_device *adev; struct acpi_device *adev;
int ret;
ptd->psv_trip_id = -1;
adev = ACPI_COMPANION(&ptd->pdev->dev); adev = ACPI_COMPANION(&ptd->pdev->dev);
if (adev) { if (!adev)
unsigned long long r; return;
acpi_status status;
status = acpi_evaluate_integer(adev->handle, "_PSV", NULL, ret = thermal_acpi_trip_passive(adev, &ptd->trips[*nr_trips]);
&r); if (ret || ptd->trips[*nr_trips].temperature <= 0)
if (ACPI_SUCCESS(status)) { return;
unsigned long trip_temp;
trip_temp = deci_kelvin_to_millicelsius(r); ++(*nr_trips);
if (trip_temp) {
ptd->psv_temp = trip_temp;
ptd->psv_trip_id = *nr_trips;
++(*nr_trips);
}
}
}
} }
#else #else
static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd, static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
int *nr_trips) int *nr_trips)
{ {
ptd->psv_trip_id = -1;
} }
#endif #endif
@ -163,21 +150,19 @@ static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips)
} }
read_trips: read_trips:
ptd->crt_trip_id = -1;
trip_temp = readw(ptd->hw_base + WPT_CTT); trip_temp = readw(ptd->hw_base + WPT_CTT);
trip_temp &= 0x1FF; trip_temp &= 0x1FF;
if (trip_temp) { if (trip_temp) {
ptd->crt_temp = GET_WPT_TEMP(trip_temp); ptd->trips[*nr_trips].temperature = GET_WPT_TEMP(trip_temp);
ptd->crt_trip_id = 0; ptd->trips[*nr_trips].type = THERMAL_TRIP_CRITICAL;
++(*nr_trips); ++(*nr_trips);
} }
ptd->hot_trip_id = -1;
trip_temp = readw(ptd->hw_base + WPT_PHL); trip_temp = readw(ptd->hw_base + WPT_PHL);
trip_temp &= 0x1FF; trip_temp &= 0x1FF;
if (trip_temp) { if (trip_temp) {
ptd->hot_temp = GET_WPT_TEMP(trip_temp); ptd->trips[*nr_trips].temperature = GET_WPT_TEMP(trip_temp);
ptd->hot_trip_id = *nr_trips; ptd->trips[*nr_trips].type = THERMAL_TRIP_HOT;
++(*nr_trips); ++(*nr_trips);
} }
@ -298,39 +283,6 @@ static int pch_thermal_get_temp(struct thermal_zone_device *tzd, int *temp)
return ptd->ops->get_temp(ptd, temp); 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) static void pch_critical(struct thermal_zone_device *tzd)
{ {
dev_dbg(&tzd->device, "%s: critical temperature reached\n", tzd->type); dev_dbg(&tzd->device, "%s: critical temperature reached\n", tzd->type);
@ -338,8 +290,6 @@ static void pch_critical(struct thermal_zone_device *tzd)
static struct thermal_zone_device_ops tzd_ops = { static struct thermal_zone_device_ops tzd_ops = {
.get_temp = pch_thermal_get_temp, .get_temp = pch_thermal_get_temp,
.get_trip_type = pch_get_trip_type,
.get_trip_temp = pch_get_trip_temp,
.critical = pch_critical, .critical = pch_critical,
}; };
@ -350,6 +300,7 @@ enum board_ids {
board_cnl, board_cnl,
board_cml, board_cml,
board_lwb, board_lwb,
board_wbg,
}; };
static const struct board_info { static const struct board_info {
@ -380,6 +331,10 @@ static const struct board_info {
.name = "pch_lewisburg", .name = "pch_lewisburg",
.ops = &pch_dev_ops_wpt, .ops = &pch_dev_ops_wpt,
}, },
[board_wbg] = {
.name = "pch_wellsburg",
.ops = &pch_dev_ops_wpt,
},
}; };
static int intel_pch_thermal_probe(struct pci_dev *pdev, static int intel_pch_thermal_probe(struct pci_dev *pdev,
@ -423,8 +378,9 @@ static int intel_pch_thermal_probe(struct pci_dev *pdev,
if (err) if (err)
goto error_cleanup; goto error_cleanup;
ptd->tzd = thermal_zone_device_register(bi->name, nr_trips, 0, ptd, ptd->tzd = thermal_zone_device_register_with_trips(bi->name, ptd->trips,
&tzd_ops, NULL, 0, 0); nr_trips, 0, ptd,
&tzd_ops, NULL, 0, 0);
if (IS_ERR(ptd->tzd)) { if (IS_ERR(ptd->tzd)) {
dev_err(&pdev->dev, "Failed to register thermal zone %s\n", dev_err(&pdev->dev, "Failed to register thermal zone %s\n",
bi->name); bi->name);
@ -495,6 +451,8 @@ static const struct pci_device_id intel_pch_thermal_id[] = {
.driver_data = board_cml, }, .driver_data = board_cml, },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_LWB), { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_LWB),
.driver_data = board_lwb, }, .driver_data = board_lwb, },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WBG),
.driver_data = board_wbg, },
{ 0, }, { 0, },
}; };
MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id); MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id);

View File

@ -7,6 +7,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/intel_tcc.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
@ -45,32 +46,6 @@
/* DTS0 and DTS 1 */ /* DTS0 and DTS 1 */
#define SOC_MAX_DTS_SENSORS 2 #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, static int sys_get_trip_temp(struct thermal_zone_device *tzd, int trip,
int *temp) int *temp)
{ {
@ -405,7 +380,7 @@ struct intel_soc_dts_sensors *intel_soc_dts_iosf_init(
{ {
struct intel_soc_dts_sensors *sensors; struct intel_soc_dts_sensors *sensors;
bool notification; bool notification;
u32 tj_max; int tj_max;
int ret; int ret;
int i; 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) if (!trip_count || read_only_trip_count > trip_count)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
if (get_tj_max(&tj_max)) tj_max = intel_tcc_get_tjmax(-1);
return ERR_PTR(-EINVAL); if (tj_max < 0)
return ERR_PTR(tj_max);
sensors = kzalloc(sizeof(*sensors), GFP_KERNEL); sensors = kzalloc(sizeof(*sensors), GFP_KERNEL);
if (!sensors) 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); EXPORT_SYMBOL_GPL(intel_soc_dts_iosf_exit);
MODULE_IMPORT_NS(INTEL_TCC);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -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);

View File

@ -7,12 +7,11 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/device.h> #include <linux/device.h>
#include <linux/intel_tcc.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/thermal.h> #include <linux/thermal.h>
#include <asm/cpu_device_id.h> #include <asm/cpu_device_id.h>
#define TCC_SHIFT 24
#define TCC_MASK (0x3fULL<<24)
#define TCC_PROGRAMMABLE BIT(30) #define TCC_PROGRAMMABLE BIT(30)
#define TCC_LOCKED BIT(31) #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 static int tcc_get_max_state(struct thermal_cooling_device *cdev, unsigned long
*state) *state)
{ {
*state = TCC_MASK >> TCC_SHIFT; *state = 0x3f;
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;
return 0; return 0;
} }
static int tcc_get_cur_state(struct thermal_cooling_device *cdev, unsigned long static int tcc_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
*state) *state)
{ {
u64 val; int offset = intel_tcc_get_offset(-1);
int err;
err = rdmsrl_safe(MSR_IA32_TEMPERATURE_TARGET, &val); if (offset < 0)
if (err) return offset;
return err;
*state = (val & TCC_MASK) >> TCC_SHIFT; *state = offset;
return 0; return 0;
} }
static int tcc_set_cur_state(struct thermal_cooling_device *cdev, unsigned long static int tcc_set_cur_state(struct thermal_cooling_device *cdev, unsigned long
state) state)
{ {
return tcc_offset_update(state); return intel_tcc_set_offset(-1, (int)state);
} }
static const struct thermal_cooling_device_ops tcc_cooling_ops = { 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_exit(tcc_cooling_exit)
MODULE_IMPORT_NS(INTEL_TCC);
MODULE_DESCRIPTION("TCC offset cooling device Driver"); MODULE_DESCRIPTION("TCC offset cooling device Driver");
MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>"); MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -7,6 +7,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/intel_tcc.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/param.h> #include <linux/param.h>
#include <linux/device.h> #include <linux/device.h>
@ -48,7 +49,6 @@ MODULE_PARM_DESC(notify_delay_ms,
struct zone_device { struct zone_device {
int cpu; int cpu;
bool work_scheduled; bool work_scheduled;
u32 tj_max;
u32 msr_pkg_therm_low; u32 msr_pkg_therm_low;
u32 msr_pkg_therm_high; u32 msr_pkg_therm_high;
struct delayed_work work; struct delayed_work work;
@ -105,38 +105,18 @@ static struct zone_device *pkg_temp_thermal_get_dev(unsigned int cpu)
return NULL; 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) static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp)
{ {
struct zone_device *zonedev = tzd->devdata; struct zone_device *zonedev = tzd->devdata;
u32 eax, edx; int val;
rdmsr_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_STATUS, val = intel_tcc_get_temp(zonedev->cpu, true);
&eax, &edx); if (val < 0)
if (eax & 0x80000000) { return val;
*temp = zonedev->tj_max - ((eax >> 16) & 0x7f) * 1000;
pr_debug("sys_get_curr_temp %d\n", *temp); *temp = val * 1000;
return 0; pr_debug("sys_get_curr_temp %d\n", *temp);
} return 0;
return -EINVAL;
} }
static int static int
@ -144,9 +124,14 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
{ {
struct zone_device *zonedev = tzd->devdata; struct zone_device *zonedev = tzd->devdata;
u32 l, h, mask, shift, intr; u32 l, h, mask, shift, intr;
int ret; int tj_max, ret;
if (trip >= MAX_NUMBER_OF_TRIPS || temp >= zonedev->tj_max) 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 >= tj_max)
return -EINVAL; return -EINVAL;
ret = rdmsr_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, ret = rdmsr_on_cpu(zonedev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
@ -171,7 +156,7 @@ sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
if (!temp) { if (!temp) {
l &= ~intr; l &= ~intr;
} else { } else {
l |= (zonedev->tj_max - temp)/1000 << shift; l |= (tj_max - temp)/1000 << shift;
l |= intr; l |= intr;
} }
@ -326,9 +311,10 @@ static struct thermal_trip *pkg_temp_thermal_trips_init(int cpu, int tj_max, int
static int pkg_temp_thermal_device_add(unsigned int cpu) static int pkg_temp_thermal_device_add(unsigned int cpu)
{ {
int id = topology_logical_die_id(cpu); int id = topology_logical_die_id(cpu);
u32 tj_max, eax, ebx, ecx, edx; u32 eax, ebx, ecx, edx;
struct zone_device *zonedev; struct zone_device *zonedev;
int thres_count, err; int thres_count, err;
int tj_max;
if (id >= max_id) if (id >= max_id)
return -ENOMEM; return -ENOMEM;
@ -340,9 +326,9 @@ static int pkg_temp_thermal_device_add(unsigned int cpu)
thres_count = clamp_val(thres_count, 0, MAX_NUMBER_OF_TRIPS); thres_count = clamp_val(thres_count, 0, MAX_NUMBER_OF_TRIPS);
err = get_tj_max(cpu, &tj_max); tj_max = intel_tcc_get_tjmax(cpu);
if (err) if (tj_max < 0)
return err; return tj_max;
zonedev = kzalloc(sizeof(*zonedev), GFP_KERNEL); zonedev = kzalloc(sizeof(*zonedev), GFP_KERNEL);
if (!zonedev) if (!zonedev)
@ -356,7 +342,6 @@ static int pkg_temp_thermal_device_add(unsigned int cpu)
INIT_DELAYED_WORK(&zonedev->work, pkg_temp_thermal_threshold_work_fn); INIT_DELAYED_WORK(&zonedev->work, pkg_temp_thermal_threshold_work_fn);
zonedev->cpu = cpu; zonedev->cpu = cpu;
zonedev->tj_max = tj_max;
zonedev->tzone = thermal_zone_device_register_with_trips("x86_pkg_temp", zonedev->tzone = thermal_zone_device_register_with_trips("x86_pkg_temp",
zonedev->trips, thres_count, zonedev->trips, thres_count,
(thres_count == MAX_NUMBER_OF_TRIPS) ? 0x03 : 0x01, (thres_count == MAX_NUMBER_OF_TRIPS) ? 0x03 : 0x01,
@ -545,6 +530,7 @@ static void __exit pkg_temp_thermal_exit(void)
} }
module_exit(pkg_temp_thermal_exit) module_exit(pkg_temp_thermal_exit)
MODULE_IMPORT_NS(INTEL_TCC);
MODULE_DESCRIPTION("X86 PKG TEMP Thermal Driver"); MODULE_DESCRIPTION("X86 PKG TEMP Thermal Driver");
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,151 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2023 Linaro Limited
* Copyright 2023 Intel Corporation
*
* Library routines for populating a generic thermal trip point structure
* with data obtained by evaluating a specific object in the ACPI Namespace.
*/
#include <linux/acpi.h>
#include <linux/units.h>
#include "thermal_core.h"
/*
* Minimum temperature for full military grade is 218°K (-55°C) and
* max temperature is 448°K (175°C). We can consider those values as
* the boundaries for the [trips] temperature returned by the
* firmware. Any values out of these boundaries may be considered
* bogus and we can assume the firmware has no data to provide.
*/
#define TEMP_MIN_DECIK 2180
#define TEMP_MAX_DECIK 4480
static int thermal_acpi_trip_init(struct acpi_device *adev,
enum thermal_trip_type type, int id,
struct thermal_trip *trip)
{
unsigned long long temp;
acpi_status status;
char obj_name[5];
switch (type) {
case THERMAL_TRIP_ACTIVE:
if (id < 0 || id > 9)
return -EINVAL;
obj_name[1] = 'A';
obj_name[2] = 'C';
obj_name[3] = '0' + id;
break;
case THERMAL_TRIP_PASSIVE:
obj_name[1] = 'P';
obj_name[2] = 'S';
obj_name[3] = 'V';
break;
case THERMAL_TRIP_HOT:
obj_name[1] = 'H';
obj_name[2] = 'O';
obj_name[3] = 'T';
break;
case THERMAL_TRIP_CRITICAL:
obj_name[1] = 'C';
obj_name[2] = 'R';
obj_name[3] = 'T';
break;
}
obj_name[0] = '_';
obj_name[4] = '\0';
status = acpi_evaluate_integer(adev->handle, obj_name, NULL, &temp);
if (ACPI_FAILURE(status)) {
acpi_handle_debug(adev->handle, "%s evaluation failed\n", obj_name);
return -ENODATA;
}
if (temp >= TEMP_MIN_DECIK && temp <= TEMP_MAX_DECIK) {
trip->temperature = deci_kelvin_to_millicelsius(temp);
} else {
acpi_handle_debug(adev->handle, "%s result %llu out of range\n",
obj_name, temp);
trip->temperature = THERMAL_TEMP_INVALID;
}
trip->hysteresis = 0;
trip->type = type;
return 0;
}
/**
* thermal_acpi_trip_active - Get the specified active trip point
* @adev: Thermal zone ACPI device object to get the description from.
* @id: Active cooling level (0 - 9).
* @trip: Trip point structure to be populated on success.
*
* Evaluate the _ACx object for the thermal zone represented by @adev to obtain
* the temperature of the active cooling trip point corresponding to the active
* cooling level given by @id and initialize @trip as an active trip point using
* that temperature value.
*
* Return 0 on success or a negative error value on failure.
*/
int thermal_acpi_trip_active(struct acpi_device *adev, int id,
struct thermal_trip *trip)
{
return thermal_acpi_trip_init(adev, THERMAL_TRIP_ACTIVE, id, trip);
}
EXPORT_SYMBOL_GPL(thermal_acpi_trip_active);
/**
* thermal_acpi_trip_passive - Get the passive trip point
* @adev: Thermal zone ACPI device object to get the description from.
* @trip: Trip point structure to be populated on success.
*
* Evaluate the _PSV object for the thermal zone represented by @adev to obtain
* the temperature of the passive cooling trip point and initialize @trip as a
* passive trip point using that temperature value.
*
* Return 0 on success or -ENODATA on failure.
*/
int thermal_acpi_trip_passive(struct acpi_device *adev, struct thermal_trip *trip)
{
return thermal_acpi_trip_init(adev, THERMAL_TRIP_PASSIVE, INT_MAX, trip);
}
EXPORT_SYMBOL_GPL(thermal_acpi_trip_passive);
/**
* thermal_acpi_trip_hot - Get the near critical trip point
* @adev: the ACPI device to get the description from.
* @trip: a &struct thermal_trip to be filled if the function succeed.
*
* Evaluate the _HOT object for the thermal zone represented by @adev to obtain
* the temperature of the trip point at which the system is expected to be put
* into the S4 sleep state and initialize @trip as a hot trip point using that
* temperature value.
*
* Return 0 on success or -ENODATA on failure.
*/
int thermal_acpi_trip_hot(struct acpi_device *adev, struct thermal_trip *trip)
{
return thermal_acpi_trip_init(adev, THERMAL_TRIP_HOT, INT_MAX, trip);
}
EXPORT_SYMBOL_GPL(thermal_acpi_trip_hot);
/**
* thermal_acpi_trip_critical - Get the critical trip point
* @adev: the ACPI device to get the description from.
* @trip: a &struct thermal_trip to be filled if the function succeed.
*
* Evaluate the _CRT object for the thermal zone represented by @adev to obtain
* the temperature of the critical cooling trip point and initialize @trip as a
* critical trip point using that temperature value.
*
* Return 0 on success or -ENODATA on failure.
*/
int thermal_acpi_trip_critical(struct acpi_device *adev, struct thermal_trip *trip)
{
return thermal_acpi_trip_init(adev, THERMAL_TRIP_CRITICAL, INT_MAX, trip);
}
EXPORT_SYMBOL_GPL(thermal_acpi_trip_critical);

18
include/linux/intel_tcc.h Normal file
View File

@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* header for Intel TCC (thermal control circuitry) library
*
* Copyright (C) 2022 Intel Corporation.
*/
#ifndef __INTEL_TCC_H__
#define __INTEL_TCC_H__
#include <linux/types.h>
int intel_tcc_get_tjmax(int cpu);
int intel_tcc_get_offset(int cpu);
int intel_tcc_set_offset(int cpu, int offset);
int intel_tcc_get_temp(int cpu, bool pkg);
#endif /* __INTEL_TCC_H__ */

View File

@ -346,6 +346,14 @@ int thermal_zone_get_num_trips(struct thermal_zone_device *tz);
int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp); int thermal_zone_get_crit_temp(struct thermal_zone_device *tz, int *temp);
#ifdef CONFIG_THERMAL_ACPI
int thermal_acpi_trip_active(struct acpi_device *adev, int id,
struct thermal_trip *trip);
int thermal_acpi_trip_passive(struct acpi_device *adev, struct thermal_trip *trip);
int thermal_acpi_trip_hot(struct acpi_device *adev, struct thermal_trip *trip);
int thermal_acpi_trip_critical(struct acpi_device *adev, struct thermal_trip *trip);
#endif
#ifdef CONFIG_THERMAL #ifdef CONFIG_THERMAL
struct thermal_zone_device *thermal_zone_device_register(const char *, int, int, struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,
void *, struct thermal_zone_device_ops *, void *, struct thermal_zone_device_ops *,