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, u64 *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 		ret = 0;
72 
73 		if (!cmd_resp)
74 			break;
75 
76 		if (cmd_id == MBOX_CMD_WORKLOAD_TYPE_READ)
77 			*cmd_resp = readl((void __iomem *) (proc_priv->mmio_base + MBOX_OFFSET_DATA));
78 		else
79 			*cmd_resp = readq((void __iomem *) (proc_priv->mmio_base + MBOX_OFFSET_DATA));
80 
81 		break;
82 	} while (--retries);
83 
84 unlock_mbox:
85 	mutex_unlock(&mbox_lock);
86 	return ret;
87 }
88 
89 int processor_thermal_send_mbox_cmd(struct pci_dev *pdev, u16 cmd_id, u32 cmd_data, u64 *cmd_resp)
90 {
91 	return send_mbox_cmd(pdev, cmd_id, cmd_data, cmd_resp);
92 }
93 EXPORT_SYMBOL_GPL(processor_thermal_send_mbox_cmd);
94 
95 /* List of workload types */
96 static const char * const workload_types[] = {
97 	"none",
98 	"idle",
99 	"semi_active",
100 	"bursty",
101 	"sustained",
102 	"battery_life",
103 	NULL
104 };
105 
106 
107 static ssize_t workload_available_types_show(struct device *dev,
108 					       struct device_attribute *attr,
109 					       char *buf)
110 {
111 	int i = 0;
112 	int ret = 0;
113 
114 	while (workload_types[i] != NULL)
115 		ret += sprintf(&buf[ret], "%s ", workload_types[i++]);
116 
117 	ret += sprintf(&buf[ret], "\n");
118 
119 	return ret;
120 }
121 
122 static DEVICE_ATTR_RO(workload_available_types);
123 
124 static ssize_t workload_type_store(struct device *dev,
125 				    struct device_attribute *attr,
126 				    const char *buf, size_t count)
127 {
128 	struct pci_dev *pdev = to_pci_dev(dev);
129 	char str_preference[15];
130 	u32 data = 0;
131 	ssize_t ret;
132 
133 	ret = sscanf(buf, "%14s", str_preference);
134 	if (ret != 1)
135 		return -EINVAL;
136 
137 	ret = match_string(workload_types, -1, str_preference);
138 	if (ret < 0)
139 		return ret;
140 
141 	ret &= 0xff;
142 
143 	if (ret)
144 		data = BIT(MBOX_DATA_BIT_VALID) | BIT(MBOX_DATA_BIT_AC_DC);
145 
146 	data |= ret;
147 
148 	ret = send_mbox_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_WRITE, data, NULL);
149 	if (ret)
150 		return false;
151 
152 	return count;
153 }
154 
155 static ssize_t workload_type_show(struct device *dev,
156 				   struct device_attribute *attr,
157 				   char *buf)
158 {
159 	struct pci_dev *pdev = to_pci_dev(dev);
160 	u64 cmd_resp;
161 	int ret;
162 
163 	ret = send_mbox_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, 0, &cmd_resp);
164 	if (ret)
165 		return false;
166 
167 	cmd_resp &= 0xff;
168 
169 	if (cmd_resp > ARRAY_SIZE(workload_types) - 1)
170 		return -EINVAL;
171 
172 	return sprintf(buf, "%s\n", workload_types[cmd_resp]);
173 }
174 
175 static DEVICE_ATTR_RW(workload_type);
176 
177 static struct attribute *workload_req_attrs[] = {
178 	&dev_attr_workload_available_types.attr,
179 	&dev_attr_workload_type.attr,
180 	NULL
181 };
182 
183 static const struct attribute_group workload_req_attribute_group = {
184 	.attrs = workload_req_attrs,
185 	.name = "workload_request"
186 };
187 
188 
189 
190 static bool workload_req_created;
191 
192 int proc_thermal_mbox_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
193 {
194 	u64 cmd_resp;
195 	int ret;
196 
197 	/* Check if there is a mailbox support, if fails return success */
198 	ret = send_mbox_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, 0, &cmd_resp);
199 	if (ret)
200 		return 0;
201 
202 	ret = sysfs_create_group(&pdev->dev.kobj, &workload_req_attribute_group);
203 	if (ret)
204 		return ret;
205 
206 	workload_req_created = true;
207 
208 	return 0;
209 }
210 EXPORT_SYMBOL_GPL(proc_thermal_mbox_add);
211 
212 void proc_thermal_mbox_remove(struct pci_dev *pdev)
213 {
214 	if (workload_req_created)
215 		sysfs_remove_group(&pdev->dev.kobj, &workload_req_attribute_group);
216 
217 	workload_req_created = false;
218 
219 }
220 EXPORT_SYMBOL_GPL(proc_thermal_mbox_remove);
221 
222 MODULE_LICENSE("GPL v2");
223