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