1acd65d5dSSrinivas Pandruvada // SPDX-License-Identifier: GPL-2.0-only
2acd65d5dSSrinivas Pandruvada /*
3acd65d5dSSrinivas Pandruvada  * Processor thermal device for newer processors
4acd65d5dSSrinivas Pandruvada  * Copyright (c) 2020, Intel Corporation.
5acd65d5dSSrinivas Pandruvada  */
6acd65d5dSSrinivas Pandruvada 
7acd65d5dSSrinivas Pandruvada #include <linux/acpi.h>
8acd65d5dSSrinivas Pandruvada #include <linux/kernel.h>
9acd65d5dSSrinivas Pandruvada #include <linux/module.h>
10acd65d5dSSrinivas Pandruvada #include <linux/pci.h>
11acd65d5dSSrinivas Pandruvada #include <linux/thermal.h>
12acd65d5dSSrinivas Pandruvada 
13acd65d5dSSrinivas Pandruvada #include "int340x_thermal_zone.h"
14acd65d5dSSrinivas Pandruvada #include "processor_thermal_device.h"
15acd65d5dSSrinivas Pandruvada 
16acd65d5dSSrinivas Pandruvada #define DRV_NAME "proc_thermal_pci"
17acd65d5dSSrinivas Pandruvada 
18acd65d5dSSrinivas Pandruvada struct proc_thermal_pci {
19acd65d5dSSrinivas Pandruvada 	struct pci_dev *pdev;
20acd65d5dSSrinivas Pandruvada 	struct proc_thermal_device *proc_priv;
21acd65d5dSSrinivas Pandruvada 	struct thermal_zone_device *tzone;
22acd65d5dSSrinivas Pandruvada 	struct delayed_work work;
23acd65d5dSSrinivas Pandruvada 	int stored_thres;
24acd65d5dSSrinivas Pandruvada 	int no_legacy;
25acd65d5dSSrinivas Pandruvada };
26acd65d5dSSrinivas Pandruvada 
27acd65d5dSSrinivas Pandruvada enum proc_thermal_mmio_type {
28acd65d5dSSrinivas Pandruvada 	PROC_THERMAL_MMIO_TJMAX,
29acd65d5dSSrinivas Pandruvada 	PROC_THERMAL_MMIO_PP0_TEMP,
30acd65d5dSSrinivas Pandruvada 	PROC_THERMAL_MMIO_PP1_TEMP,
31acd65d5dSSrinivas Pandruvada 	PROC_THERMAL_MMIO_PKG_TEMP,
32acd65d5dSSrinivas Pandruvada 	PROC_THERMAL_MMIO_THRES_0,
33acd65d5dSSrinivas Pandruvada 	PROC_THERMAL_MMIO_THRES_1,
34acd65d5dSSrinivas Pandruvada 	PROC_THERMAL_MMIO_INT_ENABLE_0,
35acd65d5dSSrinivas Pandruvada 	PROC_THERMAL_MMIO_INT_ENABLE_1,
36acd65d5dSSrinivas Pandruvada 	PROC_THERMAL_MMIO_INT_STATUS_0,
37acd65d5dSSrinivas Pandruvada 	PROC_THERMAL_MMIO_INT_STATUS_1,
38acd65d5dSSrinivas Pandruvada 	PROC_THERMAL_MMIO_MAX
39acd65d5dSSrinivas Pandruvada };
40acd65d5dSSrinivas Pandruvada 
41acd65d5dSSrinivas Pandruvada struct proc_thermal_mmio_info {
42acd65d5dSSrinivas Pandruvada 	enum proc_thermal_mmio_type mmio_type;
43acd65d5dSSrinivas Pandruvada 	u64	mmio_addr;
44acd65d5dSSrinivas Pandruvada 	u64	shift;
45acd65d5dSSrinivas Pandruvada 	u64	mask;
46acd65d5dSSrinivas Pandruvada };
47acd65d5dSSrinivas Pandruvada 
48acd65d5dSSrinivas Pandruvada static struct proc_thermal_mmio_info proc_thermal_mmio_info[] = {
49acd65d5dSSrinivas Pandruvada 	{ PROC_THERMAL_MMIO_TJMAX, 0x599c, 16, 0xff },
50acd65d5dSSrinivas Pandruvada 	{ PROC_THERMAL_MMIO_PP0_TEMP, 0x597c, 0, 0xff },
51acd65d5dSSrinivas Pandruvada 	{ PROC_THERMAL_MMIO_PP1_TEMP, 0x5980, 0, 0xff },
52acd65d5dSSrinivas Pandruvada 	{ PROC_THERMAL_MMIO_PKG_TEMP, 0x5978, 0, 0xff },
53acd65d5dSSrinivas Pandruvada 	{ PROC_THERMAL_MMIO_THRES_0, 0x5820, 8, 0x7F },
54acd65d5dSSrinivas Pandruvada 	{ PROC_THERMAL_MMIO_THRES_1, 0x5820, 16, 0x7F },
55acd65d5dSSrinivas Pandruvada 	{ PROC_THERMAL_MMIO_INT_ENABLE_0, 0x5820, 15, 0x01 },
56acd65d5dSSrinivas Pandruvada 	{ PROC_THERMAL_MMIO_INT_ENABLE_1, 0x5820, 23, 0x01 },
57acd65d5dSSrinivas Pandruvada 	{ PROC_THERMAL_MMIO_INT_STATUS_0, 0x7200, 6, 0x01 },
58acd65d5dSSrinivas Pandruvada 	{ PROC_THERMAL_MMIO_INT_STATUS_1, 0x7200, 8, 0x01 },
59acd65d5dSSrinivas Pandruvada };
60acd65d5dSSrinivas Pandruvada 
61acd65d5dSSrinivas Pandruvada #define B0D4_THERMAL_NOTIFY_DELAY	1000
62acd65d5dSSrinivas Pandruvada static int notify_delay_ms = B0D4_THERMAL_NOTIFY_DELAY;
63acd65d5dSSrinivas Pandruvada 
proc_thermal_mmio_read(struct proc_thermal_pci * pci_info,enum proc_thermal_mmio_type type,u32 * value)64acd65d5dSSrinivas Pandruvada static void proc_thermal_mmio_read(struct proc_thermal_pci *pci_info,
65acd65d5dSSrinivas Pandruvada 				    enum proc_thermal_mmio_type type,
66acd65d5dSSrinivas Pandruvada 				    u32 *value)
67acd65d5dSSrinivas Pandruvada {
68acd65d5dSSrinivas Pandruvada 	*value = ioread32(((u8 __iomem *)pci_info->proc_priv->mmio_base +
69acd65d5dSSrinivas Pandruvada 				proc_thermal_mmio_info[type].mmio_addr));
70acd65d5dSSrinivas Pandruvada 	*value >>= proc_thermal_mmio_info[type].shift;
71acd65d5dSSrinivas Pandruvada 	*value &= proc_thermal_mmio_info[type].mask;
72acd65d5dSSrinivas Pandruvada }
73acd65d5dSSrinivas Pandruvada 
proc_thermal_mmio_write(struct proc_thermal_pci * pci_info,enum proc_thermal_mmio_type type,u32 value)74acd65d5dSSrinivas Pandruvada static void proc_thermal_mmio_write(struct proc_thermal_pci *pci_info,
75acd65d5dSSrinivas Pandruvada 				     enum proc_thermal_mmio_type type,
76acd65d5dSSrinivas Pandruvada 				     u32 value)
77acd65d5dSSrinivas Pandruvada {
78acd65d5dSSrinivas Pandruvada 	u32 current_val;
79acd65d5dSSrinivas Pandruvada 	u32 mask;
80acd65d5dSSrinivas Pandruvada 
81acd65d5dSSrinivas Pandruvada 	current_val = ioread32(((u8 __iomem *)pci_info->proc_priv->mmio_base +
82acd65d5dSSrinivas Pandruvada 				proc_thermal_mmio_info[type].mmio_addr));
83acd65d5dSSrinivas Pandruvada 	mask = proc_thermal_mmio_info[type].mask << proc_thermal_mmio_info[type].shift;
84acd65d5dSSrinivas Pandruvada 	current_val &= ~mask;
85acd65d5dSSrinivas Pandruvada 
86acd65d5dSSrinivas Pandruvada 	value &= proc_thermal_mmio_info[type].mask;
87acd65d5dSSrinivas Pandruvada 	value <<= proc_thermal_mmio_info[type].shift;
88acd65d5dSSrinivas Pandruvada 
89acd65d5dSSrinivas Pandruvada 	current_val |= value;
90acd65d5dSSrinivas Pandruvada 	iowrite32(current_val, ((u8 __iomem *)pci_info->proc_priv->mmio_base +
91acd65d5dSSrinivas Pandruvada 				proc_thermal_mmio_info[type].mmio_addr));
92acd65d5dSSrinivas Pandruvada }
93acd65d5dSSrinivas Pandruvada 
94acd65d5dSSrinivas Pandruvada /*
95acd65d5dSSrinivas Pandruvada  * To avoid sending two many messages to user space, we have 1 second delay.
96acd65d5dSSrinivas Pandruvada  * On interrupt we are disabling interrupt and enabling after 1 second.
97acd65d5dSSrinivas Pandruvada  * This workload function is delayed by 1 second.
98acd65d5dSSrinivas Pandruvada  */
proc_thermal_threshold_work_fn(struct work_struct * work)99acd65d5dSSrinivas Pandruvada static void proc_thermal_threshold_work_fn(struct work_struct *work)
100acd65d5dSSrinivas Pandruvada {
101acd65d5dSSrinivas Pandruvada 	struct delayed_work *delayed_work = to_delayed_work(work);
102acd65d5dSSrinivas Pandruvada 	struct proc_thermal_pci *pci_info = container_of(delayed_work,
103acd65d5dSSrinivas Pandruvada 						struct proc_thermal_pci, work);
104acd65d5dSSrinivas Pandruvada 	struct thermal_zone_device *tzone = pci_info->tzone;
105acd65d5dSSrinivas Pandruvada 
106acd65d5dSSrinivas Pandruvada 	if (tzone)
107acd65d5dSSrinivas Pandruvada 		thermal_zone_device_update(tzone, THERMAL_TRIP_VIOLATED);
108acd65d5dSSrinivas Pandruvada 
109acd65d5dSSrinivas Pandruvada 	/* Enable interrupt flag */
110acd65d5dSSrinivas Pandruvada 	proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 1);
111acd65d5dSSrinivas Pandruvada }
112acd65d5dSSrinivas Pandruvada 
pkg_thermal_schedule_work(struct delayed_work * work)113acd65d5dSSrinivas Pandruvada static void pkg_thermal_schedule_work(struct delayed_work *work)
114acd65d5dSSrinivas Pandruvada {
115acd65d5dSSrinivas Pandruvada 	unsigned long ms = msecs_to_jiffies(notify_delay_ms);
116acd65d5dSSrinivas Pandruvada 
117acd65d5dSSrinivas Pandruvada 	schedule_delayed_work(work, ms);
118acd65d5dSSrinivas Pandruvada }
119acd65d5dSSrinivas Pandruvada 
proc_thermal_irq_handler(int irq,void * devid)120acd65d5dSSrinivas Pandruvada static irqreturn_t proc_thermal_irq_handler(int irq, void *devid)
121acd65d5dSSrinivas Pandruvada {
122acd65d5dSSrinivas Pandruvada 	struct proc_thermal_pci *pci_info = devid;
123acd65d5dSSrinivas Pandruvada 	u32 status;
124acd65d5dSSrinivas Pandruvada 
125acd65d5dSSrinivas Pandruvada 	proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_INT_STATUS_0, &status);
126acd65d5dSSrinivas Pandruvada 
127acd65d5dSSrinivas Pandruvada 	/* Disable enable interrupt flag */
128acd65d5dSSrinivas Pandruvada 	proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0);
129acd65d5dSSrinivas Pandruvada 	pci_write_config_byte(pci_info->pdev, 0xdc, 0x01);
130acd65d5dSSrinivas Pandruvada 
131acd65d5dSSrinivas Pandruvada 	pkg_thermal_schedule_work(&pci_info->work);
132acd65d5dSSrinivas Pandruvada 
133acd65d5dSSrinivas Pandruvada 	return IRQ_HANDLED;
134acd65d5dSSrinivas Pandruvada }
135acd65d5dSSrinivas Pandruvada 
sys_get_curr_temp(struct thermal_zone_device * tzd,int * temp)136acd65d5dSSrinivas Pandruvada static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp)
137acd65d5dSSrinivas Pandruvada {
1385f68d078SDaniel Lezcano 	struct proc_thermal_pci *pci_info = thermal_zone_device_priv(tzd);
139acd65d5dSSrinivas Pandruvada 	u32 _temp;
140acd65d5dSSrinivas Pandruvada 
141acd65d5dSSrinivas Pandruvada 	proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_PKG_TEMP, &_temp);
142acd65d5dSSrinivas Pandruvada 	*temp = (unsigned long)_temp * 1000;
143acd65d5dSSrinivas Pandruvada 
144acd65d5dSSrinivas Pandruvada 	return 0;
145acd65d5dSSrinivas Pandruvada }
146acd65d5dSSrinivas Pandruvada 
sys_set_trip_temp(struct thermal_zone_device * tzd,int trip,int temp)147acd65d5dSSrinivas Pandruvada static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp)
148acd65d5dSSrinivas Pandruvada {
1495f68d078SDaniel Lezcano 	struct proc_thermal_pci *pci_info = thermal_zone_device_priv(tzd);
150acd65d5dSSrinivas Pandruvada 	int tjmax, _temp;
151acd65d5dSSrinivas Pandruvada 
152acd65d5dSSrinivas Pandruvada 	if (temp <= 0) {
153acd65d5dSSrinivas Pandruvada 		cancel_delayed_work_sync(&pci_info->work);
154acd65d5dSSrinivas Pandruvada 		proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0);
155acd65d5dSSrinivas Pandruvada 		proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0, 0);
156acd65d5dSSrinivas Pandruvada 		pci_info->stored_thres = 0;
157acd65d5dSSrinivas Pandruvada 		return 0;
158acd65d5dSSrinivas Pandruvada 	}
159acd65d5dSSrinivas Pandruvada 
160acd65d5dSSrinivas Pandruvada 	proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_TJMAX, &tjmax);
161acd65d5dSSrinivas Pandruvada 	_temp = tjmax - (temp / 1000);
162acd65d5dSSrinivas Pandruvada 	if (_temp < 0)
163acd65d5dSSrinivas Pandruvada 		return -EINVAL;
164acd65d5dSSrinivas Pandruvada 
165acd65d5dSSrinivas Pandruvada 	proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0, _temp);
166acd65d5dSSrinivas Pandruvada 	proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 1);
167acd65d5dSSrinivas Pandruvada 
168acd65d5dSSrinivas Pandruvada 	pci_info->stored_thres = temp;
169acd65d5dSSrinivas Pandruvada 
170acd65d5dSSrinivas Pandruvada 	return 0;
171acd65d5dSSrinivas Pandruvada }
172acd65d5dSSrinivas Pandruvada 
get_trip_temp(struct proc_thermal_pci * pci_info)173e90eb1dfSDaniel Lezcano static int get_trip_temp(struct proc_thermal_pci *pci_info)
174e90eb1dfSDaniel Lezcano {
175e90eb1dfSDaniel Lezcano 	int temp, tjmax;
176e90eb1dfSDaniel Lezcano 
177e90eb1dfSDaniel Lezcano 	proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_THRES_0, &temp);
178e90eb1dfSDaniel Lezcano 	if (!temp)
179e90eb1dfSDaniel Lezcano 		return THERMAL_TEMP_INVALID;
180e90eb1dfSDaniel Lezcano 
181e90eb1dfSDaniel Lezcano 	proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_TJMAX, &tjmax);
182e90eb1dfSDaniel Lezcano 	temp = (tjmax - temp) * 1000;
183e90eb1dfSDaniel Lezcano 
184e90eb1dfSDaniel Lezcano 	return temp;
185e90eb1dfSDaniel Lezcano }
186e90eb1dfSDaniel Lezcano 
187e90eb1dfSDaniel Lezcano static struct thermal_trip psv_trip = {
188e90eb1dfSDaniel Lezcano 	.type = THERMAL_TRIP_PASSIVE,
189e90eb1dfSDaniel Lezcano };
190e90eb1dfSDaniel Lezcano 
191acd65d5dSSrinivas Pandruvada static struct thermal_zone_device_ops tzone_ops = {
192acd65d5dSSrinivas Pandruvada 	.get_temp = sys_get_curr_temp,
193acd65d5dSSrinivas Pandruvada 	.set_trip_temp	= sys_set_trip_temp,
194acd65d5dSSrinivas Pandruvada };
195acd65d5dSSrinivas Pandruvada 
196acd65d5dSSrinivas Pandruvada static struct thermal_zone_params tzone_params = {
197acd65d5dSSrinivas Pandruvada 	.governor_name = "user_space",
198acd65d5dSSrinivas Pandruvada 	.no_hwmon = true,
199acd65d5dSSrinivas Pandruvada };
200acd65d5dSSrinivas Pandruvada 
proc_thermal_pci_probe(struct pci_dev * pdev,const struct pci_device_id * id)201acd65d5dSSrinivas Pandruvada static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
202acd65d5dSSrinivas Pandruvada {
203acd65d5dSSrinivas Pandruvada 	struct proc_thermal_device *proc_priv;
204acd65d5dSSrinivas Pandruvada 	struct proc_thermal_pci *pci_info;
205acd65d5dSSrinivas Pandruvada 	int irq_flag = 0, irq, ret;
206acd65d5dSSrinivas Pandruvada 
207acd65d5dSSrinivas Pandruvada 	proc_priv = devm_kzalloc(&pdev->dev, sizeof(*proc_priv), GFP_KERNEL);
208acd65d5dSSrinivas Pandruvada 	if (!proc_priv)
209acd65d5dSSrinivas Pandruvada 		return -ENOMEM;
210acd65d5dSSrinivas Pandruvada 
211acd65d5dSSrinivas Pandruvada 	pci_info = devm_kzalloc(&pdev->dev, sizeof(*pci_info), GFP_KERNEL);
212acd65d5dSSrinivas Pandruvada 	if (!pci_info)
213acd65d5dSSrinivas Pandruvada 		return -ENOMEM;
214acd65d5dSSrinivas Pandruvada 
215acd65d5dSSrinivas Pandruvada 	pci_info->pdev = pdev;
216acd65d5dSSrinivas Pandruvada 	ret = pcim_enable_device(pdev);
217acd65d5dSSrinivas Pandruvada 	if (ret < 0) {
218acd65d5dSSrinivas Pandruvada 		dev_err(&pdev->dev, "error: could not enable device\n");
219acd65d5dSSrinivas Pandruvada 		return ret;
220acd65d5dSSrinivas Pandruvada 	}
221acd65d5dSSrinivas Pandruvada 
222acd65d5dSSrinivas Pandruvada 	pci_set_master(pdev);
223acd65d5dSSrinivas Pandruvada 
224acd65d5dSSrinivas Pandruvada 	INIT_DELAYED_WORK(&pci_info->work, proc_thermal_threshold_work_fn);
225acd65d5dSSrinivas Pandruvada 
226acd65d5dSSrinivas Pandruvada 	ret = proc_thermal_add(&pdev->dev, proc_priv);
227acd65d5dSSrinivas Pandruvada 	if (ret) {
228acd65d5dSSrinivas Pandruvada 		dev_err(&pdev->dev, "error: proc_thermal_add, will continue\n");
229acd65d5dSSrinivas Pandruvada 		pci_info->no_legacy = 1;
230acd65d5dSSrinivas Pandruvada 	}
231acd65d5dSSrinivas Pandruvada 
232acd65d5dSSrinivas Pandruvada 	proc_priv->priv_data = pci_info;
233acd65d5dSSrinivas Pandruvada 	pci_info->proc_priv = proc_priv;
234acd65d5dSSrinivas Pandruvada 	pci_set_drvdata(pdev, proc_priv);
235acd65d5dSSrinivas Pandruvada 
236acd65d5dSSrinivas Pandruvada 	ret = proc_thermal_mmio_add(pdev, proc_priv, id->driver_data);
237acd65d5dSSrinivas Pandruvada 	if (ret)
238acd65d5dSSrinivas Pandruvada 		goto err_ret_thermal;
239acd65d5dSSrinivas Pandruvada 
240e90eb1dfSDaniel Lezcano 	psv_trip.temperature = get_trip_temp(pci_info);
241e90eb1dfSDaniel Lezcano 
242e90eb1dfSDaniel Lezcano 	pci_info->tzone = thermal_zone_device_register_with_trips("TCPU_PCI", &psv_trip,
243e90eb1dfSDaniel Lezcano 							1, 1, pci_info,
244acd65d5dSSrinivas Pandruvada 							&tzone_ops,
245acd65d5dSSrinivas Pandruvada 							&tzone_params, 0, 0);
246ad079d98SSrinivas Pandruvada 	if (IS_ERR(pci_info->tzone)) {
247ad079d98SSrinivas Pandruvada 		ret = PTR_ERR(pci_info->tzone);
248acd65d5dSSrinivas Pandruvada 		goto err_ret_mmio;
249ad079d98SSrinivas Pandruvada 	}
250acd65d5dSSrinivas Pandruvada 
251acd65d5dSSrinivas Pandruvada 	/* request and enable interrupt */
252acd65d5dSSrinivas Pandruvada 	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
253acd65d5dSSrinivas Pandruvada 	if (ret < 0) {
254acd65d5dSSrinivas Pandruvada 		dev_err(&pdev->dev, "Failed to allocate vectors!\n");
255acd65d5dSSrinivas Pandruvada 		goto err_ret_tzone;
256acd65d5dSSrinivas Pandruvada 	}
257acd65d5dSSrinivas Pandruvada 	if (!pdev->msi_enabled && !pdev->msix_enabled)
258acd65d5dSSrinivas Pandruvada 		irq_flag = IRQF_SHARED;
259acd65d5dSSrinivas Pandruvada 
260acd65d5dSSrinivas Pandruvada 	irq =  pci_irq_vector(pdev, 0);
261acd65d5dSSrinivas Pandruvada 	ret = devm_request_threaded_irq(&pdev->dev, irq,
262acd65d5dSSrinivas Pandruvada 					proc_thermal_irq_handler, NULL,
263acd65d5dSSrinivas Pandruvada 					irq_flag, KBUILD_MODNAME, pci_info);
264acd65d5dSSrinivas Pandruvada 	if (ret) {
265acd65d5dSSrinivas Pandruvada 		dev_err(&pdev->dev, "Request IRQ %d failed\n", pdev->irq);
266acd65d5dSSrinivas Pandruvada 		goto err_free_vectors;
267acd65d5dSSrinivas Pandruvada 	}
268acd65d5dSSrinivas Pandruvada 
26952f04f10SSrinivas Pandruvada 	ret = thermal_zone_device_enable(pci_info->tzone);
27052f04f10SSrinivas Pandruvada 	if (ret)
27152f04f10SSrinivas Pandruvada 		goto err_free_vectors;
27252f04f10SSrinivas Pandruvada 
273acd65d5dSSrinivas Pandruvada 	return 0;
274acd65d5dSSrinivas Pandruvada 
275acd65d5dSSrinivas Pandruvada err_free_vectors:
276acd65d5dSSrinivas Pandruvada 	pci_free_irq_vectors(pdev);
277acd65d5dSSrinivas Pandruvada err_ret_tzone:
278acd65d5dSSrinivas Pandruvada 	thermal_zone_device_unregister(pci_info->tzone);
279acd65d5dSSrinivas Pandruvada err_ret_mmio:
280acd65d5dSSrinivas Pandruvada 	proc_thermal_mmio_remove(pdev, proc_priv);
281acd65d5dSSrinivas Pandruvada err_ret_thermal:
282acd65d5dSSrinivas Pandruvada 	if (!pci_info->no_legacy)
283acd65d5dSSrinivas Pandruvada 		proc_thermal_remove(proc_priv);
284acd65d5dSSrinivas Pandruvada 	pci_disable_device(pdev);
285acd65d5dSSrinivas Pandruvada 
286acd65d5dSSrinivas Pandruvada 	return ret;
287acd65d5dSSrinivas Pandruvada }
288acd65d5dSSrinivas Pandruvada 
proc_thermal_pci_remove(struct pci_dev * pdev)289acd65d5dSSrinivas Pandruvada static void proc_thermal_pci_remove(struct pci_dev *pdev)
290acd65d5dSSrinivas Pandruvada {
291acd65d5dSSrinivas Pandruvada 	struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
292acd65d5dSSrinivas Pandruvada 	struct proc_thermal_pci *pci_info = proc_priv->priv_data;
293acd65d5dSSrinivas Pandruvada 
294acd65d5dSSrinivas Pandruvada 	cancel_delayed_work_sync(&pci_info->work);
295acd65d5dSSrinivas Pandruvada 
296acd65d5dSSrinivas Pandruvada 	proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0, 0);
297acd65d5dSSrinivas Pandruvada 	proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0);
298acd65d5dSSrinivas Pandruvada 
299acd65d5dSSrinivas Pandruvada 	devm_free_irq(&pdev->dev, pdev->irq, pci_info);
300acd65d5dSSrinivas Pandruvada 	pci_free_irq_vectors(pdev);
301acd65d5dSSrinivas Pandruvada 
302acd65d5dSSrinivas Pandruvada 	thermal_zone_device_unregister(pci_info->tzone);
303acd65d5dSSrinivas Pandruvada 	proc_thermal_mmio_remove(pdev, pci_info->proc_priv);
304acd65d5dSSrinivas Pandruvada 	if (!pci_info->no_legacy)
305acd65d5dSSrinivas Pandruvada 		proc_thermal_remove(proc_priv);
306acd65d5dSSrinivas Pandruvada 	pci_disable_device(pdev);
307acd65d5dSSrinivas Pandruvada }
308acd65d5dSSrinivas Pandruvada 
309acd65d5dSSrinivas Pandruvada #ifdef CONFIG_PM_SLEEP
proc_thermal_pci_suspend(struct device * dev)310c4fcf1adSAntoine Tenart static int proc_thermal_pci_suspend(struct device *dev)
311c4fcf1adSAntoine Tenart {
312c4fcf1adSAntoine Tenart 	struct pci_dev *pdev = to_pci_dev(dev);
313c4fcf1adSAntoine Tenart 	struct proc_thermal_device *proc_priv;
314c4fcf1adSAntoine Tenart 	struct proc_thermal_pci *pci_info;
315c4fcf1adSAntoine Tenart 
316c4fcf1adSAntoine Tenart 	proc_priv = pci_get_drvdata(pdev);
317c4fcf1adSAntoine Tenart 	pci_info = proc_priv->priv_data;
318c4fcf1adSAntoine Tenart 
319c4fcf1adSAntoine Tenart 	if (!pci_info->no_legacy)
320c4fcf1adSAntoine Tenart 		return proc_thermal_suspend(dev);
321c4fcf1adSAntoine Tenart 
322c4fcf1adSAntoine Tenart 	return 0;
323c4fcf1adSAntoine Tenart }
proc_thermal_pci_resume(struct device * dev)324acd65d5dSSrinivas Pandruvada static int proc_thermal_pci_resume(struct device *dev)
325acd65d5dSSrinivas Pandruvada {
326acd65d5dSSrinivas Pandruvada 	struct pci_dev *pdev = to_pci_dev(dev);
327acd65d5dSSrinivas Pandruvada 	struct proc_thermal_device *proc_priv;
328acd65d5dSSrinivas Pandruvada 	struct proc_thermal_pci *pci_info;
329acd65d5dSSrinivas Pandruvada 
330acd65d5dSSrinivas Pandruvada 	proc_priv = pci_get_drvdata(pdev);
331acd65d5dSSrinivas Pandruvada 	pci_info = proc_priv->priv_data;
332acd65d5dSSrinivas Pandruvada 
333acd65d5dSSrinivas Pandruvada 	if (pci_info->stored_thres) {
334acd65d5dSSrinivas Pandruvada 		proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0,
335acd65d5dSSrinivas Pandruvada 					 pci_info->stored_thres / 1000);
336acd65d5dSSrinivas Pandruvada 		proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 1);
337acd65d5dSSrinivas Pandruvada 	}
338acd65d5dSSrinivas Pandruvada 
339acd65d5dSSrinivas Pandruvada 	if (!pci_info->no_legacy)
340acd65d5dSSrinivas Pandruvada 		return proc_thermal_resume(dev);
341acd65d5dSSrinivas Pandruvada 
342acd65d5dSSrinivas Pandruvada 	return 0;
343acd65d5dSSrinivas Pandruvada }
344acd65d5dSSrinivas Pandruvada #else
345c4fcf1adSAntoine Tenart #define proc_thermal_pci_suspend NULL
346acd65d5dSSrinivas Pandruvada #define proc_thermal_pci_resume NULL
347acd65d5dSSrinivas Pandruvada #endif
348acd65d5dSSrinivas Pandruvada 
349c4fcf1adSAntoine Tenart static SIMPLE_DEV_PM_OPS(proc_thermal_pci_pm, proc_thermal_pci_suspend,
350c4fcf1adSAntoine Tenart 			 proc_thermal_pci_resume);
351acd65d5dSSrinivas Pandruvada 
352acd65d5dSSrinivas Pandruvada static const struct pci_device_id proc_thermal_pci_ids[] = {
353acd65d5dSSrinivas Pandruvada 	{ PCI_DEVICE_DATA(INTEL, ADL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_MBOX) },
354*5bc6b1dfSSrinivas Pandruvada 	{ PCI_DEVICE_DATA(INTEL, MTLP_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_MBOX | PROC_THERMAL_FEATURE_DLVR) },
355e5b54867SSrinivas Pandruvada 	{ PCI_DEVICE_DATA(INTEL, RPL_THERMAL, PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_MBOX) },
356acd65d5dSSrinivas Pandruvada 	{ },
357acd65d5dSSrinivas Pandruvada };
358acd65d5dSSrinivas Pandruvada 
359acd65d5dSSrinivas Pandruvada MODULE_DEVICE_TABLE(pci, proc_thermal_pci_ids);
360acd65d5dSSrinivas Pandruvada 
361acd65d5dSSrinivas Pandruvada static struct pci_driver proc_thermal_pci_driver = {
362acd65d5dSSrinivas Pandruvada 	.name		= DRV_NAME,
363acd65d5dSSrinivas Pandruvada 	.probe		= proc_thermal_pci_probe,
364acd65d5dSSrinivas Pandruvada 	.remove	= proc_thermal_pci_remove,
365acd65d5dSSrinivas Pandruvada 	.id_table	= proc_thermal_pci_ids,
366acd65d5dSSrinivas Pandruvada 	.driver.pm	= &proc_thermal_pci_pm,
367acd65d5dSSrinivas Pandruvada };
368acd65d5dSSrinivas Pandruvada 
36953e41b85SShang XiaoJing module_pci_driver(proc_thermal_pci_driver);
370acd65d5dSSrinivas Pandruvada 
371acd65d5dSSrinivas Pandruvada MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
372acd65d5dSSrinivas Pandruvada MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver");
373acd65d5dSSrinivas Pandruvada MODULE_LICENSE("GPL v2");
374