.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * coretemp.c - Linux kernel module for hardware monitoring |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz> |
---|
5 | 6 | * |
---|
6 | 7 | * Inspired from many hwmon drivers |
---|
7 | | - * |
---|
8 | | - * This program is free software; you can redistribute it and/or modify |
---|
9 | | - * it under the terms of the GNU General Public License as published by |
---|
10 | | - * the Free Software Foundation; version 2 of the License. |
---|
11 | | - * |
---|
12 | | - * This program is distributed in the hope that it will be useful, |
---|
13 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
15 | | - * GNU General Public License for more details. |
---|
16 | | - * |
---|
17 | | - * You should have received a copy of the GNU General Public License |
---|
18 | | - * along with this program; if not, write to the Free Software |
---|
19 | | - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
---|
20 | | - * 02110-1301 USA. |
---|
21 | 8 | */ |
---|
22 | 9 | |
---|
23 | 10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
---|
.. | .. |
---|
58 | 45 | #define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */ |
---|
59 | 46 | #define TOTAL_ATTRS (MAX_CORE_ATTRS + 1) |
---|
60 | 47 | #define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO) |
---|
61 | | - |
---|
62 | | -#define TO_CORE_ID(cpu) (cpu_data(cpu).cpu_core_id) |
---|
63 | | -#define TO_ATTR_NO(cpu) (TO_CORE_ID(cpu) + BASE_SYSFS_ATTR_NO) |
---|
64 | 48 | |
---|
65 | 49 | #ifdef CONFIG_SMP |
---|
66 | 50 | #define for_each_sibling(i, cpu) \ |
---|
.. | .. |
---|
104 | 88 | struct platform_data { |
---|
105 | 89 | struct device *hwmon_dev; |
---|
106 | 90 | u16 pkg_id; |
---|
| 91 | + u16 cpu_map[NUM_REAL_CORES]; |
---|
| 92 | + struct ida ida; |
---|
107 | 93 | struct cpumask cpumask; |
---|
108 | 94 | struct temp_data *core_data[MAX_CORE_DATA]; |
---|
109 | 95 | struct device_attribute name_attr; |
---|
110 | 96 | }; |
---|
111 | 97 | |
---|
112 | | -/* Keep track of how many package pointers we allocated in init() */ |
---|
113 | | -static int max_packages __read_mostly; |
---|
114 | | -/* Array of package pointers. Serialized by cpu hotplug lock */ |
---|
115 | | -static struct platform_device **pkg_devices; |
---|
| 98 | +/* Keep track of how many zone pointers we allocated in init() */ |
---|
| 99 | +static int max_zones __read_mostly; |
---|
| 100 | +/* Array of zone pointers. Serialized by cpu hotplug lock */ |
---|
| 101 | +static struct platform_device **zone_devices; |
---|
116 | 102 | |
---|
117 | 103 | static ssize_t show_label(struct device *dev, |
---|
118 | 104 | struct device_attribute *devattr, char *buf) |
---|
.. | .. |
---|
256 | 242 | */ |
---|
257 | 243 | if (host_bridge && host_bridge->vendor == PCI_VENDOR_ID_INTEL) { |
---|
258 | 244 | for (i = 0; i < ARRAY_SIZE(tjmax_pci_table); i++) { |
---|
259 | | - if (host_bridge->device == tjmax_pci_table[i].device) |
---|
| 245 | + if (host_bridge->device == tjmax_pci_table[i].device) { |
---|
| 246 | + pci_dev_put(host_bridge); |
---|
260 | 247 | return tjmax_pci_table[i].tjmax; |
---|
| 248 | + } |
---|
261 | 249 | } |
---|
262 | 250 | } |
---|
| 251 | + pci_dev_put(host_bridge); |
---|
263 | 252 | |
---|
264 | 253 | for (i = 0; i < ARRAY_SIZE(tjmax_table); i++) { |
---|
265 | 254 | if (strstr(c->x86_model_id, tjmax_table[i].id)) |
---|
.. | .. |
---|
407 | 396 | "temp%d_%s", attr_no, suffixes[i]); |
---|
408 | 397 | sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr); |
---|
409 | 398 | tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i]; |
---|
410 | | - tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; |
---|
| 399 | + tdata->sd_attrs[i].dev_attr.attr.mode = 0444; |
---|
411 | 400 | tdata->sd_attrs[i].dev_attr.show = rd_ptr[i]; |
---|
412 | 401 | tdata->sd_attrs[i].index = attr_no; |
---|
413 | 402 | tdata->attrs[i] = &tdata->sd_attrs[i].dev_attr.attr; |
---|
.. | .. |
---|
435 | 424 | |
---|
436 | 425 | static struct platform_device *coretemp_get_pdev(unsigned int cpu) |
---|
437 | 426 | { |
---|
438 | | - int pkgid = topology_logical_package_id(cpu); |
---|
| 427 | + int id = topology_logical_die_id(cpu); |
---|
439 | 428 | |
---|
440 | | - if (pkgid >= 0 && pkgid < max_packages) |
---|
441 | | - return pkg_devices[pkgid]; |
---|
| 429 | + if (id >= 0 && id < max_zones) |
---|
| 430 | + return zone_devices[id]; |
---|
442 | 431 | return NULL; |
---|
443 | 432 | } |
---|
444 | 433 | |
---|
.. | .. |
---|
454 | 443 | MSR_IA32_THERM_STATUS; |
---|
455 | 444 | tdata->is_pkg_data = pkg_flag; |
---|
456 | 445 | tdata->cpu = cpu; |
---|
457 | | - tdata->cpu_core_id = TO_CORE_ID(cpu); |
---|
| 446 | + tdata->cpu_core_id = topology_core_id(cpu); |
---|
458 | 447 | tdata->attr_size = MAX_CORE_ATTRS; |
---|
459 | 448 | mutex_init(&tdata->update_lock); |
---|
460 | 449 | return tdata; |
---|
.. | .. |
---|
467 | 456 | struct platform_data *pdata = platform_get_drvdata(pdev); |
---|
468 | 457 | struct cpuinfo_x86 *c = &cpu_data(cpu); |
---|
469 | 458 | u32 eax, edx; |
---|
470 | | - int err, attr_no; |
---|
| 459 | + int err, index, attr_no; |
---|
471 | 460 | |
---|
472 | 461 | /* |
---|
473 | 462 | * Find attr number for sysfs: |
---|
.. | .. |
---|
475 | 464 | * The attr number is always core id + 2 |
---|
476 | 465 | * The Pkgtemp will always show up as temp1_*, if available |
---|
477 | 466 | */ |
---|
478 | | - attr_no = pkg_flag ? PKG_SYSFS_ATTR_NO : TO_ATTR_NO(cpu); |
---|
| 467 | + if (pkg_flag) { |
---|
| 468 | + attr_no = PKG_SYSFS_ATTR_NO; |
---|
| 469 | + } else { |
---|
| 470 | + index = ida_alloc(&pdata->ida, GFP_KERNEL); |
---|
| 471 | + if (index < 0) |
---|
| 472 | + return index; |
---|
| 473 | + pdata->cpu_map[index] = topology_core_id(cpu); |
---|
| 474 | + attr_no = index + BASE_SYSFS_ATTR_NO; |
---|
| 475 | + } |
---|
479 | 476 | |
---|
480 | | - if (attr_no > MAX_CORE_DATA - 1) |
---|
481 | | - return -ERANGE; |
---|
| 477 | + if (attr_no > MAX_CORE_DATA - 1) { |
---|
| 478 | + err = -ERANGE; |
---|
| 479 | + goto ida_free; |
---|
| 480 | + } |
---|
482 | 481 | |
---|
483 | 482 | tdata = init_temp_data(cpu, pkg_flag); |
---|
484 | | - if (!tdata) |
---|
485 | | - return -ENOMEM; |
---|
| 483 | + if (!tdata) { |
---|
| 484 | + err = -ENOMEM; |
---|
| 485 | + goto ida_free; |
---|
| 486 | + } |
---|
486 | 487 | |
---|
487 | 488 | /* Test if we can access the status register */ |
---|
488 | 489 | err = rdmsr_safe_on_cpu(cpu, tdata->status_reg, &eax, &edx); |
---|
.. | .. |
---|
518 | 519 | exit_free: |
---|
519 | 520 | pdata->core_data[attr_no] = NULL; |
---|
520 | 521 | kfree(tdata); |
---|
| 522 | +ida_free: |
---|
| 523 | + if (!pkg_flag) |
---|
| 524 | + ida_free(&pdata->ida, index); |
---|
521 | 525 | return err; |
---|
522 | 526 | } |
---|
523 | 527 | |
---|
.. | .. |
---|
532 | 536 | { |
---|
533 | 537 | struct temp_data *tdata = pdata->core_data[indx]; |
---|
534 | 538 | |
---|
| 539 | + /* if we errored on add then this is already gone */ |
---|
| 540 | + if (!tdata) |
---|
| 541 | + return; |
---|
| 542 | + |
---|
535 | 543 | /* Remove the sysfs attributes */ |
---|
536 | 544 | sysfs_remove_group(&pdata->hwmon_dev->kobj, &tdata->attr_group); |
---|
537 | 545 | |
---|
538 | 546 | kfree(pdata->core_data[indx]); |
---|
539 | 547 | pdata->core_data[indx] = NULL; |
---|
| 548 | + |
---|
| 549 | + if (indx >= BASE_SYSFS_ATTR_NO) |
---|
| 550 | + ida_free(&pdata->ida, indx - BASE_SYSFS_ATTR_NO); |
---|
540 | 551 | } |
---|
541 | 552 | |
---|
542 | | -static int coretemp_probe(struct platform_device *pdev) |
---|
| 553 | +static int coretemp_device_add(int zoneid) |
---|
543 | 554 | { |
---|
544 | | - struct device *dev = &pdev->dev; |
---|
| 555 | + struct platform_device *pdev; |
---|
545 | 556 | struct platform_data *pdata; |
---|
| 557 | + int err; |
---|
546 | 558 | |
---|
547 | | - /* Initialize the per-package data structures */ |
---|
548 | | - pdata = devm_kzalloc(dev, sizeof(struct platform_data), GFP_KERNEL); |
---|
| 559 | + /* Initialize the per-zone data structures */ |
---|
| 560 | + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); |
---|
549 | 561 | if (!pdata) |
---|
550 | 562 | return -ENOMEM; |
---|
551 | 563 | |
---|
552 | | - pdata->pkg_id = pdev->id; |
---|
553 | | - platform_set_drvdata(pdev, pdata); |
---|
| 564 | + pdata->pkg_id = zoneid; |
---|
| 565 | + ida_init(&pdata->ida); |
---|
554 | 566 | |
---|
555 | | - pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME, |
---|
556 | | - pdata, NULL); |
---|
557 | | - return PTR_ERR_OR_ZERO(pdata->hwmon_dev); |
---|
558 | | -} |
---|
559 | | - |
---|
560 | | -static int coretemp_remove(struct platform_device *pdev) |
---|
561 | | -{ |
---|
562 | | - struct platform_data *pdata = platform_get_drvdata(pdev); |
---|
563 | | - int i; |
---|
564 | | - |
---|
565 | | - for (i = MAX_CORE_DATA - 1; i >= 0; --i) |
---|
566 | | - if (pdata->core_data[i]) |
---|
567 | | - coretemp_remove_core(pdata, i); |
---|
568 | | - |
---|
569 | | - return 0; |
---|
570 | | -} |
---|
571 | | - |
---|
572 | | -static struct platform_driver coretemp_driver = { |
---|
573 | | - .driver = { |
---|
574 | | - .name = DRVNAME, |
---|
575 | | - }, |
---|
576 | | - .probe = coretemp_probe, |
---|
577 | | - .remove = coretemp_remove, |
---|
578 | | -}; |
---|
579 | | - |
---|
580 | | -static struct platform_device *coretemp_device_add(unsigned int cpu) |
---|
581 | | -{ |
---|
582 | | - int err, pkgid = topology_logical_package_id(cpu); |
---|
583 | | - struct platform_device *pdev; |
---|
584 | | - |
---|
585 | | - if (pkgid < 0) |
---|
586 | | - return ERR_PTR(-ENOMEM); |
---|
587 | | - |
---|
588 | | - pdev = platform_device_alloc(DRVNAME, pkgid); |
---|
589 | | - if (!pdev) |
---|
590 | | - return ERR_PTR(-ENOMEM); |
---|
591 | | - |
---|
592 | | - err = platform_device_add(pdev); |
---|
593 | | - if (err) { |
---|
594 | | - platform_device_put(pdev); |
---|
595 | | - return ERR_PTR(err); |
---|
| 567 | + pdev = platform_device_alloc(DRVNAME, zoneid); |
---|
| 568 | + if (!pdev) { |
---|
| 569 | + err = -ENOMEM; |
---|
| 570 | + goto err_free_pdata; |
---|
596 | 571 | } |
---|
597 | 572 | |
---|
598 | | - pkg_devices[pkgid] = pdev; |
---|
599 | | - return pdev; |
---|
| 573 | + err = platform_device_add(pdev); |
---|
| 574 | + if (err) |
---|
| 575 | + goto err_put_dev; |
---|
| 576 | + |
---|
| 577 | + platform_set_drvdata(pdev, pdata); |
---|
| 578 | + zone_devices[zoneid] = pdev; |
---|
| 579 | + return 0; |
---|
| 580 | + |
---|
| 581 | +err_put_dev: |
---|
| 582 | + platform_device_put(pdev); |
---|
| 583 | +err_free_pdata: |
---|
| 584 | + kfree(pdata); |
---|
| 585 | + return err; |
---|
| 586 | +} |
---|
| 587 | + |
---|
| 588 | +static void coretemp_device_remove(int zoneid) |
---|
| 589 | +{ |
---|
| 590 | + struct platform_device *pdev = zone_devices[zoneid]; |
---|
| 591 | + struct platform_data *pdata = platform_get_drvdata(pdev); |
---|
| 592 | + |
---|
| 593 | + ida_destroy(&pdata->ida); |
---|
| 594 | + kfree(pdata); |
---|
| 595 | + platform_device_unregister(pdev); |
---|
600 | 596 | } |
---|
601 | 597 | |
---|
602 | 598 | static int coretemp_cpu_online(unsigned int cpu) |
---|
.. | .. |
---|
620 | 616 | if (!cpu_has(c, X86_FEATURE_DTHERM)) |
---|
621 | 617 | return -ENODEV; |
---|
622 | 618 | |
---|
623 | | - if (!pdev) { |
---|
| 619 | + pdata = platform_get_drvdata(pdev); |
---|
| 620 | + if (!pdata->hwmon_dev) { |
---|
| 621 | + struct device *hwmon; |
---|
| 622 | + |
---|
624 | 623 | /* Check the microcode version of the CPU */ |
---|
625 | 624 | if (chk_ucode_version(cpu)) |
---|
626 | 625 | return -EINVAL; |
---|
.. | .. |
---|
631 | 630 | * online. So, initialize per-pkg data structures and |
---|
632 | 631 | * then bring this core online. |
---|
633 | 632 | */ |
---|
634 | | - pdev = coretemp_device_add(cpu); |
---|
635 | | - if (IS_ERR(pdev)) |
---|
636 | | - return PTR_ERR(pdev); |
---|
| 633 | + hwmon = hwmon_device_register_with_groups(&pdev->dev, DRVNAME, |
---|
| 634 | + pdata, NULL); |
---|
| 635 | + if (IS_ERR(hwmon)) |
---|
| 636 | + return PTR_ERR(hwmon); |
---|
| 637 | + pdata->hwmon_dev = hwmon; |
---|
637 | 638 | |
---|
638 | 639 | /* |
---|
639 | 640 | * Check whether pkgtemp support is available. |
---|
.. | .. |
---|
643 | 644 | coretemp_add_core(pdev, cpu, 1); |
---|
644 | 645 | } |
---|
645 | 646 | |
---|
646 | | - pdata = platform_get_drvdata(pdev); |
---|
647 | 647 | /* |
---|
648 | 648 | * Check whether a thread sibling is already online. If not add the |
---|
649 | 649 | * interface for this CPU core. |
---|
.. | .. |
---|
660 | 660 | struct platform_device *pdev = coretemp_get_pdev(cpu); |
---|
661 | 661 | struct platform_data *pd; |
---|
662 | 662 | struct temp_data *tdata; |
---|
663 | | - int indx, target; |
---|
| 663 | + int i, indx = -1, target; |
---|
664 | 664 | |
---|
665 | | - /* |
---|
666 | | - * Don't execute this on suspend as the device remove locks |
---|
667 | | - * up the machine. |
---|
668 | | - */ |
---|
| 665 | + /* No need to tear down any interfaces for suspend */ |
---|
669 | 666 | if (cpuhp_tasks_frozen) |
---|
670 | 667 | return 0; |
---|
671 | 668 | |
---|
672 | 669 | /* If the physical CPU device does not exist, just return */ |
---|
673 | | - if (!pdev) |
---|
674 | | - return 0; |
---|
675 | | - |
---|
676 | | - /* The core id is too big, just return */ |
---|
677 | | - indx = TO_ATTR_NO(cpu); |
---|
678 | | - if (indx > MAX_CORE_DATA - 1) |
---|
679 | | - return 0; |
---|
680 | | - |
---|
681 | 670 | pd = platform_get_drvdata(pdev); |
---|
| 671 | + if (!pd->hwmon_dev) |
---|
| 672 | + return 0; |
---|
| 673 | + |
---|
| 674 | + for (i = 0; i < NUM_REAL_CORES; i++) { |
---|
| 675 | + if (pd->cpu_map[i] == topology_core_id(cpu)) { |
---|
| 676 | + indx = i + BASE_SYSFS_ATTR_NO; |
---|
| 677 | + break; |
---|
| 678 | + } |
---|
| 679 | + } |
---|
| 680 | + |
---|
| 681 | + /* Too many cores and this core is not populated, just return */ |
---|
| 682 | + if (indx < 0) |
---|
| 683 | + return 0; |
---|
| 684 | + |
---|
682 | 685 | tdata = pd->core_data[indx]; |
---|
683 | 686 | |
---|
684 | 687 | cpumask_clear_cpu(cpu, &pd->cpumask); |
---|
.. | .. |
---|
698 | 701 | } |
---|
699 | 702 | |
---|
700 | 703 | /* |
---|
701 | | - * If all cores in this pkg are offline, remove the device. This |
---|
702 | | - * will invoke the platform driver remove function, which cleans up |
---|
703 | | - * the rest. |
---|
| 704 | + * If all cores in this pkg are offline, remove the interface. |
---|
704 | 705 | */ |
---|
| 706 | + tdata = pd->core_data[PKG_SYSFS_ATTR_NO]; |
---|
705 | 707 | if (cpumask_empty(&pd->cpumask)) { |
---|
706 | | - pkg_devices[topology_logical_package_id(cpu)] = NULL; |
---|
707 | | - platform_device_unregister(pdev); |
---|
| 708 | + if (tdata) |
---|
| 709 | + coretemp_remove_core(pd, PKG_SYSFS_ATTR_NO); |
---|
| 710 | + hwmon_device_unregister(pd->hwmon_dev); |
---|
| 711 | + pd->hwmon_dev = NULL; |
---|
708 | 712 | return 0; |
---|
709 | 713 | } |
---|
710 | 714 | |
---|
.. | .. |
---|
712 | 716 | * Check whether this core is the target for the package |
---|
713 | 717 | * interface. We need to assign it to some other cpu. |
---|
714 | 718 | */ |
---|
715 | | - tdata = pd->core_data[PKG_SYSFS_ATTR_NO]; |
---|
716 | 719 | if (tdata && tdata->cpu == cpu) { |
---|
717 | 720 | target = cpumask_first(&pd->cpumask); |
---|
718 | 721 | mutex_lock(&tdata->update_lock); |
---|
.. | .. |
---|
722 | 725 | return 0; |
---|
723 | 726 | } |
---|
724 | 727 | static const struct x86_cpu_id __initconst coretemp_ids[] = { |
---|
725 | | - { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, X86_FEATURE_DTHERM }, |
---|
| 728 | + X86_MATCH_VENDOR_FEATURE(INTEL, X86_FEATURE_DTHERM, NULL), |
---|
726 | 729 | {} |
---|
727 | 730 | }; |
---|
728 | 731 | MODULE_DEVICE_TABLE(x86cpu, coretemp_ids); |
---|
.. | .. |
---|
731 | 734 | |
---|
732 | 735 | static int __init coretemp_init(void) |
---|
733 | 736 | { |
---|
734 | | - int err; |
---|
| 737 | + int i, err; |
---|
735 | 738 | |
---|
736 | 739 | /* |
---|
737 | 740 | * CPUID.06H.EAX[0] indicates whether the CPU has thermal |
---|
.. | .. |
---|
741 | 744 | if (!x86_match_cpu(coretemp_ids)) |
---|
742 | 745 | return -ENODEV; |
---|
743 | 746 | |
---|
744 | | - max_packages = topology_max_packages(); |
---|
745 | | - pkg_devices = kcalloc(max_packages, sizeof(struct platform_device *), |
---|
| 747 | + max_zones = topology_max_packages() * topology_max_die_per_package(); |
---|
| 748 | + zone_devices = kcalloc(max_zones, sizeof(struct platform_device *), |
---|
746 | 749 | GFP_KERNEL); |
---|
747 | | - if (!pkg_devices) |
---|
| 750 | + if (!zone_devices) |
---|
748 | 751 | return -ENOMEM; |
---|
749 | 752 | |
---|
750 | | - err = platform_driver_register(&coretemp_driver); |
---|
751 | | - if (err) |
---|
752 | | - return err; |
---|
| 753 | + for (i = 0; i < max_zones; i++) { |
---|
| 754 | + err = coretemp_device_add(i); |
---|
| 755 | + if (err) |
---|
| 756 | + goto outzone; |
---|
| 757 | + } |
---|
753 | 758 | |
---|
754 | 759 | err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/coretemp:online", |
---|
755 | 760 | coretemp_cpu_online, coretemp_cpu_offline); |
---|
756 | 761 | if (err < 0) |
---|
757 | | - goto outdrv; |
---|
| 762 | + goto outzone; |
---|
758 | 763 | coretemp_hp_online = err; |
---|
759 | 764 | return 0; |
---|
760 | 765 | |
---|
761 | | -outdrv: |
---|
762 | | - platform_driver_unregister(&coretemp_driver); |
---|
763 | | - kfree(pkg_devices); |
---|
| 766 | +outzone: |
---|
| 767 | + while (i--) |
---|
| 768 | + coretemp_device_remove(i); |
---|
| 769 | + kfree(zone_devices); |
---|
764 | 770 | return err; |
---|
765 | 771 | } |
---|
766 | 772 | module_init(coretemp_init) |
---|
767 | 773 | |
---|
768 | 774 | static void __exit coretemp_exit(void) |
---|
769 | 775 | { |
---|
| 776 | + int i; |
---|
| 777 | + |
---|
770 | 778 | cpuhp_remove_state(coretemp_hp_online); |
---|
771 | | - platform_driver_unregister(&coretemp_driver); |
---|
772 | | - kfree(pkg_devices); |
---|
| 779 | + for (i = 0; i < max_zones; i++) |
---|
| 780 | + coretemp_device_remove(i); |
---|
| 781 | + kfree(zone_devices); |
---|
773 | 782 | } |
---|
774 | 783 | module_exit(coretemp_exit) |
---|
775 | 784 | |
---|