aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/thermal/rockchip-thermal.txt3
-rw-r--r--drivers/thermal/Kconfig4
-rw-r--r--drivers/thermal/db8500_thermal.c1
-rw-r--r--drivers/thermal/devfreq_cooling.c5
-rw-r--r--drivers/thermal/int340x_thermal/int3400_thermal.c2
-rw-r--r--drivers/thermal/intel_pch_thermal.c64
-rw-r--r--drivers/thermal/max77620_thermal.c1
-rw-r--r--drivers/thermal/qcom-spmi-temp-alarm.c6
-rw-r--r--drivers/thermal/rockchip_thermal.c7
-rw-r--r--drivers/thermal/tango_thermal.c1
-rw-r--r--drivers/thermal/thermal_hwmon.c4
-rw-r--r--drivers/thermal/ti-soc-thermal/ti-bandgap.c5
-rw-r--r--drivers/thermal/x86_pkg_temp_thermal.c587
-rw-r--r--include/linux/devfreq_cooling.h9
14 files changed, 314 insertions, 385 deletions
diff --git a/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt b/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt
index 08efe6bc2193..43003aec94bd 100644
--- a/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt
+++ b/Documentation/devicetree/bindings/thermal/rockchip-thermal.txt
@@ -22,10 +22,13 @@ Required properties:
TSADC controller.
- pinctrl-2 : The "sleep" pinctrl state, it will be in for suspend.
- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
+
+Optional properties:
- rockchip,hw-tshut-temp : The hardware-controlled shutdown temperature value.
- rockchip,hw-tshut-mode : The hardware-controlled shutdown mode 0:CRU 1:GPIO.
- rockchip,hw-tshut-polarity : The hardware-controlled active polarity 0:LOW
1:HIGH.
+- rockchip,grf : The phandle of the syscon node for the general register file.
Exiample:
tsadc: tsadc@ff280000 {
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index a13541bdc726..c2c056cc7ea5 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -177,8 +177,10 @@ config THERMAL_EMULATION
config HISI_THERMAL
tristate "Hisilicon thermal driver"
- depends on (ARCH_HISI && CPU_THERMAL && OF) || COMPILE_TEST
+ depends on ARCH_HISI || COMPILE_TEST
depends on HAS_IOMEM
+ depends on OF
+ default y
help
Enable this to plug hisilicon's thermal sensor driver into the Linux
thermal framework. cpufreq is used as the cooling device to throttle
diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c
index e776cea80cfc..f491faf16592 100644
--- a/drivers/thermal/db8500_thermal.c
+++ b/drivers/thermal/db8500_thermal.c
@@ -512,6 +512,7 @@ static const struct of_device_id db8500_thermal_match[] = {
{ .compatible = "stericsson,db8500-thermal" },
{},
};
+MODULE_DEVICE_TABLE(of, db8500_thermal_match);
#endif
static struct platform_driver db8500_thermal_driver = {
diff --git a/drivers/thermal/devfreq_cooling.c b/drivers/thermal/devfreq_cooling.c
index 81631b110e17..5a737fd5f1aa 100644
--- a/drivers/thermal/devfreq_cooling.c
+++ b/drivers/thermal/devfreq_cooling.c
@@ -238,7 +238,7 @@ get_static_power(struct devfreq_cooling_device *dfc, unsigned long freq)
return 0;
}
- return dfc->power_ops->get_static_power(voltage);
+ return dfc->power_ops->get_static_power(df, voltage);
}
/**
@@ -262,7 +262,8 @@ get_dynamic_power(struct devfreq_cooling_device *dfc, unsigned long freq,
struct devfreq_cooling_power *dfc_power = dfc->power_ops;
if (dfc_power->get_dynamic_power)
- return dfc_power->get_dynamic_power(freq, voltage);
+ return dfc_power->get_dynamic_power(dfc->devfreq, freq,
+ voltage);
freq_mhz = freq / 1000000;
power = (u64)dfc_power->dyn_power_coeff * freq_mhz * voltage * voltage;
diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c
index 5836e5554433..9413c4abf0b9 100644
--- a/drivers/thermal/int340x_thermal/int3400_thermal.c
+++ b/drivers/thermal/int340x_thermal/int3400_thermal.c
@@ -96,7 +96,7 @@ static ssize_t current_uuid_store(struct device *dev,
return -EINVAL;
}
-static DEVICE_ATTR(current_uuid, 0644, current_uuid_show, current_uuid_store);
+static DEVICE_ATTR_RW(current_uuid);
static DEVICE_ATTR_RO(available_uuids);
static struct attribute *uuid_attrs[] = {
&dev_attr_available_uuids.attr,
diff --git a/drivers/thermal/intel_pch_thermal.c b/drivers/thermal/intel_pch_thermal.c
index 19bf2028e508..2b49e8d0fe9e 100644
--- a/drivers/thermal/intel_pch_thermal.c
+++ b/drivers/thermal/intel_pch_thermal.c
@@ -29,6 +29,7 @@
#define PCH_THERMAL_DID_HSW_2 0x8C24 /* Haswell PCH */
#define PCH_THERMAL_DID_WPT 0x9CA4 /* Wildcat Point */
#define PCH_THERMAL_DID_SKL 0x9D31 /* Skylake PCH */
+#define PCH_THERMAL_DID_SKL_H 0xA131 /* Skylake PCH 100 series */
/* Wildcat Point-LP PCH Thermal registers */
#define WPT_TEMP 0x0000 /* Temperature */
@@ -273,37 +274,44 @@ static struct thermal_zone_device_ops tzd_ops = {
.get_trip_temp = pch_get_trip_temp,
};
+enum board_ids {
+ board_hsw,
+ board_wpt,
+ board_skl,
+};
+
+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,
+ },
+};
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;
- char *dev_name;
ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL);
if (!ptd)
return -ENOMEM;
- switch (pdev->device) {
- case PCH_THERMAL_DID_WPT:
- ptd->ops = &pch_dev_ops_wpt;
- dev_name = "pch_wildcat_point";
- break;
- case PCH_THERMAL_DID_SKL:
- ptd->ops = &pch_dev_ops_wpt;
- dev_name = "pch_skylake";
- break;
- case PCH_THERMAL_DID_HSW_1:
- case PCH_THERMAL_DID_HSW_2:
- ptd->ops = &pch_dev_ops_wpt;
- dev_name = "pch_haswell";
- break;
- default:
- dev_err(&pdev->dev, "unknown pch thermal device\n");
- return -ENODEV;
- }
+ ptd->ops = bi->ops;
pci_set_drvdata(pdev, ptd);
ptd->pdev = pdev;
@@ -331,11 +339,11 @@ static int intel_pch_thermal_probe(struct pci_dev *pdev,
if (err)
goto error_cleanup;
- ptd->tzd = thermal_zone_device_register(dev_name, nr_trips, 0, ptd,
+ 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",
- dev_name);
+ bi->name);
err = PTR_ERR(ptd->tzd);
goto error_cleanup;
}
@@ -380,10 +388,16 @@ static int intel_pch_thermal_resume(struct device *device)
}
static struct pci_device_id intel_pch_thermal_id[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_1) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_2) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_1),
+ .driver_data = board_hsw, },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_2),
+ .driver_data = board_hsw, },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT),
+ .driver_data = board_wpt, },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL),
+ .driver_data = board_skl, },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL_H),
+ .driver_data = board_skl, },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id);
diff --git a/drivers/thermal/max77620_thermal.c b/drivers/thermal/max77620_thermal.c
index 83905ff46e40..e9a1fe342760 100644
--- a/drivers/thermal/max77620_thermal.c
+++ b/drivers/thermal/max77620_thermal.c
@@ -149,6 +149,7 @@ static struct platform_device_id max77620_thermal_devtype[] = {
{ .name = "max77620-thermal", },
{},
};
+MODULE_DEVICE_TABLE(platform, max77620_thermal_devtype);
static struct platform_driver max77620_thermal_driver = {
.driver = {
diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom-spmi-temp-alarm.c
index 819c6d5d7aa7..f50241962ad2 100644
--- a/drivers/thermal/qcom-spmi-temp-alarm.c
+++ b/drivers/thermal/qcom-spmi-temp-alarm.c
@@ -200,7 +200,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
struct qpnp_tm_chip *chip;
struct device_node *node;
u8 type, subtype;
- u32 res[2];
+ u32 res;
int ret, irq;
node = pdev->dev.of_node;
@@ -215,7 +215,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
if (!chip->map)
return -ENXIO;
- ret = of_property_read_u32_array(node, "reg", res, 2);
+ ret = of_property_read_u32(node, "reg", &res);
if (ret < 0)
return ret;
@@ -228,7 +228,7 @@ static int qpnp_tm_probe(struct platform_device *pdev)
if (PTR_ERR(chip->adc) == -EPROBE_DEFER)
return PTR_ERR(chip->adc);
- chip->base = res[0];
+ chip->base = res;
ret = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, &type);
if (ret < 0) {
diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c
index e227a9f0acf7..b811b0fb61b1 100644
--- a/drivers/thermal/rockchip_thermal.c
+++ b/drivers/thermal/rockchip_thermal.c
@@ -524,11 +524,6 @@ static void rk_tsadcv2_initialize(struct regmap *grf, void __iomem *regs,
regs + TSADCV2_AUTO_PERIOD_HT);
writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
-
- if (IS_ERR(grf)) {
- pr_warn("%s: Missing rockchip,grf property\n", __func__);
- return;
- }
}
/**
@@ -971,6 +966,8 @@ static int rockchip_configure_from_dt(struct device *dev,
* need this property.
*/
thermal->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(thermal->grf))
+ dev_warn(dev, "Missing rockchip,grf property\n");
return 0;
}
diff --git a/drivers/thermal/tango_thermal.c b/drivers/thermal/tango_thermal.c
index 201304aeafeb..4e67795cb6ce 100644
--- a/drivers/thermal/tango_thermal.c
+++ b/drivers/thermal/tango_thermal.c
@@ -107,6 +107,7 @@ static const struct of_device_id tango_sensor_ids[] = {
},
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, tango_sensor_ids);
static struct platform_driver tango_thermal_driver = {
.probe = tango_thermal_probe,
diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
index c41c7742903a..541af5946203 100644
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -64,7 +64,7 @@ name_show(struct device *dev, struct device_attribute *attr, char *buf)
struct thermal_hwmon_device *hwmon = dev_get_drvdata(dev);
return sprintf(buf, "%s\n", hwmon->type);
}
-static DEVICE_ATTR(name, 0444, name_show, NULL);
+static DEVICE_ATTR_RO(name);
static ssize_t
temp_input_show(struct device *dev, struct device_attribute *attr, char *buf)
@@ -98,7 +98,7 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf)
int temperature;
int ret;
- ret = tz->ops->get_trip_temp(tz, 0, &temperature);
+ ret = tz->ops->get_crit_temp(tz, &temperature);
if (ret)
return ret;
diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
index 06ea9766a70a..ba9c302454fb 100644
--- a/drivers/thermal/ti-soc-thermal/ti-bandgap.c
+++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c
@@ -1298,7 +1298,7 @@ int ti_bandgap_probe(struct platform_device *pdev)
if (IS_ERR(bgp->div_clk)) {
dev_err(&pdev->dev, "failed to request div_ts_ck clock ref\n");
ret = PTR_ERR(bgp->div_clk);
- goto free_irqs;
+ goto put_fclock;
}
for (i = 0; i < bgp->conf->sensor_count; i++) {
@@ -1430,8 +1430,9 @@ disable_clk:
if (TI_BANDGAP_HAS(bgp, CLK_CTRL))
clk_disable_unprepare(bgp->fclock);
put_clks:
- clk_put(bgp->fclock);
clk_put(bgp->div_clk);
+put_fclock:
+ clk_put(bgp->fclock);
free_irqs:
if (TI_BANDGAP_HAS(bgp, TSHUT)) {
free_irq(gpio_to_irq(bgp->tshut_gpio), NULL);
diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c
index 95f4c1bcdb4c..d93eee2f101b 100644
--- a/drivers/thermal/x86_pkg_temp_thermal.c
+++ b/drivers/thermal/x86_pkg_temp_thermal.c
@@ -54,37 +54,33 @@ MODULE_PARM_DESC(notify_delay_ms,
* is some wrong values returned by cpuid for number of thresholds.
*/
#define MAX_NUMBER_OF_TRIPS 2
-/* Limit number of package temp zones */
-#define MAX_PKG_TEMP_ZONE_IDS 256
-
-struct phy_dev_entry {
- struct list_head list;
- u16 phys_proc_id;
- u16 first_cpu;
- u32 tj_max;
- int ref_cnt;
- u32 start_pkg_therm_low;
- u32 start_pkg_therm_high;
- struct thermal_zone_device *tzone;
+
+struct pkg_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 cpumask cpumask;
};
static struct thermal_zone_params pkg_temp_tz_params = {
.no_hwmon = true,
};
-/* List maintaining number of package instances */
-static LIST_HEAD(phy_dev_list);
-static DEFINE_MUTEX(phy_dev_list_mutex);
-
-/* Interrupt to work function schedule queue */
-static DEFINE_PER_CPU(struct delayed_work, pkg_temp_thermal_threshold_work);
+/* Keep track of how many package pointers we allocated in init() */
+static int max_packages __read_mostly;
+/* Array of package pointers */
+static struct pkg_device **packages;
+/* Serializes interrupt notification, work and hotplug */
+static DEFINE_SPINLOCK(pkg_temp_lock);
+/* Protects zone operation in the work function against hotplug removal */
+static DEFINE_MUTEX(thermal_zone_mutex);
-/* To track if the work is already scheduled on a package */
-static u8 *pkg_work_scheduled;
-
-/* Spin lock to prevent races with pkg_work_scheduled */
-static spinlock_t pkg_work_lock;
-static u16 max_phy_id;
+/* The dynamically assigned cpu hotplug state for module_exit() */
+static enum cpuhp_state pkg_thermal_hp_state __read_mostly;
/* Debug counters to show using debugfs */
static struct dentry *debugfs;
@@ -116,22 +112,20 @@ err_out:
return -ENOENT;
}
-static struct phy_dev_entry
- *pkg_temp_thermal_get_phy_entry(unsigned int cpu)
+/*
+ * Protection:
+ *
+ * - cpu hotplug: Read serialized by cpu hotplug lock
+ * Write must hold pkg_temp_lock
+ *
+ * - Other callsites: Must hold pkg_temp_lock
+ */
+static struct pkg_device *pkg_temp_thermal_get_dev(unsigned int cpu)
{
- u16 phys_proc_id = topology_physical_package_id(cpu);
- struct phy_dev_entry *phy_ptr;
-
- mutex_lock(&phy_dev_list_mutex);
-
- list_for_each_entry(phy_ptr, &phy_dev_list, list)
- if (phy_ptr->phys_proc_id == phys_proc_id) {
- mutex_unlock(&phy_dev_list_mutex);
- return phy_ptr;
- }
-
- mutex_unlock(&phy_dev_list_mutex);
+ int pkgid = topology_logical_package_id(cpu);
+ if (pkgid >= 0 && pkgid < max_packages)
+ return packages[pkgid];
return NULL;
}
@@ -141,61 +135,44 @@ static struct phy_dev_entry
*/
static int get_tj_max(int cpu, u32 *tj_max)
{
- u32 eax, edx;
- u32 val;
+ u32 eax, edx, val;
int err;
err = rdmsr_safe_on_cpu(cpu, 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 err;
- return 0;
-err_ret:
- *tj_max = 0;
- 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 pkg_device *pkgdev = tzd->devdata;
u32 eax, edx;
- struct phy_dev_entry *phy_dev_entry;
- phy_dev_entry = tzd->devdata;
- rdmsr_on_cpu(phy_dev_entry->first_cpu, MSR_IA32_PACKAGE_THERM_STATUS,
- &eax, &edx);
+ rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_STATUS, &eax, &edx);
if (eax & 0x80000000) {
- *temp = phy_dev_entry->tj_max -
- ((eax >> 16) & 0x7f) * 1000;
+ *temp = pkgdev->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)
+ int trip, int *temp)
{
- u32 eax, edx;
- struct phy_dev_entry *phy_dev_entry;
- u32 mask, shift;
+ struct pkg_device *pkgdev = tzd->devdata;
unsigned long thres_reg_value;
+ u32 mask, shift, eax, edx;
int ret;
if (trip >= MAX_NUMBER_OF_TRIPS)
return -EINVAL;
- phy_dev_entry = tzd->devdata;
-
if (trip) {
mask = THERM_MASK_THRESHOLD1;
shift = THERM_SHIFT_THRESHOLD1;
@@ -204,14 +181,14 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzd,
shift = THERM_SHIFT_THRESHOLD0;
}
- ret = rdmsr_on_cpu(phy_dev_entry->first_cpu,
- MSR_IA32_PACKAGE_THERM_INTERRUPT, &eax, &edx);
+ ret = rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
+ &eax, &edx);
if (ret < 0)
- return -EINVAL;
+ return ret;
thres_reg_value = (eax & mask) >> shift;
if (thres_reg_value)
- *temp = phy_dev_entry->tj_max - thres_reg_value * 1000;
+ *temp = pkgdev->tj_max - thres_reg_value * 1000;
else
*temp = 0;
pr_debug("sys_get_trip_temp %d\n", *temp);
@@ -219,24 +196,20 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzd,
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)
{
- u32 l, h;
- struct phy_dev_entry *phy_dev_entry;
- u32 mask, shift, intr;
+ struct pkg_device *pkgdev = tzd->devdata;
+ u32 l, h, mask, shift, intr;
int ret;
- phy_dev_entry = tzd->devdata;
-
- if (trip >= MAX_NUMBER_OF_TRIPS || temp >= phy_dev_entry->tj_max)
+ if (trip >= MAX_NUMBER_OF_TRIPS || temp >= pkgdev->tj_max)
return -EINVAL;
- ret = rdmsr_on_cpu(phy_dev_entry->first_cpu,
- MSR_IA32_PACKAGE_THERM_INTERRUPT,
- &l, &h);
+ ret = rdmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
+ &l, &h);
if (ret < 0)
- return -EINVAL;
+ return ret;
if (trip) {
mask = THERM_MASK_THRESHOLD1;
@@ -252,24 +225,20 @@ static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip,
* When users space sets a trip temperature == 0, which is indication
* that, it is no longer interested in receiving notifications.
*/
- if (!temp)
+ if (!temp) {
l &= ~intr;
- else {
- l |= (phy_dev_entry->tj_max - temp)/1000 << shift;
+ } else {
+ l |= (pkgdev->tj_max - temp)/1000 << shift;
l |= intr;
}
- return wrmsr_on_cpu(phy_dev_entry->first_cpu,
- MSR_IA32_PACKAGE_THERM_INTERRUPT,
- l, h);
+ return wrmsr_on_cpu(pkgdev->cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
}
-static int sys_get_trip_type(struct thermal_zone_device *thermal,
- int trip, enum thermal_trip_type *type)
+static int sys_get_trip_type(struct thermal_zone_device *thermal, int trip,
+ enum thermal_trip_type *type)
{
-
*type = THERMAL_TRIP_PASSIVE;
-
return 0;
}
@@ -281,7 +250,7 @@ static struct thermal_zone_device_ops tzone_ops = {
.set_trip_temp = sys_set_trip_temp,
};
-static bool pkg_temp_thermal_platform_thermal_rate_control(void)
+static bool pkg_thermal_rate_control(void)
{
return true;
}
@@ -289,8 +258,8 @@ static bool pkg_temp_thermal_platform_thermal_rate_control(void)
/* Enable threshold interrupt on local package/cpu */
static inline void enable_pkg_thres_interrupt(void)
{
- u32 l, h;
u8 thres_0, thres_1;
+ u32 l, h;
rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
/* only enable/disable if it had valid threshold value */
@@ -307,271 +276,232 @@ static inline void enable_pkg_thres_interrupt(void)
static inline void disable_pkg_thres_interrupt(void)
{
u32 l, h;
+
rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
- wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT,
- l & (~THERM_INT_THRESHOLD0_ENABLE) &
- (~THERM_INT_THRESHOLD1_ENABLE), h);
+
+ l &= ~(THERM_INT_THRESHOLD0_ENABLE | THERM_INT_THRESHOLD1_ENABLE);
+ wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h);
}
static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
{
- __u64 msr_val;
+ struct thermal_zone_device *tzone = NULL;
int cpu = smp_processor_id();
- int phy_id = topology_physical_package_id(cpu);
- struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu);
- bool notify = false;
- unsigned long flags;
-
- if (!phdev)
- return;
+ struct pkg_device *pkgdev;
+ u64 msr_val, wr_val;
- spin_lock_irqsave(&pkg_work_lock, flags);
+ mutex_lock(&thermal_zone_mutex);
+ spin_lock_irq(&pkg_temp_lock);
++pkg_work_cnt;
- if (unlikely(phy_id > max_phy_id)) {
- spin_unlock_irqrestore(&pkg_work_lock, flags);
+
+ pkgdev = pkg_temp_thermal_get_dev(cpu);
+ if (!pkgdev) {
+ spin_unlock_irq(&pkg_temp_lock);
+ mutex_unlock(&thermal_zone_mutex);
return;
}
- pkg_work_scheduled[phy_id] = 0;
- spin_unlock_irqrestore(&pkg_work_lock, flags);
+ pkgdev->work_scheduled = false;
- enable_pkg_thres_interrupt();
rdmsrl(MSR_IA32_PACKAGE_THERM_STATUS, msr_val);
- if (msr_val & THERM_LOG_THRESHOLD0) {
- wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS,
- msr_val & ~THERM_LOG_THRESHOLD0);
- notify = true;
- }
- if (msr_val & THERM_LOG_THRESHOLD1) {
- wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS,
- msr_val & ~THERM_LOG_THRESHOLD1);
- notify = true;
- }
- if (notify) {
- pr_debug("thermal_zone_device_update\n");
- thermal_zone_device_update(phdev->tzone,
- THERMAL_EVENT_UNSPECIFIED);
+ wr_val = msr_val & ~(THERM_LOG_THRESHOLD0 | THERM_LOG_THRESHOLD1);
+ if (wr_val != msr_val) {
+ wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, wr_val);
+ tzone = pkgdev->tzone;
}
+
+ enable_pkg_thres_interrupt();
+ spin_unlock_irq(&pkg_temp_lock);
+
+ /*
+ * If tzone is not NULL, then thermal_zone_mutex will prevent the
+ * concurrent removal in the cpu offline callback.
+ */
+ if (tzone)
+ thermal_zone_device_update(tzone, THERMAL_EVENT_UNSPECIFIED);
+
+ mutex_unlock(&thermal_zone_mutex);
}
-static int pkg_temp_thermal_platform_thermal_notify(__u64 msr_val)
+static void pkg_thermal_schedule_work(int cpu, struct delayed_work *work)
+{
+ unsigned long ms = msecs_to_jiffies(notify_delay_ms);
+
+ schedule_delayed_work_on(cpu, work, ms);
+}
+
+static int pkg_thermal_notify(u64 msr_val)
{
- unsigned long flags;
int cpu = smp_processor_id();
- int phy_id = topology_physical_package_id(cpu);
+ struct pkg_device *pkgdev;
+ unsigned long flags;
- /*
- * When a package is in interrupted state, all CPU's in that package
- * are in the same interrupt state. So scheduling on any one CPU in
- * the package is enough and simply return for others.
- */
- spin_lock_irqsave(&pkg_work_lock, flags);
+ spin_lock_irqsave(&pkg_temp_lock, flags);
++pkg_interrupt_cnt;
- if (unlikely(phy_id > max_phy_id) || unlikely(!pkg_work_scheduled) ||
- pkg_work_scheduled[phy_id]) {
- disable_pkg_thres_interrupt();
- spin_unlock_irqrestore(&pkg_work_lock, flags);
- return -EINVAL;
- }
- pkg_work_scheduled[phy_id] = 1;
- spin_unlock_irqrestore(&pkg_work_lock, flags);
disable_pkg_thres_interrupt();
- schedule_delayed_work_on(cpu,
- &per_cpu(pkg_temp_thermal_threshold_work, cpu),
- msecs_to_jiffies(notify_delay_ms));
- return 0;
-}
-static int find_siblings_cpu(int cpu)
-{
- int i;
- int id = topology_physical_package_id(cpu);
-
- for_each_online_cpu(i)
- if (i != cpu && topology_physical_package_id(i) == id)
- return i;
+ /* Work is per package, so scheduling it once is enough. */
+ pkgdev = pkg_temp_thermal_get_dev(cpu);
+ if (pkgdev && !pkgdev->work_scheduled) {
+ pkgdev->work_scheduled = true;
+ pkg_thermal_schedule_work(pkgdev->cpu, &pkgdev->work);
+ }
+ spin_unlock_irqrestore(&pkg_temp_lock, flags);
return 0;
}
static int pkg_temp_thermal_device_add(unsigned int cpu)
{
- int err;
- u32 tj_max;
- struct phy_dev_entry *phy_dev_entry;
- int thres_count;
- u32 eax, ebx, ecx, edx;
- u8 *temp;
- unsigned long flags;
+ int pkgid = topology_logical_package_id(cpu);
+ u32 tj_max, eax, ebx, ecx, edx;
+ struct pkg_device *pkgdev;
+ int thres_count, err;
+
+ if (pkgid >= max_packages)
+ return -ENOMEM;
cpuid(6, &eax, &ebx, &ecx, &edx);
thres_count = ebx & 0x07;
if (!thres_count)
return -ENODEV;
- if (topology_physical_package_id(cpu) > MAX_PKG_TEMP_ZONE_IDS)
- return -ENODEV;
-
thres_count = clamp_val(thres_count, 0, MAX_NUMBER_OF_TRIPS);
err = get_tj_max(cpu, &tj_max);
if (err)
- goto err_ret;
-
- mutex_lock(&phy_dev_list_mutex);
+ return err;
- phy_dev_entry = kzalloc(sizeof(*phy_dev_entry), GFP_KERNEL);
- if (!phy_dev_entry) {
- err = -ENOMEM;
- goto err_ret_unlock;
- }
+ pkgdev = kzalloc(sizeof(*pkgdev), GFP_KERNEL);
+ if (!pkgdev)
+ return -ENOMEM;
- spin_lock_irqsave(&pkg_work_lock, flags);
- if (topology_physical_package_id(cpu) > max_phy_id)
- max_phy_id = topology_physical_package_id(cpu);
- temp = krealloc(pkg_work_scheduled,
- (max_phy_id+1) * sizeof(u8), GFP_ATOMIC);
- if (!temp) {
- spin_unlock_irqrestore(&pkg_work_lock, flags);
- err = -ENOMEM;
- goto err_ret_free;
- }
- pkg_work_scheduled = temp;
- pkg_work_scheduled[topology_physical_package_id(cpu)] = 0;
- spin_unlock_irqrestore(&pkg_work_lock, flags);
-
- phy_dev_entry->phys_proc_id = topology_physical_package_id(cpu);
- phy_dev_entry->first_cpu = cpu;
- phy_dev_entry->tj_max = tj_max;
- phy_dev_entry->ref_cnt = 1;
- phy_dev_entry->tzone = thermal_zone_device_register("x86_pkg_temp",
+ INIT_DELAYED_WORK(&pkgdev->work, pkg_temp_thermal_threshold_work_fn);
+ pkgdev->cpu = cpu;
+ pkgdev->tj_max = tj_max;
+ pkgdev->tzone = thermal_zone_device_register("x86_pkg_temp",
thres_count,
- (thres_count == MAX_NUMBER_OF_TRIPS) ?
- 0x03 : 0x01,
- phy_dev_entry, &tzone_ops, &pkg_temp_tz_params, 0, 0);
- if (IS_ERR(phy_dev_entry->tzone)) {
- err = PTR_ERR(phy_dev_entry->tzone);
- goto err_ret_free;
+ (thres_count == MAX_NUMBER_OF_TRIPS) ? 0x03 : 0x01,
+ pkgdev, &tzone_ops, &pkg_temp_tz_params, 0, 0);
+ if (IS_ERR(pkgdev->tzone)) {
+ err = PTR_ERR(pkgdev->tzone);
+ kfree(pkgdev);
+ return err;
}
/* Store MSR value for package thermal interrupt, to restore at exit */
- rdmsr_on_cpu(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT,
- &phy_dev_entry->start_pkg_therm_low,
- &phy_dev_entry->start_pkg_therm_high);
-
- list_add_tail(&phy_dev_entry->list, &phy_dev_list);
- pr_debug("pkg_temp_thermal_device_add :phy_id %d cpu %d\n",
- phy_dev_entry->phys_proc_id, cpu);
-
- mutex_unlock(&phy_dev_list_mutex);
+ rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, pkgdev->msr_pkg_therm_low,
+ pkgdev->msr_pkg_therm_high);
+ cpumask_set_cpu(cpu, &pkgdev->cpumask);
+ spin_lock_irq(&pkg_temp_lock);
+ packages[pkgid] = pkgdev;
+ spin_unlock_irq(&pkg_temp_lock);
return 0;
-
-err_ret_free:
- kfree(phy_dev_entry);
-err_ret_unlock:
- mutex_unlock(&phy_dev_list_mutex);
-
-err_ret:
- return err;
}
-static int pkg_temp_thermal_device_remove(unsigned int cpu)
+static int pkg_thermal_cpu_offline(unsigned int cpu)
{
- struct phy_dev_entry *n;
- u16 phys_proc_id = topology_physical_package_id(cpu);
- struct phy_dev_entry *phdev =
- pkg_temp_thermal_get_phy_entry(cpu);
+ struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu);
+ bool lastcpu, was_target;
+ int target;
- if (!phdev)
- return -ENODEV;
+ if (!pkgdev)
+ return 0;
- mutex_lock(&phy_dev_list_mutex);
- /* If we are loosing the first cpu for this package, we need change */
- if (phdev->first_cpu == cpu) {
- phdev->first_cpu = find_siblings_cpu(cpu);
- pr_debug("thermal_device_remove: first cpu switched %d\n",
- phdev->first_cpu);
+ target = cpumask_any_but(&pkgdev->cpumask, cpu);
+ cpumask_clear_cpu(cpu, &pkgdev->cpumask);
+ lastcpu = target >= nr_cpu_ids;
+ /*
+ * Remove the sysfs files, if this is the last cpu in the package
+ * before doing further cleanups.
+ */
+ if (lastcpu) {
+ struct thermal_zone_device *tzone = pkgdev->tzone;
+
+ /*
+ * We must protect against a work function calling
+ * thermal_zone_update, after/while unregister. We null out
+ * the pointer under the zone mutex, so the worker function
+ * won't try to call.
+ */
+ mutex_lock(&thermal_zone_mutex);
+ pkgdev->tzone = NULL;
+ mutex_unlock(&thermal_zone_mutex);
+
+ thermal_zone_device_unregister(tzone);
}
+
+ /* Protect against work and interrupts */
+ spin_lock_irq(&pkg_temp_lock);
+
/*
- * It is possible that no siblings left as this was the last cpu
- * going offline. We don't need to worry about this assignment
- * as the phydev entry will be removed in this case and
- * thermal zone is removed.
- */
- --phdev->ref_cnt;
- pr_debug("thermal_device_remove: pkg: %d cpu %d ref_cnt %d\n",
- phys_proc_id, cpu, phdev->ref_cnt);
- if (!phdev->ref_cnt)
- list_for_each_entry_safe(phdev, n, &phy_dev_list, list) {
- if (phdev->phys_proc_id == phys_proc_id) {
- thermal_zone_device_unregister(phdev->tzone);
- list_del(&phdev->list);
- kfree(phdev);
- break;
- }
- }
- mutex_unlock(&phy_dev_list_mutex);
+ * Check whether this cpu was the current target and store the new
+ * one. When we drop the lock, then the interrupt notify function
+ * will see the new target.
+ */
+ was_target = pkgdev->cpu == cpu;
+ pkgdev->cpu = target;
- return 0;
-}
+ /*
+ * If this is the last CPU in the package remove the package
+ * reference from the array and restore the interrupt MSR. When we
+ * drop the lock neither the interrupt notify function nor the
+ * worker will see the package anymore.
+ */
+ if (lastcpu) {
+ packages[topology_logical_package_id(cpu)] = NULL;
+ /* After this point nothing touches the MSR anymore. */
+ wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT,
+ pkgdev->msr_pkg_therm_low, pkgdev->msr_pkg_therm_high);
+ }
-static int get_core_online(unsigned int cpu)
-{
- struct cpuinfo_x86 *c = &cpu_data(cpu);
- struct phy_dev_entry *phdev = pkg_temp_thermal_get_phy_entry(cpu);
-
- /* Check if there is already an instance for this package */
- if (!phdev) {
- if (!cpu_has(c, X86_FEATURE_DTHERM) ||
- !cpu_has(c, X86_FEATURE_PTS))
- return -ENODEV;
- if (pkg_temp_thermal_device_add(cpu))
- return -ENODEV;
- } else {
- mutex_lock(&phy_dev_list_mutex);
- ++phdev->ref_cnt;
- pr_debug("get_core_online: cpu %d ref_cnt %d\n",
- cpu, phdev->ref_cnt);
- mutex_unlock(&phy_dev_list_mutex);
+ /*
+ * Check whether there is work scheduled and whether the work is
+ * targeted at the outgoing CPU.
+ */
+ if (pkgdev->work_scheduled && was_target) {
+ /*
+ * To cancel the work we need to drop the lock, otherwise
+ * we might deadlock if the work needs to be flushed.
+ */
+ spin_unlock_irq(&pkg_temp_lock);
+ cancel_delayed_work_sync(&pkgdev->work);
+ spin_lock_irq(&pkg_temp_lock);
+ /*
+ * If this is not the last cpu in the package and the work
+ * did not run after we dropped the lock above, then we
+ * need to reschedule the work, otherwise the interrupt
+ * stays disabled forever.
+ */
+ if (!lastcpu && pkgdev->work_scheduled)
+ pkg_thermal_schedule_work(target, &pkgdev->work);
}
- INIT_DELAYED_WORK(&per_cpu(pkg_temp_thermal_threshold_work, cpu),
- pkg_temp_thermal_threshold_work_fn);
- pr_debug("get_core_online: cpu %d successful\n", cpu);
+ spin_unlock_irq(&pkg_temp_lock);
+ /* Final cleanup if this is the last cpu */
+ if (lastcpu)
+ kfree(pkgdev);
return 0;
}
-static void put_core_offline(unsigned int cpu)
+static int pkg_thermal_cpu_online(unsigned int cpu)
{
- if (!pkg_temp_thermal_device_remove(cpu))
- cancel_delayed_work_sync(
- &per_cpu(pkg_temp_thermal_threshold_work, cpu));
+ struct pkg_device *pkgdev = pkg_temp_thermal_get_dev(cpu);
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
- pr_debug("put_core_offline: cpu %d\n", cpu);
-}
+ /* Paranoia check */
+ if (!cpu_has(c, X86_FEATURE_DTHERM) || !cpu_has(c, X86_FEATURE_PTS))
+ return -ENODEV;
-static int pkg_temp_thermal_cpu_callback(struct notifier_block *nfb,
- unsigned long action, void *hcpu)
-{
- unsigned int cpu = (unsigned long) hcpu;
-
- switch (action & ~CPU_TASKS_FROZEN) {
- case CPU_ONLINE:
- case CPU_DOWN_FAILED:
- get_core_online(cpu);
- break;
- case CPU_DOWN_PREPARE:
- put_core_offline(cpu);
- break;
+ /* If the package exists, nothing to do */
+ if (pkgdev) {
+ cpumask_set_cpu(cpu, &pkgdev->cpumask);
+ return 0;
}
- return NOTIFY_OK;
+ return pkg_temp_thermal_device_add(cpu);
}
-static struct notifier_block pkg_temp_thermal_notifier __refdata = {
- .notifier_call = pkg_temp_thermal_cpu_callback,
-};
-
static const struct x86_cpu_id __initconst pkg_temp_thermal_ids[] = {
{ X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_PTS },
{}
@@ -580,71 +510,46 @@ MODULE_DEVICE_TABLE(x86cpu, pkg_temp_thermal_ids);
static int __init pkg_temp_thermal_init(void)
{
- int i;
+ int ret;
if (!x86_match_cpu(pkg_temp_thermal_ids))
return -ENODEV;
- spin_lock_init(&pkg_work_lock);
- platform_thermal_package_notify =
- pkg_temp_thermal_platform_thermal_notify;
- platform_thermal_package_rate_control =
- pkg_temp_thermal_platform_thermal_rate_control;
+ max_packages = topology_max_packages();
+ packages = kzalloc(max_packages * sizeof(struct pkg_device *), GFP_KERNEL);
+ if (!packages)
+ return -ENOMEM;
- cpu_notifier_register_begin();
- for_each_online_cpu(i)
- if (get_core_online(i))
- goto err_ret;
- __register_hotcpu_notifier(&pkg_temp_thermal_notifier);
- cpu_notifier_register_done();
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "thermal/x86_pkg:online",
+ pkg_thermal_cpu_online, pkg_thermal_cpu_offline);
+ if (ret < 0)
+ goto err;
- pkg_temp_debugfs_init(); /* Don't care if fails */
+ /* Store the state for module exit */
+ pkg_thermal_hp_state = ret;
- return 0;
+ platform_thermal_package_notify = pkg_thermal_notify;
+ platform_thermal_package_rate_control = pkg_thermal_rate_control;
-err_ret:
- for_each_online_cpu(i)
- put_core_offline(i);
- cpu_notifier_register_done();
- kfree(pkg_work_scheduled);
- platform_thermal_package_notify = NULL;
- platform_thermal_package_rate_control = NULL;
+ /* Don't care if it fails */
+ pkg_temp_debugfs_init();
+ return 0;
- return -ENODEV;
+err:
+ kfree(packages);
+ return ret;
}
+module_init(pkg_temp_thermal_init)
static void __exit pkg_temp_thermal_exit(void)
{
- struct phy_dev_entry *phdev, *n;
- int i;
-
- cpu_notifier_register_begin();
- __unregister_hotcpu_notifier(&pkg_temp_thermal_notifier);
- mutex_lock(&phy_dev_list_mutex);
- list_for_each_entry_safe(phdev, n, &phy_dev_list, list) {
- /* Retore old MSR value for package thermal interrupt */
- wrmsr_on_cpu(phdev->first_cpu,
- MSR_IA32_PACKAGE_THERM_INTERRUPT,
- phdev->start_pkg_therm_low,
- phdev->start_pkg_therm_high);
- thermal_zone_device_unregister(phdev->tzone);
- list_del(&phdev->list);
- kfree(phdev);
- }
- mutex_unlock(&phy_dev_list_mutex);
platform_thermal_package_notify = NULL;
platform_thermal_package_rate_control = NULL;
- for_each_online_cpu(i)
- cancel_delayed_work_sync(
- &per_cpu(pkg_temp_thermal_threshold_work, i));
- cpu_notifier_register_done();
-
- kfree(pkg_work_scheduled);
+ cpuhp_remove_state(pkg_thermal_hp_state);
debugfs_remove_recursive(debugfs);
+ kfree(packages);
}
-
-module_init(pkg_temp_thermal_init)
module_exit(pkg_temp_thermal_exit)
MODULE_DESCRIPTION("X86 PKG TEMP Thermal Driver");
diff --git a/include/linux/devfreq_cooling.h b/include/linux/devfreq_cooling.h
index 7adf6cc4b305..c35d0c0e0ada 100644
--- a/include/linux/devfreq_cooling.h
+++ b/include/linux/devfreq_cooling.h
@@ -20,7 +20,6 @@
#include <linux/devfreq.h>
#include <linux/thermal.h>
-#ifdef CONFIG_DEVFREQ_THERMAL
/**
* struct devfreq_cooling_power - Devfreq cooling power ops
@@ -37,12 +36,16 @@
* @dyn_power_coeff * frequency * voltage^2
*/
struct devfreq_cooling_power {
- unsigned long (*get_static_power)(unsigned long voltage);
- unsigned long (*get_dynamic_power)(unsigned long freq,
+ unsigned long (*get_static_power)(struct devfreq *devfreq,
+ unsigned long voltage);
+ unsigned long (*get_dynamic_power)(struct devfreq *devfreq,
+ unsigned long freq,
unsigned long voltage);
unsigned long dyn_power_coeff;
};
+#ifdef CONFIG_DEVFREQ_THERMAL
+
struct thermal_cooling_device *
of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df,
struct devfreq_cooling_power *dfc_power);