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");