brintos

brintos / linux-shallow public Read only

0
0
Text · 14.0 KiB · ba5d36d Raw
518 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Processor thermal device for newer processors4 * Copyright (c) 2020, Intel Corporation.5 */6 7#include <linux/acpi.h>8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/pci.h>11#include <linux/thermal.h>12 13#include "int340x_thermal_zone.h"14#include "processor_thermal_device.h"15 16#define DRV_NAME "proc_thermal_pci"17 18static bool use_msi;19module_param(use_msi, bool, 0644);20MODULE_PARM_DESC(use_msi,21	"Use PCI MSI based interrupts for processor thermal device.");22 23struct proc_thermal_pci {24	struct pci_dev *pdev;25	struct proc_thermal_device *proc_priv;26	struct thermal_zone_device *tzone;27	struct delayed_work work;28	int stored_thres;29	int no_legacy;30};31 32enum proc_thermal_mmio_type {33	PROC_THERMAL_MMIO_TJMAX,34	PROC_THERMAL_MMIO_PP0_TEMP,35	PROC_THERMAL_MMIO_PP1_TEMP,36	PROC_THERMAL_MMIO_PKG_TEMP,37	PROC_THERMAL_MMIO_THRES_0,38	PROC_THERMAL_MMIO_THRES_1,39	PROC_THERMAL_MMIO_INT_ENABLE_0,40	PROC_THERMAL_MMIO_INT_ENABLE_1,41	PROC_THERMAL_MMIO_INT_STATUS_0,42	PROC_THERMAL_MMIO_INT_STATUS_1,43	PROC_THERMAL_MMIO_MAX44};45 46struct proc_thermal_mmio_info {47	enum proc_thermal_mmio_type mmio_type;48	u64	mmio_addr;49	u64	shift;50	u64	mask;51};52 53static struct proc_thermal_mmio_info proc_thermal_mmio_info[] = {54	{ PROC_THERMAL_MMIO_TJMAX, 0x599c, 16, 0xff },55	{ PROC_THERMAL_MMIO_PP0_TEMP, 0x597c, 0, 0xff },56	{ PROC_THERMAL_MMIO_PP1_TEMP, 0x5980, 0, 0xff },57	{ PROC_THERMAL_MMIO_PKG_TEMP, 0x5978, 0, 0xff },58	{ PROC_THERMAL_MMIO_THRES_0, 0x5820, 8, 0x7F },59	{ PROC_THERMAL_MMIO_THRES_1, 0x5820, 16, 0x7F },60	{ PROC_THERMAL_MMIO_INT_ENABLE_0, 0x5820, 15, 0x01 },61	{ PROC_THERMAL_MMIO_INT_ENABLE_1, 0x5820, 23, 0x01 },62	{ PROC_THERMAL_MMIO_INT_STATUS_0, 0x7200, 6, 0x01 },63	{ PROC_THERMAL_MMIO_INT_STATUS_1, 0x7200, 8, 0x01 },64};65 66/* List of supported MSI IDs (sources) */67enum proc_thermal_msi_ids {68	PKG_THERMAL,69	DDR_THERMAL,70	THERM_POWER_FLOOR,71	WORKLOAD_CHANGE,72	MSI_THERMAL_MAX73};74 75/* Stores IRQ associated with a MSI ID */76static int proc_thermal_msi_map[MSI_THERMAL_MAX];77 78#define B0D4_THERMAL_NOTIFY_DELAY	100079static int notify_delay_ms = B0D4_THERMAL_NOTIFY_DELAY;80 81static void proc_thermal_mmio_read(struct proc_thermal_pci *pci_info,82				    enum proc_thermal_mmio_type type,83				    u32 *value)84{85	*value = ioread32(((u8 __iomem *)pci_info->proc_priv->mmio_base +86				proc_thermal_mmio_info[type].mmio_addr));87	*value >>= proc_thermal_mmio_info[type].shift;88	*value &= proc_thermal_mmio_info[type].mask;89}90 91static void proc_thermal_mmio_write(struct proc_thermal_pci *pci_info,92				     enum proc_thermal_mmio_type type,93				     u32 value)94{95	u32 current_val;96	u32 mask;97 98	current_val = ioread32(((u8 __iomem *)pci_info->proc_priv->mmio_base +99				proc_thermal_mmio_info[type].mmio_addr));100	mask = proc_thermal_mmio_info[type].mask << proc_thermal_mmio_info[type].shift;101	current_val &= ~mask;102 103	value &= proc_thermal_mmio_info[type].mask;104	value <<= proc_thermal_mmio_info[type].shift;105 106	current_val |= value;107	iowrite32(current_val, ((u8 __iomem *)pci_info->proc_priv->mmio_base +108				proc_thermal_mmio_info[type].mmio_addr));109}110 111/*112 * To avoid sending two many messages to user space, we have 1 second delay.113 * On interrupt we are disabling interrupt and enabling after 1 second.114 * This workload function is delayed by 1 second.115 */116static void proc_thermal_threshold_work_fn(struct work_struct *work)117{118	struct delayed_work *delayed_work = to_delayed_work(work);119	struct proc_thermal_pci *pci_info = container_of(delayed_work,120						struct proc_thermal_pci, work);121	struct thermal_zone_device *tzone = pci_info->tzone;122 123	if (tzone)124		thermal_zone_device_update(tzone, THERMAL_TRIP_VIOLATED);125 126	/* Enable interrupt flag */127	proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 1);128}129 130static void pkg_thermal_schedule_work(struct delayed_work *work)131{132	unsigned long ms = msecs_to_jiffies(notify_delay_ms);133 134	schedule_delayed_work(work, ms);135}136 137static void proc_thermal_clear_soc_int_status(struct proc_thermal_device *proc_priv)138{139	u64 status;140 141	if (!(proc_priv->mmio_feature_mask &142	    (PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR)))143		return;144 145	status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);146	writeq(status & ~SOC_WT_RES_INT_STATUS_MASK,147	       proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);148}149 150static irqreturn_t proc_thermal_irq_thread_handler(int irq, void *devid)151{152	struct proc_thermal_pci *pci_info = devid;153 154	proc_thermal_wt_intr_callback(pci_info->pdev, pci_info->proc_priv);155	proc_thermal_power_floor_intr_callback(pci_info->pdev, pci_info->proc_priv);156	proc_thermal_clear_soc_int_status(pci_info->proc_priv);157 158	return IRQ_HANDLED;159}160 161static int proc_thermal_match_msi_irq(int irq)162{163	int i;164 165	if (!use_msi)166		goto msi_fail;167 168	for (i = 0; i < MSI_THERMAL_MAX; i++) {169		if (proc_thermal_msi_map[i] == irq)170			return i;171	}172 173msi_fail:174	return -EOPNOTSUPP;175}176 177static irqreturn_t proc_thermal_irq_handler(int irq, void *devid)178{179	struct proc_thermal_pci *pci_info = devid;180	struct proc_thermal_device *proc_priv;181	int ret = IRQ_NONE, msi_id;182	u32 status;183 184	proc_priv = pci_info->proc_priv;185 186	msi_id = proc_thermal_match_msi_irq(irq);187 188	if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_WT_HINT) {189		if (msi_id == WORKLOAD_CHANGE || proc_thermal_check_wt_intr(pci_info->proc_priv))190			ret = IRQ_WAKE_THREAD;191	}192 193	if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_POWER_FLOOR) {194		if (msi_id == THERM_POWER_FLOOR ||195		    proc_thermal_check_power_floor_intr(pci_info->proc_priv))196			ret = IRQ_WAKE_THREAD;197	}198 199	/*200	 * Since now there are two sources of interrupts: one from thermal threshold201	 * and another from workload hint, add a check if there was really a threshold202	 * interrupt before scheduling work function for thermal threshold.203	 */204	proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_INT_STATUS_0, &status);205	if (msi_id == PKG_THERMAL || status) {206		/* Disable enable interrupt flag */207		proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0);208		pkg_thermal_schedule_work(&pci_info->work);209		ret = IRQ_HANDLED;210	}211 212	pci_write_config_byte(pci_info->pdev, 0xdc, 0x01);213 214	return ret;215}216 217static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp)218{219	struct proc_thermal_pci *pci_info = thermal_zone_device_priv(tzd);220	u32 _temp;221 222	proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_PKG_TEMP, &_temp);223	*temp = (unsigned long)_temp * 1000;224 225	return 0;226}227 228static int sys_set_trip_temp(struct thermal_zone_device *tzd,229			     const struct thermal_trip *trip, int temp)230{231	struct proc_thermal_pci *pci_info = thermal_zone_device_priv(tzd);232	int tjmax, _temp;233 234	if (temp <= 0) {235		cancel_delayed_work_sync(&pci_info->work);236		proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0);237		proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0, 0);238		pci_info->stored_thres = 0;239		return 0;240	}241 242	proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_TJMAX, &tjmax);243	_temp = tjmax - (temp / 1000);244	if (_temp < 0)245		return -EINVAL;246 247	proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0, _temp);248	proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 1);249 250	pci_info->stored_thres = temp;251 252	return 0;253}254 255static int get_trip_temp(struct proc_thermal_pci *pci_info)256{257	int temp, tjmax;258 259	proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_THRES_0, &temp);260	if (!temp)261		return THERMAL_TEMP_INVALID;262 263	proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_TJMAX, &tjmax);264	temp = (tjmax - temp) * 1000;265 266	return temp;267}268 269static const struct thermal_zone_device_ops tzone_ops = {270	.get_temp = sys_get_curr_temp,271	.set_trip_temp	= sys_set_trip_temp,272};273 274static struct thermal_zone_params tzone_params = {275	.governor_name = "user_space",276	.no_hwmon = true,277};278 279static bool msi_irq;280 281static void proc_thermal_free_msi(struct pci_dev *pdev, struct proc_thermal_pci *pci_info)282{283	int i;284 285	for (i = 0; i < MSI_THERMAL_MAX; i++) {286		if (proc_thermal_msi_map[i])287			devm_free_irq(&pdev->dev, proc_thermal_msi_map[i], pci_info);288	}289 290	pci_free_irq_vectors(pdev);291}292 293static int proc_thermal_setup_msi(struct pci_dev *pdev, struct proc_thermal_pci *pci_info)294{295	int ret, i, irq, count;296 297	count = pci_alloc_irq_vectors(pdev, 1, MSI_THERMAL_MAX, PCI_IRQ_MSI | PCI_IRQ_MSIX);298	if (count < 0) {299		dev_err(&pdev->dev, "Failed to allocate vectors!\n");300		return count;301	}302 303	dev_info(&pdev->dev, "msi enabled:%d msix enabled:%d\n", pdev->msi_enabled,304		 pdev->msix_enabled);305 306	for (i = 0; i < count; i++) {307		irq =  pci_irq_vector(pdev, i);308 309		ret = devm_request_threaded_irq(&pdev->dev, irq, proc_thermal_irq_handler,310						proc_thermal_irq_thread_handler,311						0, KBUILD_MODNAME, pci_info);312		if (ret) {313			dev_err(&pdev->dev, "Request IRQ %d failed\n", irq);314			goto err_free_msi_vectors;315		}316 317		proc_thermal_msi_map[i] = irq;318	}319 320	msi_irq = true;321 322	return 0;323 324err_free_msi_vectors:325	proc_thermal_free_msi(pdev, pci_info);326 327	return ret;328}329 330static int proc_thermal_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)331{332	struct proc_thermal_device *proc_priv;333	struct proc_thermal_pci *pci_info;334	struct thermal_trip psv_trip = {335		.type = THERMAL_TRIP_PASSIVE,336		.flags = THERMAL_TRIP_FLAG_RW_TEMP,337	};338	int irq_flag = 0, irq, ret;339 340	proc_priv = devm_kzalloc(&pdev->dev, sizeof(*proc_priv), GFP_KERNEL);341	if (!proc_priv)342		return -ENOMEM;343 344	pci_info = devm_kzalloc(&pdev->dev, sizeof(*pci_info), GFP_KERNEL);345	if (!pci_info)346		return -ENOMEM;347 348	pci_info->pdev = pdev;349	ret = pcim_enable_device(pdev);350	if (ret < 0) {351		dev_err(&pdev->dev, "error: could not enable device\n");352		return ret;353	}354 355	pci_set_master(pdev);356 357	INIT_DELAYED_WORK(&pci_info->work, proc_thermal_threshold_work_fn);358 359	proc_priv->priv_data = pci_info;360	pci_info->proc_priv = proc_priv;361	pci_set_drvdata(pdev, proc_priv);362 363	ret = proc_thermal_mmio_add(pdev, proc_priv, id->driver_data);364	if (ret)365		return ret;366 367	ret = proc_thermal_add(&pdev->dev, proc_priv);368	if (ret) {369		dev_err(&pdev->dev, "error: proc_thermal_add, will continue\n");370		pci_info->no_legacy = 1;371	}372 373	psv_trip.temperature = get_trip_temp(pci_info);374 375	pci_info->tzone = thermal_zone_device_register_with_trips("TCPU_PCI", &psv_trip,376							1, pci_info,377							&tzone_ops,378							&tzone_params, 0, 0);379	if (IS_ERR(pci_info->tzone)) {380		ret = PTR_ERR(pci_info->tzone);381		goto err_del_legacy;382	}383 384	if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_MSI_SUPPORT)385		use_msi = true;386 387	if (use_msi) {388		ret = proc_thermal_setup_msi(pdev, pci_info);389		if (ret)390			goto err_ret_tzone;391	} else {392		irq_flag = IRQF_SHARED;393		irq = pdev->irq;394 395		ret = devm_request_threaded_irq(&pdev->dev, irq, proc_thermal_irq_handler,396						proc_thermal_irq_thread_handler, irq_flag,397						KBUILD_MODNAME, pci_info);398		if (ret) {399			dev_err(&pdev->dev, "Request IRQ %d failed\n", pdev->irq);400			goto err_ret_tzone;401		}402	}403 404	ret = thermal_zone_device_enable(pci_info->tzone);405	if (ret)406		goto err_free_vectors;407 408	return 0;409 410err_free_vectors:411	if (msi_irq)412		proc_thermal_free_msi(pdev, pci_info);413err_ret_tzone:414	thermal_zone_device_unregister(pci_info->tzone);415err_del_legacy:416	if (!pci_info->no_legacy)417		proc_thermal_remove(proc_priv);418	proc_thermal_mmio_remove(pdev, proc_priv);419 420	return ret;421}422 423static void proc_thermal_pci_remove(struct pci_dev *pdev)424{425	struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);426	struct proc_thermal_pci *pci_info = proc_priv->priv_data;427 428	cancel_delayed_work_sync(&pci_info->work);429 430	proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0, 0);431	proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0);432 433	if (msi_irq)434		proc_thermal_free_msi(pdev, pci_info);435 436	thermal_zone_device_unregister(pci_info->tzone);437	proc_thermal_mmio_remove(pdev, pci_info->proc_priv);438	if (!pci_info->no_legacy)439		proc_thermal_remove(proc_priv);440}441 442#ifdef CONFIG_PM_SLEEP443static int proc_thermal_pci_suspend(struct device *dev)444{445	struct pci_dev *pdev = to_pci_dev(dev);446	struct proc_thermal_device *proc_priv;447	struct proc_thermal_pci *pci_info;448 449	proc_priv = pci_get_drvdata(pdev);450	pci_info = proc_priv->priv_data;451 452	if (!pci_info->no_legacy)453		return proc_thermal_suspend(dev);454 455	return 0;456}457static int proc_thermal_pci_resume(struct device *dev)458{459	struct pci_dev *pdev = to_pci_dev(dev);460	struct proc_thermal_device *proc_priv;461	struct proc_thermal_pci *pci_info;462 463	proc_priv = pci_get_drvdata(pdev);464	pci_info = proc_priv->priv_data;465 466	if (pci_info->stored_thres) {467		proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_THRES_0,468					 pci_info->stored_thres / 1000);469		proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 1);470	}471 472	if (!pci_info->no_legacy)473		return proc_thermal_resume(dev);474 475	return 0;476}477#else478#define proc_thermal_pci_suspend NULL479#define proc_thermal_pci_resume NULL480#endif481 482static SIMPLE_DEV_PM_OPS(proc_thermal_pci_pm, proc_thermal_pci_suspend,483			 proc_thermal_pci_resume);484 485static const struct pci_device_id proc_thermal_pci_ids[] = {486	{ PCI_DEVICE_DATA(INTEL, ADL_THERMAL, PROC_THERMAL_FEATURE_RAPL |487	  PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_REQ) },488	{ PCI_DEVICE_DATA(INTEL, LNLM_THERMAL, PROC_THERMAL_FEATURE_MSI_SUPPORT |489	  PROC_THERMAL_FEATURE_RAPL | PROC_THERMAL_FEATURE_DLVR |490	  PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR) },491	{ PCI_DEVICE_DATA(INTEL, MTLP_THERMAL, PROC_THERMAL_FEATURE_RAPL |492	  PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_DLVR |493	  PROC_THERMAL_FEATURE_WT_HINT | PROC_THERMAL_FEATURE_POWER_FLOOR) },494	{ PCI_DEVICE_DATA(INTEL, ARL_S_THERMAL, PROC_THERMAL_FEATURE_RAPL |495	  PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_DLVR | PROC_THERMAL_FEATURE_WT_HINT) },496	{ PCI_DEVICE_DATA(INTEL, RPL_THERMAL, PROC_THERMAL_FEATURE_RAPL |497	  PROC_THERMAL_FEATURE_FIVR | PROC_THERMAL_FEATURE_DVFS | PROC_THERMAL_FEATURE_WT_REQ) },498	{ },499};500 501MODULE_DEVICE_TABLE(pci, proc_thermal_pci_ids);502 503static struct pci_driver proc_thermal_pci_driver = {504	.name		= DRV_NAME,505	.probe		= proc_thermal_pci_probe,506	.remove	= proc_thermal_pci_remove,507	.id_table	= proc_thermal_pci_ids,508	.driver.pm	= &proc_thermal_pci_pm,509};510 511module_pci_driver(proc_thermal_pci_driver);512 513MODULE_IMPORT_NS(INT340X_THERMAL);514 515MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");516MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver");517MODULE_LICENSE("GPL v2");518