summaryrefslogtreecommitdiff
path: root/drivers/thermal/intel/intel_hfi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/thermal/intel/intel_hfi.c')
-rw-r--r--drivers/thermal/intel/intel_hfi.c113
1 files changed, 97 insertions, 16 deletions
diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c
index 40d664a66cdc..a180a98bb9f1 100644
--- a/drivers/thermal/intel/intel_hfi.c
+++ b/drivers/thermal/intel/intel_hfi.c
@@ -159,14 +159,15 @@ struct hfi_cpu_info {
static DEFINE_PER_CPU(struct hfi_cpu_info, hfi_cpu_info) = { .index = -1 };
static int max_hfi_instances;
+static int hfi_clients_nr;
static struct hfi_instance *hfi_instances;
static struct hfi_features hfi_features;
static DEFINE_MUTEX(hfi_instance_lock);
static struct workqueue_struct *hfi_updates_wq;
-#define HFI_UPDATE_INTERVAL HZ
-#define HFI_MAX_THERM_NOTIFY_COUNT 16
+#define HFI_UPDATE_DELAY_MS 100
+#define HFI_THERMNL_CAPS_PER_EVENT 64
static void get_hfi_caps(struct hfi_instance *hfi_instance,
struct thermal_genl_cpu_caps *cpu_caps)
@@ -217,14 +218,14 @@ static void update_capabilities(struct hfi_instance *hfi_instance)
get_hfi_caps(hfi_instance, cpu_caps);
- if (cpu_count < HFI_MAX_THERM_NOTIFY_COUNT)
+ if (cpu_count < HFI_THERMNL_CAPS_PER_EVENT)
goto last_cmd;
- /* Process complete chunks of HFI_MAX_THERM_NOTIFY_COUNT capabilities. */
+ /* Process complete chunks of HFI_THERMNL_CAPS_PER_EVENT capabilities. */
for (i = 0;
- (i + HFI_MAX_THERM_NOTIFY_COUNT) <= cpu_count;
- i += HFI_MAX_THERM_NOTIFY_COUNT)
- thermal_genl_cpu_capability_event(HFI_MAX_THERM_NOTIFY_COUNT,
+ (i + HFI_THERMNL_CAPS_PER_EVENT) <= cpu_count;
+ i += HFI_THERMNL_CAPS_PER_EVENT)
+ thermal_genl_cpu_capability_event(HFI_THERMNL_CAPS_PER_EVENT,
&cpu_caps[i]);
cpu_count = cpu_count - i;
@@ -321,7 +322,7 @@ void intel_hfi_process_event(__u64 pkg_therm_status_msr_val)
raw_spin_unlock(&hfi_instance->event_lock);
queue_delayed_work(hfi_updates_wq, &hfi_instance->update_work,
- HFI_UPDATE_INTERVAL);
+ msecs_to_jiffies(HFI_UPDATE_DELAY_MS));
}
static void init_hfi_cpu_index(struct hfi_cpu_info *info)
@@ -477,8 +478,11 @@ void intel_hfi_online(unsigned int cpu)
enable:
cpumask_set_cpu(cpu, hfi_instance->cpus);
- /* Enable this HFI instance if this is its first online CPU. */
- if (cpumask_weight(hfi_instance->cpus) == 1) {
+ /*
+ * Enable this HFI instance if this is its first online CPU and
+ * there are user-space clients of thermal events.
+ */
+ if (cpumask_weight(hfi_instance->cpus) == 1 && hfi_clients_nr > 0) {
hfi_set_hw_table(hfi_instance);
hfi_enable();
}
@@ -573,18 +577,33 @@ static __init int hfi_parse_features(void)
return 0;
}
-static void hfi_do_enable(void)
+/*
+ * If concurrency is not prevented by other means, the HFI enable/disable
+ * routines must be called under hfi_instance_lock."
+ */
+static void hfi_enable_instance(void *ptr)
+{
+ hfi_set_hw_table(ptr);
+ hfi_enable();
+}
+
+static void hfi_disable_instance(void *ptr)
+{
+ hfi_disable();
+}
+
+static void hfi_syscore_resume(void)
{
/* This code runs only on the boot CPU. */
struct hfi_cpu_info *info = &per_cpu(hfi_cpu_info, 0);
struct hfi_instance *hfi_instance = info->hfi_instance;
/* No locking needed. There is no concurrency with CPU online. */
- hfi_set_hw_table(hfi_instance);
- hfi_enable();
+ if (hfi_clients_nr > 0)
+ hfi_enable_instance(hfi_instance);
}
-static int hfi_do_disable(void)
+static int hfi_syscore_suspend(void)
{
/* No locking needed. There is no concurrency with CPU offline. */
hfi_disable();
@@ -593,8 +612,58 @@ static int hfi_do_disable(void)
}
static struct syscore_ops hfi_pm_ops = {
- .resume = hfi_do_enable,
- .suspend = hfi_do_disable,
+ .resume = hfi_syscore_resume,
+ .suspend = hfi_syscore_suspend,
+};
+
+static int hfi_thermal_notify(struct notifier_block *nb, unsigned long state,
+ void *_notify)
+{
+ struct thermal_genl_notify *notify = _notify;
+ struct hfi_instance *hfi_instance;
+ smp_call_func_t func = NULL;
+ unsigned int cpu;
+ int i;
+
+ if (notify->mcgrp != THERMAL_GENL_EVENT_GROUP)
+ return NOTIFY_DONE;
+
+ if (state != THERMAL_NOTIFY_BIND && state != THERMAL_NOTIFY_UNBIND)
+ return NOTIFY_DONE;
+
+ mutex_lock(&hfi_instance_lock);
+
+ switch (state) {
+ case THERMAL_NOTIFY_BIND:
+ if (++hfi_clients_nr == 1)
+ func = hfi_enable_instance;
+ break;
+ case THERMAL_NOTIFY_UNBIND:
+ if (--hfi_clients_nr == 0)
+ func = hfi_disable_instance;
+ break;
+ }
+
+ if (!func)
+ goto out;
+
+ for (i = 0; i < max_hfi_instances; i++) {
+ hfi_instance = &hfi_instances[i];
+ if (cpumask_empty(hfi_instance->cpus))
+ continue;
+
+ cpu = cpumask_any(hfi_instance->cpus);
+ smp_call_function_single(cpu, func, hfi_instance, true);
+ }
+
+out:
+ mutex_unlock(&hfi_instance_lock);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block hfi_thermal_nb = {
+ .notifier_call = hfi_thermal_notify,
};
void __init intel_hfi_init(void)
@@ -628,10 +697,22 @@ void __init intel_hfi_init(void)
if (!hfi_updates_wq)
goto err_nomem;
+ /*
+ * Both thermal core and Intel HFI can not be build as modules.
+ * As kernel build-in drivers they are initialized before user-space
+ * starts, hence we can not miss BIND/UNBIND events when applications
+ * add/remove thermal multicast group to/from a netlink socket.
+ */
+ if (thermal_genl_register_notifier(&hfi_thermal_nb))
+ goto err_nl_notif;
+
register_syscore_ops(&hfi_pm_ops);
return;
+err_nl_notif:
+ destroy_workqueue(hfi_updates_wq);
+
err_nomem:
for (j = 0; j < i; ++j) {
hfi_instance = &hfi_instances[j];