cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

intel_pch_thermal.c (14357B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* intel_pch_thermal.c - Intel PCH Thermal driver
      3 *
      4 * Copyright (c) 2015, Intel Corporation.
      5 *
      6 * Authors:
      7 *     Tushar Dave <tushar.n.dave@intel.com>
      8 */
      9
     10#include <linux/acpi.h>
     11#include <linux/delay.h>
     12#include <linux/module.h>
     13#include <linux/init.h>
     14#include <linux/pci.h>
     15#include <linux/pm.h>
     16#include <linux/suspend.h>
     17#include <linux/thermal.h>
     18#include <linux/types.h>
     19#include <linux/units.h>
     20
     21/* Intel PCH thermal Device IDs */
     22#define PCH_THERMAL_DID_HSW_1	0x9C24 /* Haswell PCH */
     23#define PCH_THERMAL_DID_HSW_2	0x8C24 /* Haswell PCH */
     24#define PCH_THERMAL_DID_WPT	0x9CA4 /* Wildcat Point */
     25#define PCH_THERMAL_DID_SKL	0x9D31 /* Skylake PCH */
     26#define PCH_THERMAL_DID_SKL_H	0xA131 /* Skylake PCH 100 series */
     27#define PCH_THERMAL_DID_CNL	0x9Df9 /* CNL PCH */
     28#define PCH_THERMAL_DID_CNL_H	0xA379 /* CNL-H PCH */
     29#define PCH_THERMAL_DID_CNL_LP	0x02F9 /* CNL-LP PCH */
     30#define PCH_THERMAL_DID_CML_H	0X06F9 /* CML-H PCH */
     31#define PCH_THERMAL_DID_LWB	0xA1B1 /* Lewisburg PCH */
     32
     33/* Wildcat Point-LP  PCH Thermal registers */
     34#define WPT_TEMP	0x0000	/* Temperature */
     35#define WPT_TSC	0x04	/* Thermal Sensor Control */
     36#define WPT_TSS	0x06	/* Thermal Sensor Status */
     37#define WPT_TSEL	0x08	/* Thermal Sensor Enable and Lock */
     38#define WPT_TSREL	0x0A	/* Thermal Sensor Report Enable and Lock */
     39#define WPT_TSMIC	0x0C	/* Thermal Sensor SMI Control */
     40#define WPT_CTT	0x0010	/* Catastrophic Trip Point */
     41#define WPT_TSPM	0x001C	/* Thermal Sensor Power Management */
     42#define WPT_TAHV	0x0014	/* Thermal Alert High Value */
     43#define WPT_TALV	0x0018	/* Thermal Alert Low Value */
     44#define WPT_TL		0x00000040	/* Throttle Value */
     45#define WPT_PHL	0x0060	/* PCH Hot Level */
     46#define WPT_PHLC	0x62	/* PHL Control */
     47#define WPT_TAS	0x80	/* Thermal Alert Status */
     48#define WPT_TSPIEN	0x82	/* PCI Interrupt Event Enables */
     49#define WPT_TSGPEN	0x84	/* General Purpose Event Enables */
     50
     51/*  Wildcat Point-LP  PCH Thermal Register bit definitions */
     52#define WPT_TEMP_TSR	0x01ff	/* Temp TS Reading */
     53#define WPT_TSC_CPDE	0x01	/* Catastrophic Power-Down Enable */
     54#define WPT_TSS_TSDSS	0x10	/* Thermal Sensor Dynamic Shutdown Status */
     55#define WPT_TSS_GPES	0x08	/* GPE status */
     56#define WPT_TSEL_ETS	0x01    /* Enable TS */
     57#define WPT_TSEL_PLDB	0x80	/* TSEL Policy Lock-Down Bit */
     58#define WPT_TL_TOL	0x000001FF	/* T0 Level */
     59#define WPT_TL_T1L	0x1ff00000	/* T1 Level */
     60#define WPT_TL_TTEN	0x20000000	/* TT Enable */
     61
     62/* Resolution of 1/2 degree C and an offset of -50C */
     63#define PCH_TEMP_OFFSET	(-50)
     64#define GET_WPT_TEMP(x)	((x) * MILLIDEGREE_PER_DEGREE / 2 + WPT_TEMP_OFFSET)
     65#define WPT_TEMP_OFFSET	(PCH_TEMP_OFFSET * MILLIDEGREE_PER_DEGREE)
     66#define GET_PCH_TEMP(x)	(((x) / 2) + PCH_TEMP_OFFSET)
     67
     68/* Amount of time for each cooling delay, 100ms by default for now */
     69static unsigned int delay_timeout = 100;
     70module_param(delay_timeout, int, 0644);
     71MODULE_PARM_DESC(delay_timeout, "amount of time delay for each iteration.");
     72
     73/* Number of iterations for cooling delay, 600 counts by default for now */
     74static unsigned int delay_cnt = 600;
     75module_param(delay_cnt, int, 0644);
     76MODULE_PARM_DESC(delay_cnt, "total number of iterations for time delay.");
     77
     78static char driver_name[] = "Intel PCH thermal driver";
     79
     80struct pch_thermal_device {
     81	void __iomem *hw_base;
     82	const struct pch_dev_ops *ops;
     83	struct pci_dev *pdev;
     84	struct thermal_zone_device *tzd;
     85	int crt_trip_id;
     86	unsigned long crt_temp;
     87	int hot_trip_id;
     88	unsigned long hot_temp;
     89	int psv_trip_id;
     90	unsigned long psv_temp;
     91	bool bios_enabled;
     92};
     93
     94#ifdef CONFIG_ACPI
     95
     96/*
     97 * On some platforms, there is a companion ACPI device, which adds
     98 * passive trip temperature using _PSV method. There is no specific
     99 * passive temperature setting in MMIO interface of this PCI device.
    100 */
    101static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
    102				      int *nr_trips)
    103{
    104	struct acpi_device *adev;
    105
    106	ptd->psv_trip_id = -1;
    107
    108	adev = ACPI_COMPANION(&ptd->pdev->dev);
    109	if (adev) {
    110		unsigned long long r;
    111		acpi_status status;
    112
    113		status = acpi_evaluate_integer(adev->handle, "_PSV", NULL,
    114					       &r);
    115		if (ACPI_SUCCESS(status)) {
    116			unsigned long trip_temp;
    117
    118			trip_temp = deci_kelvin_to_millicelsius(r);
    119			if (trip_temp) {
    120				ptd->psv_temp = trip_temp;
    121				ptd->psv_trip_id = *nr_trips;
    122				++(*nr_trips);
    123			}
    124		}
    125	}
    126}
    127#else
    128static void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
    129				      int *nr_trips)
    130{
    131	ptd->psv_trip_id = -1;
    132
    133}
    134#endif
    135
    136static int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips)
    137{
    138	u8 tsel;
    139	u16 trip_temp;
    140
    141	*nr_trips = 0;
    142
    143	/* Check if BIOS has already enabled thermal sensor */
    144	if (WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL)) {
    145		ptd->bios_enabled = true;
    146		goto read_trips;
    147	}
    148
    149	tsel = readb(ptd->hw_base + WPT_TSEL);
    150	/*
    151	 * When TSEL's Policy Lock-Down bit is 1, TSEL become RO.
    152	 * If so, thermal sensor cannot enable. Bail out.
    153	 */
    154	if (tsel & WPT_TSEL_PLDB) {
    155		dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
    156		return -ENODEV;
    157	}
    158
    159	writeb(tsel|WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
    160	if (!(WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL))) {
    161		dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
    162		return -ENODEV;
    163	}
    164
    165read_trips:
    166	ptd->crt_trip_id = -1;
    167	trip_temp = readw(ptd->hw_base + WPT_CTT);
    168	trip_temp &= 0x1FF;
    169	if (trip_temp) {
    170		ptd->crt_temp = GET_WPT_TEMP(trip_temp);
    171		ptd->crt_trip_id = 0;
    172		++(*nr_trips);
    173	}
    174
    175	ptd->hot_trip_id = -1;
    176	trip_temp = readw(ptd->hw_base + WPT_PHL);
    177	trip_temp &= 0x1FF;
    178	if (trip_temp) {
    179		ptd->hot_temp = GET_WPT_TEMP(trip_temp);
    180		ptd->hot_trip_id = *nr_trips;
    181		++(*nr_trips);
    182	}
    183
    184	pch_wpt_add_acpi_psv_trip(ptd, nr_trips);
    185
    186	return 0;
    187}
    188
    189static int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp)
    190{
    191	*temp = GET_WPT_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP));
    192
    193	return 0;
    194}
    195
    196/* Cool the PCH when it's overheat in .suspend_noirq phase */
    197static int pch_wpt_suspend(struct pch_thermal_device *ptd)
    198{
    199	u8 tsel;
    200	int pch_delay_cnt = 0;
    201	u16 pch_thr_temp, pch_cur_temp;
    202
    203	/* Shutdown the thermal sensor if it is not enabled by BIOS */
    204	if (!ptd->bios_enabled) {
    205		tsel = readb(ptd->hw_base + WPT_TSEL);
    206		writeb(tsel & 0xFE, ptd->hw_base + WPT_TSEL);
    207		return 0;
    208	}
    209
    210	/* Do not check temperature if it is not a S0ix capable platform */
    211#ifdef CONFIG_ACPI
    212	if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
    213		return 0;
    214#else
    215	return 0;
    216#endif
    217
    218	/* Do not check temperature if it is not s2idle */
    219	if (pm_suspend_via_firmware())
    220		return 0;
    221
    222	/* Get the PCH temperature threshold value */
    223	pch_thr_temp = GET_PCH_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TSPM));
    224
    225	/* Get the PCH current temperature value */
    226	pch_cur_temp = GET_PCH_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP));
    227
    228	/*
    229	 * If current PCH temperature is higher than configured PCH threshold
    230	 * value, run some delay loop with sleep to let the current temperature
    231	 * go down below the threshold value which helps to allow system enter
    232	 * lower power S0ix suspend state. Even after delay loop if PCH current
    233	 * temperature stays above threshold, notify the warning message
    234	 * which helps to indentify the reason why S0ix entry was rejected.
    235	 */
    236	while (pch_delay_cnt < delay_cnt) {
    237		if (pch_cur_temp < pch_thr_temp)
    238			break;
    239
    240		if (pm_wakeup_pending()) {
    241			dev_warn(&ptd->pdev->dev, "Wakeup event detected, abort cooling\n");
    242			return 0;
    243		}
    244
    245		pch_delay_cnt++;
    246		dev_dbg(&ptd->pdev->dev,
    247			"CPU-PCH current temp [%dC] higher than the threshold temp [%dC], sleep %d times for %d ms duration\n",
    248			pch_cur_temp, pch_thr_temp, pch_delay_cnt, delay_timeout);
    249		msleep(delay_timeout);
    250		/* Read the PCH current temperature for next cycle. */
    251		pch_cur_temp = GET_PCH_TEMP(WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP));
    252	}
    253
    254	if (pch_cur_temp >= pch_thr_temp)
    255		dev_warn(&ptd->pdev->dev,
    256			"CPU-PCH is hot [%dC] after %d ms delay. S0ix might fail\n",
    257			pch_cur_temp, pch_delay_cnt * delay_timeout);
    258	else {
    259		if (pch_delay_cnt)
    260			dev_info(&ptd->pdev->dev,
    261				"CPU-PCH is cool [%dC] after %d ms delay\n",
    262				pch_cur_temp, pch_delay_cnt * delay_timeout);
    263		else
    264			dev_info(&ptd->pdev->dev,
    265				"CPU-PCH is cool [%dC]\n",
    266				pch_cur_temp);
    267	}
    268
    269	return 0;
    270}
    271
    272static int pch_wpt_resume(struct pch_thermal_device *ptd)
    273{
    274	u8 tsel;
    275
    276	if (ptd->bios_enabled)
    277		return 0;
    278
    279	tsel = readb(ptd->hw_base + WPT_TSEL);
    280
    281	writeb(tsel | WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
    282
    283	return 0;
    284}
    285
    286struct pch_dev_ops {
    287	int (*hw_init)(struct pch_thermal_device *ptd, int *nr_trips);
    288	int (*get_temp)(struct pch_thermal_device *ptd, int *temp);
    289	int (*suspend)(struct pch_thermal_device *ptd);
    290	int (*resume)(struct pch_thermal_device *ptd);
    291};
    292
    293
    294/* dev ops for Wildcat Point */
    295static const struct pch_dev_ops pch_dev_ops_wpt = {
    296	.hw_init = pch_wpt_init,
    297	.get_temp = pch_wpt_get_temp,
    298	.suspend = pch_wpt_suspend,
    299	.resume = pch_wpt_resume,
    300};
    301
    302static int pch_thermal_get_temp(struct thermal_zone_device *tzd, int *temp)
    303{
    304	struct pch_thermal_device *ptd = tzd->devdata;
    305
    306	return	ptd->ops->get_temp(ptd, temp);
    307}
    308
    309static int pch_get_trip_type(struct thermal_zone_device *tzd, int trip,
    310			     enum thermal_trip_type *type)
    311{
    312	struct pch_thermal_device *ptd = tzd->devdata;
    313
    314	if (ptd->crt_trip_id == trip)
    315		*type = THERMAL_TRIP_CRITICAL;
    316	else if (ptd->hot_trip_id == trip)
    317		*type = THERMAL_TRIP_HOT;
    318	else if (ptd->psv_trip_id == trip)
    319		*type = THERMAL_TRIP_PASSIVE;
    320	else
    321		return -EINVAL;
    322
    323	return 0;
    324}
    325
    326static int pch_get_trip_temp(struct thermal_zone_device *tzd, int trip, int *temp)
    327{
    328	struct pch_thermal_device *ptd = tzd->devdata;
    329
    330	if (ptd->crt_trip_id == trip)
    331		*temp = ptd->crt_temp;
    332	else if (ptd->hot_trip_id == trip)
    333		*temp = ptd->hot_temp;
    334	else if (ptd->psv_trip_id == trip)
    335		*temp = ptd->psv_temp;
    336	else
    337		return -EINVAL;
    338
    339	return 0;
    340}
    341
    342static void pch_critical(struct thermal_zone_device *tzd)
    343{
    344	dev_dbg(&tzd->device, "%s: critical temperature reached\n", tzd->type);
    345}
    346
    347static struct thermal_zone_device_ops tzd_ops = {
    348	.get_temp = pch_thermal_get_temp,
    349	.get_trip_type = pch_get_trip_type,
    350	.get_trip_temp = pch_get_trip_temp,
    351	.critical = pch_critical,
    352};
    353
    354enum board_ids {
    355	board_hsw,
    356	board_wpt,
    357	board_skl,
    358	board_cnl,
    359	board_cml,
    360	board_lwb,
    361};
    362
    363static const struct board_info {
    364	const char *name;
    365	const struct pch_dev_ops *ops;
    366} board_info[] = {
    367	[board_hsw] = {
    368		.name = "pch_haswell",
    369		.ops = &pch_dev_ops_wpt,
    370	},
    371	[board_wpt] = {
    372		.name = "pch_wildcat_point",
    373		.ops = &pch_dev_ops_wpt,
    374	},
    375	[board_skl] = {
    376		.name = "pch_skylake",
    377		.ops = &pch_dev_ops_wpt,
    378	},
    379	[board_cnl] = {
    380		.name = "pch_cannonlake",
    381		.ops = &pch_dev_ops_wpt,
    382	},
    383	[board_cml] = {
    384		.name = "pch_cometlake",
    385		.ops = &pch_dev_ops_wpt,
    386	},
    387	[board_lwb] = {
    388		.name = "pch_lewisburg",
    389		.ops = &pch_dev_ops_wpt,
    390	},
    391};
    392
    393static int intel_pch_thermal_probe(struct pci_dev *pdev,
    394				   const struct pci_device_id *id)
    395{
    396	enum board_ids board_id = id->driver_data;
    397	const struct board_info *bi = &board_info[board_id];
    398	struct pch_thermal_device *ptd;
    399	int err;
    400	int nr_trips;
    401
    402	ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL);
    403	if (!ptd)
    404		return -ENOMEM;
    405
    406	ptd->ops = bi->ops;
    407
    408	pci_set_drvdata(pdev, ptd);
    409	ptd->pdev = pdev;
    410
    411	err = pci_enable_device(pdev);
    412	if (err) {
    413		dev_err(&pdev->dev, "failed to enable pci device\n");
    414		return err;
    415	}
    416
    417	err = pci_request_regions(pdev, driver_name);
    418	if (err) {
    419		dev_err(&pdev->dev, "failed to request pci region\n");
    420		goto error_disable;
    421	}
    422
    423	ptd->hw_base = pci_ioremap_bar(pdev, 0);
    424	if (!ptd->hw_base) {
    425		err = -ENOMEM;
    426		dev_err(&pdev->dev, "failed to map mem base\n");
    427		goto error_release;
    428	}
    429
    430	err = ptd->ops->hw_init(ptd, &nr_trips);
    431	if (err)
    432		goto error_cleanup;
    433
    434	ptd->tzd = thermal_zone_device_register(bi->name, nr_trips, 0, ptd,
    435						&tzd_ops, NULL, 0, 0);
    436	if (IS_ERR(ptd->tzd)) {
    437		dev_err(&pdev->dev, "Failed to register thermal zone %s\n",
    438			bi->name);
    439		err = PTR_ERR(ptd->tzd);
    440		goto error_cleanup;
    441	}
    442	err = thermal_zone_device_enable(ptd->tzd);
    443	if (err)
    444		goto err_unregister;
    445
    446	return 0;
    447
    448err_unregister:
    449	thermal_zone_device_unregister(ptd->tzd);
    450error_cleanup:
    451	iounmap(ptd->hw_base);
    452error_release:
    453	pci_release_regions(pdev);
    454error_disable:
    455	pci_disable_device(pdev);
    456	dev_err(&pdev->dev, "pci device failed to probe\n");
    457	return err;
    458}
    459
    460static void intel_pch_thermal_remove(struct pci_dev *pdev)
    461{
    462	struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
    463
    464	thermal_zone_device_unregister(ptd->tzd);
    465	iounmap(ptd->hw_base);
    466	pci_set_drvdata(pdev, NULL);
    467	pci_release_regions(pdev);
    468	pci_disable_device(pdev);
    469}
    470
    471static int intel_pch_thermal_suspend_noirq(struct device *device)
    472{
    473	struct pch_thermal_device *ptd = dev_get_drvdata(device);
    474
    475	return ptd->ops->suspend(ptd);
    476}
    477
    478static int intel_pch_thermal_resume(struct device *device)
    479{
    480	struct pch_thermal_device *ptd = dev_get_drvdata(device);
    481
    482	return ptd->ops->resume(ptd);
    483}
    484
    485static const struct pci_device_id intel_pch_thermal_id[] = {
    486	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_1),
    487		.driver_data = board_hsw, },
    488	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_2),
    489		.driver_data = board_hsw, },
    490	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT),
    491		.driver_data = board_wpt, },
    492	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL),
    493		.driver_data = board_skl, },
    494	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL_H),
    495		.driver_data = board_skl, },
    496	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL),
    497		.driver_data = board_cnl, },
    498	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_H),
    499		.driver_data = board_cnl, },
    500	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_LP),
    501		.driver_data = board_cnl, },
    502	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CML_H),
    503		.driver_data = board_cml, },
    504	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_LWB),
    505		.driver_data = board_lwb, },
    506	{ 0, },
    507};
    508MODULE_DEVICE_TABLE(pci, intel_pch_thermal_id);
    509
    510static const struct dev_pm_ops intel_pch_pm_ops = {
    511	.suspend_noirq = intel_pch_thermal_suspend_noirq,
    512	.resume = intel_pch_thermal_resume,
    513};
    514
    515static struct pci_driver intel_pch_thermal_driver = {
    516	.name		= "intel_pch_thermal",
    517	.id_table	= intel_pch_thermal_id,
    518	.probe		= intel_pch_thermal_probe,
    519	.remove		= intel_pch_thermal_remove,
    520	.driver.pm	= &intel_pch_pm_ops,
    521};
    522
    523module_pci_driver(intel_pch_thermal_driver);
    524
    525MODULE_LICENSE("GPL v2");
    526MODULE_DESCRIPTION("Intel PCH Thermal driver");