hwmon: coretemp: update hotplug condition check
[firefly-linux-kernel-4.4.55.git] / drivers / hwmon / coretemp.c
index e9b7fbc5a4476b9cfdb2bd01ec58719b097cf7cb..c05dc91ff4198a5b01018a4db6dee1b426e14d80 100644 (file)
@@ -53,6 +53,7 @@ struct coretemp_data {
        struct mutex update_lock;
        const char *name;
        u32 id;
+       u16 core_id;
        char valid;             /* zero until following fields are valid */
        unsigned long last_updated;     /* in jiffies */
        int temp;
@@ -75,7 +76,7 @@ static ssize_t show_name(struct device *dev, struct device_attribute
        if (attr->index == SHOW_NAME)
                ret = sprintf(buf, "%s\n", data->name);
        else    /* show label */
-               ret = sprintf(buf, "Core %d\n", data->id);
+               ret = sprintf(buf, "Core %d\n", data->core_id);
        return ret;
 }
 
@@ -241,6 +242,55 @@ static int __devinit adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *
        return tjmax;
 }
 
+static int __devinit get_tjmax(struct cpuinfo_x86 *c, u32 id,
+                              struct device *dev)
+{
+       /* The 100C is default for both mobile and non mobile CPUs */
+       int err;
+       u32 eax, edx;
+       u32 val;
+
+       /* A new feature of current Intel(R) processors, the
+          IA32_TEMPERATURE_TARGET contains the TjMax value */
+       err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
+       if (err) {
+               dev_warn(dev, "Unable to read TjMax from CPU.\n");
+       } else {
+               val = (eax >> 16) & 0xff;
+               /*
+                * If the TjMax is not plausible, an assumption
+                * will be used
+                */
+               if ((val > 80) && (val < 120)) {
+                       dev_info(dev, "TjMax is %d C.\n", val);
+                       return val * 1000;
+               }
+       }
+
+       /*
+        * An assumption is made for early CPUs and unreadable MSR.
+        * NOTE: the given value may not be correct.
+        */
+
+       switch (c->x86_model) {
+       case 0xe:
+       case 0xf:
+       case 0x16:
+       case 0x1a:
+               dev_warn(dev, "TjMax is assumed as 100 C!\n");
+               return 100000;
+               break;
+       case 0x17:
+       case 0x1c:              /* Atom CPUs */
+               return adjust_tjmax(c, id, dev);
+               break;
+       default:
+               dev_warn(dev, "CPU (model=0x%x) is not supported yet,"
+                       " using default TjMax of 100C.\n", c->x86_model);
+               return 100000;
+       }
+}
+
 static int __devinit coretemp_probe(struct platform_device *pdev)
 {
        struct coretemp_data *data;
@@ -255,6 +305,9 @@ static int __devinit coretemp_probe(struct platform_device *pdev)
        }
 
        data->id = pdev->id;
+#ifdef CONFIG_SMP
+       data->core_id = c->cpu_core_id;
+#endif
        data->name = "coretemp";
        mutex_init(&data->update_lock);
 
@@ -283,14 +336,18 @@ static int __devinit coretemp_probe(struct platform_device *pdev)
                }
        }
 
-       data->tjmax = adjust_tjmax(c, data->id, &pdev->dev);
+       data->tjmax = get_tjmax(c, data->id, &pdev->dev);
        platform_set_drvdata(pdev, data);
 
