18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* intel_pch_thermal.c - Intel PCH Thermal driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (c) 2015, Intel Corporation.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Authors:
78c2ecf20Sopenharmony_ci *     Tushar Dave <tushar.n.dave@intel.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/types.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/pci.h>
148c2ecf20Sopenharmony_ci#include <linux/acpi.h>
158c2ecf20Sopenharmony_ci#include <linux/thermal.h>
168c2ecf20Sopenharmony_ci#include <linux/units.h>
178c2ecf20Sopenharmony_ci#include <linux/pm.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* Intel PCH thermal Device IDs */
208c2ecf20Sopenharmony_ci#define PCH_THERMAL_DID_HSW_1	0x9C24 /* Haswell PCH */
218c2ecf20Sopenharmony_ci#define PCH_THERMAL_DID_HSW_2	0x8C24 /* Haswell PCH */
228c2ecf20Sopenharmony_ci#define PCH_THERMAL_DID_WPT	0x9CA4 /* Wildcat Point */
238c2ecf20Sopenharmony_ci#define PCH_THERMAL_DID_SKL	0x9D31 /* Skylake PCH */
248c2ecf20Sopenharmony_ci#define PCH_THERMAL_DID_SKL_H	0xA131 /* Skylake PCH 100 series */
258c2ecf20Sopenharmony_ci#define PCH_THERMAL_DID_CNL	0x9Df9 /* CNL PCH */
268c2ecf20Sopenharmony_ci#define PCH_THERMAL_DID_CNL_H	0xA379 /* CNL-H PCH */
278c2ecf20Sopenharmony_ci#define PCH_THERMAL_DID_CNL_LP	0x02F9 /* CNL-LP PCH */
288c2ecf20Sopenharmony_ci#define PCH_THERMAL_DID_CML_H	0X06F9 /* CML-H PCH */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/* Wildcat Point-LP  PCH Thermal registers */
318c2ecf20Sopenharmony_ci#define WPT_TEMP	0x0000	/* Temperature */
328c2ecf20Sopenharmony_ci#define WPT_TSC	0x04	/* Thermal Sensor Control */
338c2ecf20Sopenharmony_ci#define WPT_TSS	0x06	/* Thermal Sensor Status */
348c2ecf20Sopenharmony_ci#define WPT_TSEL	0x08	/* Thermal Sensor Enable and Lock */
358c2ecf20Sopenharmony_ci#define WPT_TSREL	0x0A	/* Thermal Sensor Report Enable and Lock */
368c2ecf20Sopenharmony_ci#define WPT_TSMIC	0x0C	/* Thermal Sensor SMI Control */
378c2ecf20Sopenharmony_ci#define WPT_CTT	0x0010	/* Catastrophic Trip Point */
388c2ecf20Sopenharmony_ci#define WPT_TAHV	0x0014	/* Thermal Alert High Value */
398c2ecf20Sopenharmony_ci#define WPT_TALV	0x0018	/* Thermal Alert Low Value */
408c2ecf20Sopenharmony_ci#define WPT_TL		0x00000040	/* Throttle Value */
418c2ecf20Sopenharmony_ci#define WPT_PHL	0x0060	/* PCH Hot Level */
428c2ecf20Sopenharmony_ci#define WPT_PHLC	0x62	/* PHL Control */
438c2ecf20Sopenharmony_ci#define WPT_TAS	0x80	/* Thermal Alert Status */
448c2ecf20Sopenharmony_ci#define WPT_TSPIEN	0x82	/* PCI Interrupt Event Enables */
458c2ecf20Sopenharmony_ci#define WPT_TSGPEN	0x84	/* General Purpose Event Enables */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*  Wildcat Point-LP  PCH Thermal Register bit definitions */
488c2ecf20Sopenharmony_ci#define WPT_TEMP_TSR	0x01ff	/* Temp TS Reading */
498c2ecf20Sopenharmony_ci#define WPT_TSC_CPDE	0x01	/* Catastrophic Power-Down Enable */
508c2ecf20Sopenharmony_ci#define WPT_TSS_TSDSS	0x10	/* Thermal Sensor Dynamic Shutdown Status */
518c2ecf20Sopenharmony_ci#define WPT_TSS_GPES	0x08	/* GPE status */
528c2ecf20Sopenharmony_ci#define WPT_TSEL_ETS	0x01    /* Enable TS */
538c2ecf20Sopenharmony_ci#define WPT_TSEL_PLDB	0x80	/* TSEL Policy Lock-Down Bit */
548c2ecf20Sopenharmony_ci#define WPT_TL_TOL	0x000001FF	/* T0 Level */
558c2ecf20Sopenharmony_ci#define WPT_TL_T1L	0x1ff00000	/* T1 Level */
568c2ecf20Sopenharmony_ci#define WPT_TL_TTEN	0x20000000	/* TT Enable */
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic char driver_name[] = "Intel PCH thermal driver";
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistruct pch_thermal_device {
618c2ecf20Sopenharmony_ci	void __iomem *hw_base;
628c2ecf20Sopenharmony_ci	const struct pch_dev_ops *ops;
638c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
648c2ecf20Sopenharmony_ci	struct thermal_zone_device *tzd;
658c2ecf20Sopenharmony_ci	int crt_trip_id;
668c2ecf20Sopenharmony_ci	unsigned long crt_temp;
678c2ecf20Sopenharmony_ci	int hot_trip_id;
688c2ecf20Sopenharmony_ci	unsigned long hot_temp;
698c2ecf20Sopenharmony_ci	int psv_trip_id;
708c2ecf20Sopenharmony_ci	unsigned long psv_temp;
718c2ecf20Sopenharmony_ci	bool bios_enabled;
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/*
778c2ecf20Sopenharmony_ci * On some platforms, there is a companion ACPI device, which adds
788c2ecf20Sopenharmony_ci * passive trip temperature using _PSV method. There is no specific
798c2ecf20Sopenharmony_ci * passive temperature setting in MMIO interface of this PCI device.
808c2ecf20Sopenharmony_ci */
818c2ecf20Sopenharmony_cistatic void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
828c2ecf20Sopenharmony_ci				      int *nr_trips)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct acpi_device *adev;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	ptd->psv_trip_id = -1;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	adev = ACPI_COMPANION(&ptd->pdev->dev);
898c2ecf20Sopenharmony_ci	if (adev) {
908c2ecf20Sopenharmony_ci		unsigned long long r;
918c2ecf20Sopenharmony_ci		acpi_status status;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		status = acpi_evaluate_integer(adev->handle, "_PSV", NULL,
948c2ecf20Sopenharmony_ci					       &r);
958c2ecf20Sopenharmony_ci		if (ACPI_SUCCESS(status)) {
968c2ecf20Sopenharmony_ci			unsigned long trip_temp;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci			trip_temp = deci_kelvin_to_millicelsius(r);
998c2ecf20Sopenharmony_ci			if (trip_temp) {
1008c2ecf20Sopenharmony_ci				ptd->psv_temp = trip_temp;
1018c2ecf20Sopenharmony_ci				ptd->psv_trip_id = *nr_trips;
1028c2ecf20Sopenharmony_ci				++(*nr_trips);
1038c2ecf20Sopenharmony_ci			}
1048c2ecf20Sopenharmony_ci		}
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci#else
1088c2ecf20Sopenharmony_cistatic void pch_wpt_add_acpi_psv_trip(struct pch_thermal_device *ptd,
1098c2ecf20Sopenharmony_ci				      int *nr_trips)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	ptd->psv_trip_id = -1;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci#endif
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic int pch_wpt_init(struct pch_thermal_device *ptd, int *nr_trips)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	u8 tsel;
1198c2ecf20Sopenharmony_ci	u16 trip_temp;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	*nr_trips = 0;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	/* Check if BIOS has already enabled thermal sensor */
1248c2ecf20Sopenharmony_ci	if (WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL)) {
1258c2ecf20Sopenharmony_ci		ptd->bios_enabled = true;
1268c2ecf20Sopenharmony_ci		goto read_trips;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	tsel = readb(ptd->hw_base + WPT_TSEL);
1308c2ecf20Sopenharmony_ci	/*
1318c2ecf20Sopenharmony_ci	 * When TSEL's Policy Lock-Down bit is 1, TSEL become RO.
1328c2ecf20Sopenharmony_ci	 * If so, thermal sensor cannot enable. Bail out.
1338c2ecf20Sopenharmony_ci	 */
1348c2ecf20Sopenharmony_ci	if (tsel & WPT_TSEL_PLDB) {
1358c2ecf20Sopenharmony_ci		dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
1368c2ecf20Sopenharmony_ci		return -ENODEV;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	writeb(tsel|WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
1408c2ecf20Sopenharmony_ci	if (!(WPT_TSEL_ETS & readb(ptd->hw_base + WPT_TSEL))) {
1418c2ecf20Sopenharmony_ci		dev_err(&ptd->pdev->dev, "Sensor can't be enabled\n");
1428c2ecf20Sopenharmony_ci		return -ENODEV;
1438c2ecf20Sopenharmony_ci	}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ciread_trips:
1468c2ecf20Sopenharmony_ci	ptd->crt_trip_id = -1;
1478c2ecf20Sopenharmony_ci	trip_temp = readw(ptd->hw_base + WPT_CTT);
1488c2ecf20Sopenharmony_ci	trip_temp &= 0x1FF;
1498c2ecf20Sopenharmony_ci	if (trip_temp) {
1508c2ecf20Sopenharmony_ci		/* Resolution of 1/2 degree C and an offset of -50C */
1518c2ecf20Sopenharmony_ci		ptd->crt_temp = trip_temp * 1000 / 2 - 50000;
1528c2ecf20Sopenharmony_ci		ptd->crt_trip_id = 0;
1538c2ecf20Sopenharmony_ci		++(*nr_trips);
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ptd->hot_trip_id = -1;
1578c2ecf20Sopenharmony_ci	trip_temp = readw(ptd->hw_base + WPT_PHL);
1588c2ecf20Sopenharmony_ci	trip_temp &= 0x1FF;
1598c2ecf20Sopenharmony_ci	if (trip_temp) {
1608c2ecf20Sopenharmony_ci		/* Resolution of 1/2 degree C and an offset of -50C */
1618c2ecf20Sopenharmony_ci		ptd->hot_temp = trip_temp * 1000 / 2 - 50000;
1628c2ecf20Sopenharmony_ci		ptd->hot_trip_id = *nr_trips;
1638c2ecf20Sopenharmony_ci		++(*nr_trips);
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	pch_wpt_add_acpi_psv_trip(ptd, nr_trips);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int pch_wpt_get_temp(struct pch_thermal_device *ptd, int *temp)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	u16 wpt_temp;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	wpt_temp = WPT_TEMP_TSR & readw(ptd->hw_base + WPT_TEMP);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/* Resolution of 1/2 degree C and an offset of -50C */
1788c2ecf20Sopenharmony_ci	*temp = (wpt_temp * 1000 / 2 - 50000);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return 0;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic int pch_wpt_suspend(struct pch_thermal_device *ptd)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	u8 tsel;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (ptd->bios_enabled)
1888c2ecf20Sopenharmony_ci		return 0;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	tsel = readb(ptd->hw_base + WPT_TSEL);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	writeb(tsel & 0xFE, ptd->hw_base + WPT_TSEL);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic int pch_wpt_resume(struct pch_thermal_device *ptd)
1988c2ecf20Sopenharmony_ci{
1998c2ecf20Sopenharmony_ci	u8 tsel;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (ptd->bios_enabled)
2028c2ecf20Sopenharmony_ci		return 0;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	tsel = readb(ptd->hw_base + WPT_TSEL);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	writeb(tsel | WPT_TSEL_ETS, ptd->hw_base + WPT_TSEL);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return 0;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistruct pch_dev_ops {
2128c2ecf20Sopenharmony_ci	int (*hw_init)(struct pch_thermal_device *ptd, int *nr_trips);
2138c2ecf20Sopenharmony_ci	int (*get_temp)(struct pch_thermal_device *ptd, int *temp);
2148c2ecf20Sopenharmony_ci	int (*suspend)(struct pch_thermal_device *ptd);
2158c2ecf20Sopenharmony_ci	int (*resume)(struct pch_thermal_device *ptd);
2168c2ecf20Sopenharmony_ci};
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/* dev ops for Wildcat Point */
2208c2ecf20Sopenharmony_cistatic const struct pch_dev_ops pch_dev_ops_wpt = {
2218c2ecf20Sopenharmony_ci	.hw_init = pch_wpt_init,
2228c2ecf20Sopenharmony_ci	.get_temp = pch_wpt_get_temp,
2238c2ecf20Sopenharmony_ci	.suspend = pch_wpt_suspend,
2248c2ecf20Sopenharmony_ci	.resume = pch_wpt_resume,
2258c2ecf20Sopenharmony_ci};
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic int pch_thermal_get_temp(struct thermal_zone_device *tzd, int *temp)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	struct pch_thermal_device *ptd = tzd->devdata;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	return	ptd->ops->get_temp(ptd, temp);
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic int pch_get_trip_type(struct thermal_zone_device *tzd, int trip,
2358c2ecf20Sopenharmony_ci			     enum thermal_trip_type *type)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct pch_thermal_device *ptd = tzd->devdata;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (ptd->crt_trip_id == trip)
2408c2ecf20Sopenharmony_ci		*type = THERMAL_TRIP_CRITICAL;
2418c2ecf20Sopenharmony_ci	else if (ptd->hot_trip_id == trip)
2428c2ecf20Sopenharmony_ci		*type = THERMAL_TRIP_HOT;
2438c2ecf20Sopenharmony_ci	else if (ptd->psv_trip_id == trip)
2448c2ecf20Sopenharmony_ci		*type = THERMAL_TRIP_PASSIVE;
2458c2ecf20Sopenharmony_ci	else
2468c2ecf20Sopenharmony_ci		return -EINVAL;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return 0;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int pch_get_trip_temp(struct thermal_zone_device *tzd, int trip, int *temp)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct pch_thermal_device *ptd = tzd->devdata;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (ptd->crt_trip_id == trip)
2568c2ecf20Sopenharmony_ci		*temp = ptd->crt_temp;
2578c2ecf20Sopenharmony_ci	else if (ptd->hot_trip_id == trip)
2588c2ecf20Sopenharmony_ci		*temp = ptd->hot_temp;
2598c2ecf20Sopenharmony_ci	else if (ptd->psv_trip_id == trip)
2608c2ecf20Sopenharmony_ci		*temp = ptd->psv_temp;
2618c2ecf20Sopenharmony_ci	else
2628c2ecf20Sopenharmony_ci		return -EINVAL;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	return 0;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic struct thermal_zone_device_ops tzd_ops = {
2688c2ecf20Sopenharmony_ci	.get_temp = pch_thermal_get_temp,
2698c2ecf20Sopenharmony_ci	.get_trip_type = pch_get_trip_type,
2708c2ecf20Sopenharmony_ci	.get_trip_temp = pch_get_trip_temp,
2718c2ecf20Sopenharmony_ci};
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cienum board_ids {
2748c2ecf20Sopenharmony_ci	board_hsw,
2758c2ecf20Sopenharmony_ci	board_wpt,
2768c2ecf20Sopenharmony_ci	board_skl,
2778c2ecf20Sopenharmony_ci	board_cnl,
2788c2ecf20Sopenharmony_ci	board_cml,
2798c2ecf20Sopenharmony_ci};
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic const struct board_info {
2828c2ecf20Sopenharmony_ci	const char *name;
2838c2ecf20Sopenharmony_ci	const struct pch_dev_ops *ops;
2848c2ecf20Sopenharmony_ci} board_info[] = {
2858c2ecf20Sopenharmony_ci	[board_hsw] = {
2868c2ecf20Sopenharmony_ci		.name = "pch_haswell",
2878c2ecf20Sopenharmony_ci		.ops = &pch_dev_ops_wpt,
2888c2ecf20Sopenharmony_ci	},
2898c2ecf20Sopenharmony_ci	[board_wpt] = {
2908c2ecf20Sopenharmony_ci		.name = "pch_wildcat_point",
2918c2ecf20Sopenharmony_ci		.ops = &pch_dev_ops_wpt,
2928c2ecf20Sopenharmony_ci	},
2938c2ecf20Sopenharmony_ci	[board_skl] = {
2948c2ecf20Sopenharmony_ci		.name = "pch_skylake",
2958c2ecf20Sopenharmony_ci		.ops = &pch_dev_ops_wpt,
2968c2ecf20Sopenharmony_ci	},
2978c2ecf20Sopenharmony_ci	[board_cnl] = {
2988c2ecf20Sopenharmony_ci		.name = "pch_cannonlake",
2998c2ecf20Sopenharmony_ci		.ops = &pch_dev_ops_wpt,
3008c2ecf20Sopenharmony_ci	},
3018c2ecf20Sopenharmony_ci	[board_cml] = {
3028c2ecf20Sopenharmony_ci		.name = "pch_cometlake",
3038c2ecf20Sopenharmony_ci		.ops = &pch_dev_ops_wpt,
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci};
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_cistatic int intel_pch_thermal_probe(struct pci_dev *pdev,
3088c2ecf20Sopenharmony_ci				   const struct pci_device_id *id)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	enum board_ids board_id = id->driver_data;
3118c2ecf20Sopenharmony_ci	const struct board_info *bi = &board_info[board_id];
3128c2ecf20Sopenharmony_ci	struct pch_thermal_device *ptd;
3138c2ecf20Sopenharmony_ci	int err;
3148c2ecf20Sopenharmony_ci	int nr_trips;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	ptd = devm_kzalloc(&pdev->dev, sizeof(*ptd), GFP_KERNEL);
3178c2ecf20Sopenharmony_ci	if (!ptd)
3188c2ecf20Sopenharmony_ci		return -ENOMEM;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	ptd->ops = bi->ops;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, ptd);
3238c2ecf20Sopenharmony_ci	ptd->pdev = pdev;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	err = pci_enable_device(pdev);
3268c2ecf20Sopenharmony_ci	if (err) {
3278c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable pci device\n");
3288c2ecf20Sopenharmony_ci		return err;
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	err = pci_request_regions(pdev, driver_name);
3328c2ecf20Sopenharmony_ci	if (err) {
3338c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to request pci region\n");
3348c2ecf20Sopenharmony_ci		goto error_disable;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	ptd->hw_base = pci_ioremap_bar(pdev, 0);
3388c2ecf20Sopenharmony_ci	if (!ptd->hw_base) {
3398c2ecf20Sopenharmony_ci		err = -ENOMEM;
3408c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to map mem base\n");
3418c2ecf20Sopenharmony_ci		goto error_release;
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	err = ptd->ops->hw_init(ptd, &nr_trips);
3458c2ecf20Sopenharmony_ci	if (err)
3468c2ecf20Sopenharmony_ci		goto error_cleanup;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	ptd->tzd = thermal_zone_device_register(bi->name, nr_trips, 0, ptd,
3498c2ecf20Sopenharmony_ci						&tzd_ops, NULL, 0, 0);
3508c2ecf20Sopenharmony_ci	if (IS_ERR(ptd->tzd)) {
3518c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to register thermal zone %s\n",
3528c2ecf20Sopenharmony_ci			bi->name);
3538c2ecf20Sopenharmony_ci		err = PTR_ERR(ptd->tzd);
3548c2ecf20Sopenharmony_ci		goto error_cleanup;
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci	err = thermal_zone_device_enable(ptd->tzd);
3578c2ecf20Sopenharmony_ci	if (err)
3588c2ecf20Sopenharmony_ci		goto err_unregister;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	return 0;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cierr_unregister:
3638c2ecf20Sopenharmony_ci	thermal_zone_device_unregister(ptd->tzd);
3648c2ecf20Sopenharmony_cierror_cleanup:
3658c2ecf20Sopenharmony_ci	iounmap(ptd->hw_base);
3668c2ecf20Sopenharmony_cierror_release:
3678c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
3688c2ecf20Sopenharmony_cierror_disable:
3698c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
3708c2ecf20Sopenharmony_ci	dev_err(&pdev->dev, "pci device failed to probe\n");
3718c2ecf20Sopenharmony_ci	return err;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic void intel_pch_thermal_remove(struct pci_dev *pdev)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct pch_thermal_device *ptd = pci_get_drvdata(pdev);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	thermal_zone_device_unregister(ptd->tzd);
3798c2ecf20Sopenharmony_ci	iounmap(ptd->hw_base);
3808c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, NULL);
3818c2ecf20Sopenharmony_ci	pci_release_regions(pdev);
3828c2ecf20Sopenharmony_ci	pci_disable_device(pdev);
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic int intel_pch_thermal_suspend(struct device *device)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	struct pch_thermal_device *ptd = dev_get_drvdata(device);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	return ptd->ops->suspend(ptd);
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int intel_pch_thermal_resume(struct device *device)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct pch_thermal_device *ptd = dev_get_drvdata(device);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	return ptd->ops->resume(ptd);
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic const struct pci_device_id intel_pch_thermal_id[] = {
4008c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_1),
4018c2ecf20Sopenharmony_ci		.driver_data = board_hsw, },
4028c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_HSW_2),
4038c2ecf20Sopenharmony_ci		.driver_data = board_hsw, },
4048c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_WPT),
4058c2ecf20Sopenharmony_ci		.driver_data = board_wpt, },
4068c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL),
4078c2ecf20Sopenharmony_ci		.driver_data = board_skl, },
4088c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_SKL_H),
4098c2ecf20Sopenharmony_ci		.driver_data = board_skl, },
4108c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL),
4118c2ecf20Sopenharmony_ci		.driver_data = board_cnl, },
4128c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_H),
4138c2ecf20Sopenharmony_ci		.driver_data = board_cnl, },
4148c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CNL_LP),
4158c2ecf20Sopenharmony_ci		.driver_data = board_cnl, },
4168c2ecf20Sopenharmony_ci	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCH_THERMAL_DID_CML_H),
4178c2ecf20Sopenharmony_ci		.driver_data = board_cml, },
4188c2ecf20Sopenharmony_ci	{ 0, },
4198c2ecf20Sopenharmony_ci};
4208c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, intel_pch_thermal_id);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic const struct dev_pm_ops intel_pch_pm_ops = {
4238c2ecf20Sopenharmony_ci	.suspend = intel_pch_thermal_suspend,
4248c2ecf20Sopenharmony_ci	.resume = intel_pch_thermal_resume,
4258c2ecf20Sopenharmony_ci};
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic struct pci_driver intel_pch_thermal_driver = {
4288c2ecf20Sopenharmony_ci	.name		= "intel_pch_thermal",
4298c2ecf20Sopenharmony_ci	.id_table	= intel_pch_thermal_id,
4308c2ecf20Sopenharmony_ci	.probe		= intel_pch_thermal_probe,
4318c2ecf20Sopenharmony_ci	.remove		= intel_pch_thermal_remove,
4328c2ecf20Sopenharmony_ci	.driver.pm	= &intel_pch_pm_ops,
4338c2ecf20Sopenharmony_ci};
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cimodule_pci_driver(intel_pch_thermal_driver);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
4388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel PCH Thermal driver");
439