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