Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
4a62d58
Documentation
LICENSES
arch
block
certs
crypto
drivers
accel
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cache
cdrom
cdx
char
clk
clocksource
comedi
connector
counter
cpufreq
cpuidle
crypto
cxl
dax
dca
devfreq
dio
dma-buf
dma
dpll
edac
eisa
extcon
firewire
firmware
fpga
fsi
gnss
gpio
gpu
greybus
hid
hsi
hte
hv
hwmon
hwspinlock
hwtracing
i2c
i3c
idle
iio
infiniband
input
interconnect
iommu
ipack
irqchip
isdn
leds
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
most
mtd
mux
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
opp
parisc
parport
pci
pcmcia
peci
perf
phy
pinctrl
platform
pmdomain
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sh
siox
slimbus
soc
soundwire
spi
spmi
ssb
staging
target
tc
tee
thermal
broadcom
intel
int340x_thermal
Kconfig
Makefile
acpi_thermal_rel.c
acpi_thermal_rel.h
int3400_thermal.c
int3401_thermal.c
int3402_thermal.c
int3403_thermal.c
int3406_thermal.c
int340x_thermal_zone.c
int340x_thermal_zone.h
processor_thermal_device.c
processor_thermal_device.h
processor_thermal_device_pci.c
processor_thermal_device_pci_legacy.c
processor_thermal_mbox.c
processor_thermal_power_floor.c
processor_thermal_rapl.c
processor_thermal_rfim.c
processor_thermal_wt_hint.c
processor_thermal_wt_req.c
Kconfig
Makefile
intel_bxt_pmic_thermal.c
intel_hfi.c
intel_hfi.h
intel_pch_thermal.c
intel_powerclamp.c
intel_quark_dts_thermal.c
intel_soc_dts_iosf.c
intel_soc_dts_iosf.h
intel_soc_dts_thermal.c
intel_tcc.c
intel_tcc_cooling.c
therm_throt.c
thermal_interrupt.h
x86_pkg_temp_thermal.c
mediatek
qcom
samsung
st
tegra
ti-soc-thermal
Kconfig
Makefile
amlogic_thermal.c
armada_thermal.c
cpufreq_cooling.c
cpuidle_cooling.c
da9062-thermal.c
db8500_thermal.c
devfreq_cooling.c
dove_thermal.c
gov_bang_bang.c
gov_fair_share.c
gov_power_allocator.c
gov_step_wise.c
gov_user_space.c
hisi_thermal.c
imx8mm_thermal.c
imx_sc_thermal.c
imx_thermal.c
k3_bandgap.c
k3_j72xx_bandgap.c
khadas_mcu_fan.c
kirkwood_thermal.c
loongson2_thermal.c
max77620_thermal.c
qoriq_thermal.c
rcar_gen3_thermal.c
rcar_thermal.c
rockchip_thermal.c
rzg2l_thermal.c
spear_thermal.c
sprd_thermal.c
sun8i_thermal.c
thermal-generic-adc.c
thermal_core.c
thermal_core.h
thermal_debugfs.c
thermal_debugfs.h
thermal_helpers.c
thermal_hwmon.c
thermal_hwmon.h
thermal_mmio.c
thermal_netlink.c
thermal_netlink.h
thermal_of.c
thermal_sysfs.c
thermal_trace.h
thermal_trace_ipa.h
thermal_trip.c
uniphier_thermal.c
thunderbolt
tty
ufs
uio
usb
vdpa
vfio
vhost
video
virt
virtio
w1
watchdog
xen
zorro
Kconfig
Makefile
fs
include
init
io_uring
ipc
kernel
lib
mm
net
rust
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.editorconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
.rustfmt.toml
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
drivers
/
thermal
/
intel
/
int340x_thermal
/
processor_thermal_device_pci.c
Blame
Blame
Latest commit
History
History
436 lines (351 loc) · 12.2 KB
Breadcrumbs
linux
/
drivers
/
thermal
/
intel
/
int340x_thermal
/
processor_thermal_device_pci.c
Top
File metadata and controls
Code
Blame
436 lines (351 loc) · 12.2 KB
Raw
// SPDX-License-Identifier: GPL-2.0-only /* * Processor thermal device for newer processors * Copyright (c) 2020, Intel Corporation. */ #include <linux/acpi.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/thermal.h> #include "int340x_thermal_zone.h" #include "processor_thermal_device.h" #define DRV_NAME "proc_thermal_pci" static bool use_msi; module_param(use_msi, bool, 0644); MODULE_PARM_DESC(use_msi, "Use PCI MSI based interrupts for processor thermal device."); struct proc_thermal_pci { struct pci_dev *pdev; struct proc_thermal_device *proc_priv; struct thermal_zone_device *tzone; struct delayed_work work; int stored_thres; int no_legacy; }; enum proc_thermal_mmio_type { PROC_THERMAL_MMIO_TJMAX, PROC_THERMAL_MMIO_PP0_TEMP, PROC_THERMAL_MMIO_PP1_TEMP, PROC_THERMAL_MMIO_PKG_TEMP, PROC_THERMAL_MMIO_THRES_0, PROC_THERMAL_MMIO_THRES_1, PROC_THERMAL_MMIO_INT_ENABLE_0, PROC_THERMAL_MMIO_INT_ENABLE_1, PROC_THERMAL_MMIO_INT_STATUS_0, PROC_THERMAL_MMIO_INT_STATUS_1, PROC_THERMAL_MMIO_MAX }; struct proc_thermal_mmio_info { enum proc_thermal_mmio_type mmio_type; u64 mmio_addr; u64 shift; u64 mask; }; static struct proc_thermal_mmio_info proc_thermal_mmio_info[] = { { PROC_THERMAL_MMIO_TJMAX, 0x599c, 16, 0xff }, { PROC_THERMAL_MMIO_PP0_TEMP, 0x597c, 0, 0xff }, { PROC_THERMAL_MMIO_PP1_TEMP, 0x5980, 0, 0xff }, { PROC_THERMAL_MMIO_PKG_TEMP, 0x5978, 0, 0xff }, { PROC_THERMAL_MMIO_THRES_0, 0x5820, 8, 0x7F }, { PROC_THERMAL_MMIO_THRES_1, 0x5820, 16, 0x7F }, { PROC_THERMAL_MMIO_INT_ENABLE_0, 0x5820, 15, 0x01 }, { PROC_THERMAL_MMIO_INT_ENABLE_1, 0x5820, 23, 0x01 }, { PROC_THERMAL_MMIO_INT_STATUS_0, 0x7200, 6, 0x01 }, { PROC_THERMAL_MMIO_INT_STATUS_1, 0x7200, 8, 0x01 }, }; #define B0D4_THERMAL_NOTIFY_DELAY 1000 static int notify_delay_ms = B0D4_THERMAL_NOTIFY_DELAY; static void proc_thermal_mmio_read(struct proc_thermal_pci *pci_info, enum proc_thermal_mmio_type type, u32 *value) { *value = ioread32(((u8 __iomem *)pci_info->proc_priv->mmio_base + proc_thermal_mmio_info[type].mmio_addr)); *value >>= proc_thermal_mmio_info[type].shift; *value &= proc_thermal_mmio_info[type].mask; } static void proc_thermal_mmio_write(struct proc_thermal_pci *pci_info, enum proc_thermal_mmio_type type, u32 value) { u32 current_val; u32 mask; current_val = ioread32(((u8 __iomem *)pci_info->proc_priv->mmio_base + proc_thermal_mmio_info[type].mmio_addr)); mask = proc_thermal_mmio_info[type].mask << proc_thermal_mmio_info[type].shift; current_val &= ~mask; value &= proc_thermal_mmio_info[type].mask; value <<= proc_thermal_mmio_info[type].shift; current_val |= value; iowrite32(current_val, ((u8 __iomem *)pci_info->proc_priv->mmio_base + proc_thermal_mmio_info[type].mmio_addr)); } /* * To avoid sending two many messages to user space, we have 1 second delay. * On interrupt we are disabling interrupt and enabling after 1 second. * This workload function is delayed by 1 second. */ static void proc_thermal_threshold_work_fn(struct work_struct *work) { struct delayed_work *delayed_work = to_delayed_work(work); struct proc_thermal_pci *pci_info = container_of(delayed_work, struct proc_thermal_pci, work); struct thermal_zone_device *tzone = pci_info->tzone; if (tzone) thermal_zone_device_update(tzone, THERMAL_TRIP_VIOLATED); /* Enable interrupt flag */ proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 1); } static void pkg_thermal_schedule_work(struct delayed_work *work) { unsigned long ms = msecs_to_jiffies(notify_delay_ms); schedule_delayed_work(work, ms); } static void proc_thermal_clear_soc_int_status(struct proc_thermal_device *proc_priv) { u64 status; if (!(proc_priv->mmio_feature_mask & (PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR))) return; status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET); writeq(status & ~SOC_WT_RES_INT_STATUS_MASK, proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET); } static irqreturn_t proc_thermal_irq_thread_handler(int irq, void *devid) { struct proc_thermal_pci *pci_info = devid; proc_thermal_wt_intr_callback(pci_info->pdev, pci_info->proc_priv); proc_thermal_power_floor_intr_callback(pci_info->pdev, pci_info->proc_priv); proc_thermal_clear_soc_int_status(pci_info->proc_priv); return IRQ_HANDLED; } static irqreturn_t proc_thermal_irq_handler(int irq, void *devid) { struct proc_thermal_pci *pci_info = devid; struct proc_thermal_device *proc_priv; int ret = IRQ_HANDLED; u32 status; proc_priv = pci_info->proc_priv; if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_WT_HINT) { if (proc_thermal_check_wt_intr(pci_info->proc_priv)) ret = IRQ_WAKE_THREAD; } if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_POWER_FLOOR) { if (proc_thermal_check_power_floor_intr(pci_info->proc_priv)) ret = IRQ_WAKE_THREAD; } /* * Since now there are two sources of interrupts: one from thermal threshold * and another from workload hint, add a check if there was really a threshold * interrupt before scheduling work function for thermal threshold. */ proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_INT_STATUS_0, &status); if (status) { /* Disable enable interrupt flag */ proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0); pkg_thermal_schedule_work(&pci_info->work); } pci_write_config_byte(pci_info->pdev, 0xdc, 0x01); return ret; } static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp) { struct proc_thermal_pci *pci_info = thermal_zone_device_priv(tzd); u32 _temp; proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_PKG_TEMP, &_temp); *temp = (unsigned long)_temp * 1000; return 0; } static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp) { struct proc_thermal_pci *pci_info = thermal_zone_device_priv(tzd); int tjmax, _temp; if (temp <= 0) { cancel_delayed_work_sync(&pci_info->work); proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0); proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0, 0); pci_info->stored_thres = 0; return 0; } proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_TJMAX, &tjmax); _temp = tjmax - (temp / 1000); if (_temp < 0) return -EINVAL; proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0, _temp); proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 1); pci_info->stored_thres = temp; 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 const struct thermal_zone_device_ops tzone_ops = { .get_temp = sys_get_curr_temp, .set_trip_temp = sys_set_trip_temp, }; static struct thermal_zone_params tzone_params = { .governor_name = "user_space", .no_hwmon = true, }; static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct proc_thermal_device *proc_priv; struct proc_thermal_pci *pci_info; struct thermal_trip psv_trip = { .type = THERMAL_TRIP_PASSIVE, .flags = THERMAL_TRIP_FLAG_RW_TEMP, }; int irq_flag = 0, irq, ret; bool msi_irq = false; proc_priv = devm_kzalloc(&pdev->dev, sizeof(*proc_priv), GFP_KERNEL); if (!proc_priv) return -ENOMEM; pci_info = devm_kzalloc(&pdev->dev, sizeof(*pci_info), GFP_KERNEL); if (!pci_info) return -ENOMEM; pci_info->pdev = pdev; ret = pcim_enable_device(pdev); if (ret < 0) { dev_err(&pdev->dev, "error: could not enable device\n"); return ret; } pci_set_master(pdev); INIT_DELAYED_WORK(&pci_info->work, proc_thermal_threshold_work_fn); proc_priv->priv_data = pci_info; pci_info->proc_priv = proc_priv; pci_set_drvdata(pdev, proc_priv); ret = proc_thermal_mmio_add(pdev, proc_priv, id->driver_data); if (ret) return ret; ret = proc_thermal_add(&pdev->dev, proc_priv); if (ret) { dev_err(&pdev->dev, "error: proc_thermal_add, will continue\n"); pci_info->no_legacy = 1; } psv_trip.temperature = get_trip_temp(pci_info); pci_info->tzone = thermal_zone_device_register_with_trips("TCPU_PCI", &psv_trip, 1, pci_info, &tzone_ops, &tzone_params, 0, 0); if (IS_ERR(pci_info->tzone)) { ret = PTR_ERR(pci_info->tzone); goto err_del_legacy; } if (use_msi && (pdev->msi_enabled || pdev->msix_enabled)) { /* request and enable interrupt */ ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); if (ret < 0) { dev_err(&pdev->dev, "Failed to allocate vectors!\n"); goto err_ret_tzone; } irq = pci_irq_vector(pdev, 0); msi_irq = true; } else { irq_flag = IRQF_SHARED; irq = pdev->irq; } ret = devm_request_threaded_irq(&pdev->dev, irq, proc_thermal_irq_handler, proc_thermal_irq_thread_handler, irq_flag, KBUILD_MODNAME, pci_info); if (ret) { dev_err(&pdev->dev, "Request IRQ %d failed\n", pdev->irq); goto err_free_vectors; } ret = thermal_zone_device_enable(pci_info->tzone); if (ret) goto err_free_vectors; return 0; err_free_vectors: if (msi_irq) pci_free_irq_vectors(pdev); err_ret_tzone: thermal_zone_device_unregister(pci_info->tzone); err_del_legacy: if (!pci_info->no_legacy) proc_thermal_remove(proc_priv); proc_thermal_mmio_remove(pdev, proc_priv); pci_disable_device(pdev); return ret; } static void proc_thermal_pci_remove(struct pci_dev *pdev) { struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev); struct proc_thermal_pci *pci_info = proc_priv->priv_data; cancel_delayed_work_sync(&pci_info->work); proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0, 0); proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0); devm_free_irq(&pdev->dev, pdev->irq, pci_info); pci_free_irq_vectors(pdev); thermal_zone_device_unregister(pci_info->tzone); proc_thermal_mmio_remove(pdev, pci_info->proc_priv); if (!pci_info->no_legacy) proc_thermal_remove(proc_priv); pci_disable_device(pdev); } #ifdef CONFIG_PM_SLEEP static int proc_thermal_pci_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct proc_thermal_device *proc_priv; struct proc_thermal_pci *pci_info; proc_priv = pci_get_drvdata(pdev); pci_info = proc_priv->priv_data; if (!pci_info->no_legacy) return proc_thermal_suspend(dev); return 0; } static int proc_thermal_pci_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct proc_thermal_device *proc_priv; struct proc_thermal_pci *pci_info; proc_priv = pci_get_drvdata(pdev); pci_info = proc_priv->priv_data; if (pci_info->stored_thres) { proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0, pci_info->stored_thres / 1000); proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 1); } if (!pci_info->no_legacy) return proc_thermal_resume(dev); return 0; } #else #define proc_thermal_pci_suspend NULL #define proc_thermal_pci_resume NULL #endif static SIMPLE_DEV_PM_OPS(proc_thermal_pci_pm, proc_thermal_pci_suspend, proc_thermal_pci_resume); static const struct pci_device_id proc_thermal_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, ADL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_REQ) }, { PCI_DEVICE_DATA(INTEL, MTLP_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR) }, { PCI_DEVICE_DATA(INTEL, ARL_S_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_WT_HINT) }, { PCI_DEVICE_DATA(INTEL, RPL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_REQ) }, { }, }; MODULE_DEVICE_TABLE(pci, proc_thermal_pci_ids); static struct pci_driver proc_thermal_pci_driver = { .name = DRV_NAME, .probe = proc_thermal_pci_probe, .remove = proc_thermal_pci_remove, .id_table = proc_thermal_pci_ids, .driver.pm = &proc_thermal_pci_pm, }; module_pci_driver(proc_thermal_pci_driver); MODULE_IMPORT_NS(INT340X_THERMAL); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver"); MODULE_LICENSE("GPL v2");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
You can’t perform that action at this time.