Received: by 2002:a05:6a10:206:0:0:0:0 with SMTP id 6csp4742053pxj; Tue, 25 May 2021 15:31:32 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwVR3rPiPhw5T2vp0mmU97WnZWLo2XmPZcWQRusKQMYD9wUzHZJ7iLHGxKkF6Gl06bC3agB X-Received: by 2002:a92:c5ac:: with SMTP id r12mr21210820ilt.283.1621981892006; Tue, 25 May 2021 15:31:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1621981892; cv=none; d=google.com; s=arc-20160816; b=aYRDi7VvqHnGQuxAGcvYC2gP8SzlX3cUQc6eu0Z+d5CGXMLSYwAgEOKEbBt4pIDszn HxuiYE29lQXKEwd/kUWXXRd2OHhteWYzex13ZwujrQUXCUNZ0fsOfu3HpENz13FabHS5 /DcA73iwkLnMWv5cNRn+bNeiVyIZ5BXIFVw12+8dRZ1fncgBSNnQIyecBUyN9H2wmCfE v+XvvrDRtKT8Ce0YTNpyi+VsQMZQcr8fSk4XOn3rdFV9sqd8m2tZmwgEEUnaasBoEXuC vrpuYO5eYGcVTGCBrjsVDFMCEiKjT1co/S3p9l4T1k0ckiHcXbqG/Sgpr/jiqycjsYNq 7kQQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :ironport-sdr:ironport-sdr; bh=psEkGNvtehmJ5DA1P+J7kXKp47ygYvgiTBUhxz9gNrY=; b=hNaQu3TP9PhAYM9LZTuctPfBGBzKW1XaP4I85+aF8KfEpq6V3vPkxzmO8OswzuZUc4 cFWahUOAA69+69cHeqEmU2Pmq+SXmOc5kVoOG4Y9M7XDXC94RuLX4saAzEzCXqPVi8OS v9rxGdrcSzJBEHd3IJzjw3qDPjzA5rMjapcgaAMVYEpIQYCcA3V5dFLSSoxd5oahkhaW lgVI3AuG5hIDZVe89mKXEFGuLMCmYXjFAfcY6r+2vMjvJfVJYnz7hY68YlumblUFlun7 aRB96dc4oGWhnhX1lbf//gegqLcJ/QkuIVG6aInwo6wGaliPgC4pBgYVwNPNuIAi9TiN U8ZA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id z89si19527880jah.125.2021.05.25.15.31.19; Tue, 25 May 2021 15:31:31 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233375AbhEYUty (ORCPT + 99 others); Tue, 25 May 2021 16:49:54 -0400 Received: from mga18.intel.com ([134.134.136.126]:9161 "EHLO mga18.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233384AbhEYUtu (ORCPT ); Tue, 25 May 2021 16:49:50 -0400 IronPort-SDR: K2w9x9YtW4eYHWoc2v9ope6cWSP7FWxcKxshCYwUyt6U3F4m4FHjQEMOVRG3MwkEN20BrIy1jl ACO9fXv5zxxQ== X-IronPort-AV: E=McAfee;i="6200,9189,9995"; a="189676394" X-IronPort-AV: E=Sophos;i="5.82,329,1613462400"; d="scan'208";a="189676394" Received: from orsmga007.jf.intel.com ([10.7.209.58]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 25 May 2021 13:48:18 -0700 IronPort-SDR: 6SeKvANRuoZ1ilUbbhMJq4mhZs6RnpgNN6w9K2S+fT858LTdPaWcPNDGDtKT9qFUkX5MrAna/7 516GeSJqrsjg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.82,329,1613462400"; d="scan'208";a="435842398" Received: from spandruv-desk.jf.intel.com ([10.54.75.21]) by orsmga007.jf.intel.com with ESMTP; 25 May 2021 13:48:18 -0700 From: Srinivas Pandruvada To: daniel.lezcano@linaro.org, rui.zhang@intel.com, amitk@kernel.org Cc: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org, Srinivas Pandruvada Subject: [PATCH 2/2] thermal: int340x: processor_thermal: Add PCI MMIO based thermal driver Date: Tue, 25 May 2021 13:48:11 -0700 Message-Id: <20210525204811.3793651-3-srinivas.pandruvada@linux.intel.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210525204811.3793651-1-srinivas.pandruvada@linux.intel.com> References: <20210525204811.3793651-1-srinivas.pandruvada@linux.intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add a new PCI driver which register a thermal zone and allows to get notification for threshold violation by a RW trip point. These notifications are delivered from the device using MSI based interrupt. The main difference between this new PCI driver and the existing one is that the temperature and trip points directly use PCI MMIO instead of using ACPI methods. This driver registers a thermal zone "TCPU_PCI" in addition to the legacy processor thermal device, which uses ACPI companion device to set name, temperature and trips. This driver is anabled for AlderLake. Signed-off-by: Srinivas Pandruvada --- .../thermal/intel/int340x_thermal/Makefile | 1 + .../processor_thermal_device.h | 1 + .../processor_thermal_device_pci.c | 371 ++++++++++++++++++ .../processor_thermal_device_pci_legacy.c | 1 - 4 files changed, 373 insertions(+), 1 deletion(-) create mode 100644 drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c diff --git a/drivers/thermal/intel/int340x_thermal/Makefile b/drivers/thermal/intel/int340x_thermal/Makefile index aaf02d5deed5..4e852ce4a5d5 100644 --- a/drivers/thermal/intel/int340x_thermal/Makefile +++ b/drivers/thermal/intel/int340x_thermal/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o obj-$(CONFIG_INT340X_THERMAL) += int3401_thermal.o obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device_pci_legacy.o +obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device_pci.o obj-$(CONFIG_PROC_THERMAL_MMIO_RAPL) += processor_thermal_rapl.o obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_rfim.o obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_mbox.o diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h index 5e51f1173d00..5a1cfe4864f1 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device.h @@ -44,6 +44,7 @@ struct proc_thermal_device { struct intel_soc_dts_sensors *soc_dts; u32 mmio_feature_mask; void __iomem *mmio_base; + void *priv_data; }; struct rapl_mmio_regs { diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c new file mode 100644 index 000000000000..ad7e2e305abf --- /dev/null +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Processor thermal device for newer processors + * Copyright (c) 2020, Intel Corporation. + */ + +#include +#include +#include +#include +#include + +#include "int340x_thermal_zone.h" +#include "processor_thermal_device.h" + +#define DRV_NAME "proc_thermal_pci" + +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 irqreturn_t proc_thermal_irq_handler(int irq, void *devid) +{ + struct proc_thermal_pci *pci_info = devid; + u32 status; + + proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_INT_STATUS_0, &status); + + /* Disable enable interrupt flag */ + proc_thermal_mmio_write(pci_info, PROC_THERMAL_MMIO_INT_ENABLE_0, 0); + pci_write_config_byte(pci_info->pdev, 0xdc, 0x01); + + pkg_thermal_schedule_work(&pci_info->work); + + return IRQ_HANDLED; +} + +static int sys_get_curr_temp(struct thermal_zone_device *tzd, int *temp) +{ + struct proc_thermal_pci *pci_info = tzd->devdata; + u32 _temp; + + proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_PKG_TEMP, &_temp); + *temp = (unsigned long)_temp * 1000; + + return 0; +} + +static int sys_get_trip_temp(struct thermal_zone_device *tzd, + int trip, int *temp) +{ + struct proc_thermal_pci *pci_info = tzd->devdata; + u32 _temp; + + proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_THRES_0, &_temp); + if (!_temp) { + *temp = THERMAL_TEMP_INVALID; + } else { + int tjmax; + + proc_thermal_mmio_read(pci_info, PROC_THERMAL_MMIO_TJMAX, &tjmax); + _temp = tjmax - _temp; + *temp = (unsigned long)_temp * 1000; + } + + return 0; +} + +static int sys_get_trip_type(struct thermal_zone_device *tzd, int trip, + enum thermal_trip_type *type) +{ + *type = THERMAL_TRIP_PASSIVE; + + return 0; +} + +static int sys_set_trip_temp(struct thermal_zone_device *tzd, int trip, int temp) +{ + struct proc_thermal_pci *pci_info = tzd->devdata; + 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); + thermal_zone_device_disable(tzd); + 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); + + thermal_zone_device_enable(tzd); + pci_info->stored_thres = temp; + + return 0; +} + +static struct thermal_zone_device_ops tzone_ops = { + .get_temp = sys_get_curr_temp, + .get_trip_temp = sys_get_trip_temp, + .get_trip_type = sys_get_trip_type, + .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; + int irq_flag = 0, irq, ret; + + 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); + + 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; + } + + 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) + goto err_ret_thermal; + + pci_info->tzone = thermal_zone_device_register("TCPU_PCI", 1, 1, pci_info, + &tzone_ops, + &tzone_params, 0, 0); + if (IS_ERR(pci_info->tzone)) + goto err_ret_mmio; + + /* 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; + } + if (!pdev->msi_enabled && !pdev->msix_enabled) + irq_flag = IRQF_SHARED; + + irq = pci_irq_vector(pdev, 0); + ret = devm_request_threaded_irq(&pdev->dev, irq, + proc_thermal_irq_handler, NULL, + irq_flag, KBUILD_MODNAME, pci_info); + if (ret) { + dev_err(&pdev->dev, "Request IRQ %d failed\n", pdev->irq); + goto err_free_vectors; + } + + return 0; + +err_free_vectors: + pci_free_irq_vectors(pdev); +err_ret_tzone: + thermal_zone_device_unregister(pci_info->tzone); +err_ret_mmio: + proc_thermal_mmio_remove(pdev, proc_priv); +err_ret_thermal: + if (!pci_info->no_legacy) + proc_thermal_remove(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_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_resume NULL +#endif + +static SIMPLE_DEV_PM_OPS(proc_thermal_pci_pm, NULL, 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_MBOX) }, + { }, +}; + +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, +}; + +static int __init proc_thermal_init(void) +{ + return pci_register_driver(&proc_thermal_pci_driver); +} + +static void __exit proc_thermal_exit(void) +{ + pci_unregister_driver(&proc_thermal_pci_driver); +} + +module_init(proc_thermal_init); +module_exit(proc_thermal_exit); + +MODULE_AUTHOR("Srinivas Pandruvada "); +MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci_legacy.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci_legacy.c index 21cf7511d376..f5fc1791b11e 100644 --- a/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci_legacy.c +++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_device_pci_legacy.c @@ -118,7 +118,6 @@ static int proc_thermal_pci_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(proc_thermal_pci_pm, NULL, 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_MBOX) }, { PCI_DEVICE_DATA(INTEL, BDW_THERMAL, 0) }, { PCI_DEVICE_DATA(INTEL, BSW_THERMAL, 0) }, { PCI_DEVICE_DATA(INTEL, BXT0_THERMAL, 0) }, -- 2.27.0