-       /* read the still undocumented IA32_TEMPERATURE_TARGET it exists
-          on older CPUs but not in this register, Atoms don't have it either */
+       /*
+        * read the still undocumented IA32_TEMPERATURE_TARGET. It exists
+        * on older CPUs but not in this register,
+        * Atoms don't have it either.
+        */
 
        if ((c->x86_model > 0xe) && (c->x86_model != 0x1c)) {
-               err = rdmsr_safe_on_cpu(data->id, 0x1a2, &eax, &edx);
+               err = rdmsr_safe_on_cpu(data->id, MSR_IA32_TEMPERATURE_TARGET,
+                   &eax, &edx);
                if (err) {
                        dev_warn(&pdev->dev, "Unable to read"
                                        " IA32_TEMPERATURE_TARGET MSR\n");
@@ -352,6 +409,10 @@ struct pdev_entry {
        struct list_head list;
        struct platform_device *pdev;
        unsigned int cpu;
+#ifdef CONFIG_SMP
+       u16 phys_proc_id;
+       u16 cpu_core_id;
+#endif
 };
 
 static LIST_HEAD(pdev_list);
@@ -362,6 +423,22 @@ static int __cpuinit coretemp_device_add(unsigned int cpu)
        int err;
        struct platform_device *pdev;
        struct pdev_entry *pdev_entry;
+#ifdef CONFIG_SMP
+       struct cpuinfo_x86 *c = &cpu_data(cpu);
+#endif
+
+       mutex_lock(&pdev_list_mutex);
+
+#ifdef CONFIG_SMP
+       /* Skip second HT entry of each core */
+       list_for_each_entry(pdev_entry, &pdev_list, list) {
+               if (c->phys_proc_id == pdev_entry->phys_proc_id &&
+                   c->cpu_core_id == pdev_entry->cpu_core_id) {
+                       err = 0;        /* Not an error */
+                       goto exit;
+               }
+       }
+#endif
 
        pdev = platform_device_alloc(DRVNAME, cpu);
        if (!pdev) {
@@ -385,7 +462,10 @@ static int __cpuinit coretemp_device_add(unsigned int cpu)
 
        pdev_entry->pdev = pdev;
        pdev_entry->cpu = cpu;
-       mutex_lock(&pdev_list_mutex);
+#ifdef CONFIG_SMP
+       pdev_entry->phys_proc_id = c->phys_proc_id;
+       pdev_entry->cpu_core_id = c->cpu_core_id;
+#endif
        list_add_tail(&pdev_entry->list, &pdev_list);
        mutex_unlock(&pdev_list_mutex);
 
@@ -396,6 +476,7 @@ exit_device_free:
 exit_device_put:
        platform_device_put(pdev);
 exit:
+       mutex_unlock(&pdev_list_mutex);
        return err;
 }
 
@@ -421,10 +502,13 @@ static int __cpuinit coretemp_cpu_callback(struct notifier_block *nfb,
 
        switch (action) {
        case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
        case CPU_DOWN_FAILED:
+       case CPU_DOWN_FAILED_FROZEN:
                coretemp_device_add(cpu);
                break;
        case CPU_DOWN_PREPARE:
+       case CPU_DOWN_PREPARE_FROZEN:
                coretemp_device_remove(cpu);
                break;
        }
@@ -451,28 +535,20 @@ static int __init coretemp_init(void)
 
        for_each_online_cpu(i) {
                struct cpuinfo_x86 *c = &cpu_data(i);
+               /*
+                * CPUID.06H.EAX[0] indicates whether the CPU has thermal
+                * sensors. We check this bit only, all the early CPUs
+                * without thermal sensors will be filtered out.
+                */
+               if (c->cpuid_level >= 6 && (cpuid_eax(0x06) & 0x01)) {
+                       err = coretemp_device_add(i);
+                       if (err)
+                               goto exit_devices_unreg;
 
-               /* check if family 6, models 0xe (Pentium M DC),
-                 0xf (Core 2 DC 65nm), 0x16 (Core 2 SC 65nm),
-                 0x17 (Penryn 45nm), 0x1a (Nehalem), 0x1c (Atom),
-                 0x1e (Lynnfield) */
-               if ((c->cpuid_level < 0) || (c->x86 != 0x6) ||
-                   !((c->x86_model == 0xe) || (c->x86_model == 0xf) ||
-                       (c->x86_model == 0x16) || (c->x86_model == 0x17) ||
-                       (c->x86_model == 0x1a) || (c->x86_model == 0x1c) ||
-                       (c->x86_model == 0x1e))) {
-
-                       /* supported CPU not found, but report the unknown
-                          family 6 CPU */
-                       if ((c->x86 == 0x6) && (c->x86_model > 0xf))
-                               printk(KERN_WARNING DRVNAME ": Unknown CPU "
-                                       "model 0x%x\n", c->x86_model);
-                       continue;
+               } else {
+                       printk(KERN_INFO DRVNAME ": CPU (model=0x%x)"
+                               " has no thermal sensor.\n", c->x86_model);
                }
-
-               err = coretemp_device_add(i);
-               if (err)
-                       goto exit_devices_unreg;
        }
        if (list_empty(&pdev_list)) {
                err = -ENODEV;
@@ -493,7 +569,9 @@ exit_devices_unreg:
        }
        mutex_unlock(&pdev_list_mutex);
 exit_driver_unreg:
+#ifndef CONFIG_HOTPLUG_CPU
        platform_driver_unregister(&coretemp_driver);
+#endif
 exit:
        return err;
 }