| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * PCIe cooling device | |
| * | |
| * Copyright (C) 2023-2024 Intel Corporation | |
| */ | |
| #include <linux/build_bug.h> | |
| #include <linux/cleanup.h> | |
| #include <linux/err.h> | |
| #include <linux/module.h> | |
| #include <linux/pci.h> | |
| #include <linux/pci-bwctrl.h> | |
| #include <linux/slab.h> | |
| #include <linux/sprintf.h> | |
| #include <linux/thermal.h> | |
| #define COOLING_DEV_TYPE_PREFIX "PCIe_Port_Link_Speed_" | |
| static int pcie_cooling_get_max_level(struct thermal_cooling_device *cdev, unsigned long *state) | |
| { | |
| struct pci_dev *port = cdev->devdata; | |
| /* cooling state 0 is same as the maximum PCIe speed */ | |
| *state = port->subordinate->max_bus_speed - PCIE_SPEED_2_5GT; | |
| return 0; | |
| } | |
| static int pcie_cooling_get_cur_level(struct thermal_cooling_device *cdev, unsigned long *state) | |
| { | |
| struct pci_dev *port = cdev->devdata; | |
| /* cooling state 0 is same as the maximum PCIe speed */ | |
| *state = cdev->max_state - (port->subordinate->cur_bus_speed - PCIE_SPEED_2_5GT); | |
| return 0; | |
| } | |
| static int pcie_cooling_set_cur_level(struct thermal_cooling_device *cdev, unsigned long state) | |
| { | |
| struct pci_dev *port = cdev->devdata; | |
| enum pci_bus_speed speed; | |
| /* cooling state 0 is same as the maximum PCIe speed */ | |
| speed = (cdev->max_state - state) + PCIE_SPEED_2_5GT; | |
| return pcie_set_target_speed(port, speed, true); | |
| } | |
| static struct thermal_cooling_device_ops pcie_cooling_ops = { | |
| .get_max_state = pcie_cooling_get_max_level, | |
| .get_cur_state = pcie_cooling_get_cur_level, | |
| .set_cur_state = pcie_cooling_set_cur_level, | |
| }; | |
| struct thermal_cooling_device *pcie_cooling_device_register(struct pci_dev *port) | |
| { | |
| char *name __free(kfree) = | |
| kasprintf(GFP_KERNEL, COOLING_DEV_TYPE_PREFIX "%s", pci_name(port)); | |
| if (!name) | |
| return ERR_PTR(-ENOMEM); | |
| return thermal_cooling_device_register(name, port, &pcie_cooling_ops); | |
| } | |
| void pcie_cooling_device_unregister(struct thermal_cooling_device *cdev) | |
| { | |
| thermal_cooling_device_unregister(cdev); | |
| } | |
| /* For bus_speed <-> state arithmetic */ | |
| static_assert(PCIE_SPEED_2_5GT + 1 == PCIE_SPEED_5_0GT); | |
| static_assert(PCIE_SPEED_5_0GT + 1 == PCIE_SPEED_8_0GT); | |
| static_assert(PCIE_SPEED_8_0GT + 1 == PCIE_SPEED_16_0GT); | |
| static_assert(PCIE_SPEED_16_0GT + 1 == PCIE_SPEED_32_0GT); | |
| static_assert(PCIE_SPEED_32_0GT + 1 == PCIE_SPEED_64_0GT); | |
| MODULE_AUTHOR("Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>"); | |
| MODULE_DESCRIPTION("PCIe cooling driver"); |