cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

processor_thermal_mbox.c (5285B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * processor thermal device mailbox driver for Workload type hints
      4 * Copyright (c) 2020, Intel Corporation.
      5 */
      6
      7#include <linux/kernel.h>
      8#include <linux/module.h>
      9#include <linux/pci.h>
     10#include <linux/io-64-nonatomic-lo-hi.h>
     11#include "processor_thermal_device.h"
     12
     13#define MBOX_CMD_WORKLOAD_TYPE_READ	0x0E
     14#define MBOX_CMD_WORKLOAD_TYPE_WRITE	0x0F
     15
     16#define MBOX_OFFSET_DATA		0x5810
     17#define MBOX_OFFSET_INTERFACE		0x5818
     18
     19#define MBOX_BUSY_BIT			31
     20#define MBOX_RETRY_COUNT		100
     21
     22#define MBOX_DATA_BIT_VALID		31
     23#define MBOX_DATA_BIT_AC_DC		30
     24
     25static DEFINE_MUTEX(mbox_lock);
     26
     27static int wait_for_mbox_ready(struct proc_thermal_device *proc_priv)
     28{
     29	u32 retries, data;
     30	int ret;
     31
     32	/* Poll for rb bit == 0 */
     33	retries = MBOX_RETRY_COUNT;
     34	do {
     35		data = readl(proc_priv->mmio_base + MBOX_OFFSET_INTERFACE);
     36		if (data & BIT_ULL(MBOX_BUSY_BIT)) {
     37			ret = -EBUSY;
     38			continue;
     39		}
     40		ret = 0;
     41		break;
     42	} while (--retries);
     43
     44	return ret;
     45}
     46
     47static int send_mbox_write_cmd(struct pci_dev *pdev, u16 id, u32 data)
     48{
     49	struct proc_thermal_device *proc_priv;
     50	u32 reg_data;
     51	int ret;
     52
     53	proc_priv = pci_get_drvdata(pdev);
     54
     55	mutex_lock(&mbox_lock);
     56
     57	ret = wait_for_mbox_ready(proc_priv);
     58	if (ret)
     59		goto unlock_mbox;
     60
     61	writel(data, (proc_priv->mmio_base + MBOX_OFFSET_DATA));
     62	/* Write command register */
     63	reg_data = BIT_ULL(MBOX_BUSY_BIT) | id;
     64	writel(reg_data, (proc_priv->mmio_base + MBOX_OFFSET_INTERFACE));
     65
     66	ret = wait_for_mbox_ready(proc_priv);
     67
     68unlock_mbox:
     69	mutex_unlock(&mbox_lock);
     70	return ret;
     71}
     72
     73static int send_mbox_read_cmd(struct pci_dev *pdev, u16 id, u64 *resp)
     74{
     75	struct proc_thermal_device *proc_priv;
     76	u32 reg_data;
     77	int ret;
     78
     79	proc_priv = pci_get_drvdata(pdev);
     80
     81	mutex_lock(&mbox_lock);
     82
     83	ret = wait_for_mbox_ready(proc_priv);
     84	if (ret)
     85		goto unlock_mbox;
     86
     87	/* Write command register */
     88	reg_data = BIT_ULL(MBOX_BUSY_BIT) | id;
     89	writel(reg_data, (proc_priv->mmio_base + MBOX_OFFSET_INTERFACE));
     90
     91	ret = wait_for_mbox_ready(proc_priv);
     92	if (ret)
     93		goto unlock_mbox;
     94
     95	if (id == MBOX_CMD_WORKLOAD_TYPE_READ)
     96		*resp = readl(proc_priv->mmio_base + MBOX_OFFSET_DATA);
     97	else
     98		*resp = readq(proc_priv->mmio_base + MBOX_OFFSET_DATA);
     99
    100unlock_mbox:
    101	mutex_unlock(&mbox_lock);
    102	return ret;
    103}
    104
    105int processor_thermal_send_mbox_read_cmd(struct pci_dev *pdev, u16 id, u64 *resp)
    106{
    107	return send_mbox_read_cmd(pdev, id, resp);
    108}
    109EXPORT_SYMBOL_NS_GPL(processor_thermal_send_mbox_read_cmd, INT340X_THERMAL);
    110
    111int processor_thermal_send_mbox_write_cmd(struct pci_dev *pdev, u16 id, u32 data)
    112{
    113	return send_mbox_write_cmd(pdev, id, data);
    114}
    115EXPORT_SYMBOL_NS_GPL(processor_thermal_send_mbox_write_cmd, INT340X_THERMAL);
    116
    117/* List of workload types */
    118static const char * const workload_types[] = {
    119	"none",
    120	"idle",
    121	"semi_active",
    122	"bursty",
    123	"sustained",
    124	"battery_life",
    125	NULL
    126};
    127
    128static ssize_t workload_available_types_show(struct device *dev,
    129					       struct device_attribute *attr,
    130					       char *buf)
    131{
    132	int i = 0;
    133	int ret = 0;
    134
    135	while (workload_types[i] != NULL)
    136		ret += sprintf(&buf[ret], "%s ", workload_types[i++]);
    137
    138	ret += sprintf(&buf[ret], "\n");
    139
    140	return ret;
    141}
    142
    143static DEVICE_ATTR_RO(workload_available_types);
    144
    145static ssize_t workload_type_store(struct device *dev,
    146				    struct device_attribute *attr,
    147				    const char *buf, size_t count)
    148{
    149	struct pci_dev *pdev = to_pci_dev(dev);
    150	char str_preference[15];
    151	u32 data = 0;
    152	ssize_t ret;
    153
    154	ret = sscanf(buf, "%14s", str_preference);
    155	if (ret != 1)
    156		return -EINVAL;
    157
    158	ret = match_string(workload_types, -1, str_preference);
    159	if (ret < 0)
    160		return ret;
    161
    162	ret &= 0xff;
    163
    164	if (ret)
    165		data = BIT(MBOX_DATA_BIT_VALID) | BIT(MBOX_DATA_BIT_AC_DC);
    166
    167	data |= ret;
    168
    169	ret = send_mbox_write_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_WRITE, data);
    170	if (ret)
    171		return false;
    172
    173	return count;
    174}
    175
    176static ssize_t workload_type_show(struct device *dev,
    177				   struct device_attribute *attr,
    178				   char *buf)
    179{
    180	struct pci_dev *pdev = to_pci_dev(dev);
    181	u64 cmd_resp;
    182	int ret;
    183
    184	ret = send_mbox_read_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, &cmd_resp);
    185	if (ret)
    186		return false;
    187
    188	cmd_resp &= 0xff;
    189
    190	if (cmd_resp > ARRAY_SIZE(workload_types) - 1)
    191		return -EINVAL;
    192
    193	return sprintf(buf, "%s\n", workload_types[cmd_resp]);
    194}
    195
    196static DEVICE_ATTR_RW(workload_type);
    197
    198static struct attribute *workload_req_attrs[] = {
    199	&dev_attr_workload_available_types.attr,
    200	&dev_attr_workload_type.attr,
    201	NULL
    202};
    203
    204static const struct attribute_group workload_req_attribute_group = {
    205	.attrs = workload_req_attrs,
    206	.name = "workload_request"
    207};
    208
    209static bool workload_req_created;
    210
    211int proc_thermal_mbox_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
    212{
    213	u64 cmd_resp;
    214	int ret;
    215
    216	/* Check if there is a mailbox support, if fails return success */
    217	ret = send_mbox_read_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, &cmd_resp);
    218	if (ret)
    219		return 0;
    220
    221	ret = sysfs_create_group(&pdev->dev.kobj, &workload_req_attribute_group);
    222	if (ret)
    223		return ret;
    224
    225	workload_req_created = true;
    226
    227	return 0;
    228}
    229EXPORT_SYMBOL_GPL(proc_thermal_mbox_add);
    230
    231void proc_thermal_mbox_remove(struct pci_dev *pdev)
    232{
    233	if (workload_req_created)
    234		sysfs_remove_group(&pdev->dev.kobj, &workload_req_attribute_group);
    235
    236	workload_req_created = false;
    237
    238}
    239EXPORT_SYMBOL_GPL(proc_thermal_mbox_remove);
    240
    241MODULE_LICENSE("GPL v2");