1729a866aSSrinivas Pandruvada // SPDX-License-Identifier: GPL-2.0-only
2729a866aSSrinivas Pandruvada /*
3729a866aSSrinivas Pandruvada  * processor thermal device mailbox driver for Workload type hints
4729a866aSSrinivas Pandruvada  * Copyright (c) 2020, Intel Corporation.
5729a866aSSrinivas Pandruvada  */
6729a866aSSrinivas Pandruvada 
7729a866aSSrinivas Pandruvada #include <linux/kernel.h>
8729a866aSSrinivas Pandruvada #include <linux/module.h>
9729a866aSSrinivas Pandruvada #include <linux/pci.h>
10c3f28096SLinus Torvalds #include <linux/io-64-nonatomic-lo-hi.h>
11729a866aSSrinivas Pandruvada #include "processor_thermal_device.h"
12729a866aSSrinivas Pandruvada 
13729a866aSSrinivas Pandruvada #define MBOX_CMD_WORKLOAD_TYPE_READ	0x0E
14729a866aSSrinivas Pandruvada #define MBOX_CMD_WORKLOAD_TYPE_WRITE	0x0F
15729a866aSSrinivas Pandruvada 
16729a866aSSrinivas Pandruvada #define MBOX_OFFSET_DATA		0x5810
17729a866aSSrinivas Pandruvada #define MBOX_OFFSET_INTERFACE		0x5818
18729a866aSSrinivas Pandruvada 
19729a866aSSrinivas Pandruvada #define MBOX_BUSY_BIT			31
20729a866aSSrinivas Pandruvada #define MBOX_RETRY_COUNT		100
21729a866aSSrinivas Pandruvada 
22729a866aSSrinivas Pandruvada #define MBOX_DATA_BIT_VALID		31
23729a866aSSrinivas Pandruvada #define MBOX_DATA_BIT_AC_DC		30
24729a866aSSrinivas Pandruvada 
25729a866aSSrinivas Pandruvada static DEFINE_MUTEX(mbox_lock);
26729a866aSSrinivas Pandruvada 
wait_for_mbox_ready(struct proc_thermal_device * proc_priv)27*8fdaa9a6SSumeet Pawnikar static int wait_for_mbox_ready(struct proc_thermal_device *proc_priv)
28729a866aSSrinivas Pandruvada {
29729a866aSSrinivas Pandruvada 	u32 retries, data;
30729a866aSSrinivas Pandruvada 	int ret;
31729a866aSSrinivas Pandruvada 
32729a866aSSrinivas Pandruvada 	/* Poll for rb bit == 0 */
33729a866aSSrinivas Pandruvada 	retries = MBOX_RETRY_COUNT;
34729a866aSSrinivas Pandruvada 	do {
35*8fdaa9a6SSumeet Pawnikar 		data = readl(proc_priv->mmio_base + MBOX_OFFSET_INTERFACE);
36729a866aSSrinivas Pandruvada 		if (data & BIT_ULL(MBOX_BUSY_BIT)) {
37729a866aSSrinivas Pandruvada 			ret = -EBUSY;
38729a866aSSrinivas Pandruvada 			continue;
39729a866aSSrinivas Pandruvada 		}
40729a866aSSrinivas Pandruvada 		ret = 0;
41729a866aSSrinivas Pandruvada 		break;
42729a866aSSrinivas Pandruvada 	} while (--retries);
43729a866aSSrinivas Pandruvada 
44*8fdaa9a6SSumeet Pawnikar 	return ret;
45*8fdaa9a6SSumeet Pawnikar }
46*8fdaa9a6SSumeet Pawnikar 
send_mbox_write_cmd(struct pci_dev * pdev,u16 id,u32 data)47*8fdaa9a6SSumeet Pawnikar static int send_mbox_write_cmd(struct pci_dev *pdev, u16 id, u32 data)
48*8fdaa9a6SSumeet Pawnikar {
49*8fdaa9a6SSumeet Pawnikar 	struct proc_thermal_device *proc_priv;
50*8fdaa9a6SSumeet Pawnikar 	u32 reg_data;
51*8fdaa9a6SSumeet Pawnikar 	int ret;
52*8fdaa9a6SSumeet Pawnikar 
53*8fdaa9a6SSumeet Pawnikar 	proc_priv = pci_get_drvdata(pdev);
54*8fdaa9a6SSumeet Pawnikar 
55*8fdaa9a6SSumeet Pawnikar 	mutex_lock(&mbox_lock);
56*8fdaa9a6SSumeet Pawnikar 
57*8fdaa9a6SSumeet Pawnikar 	ret = wait_for_mbox_ready(proc_priv);
58729a866aSSrinivas Pandruvada 	if (ret)
59729a866aSSrinivas Pandruvada 		goto unlock_mbox;
60729a866aSSrinivas Pandruvada 
61*8fdaa9a6SSumeet Pawnikar 	writel(data, (proc_priv->mmio_base + MBOX_OFFSET_DATA));
62729a866aSSrinivas Pandruvada 	/* Write command register */
63*8fdaa9a6SSumeet Pawnikar 	reg_data = BIT_ULL(MBOX_BUSY_BIT) | id;
64*8fdaa9a6SSumeet Pawnikar 	writel(reg_data, (proc_priv->mmio_base + MBOX_OFFSET_INTERFACE));
65729a866aSSrinivas Pandruvada 
66*8fdaa9a6SSumeet Pawnikar 	ret = wait_for_mbox_ready(proc_priv);
67729a866aSSrinivas Pandruvada 
68729a866aSSrinivas Pandruvada unlock_mbox:
69729a866aSSrinivas Pandruvada 	mutex_unlock(&mbox_lock);
70729a866aSSrinivas Pandruvada 	return ret;
71729a866aSSrinivas Pandruvada }
72729a866aSSrinivas Pandruvada 
send_mbox_read_cmd(struct pci_dev * pdev,u16 id,u64 * resp)73*8fdaa9a6SSumeet Pawnikar static int send_mbox_read_cmd(struct pci_dev *pdev, u16 id, u64 *resp)
74fb5a6ec8SSrinivas Pandruvada {
75*8fdaa9a6SSumeet Pawnikar 	struct proc_thermal_device *proc_priv;
76*8fdaa9a6SSumeet Pawnikar 	u32 reg_data;
77*8fdaa9a6SSumeet Pawnikar 	int ret;
78*8fdaa9a6SSumeet Pawnikar 
79*8fdaa9a6SSumeet Pawnikar 	proc_priv = pci_get_drvdata(pdev);
80*8fdaa9a6SSumeet Pawnikar 
81*8fdaa9a6SSumeet Pawnikar 	mutex_lock(&mbox_lock);
82*8fdaa9a6SSumeet Pawnikar 
83*8fdaa9a6SSumeet Pawnikar 	ret = wait_for_mbox_ready(proc_priv);
84*8fdaa9a6SSumeet Pawnikar 	if (ret)
85*8fdaa9a6SSumeet Pawnikar 		goto unlock_mbox;
86*8fdaa9a6SSumeet Pawnikar 
87*8fdaa9a6SSumeet Pawnikar 	/* Write command register */
88*8fdaa9a6SSumeet Pawnikar 	reg_data = BIT_ULL(MBOX_BUSY_BIT) | id;
89*8fdaa9a6SSumeet Pawnikar 	writel(reg_data, (proc_priv->mmio_base + MBOX_OFFSET_INTERFACE));
90*8fdaa9a6SSumeet Pawnikar 
91*8fdaa9a6SSumeet Pawnikar 	ret = wait_for_mbox_ready(proc_priv);
92*8fdaa9a6SSumeet Pawnikar 	if (ret)
93*8fdaa9a6SSumeet Pawnikar 		goto unlock_mbox;
94*8fdaa9a6SSumeet Pawnikar 
95*8fdaa9a6SSumeet Pawnikar 	if (id == MBOX_CMD_WORKLOAD_TYPE_READ)
96*8fdaa9a6SSumeet Pawnikar 		*resp = readl(proc_priv->mmio_base + MBOX_OFFSET_DATA);
97*8fdaa9a6SSumeet Pawnikar 	else
98*8fdaa9a6SSumeet Pawnikar 		*resp = readq(proc_priv->mmio_base + MBOX_OFFSET_DATA);
99*8fdaa9a6SSumeet Pawnikar 
100*8fdaa9a6SSumeet Pawnikar unlock_mbox:
101*8fdaa9a6SSumeet Pawnikar 	mutex_unlock(&mbox_lock);
102*8fdaa9a6SSumeet Pawnikar 	return ret;
103fb5a6ec8SSrinivas Pandruvada }
104*8fdaa9a6SSumeet Pawnikar 
processor_thermal_send_mbox_read_cmd(struct pci_dev * pdev,u16 id,u64 * resp)105*8fdaa9a6SSumeet Pawnikar int processor_thermal_send_mbox_read_cmd(struct pci_dev *pdev, u16 id, u64 *resp)
106*8fdaa9a6SSumeet Pawnikar {
107*8fdaa9a6SSumeet Pawnikar 	return send_mbox_read_cmd(pdev, id, resp);
108*8fdaa9a6SSumeet Pawnikar }
109*8fdaa9a6SSumeet Pawnikar EXPORT_SYMBOL_NS_GPL(processor_thermal_send_mbox_read_cmd, INT340X_THERMAL);
110*8fdaa9a6SSumeet Pawnikar 
processor_thermal_send_mbox_write_cmd(struct pci_dev * pdev,u16 id,u32 data)111*8fdaa9a6SSumeet Pawnikar int processor_thermal_send_mbox_write_cmd(struct pci_dev *pdev, u16 id, u32 data)
112*8fdaa9a6SSumeet Pawnikar {
113*8fdaa9a6SSumeet Pawnikar 	return send_mbox_write_cmd(pdev, id, data);
114*8fdaa9a6SSumeet Pawnikar }
115*8fdaa9a6SSumeet Pawnikar EXPORT_SYMBOL_NS_GPL(processor_thermal_send_mbox_write_cmd, INT340X_THERMAL);
116fb5a6ec8SSrinivas Pandruvada 
117729a866aSSrinivas Pandruvada /* List of workload types */
118729a866aSSrinivas Pandruvada static const char * const workload_types[] = {
119729a866aSSrinivas Pandruvada 	"none",
120729a866aSSrinivas Pandruvada 	"idle",
121729a866aSSrinivas Pandruvada 	"semi_active",
122ac35e6cbSSrinivas Pandruvada 	"bursty",
123729a866aSSrinivas Pandruvada 	"sustained",
124729a866aSSrinivas Pandruvada 	"battery_life",
125729a866aSSrinivas Pandruvada 	NULL
126729a866aSSrinivas Pandruvada };
127729a866aSSrinivas Pandruvada 
workload_available_types_show(struct device * dev,struct device_attribute * attr,char * buf)128729a866aSSrinivas Pandruvada static ssize_t workload_available_types_show(struct device *dev,
129729a866aSSrinivas Pandruvada 					       struct device_attribute *attr,
130729a866aSSrinivas Pandruvada 					       char *buf)
131729a866aSSrinivas Pandruvada {
132729a866aSSrinivas Pandruvada 	int i = 0;
133729a866aSSrinivas Pandruvada 	int ret = 0;
134729a866aSSrinivas Pandruvada 
135729a866aSSrinivas Pandruvada 	while (workload_types[i] != NULL)
136729a866aSSrinivas Pandruvada 		ret += sprintf(&buf[ret], "%s ", workload_types[i++]);
137729a866aSSrinivas Pandruvada 
138729a866aSSrinivas Pandruvada 	ret += sprintf(&buf[ret], "\n");
139729a866aSSrinivas Pandruvada 
140729a866aSSrinivas Pandruvada 	return ret;
141729a866aSSrinivas Pandruvada }
142729a866aSSrinivas Pandruvada 
143729a866aSSrinivas Pandruvada static DEVICE_ATTR_RO(workload_available_types);
144729a866aSSrinivas Pandruvada 
workload_type_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)145729a866aSSrinivas Pandruvada static ssize_t workload_type_store(struct device *dev,
146729a866aSSrinivas Pandruvada 				    struct device_attribute *attr,
147729a866aSSrinivas Pandruvada 				    const char *buf, size_t count)
148729a866aSSrinivas Pandruvada {
149729a866aSSrinivas Pandruvada 	struct pci_dev *pdev = to_pci_dev(dev);
150729a866aSSrinivas Pandruvada 	char str_preference[15];
151729a866aSSrinivas Pandruvada 	u32 data = 0;
152729a866aSSrinivas Pandruvada 	ssize_t ret;
153729a866aSSrinivas Pandruvada 
154729a866aSSrinivas Pandruvada 	ret = sscanf(buf, "%14s", str_preference);
155729a866aSSrinivas Pandruvada 	if (ret != 1)
156729a866aSSrinivas Pandruvada 		return -EINVAL;
157729a866aSSrinivas Pandruvada 
158729a866aSSrinivas Pandruvada 	ret = match_string(workload_types, -1, str_preference);
159729a866aSSrinivas Pandruvada 	if (ret < 0)
160729a866aSSrinivas Pandruvada 		return ret;
161729a866aSSrinivas Pandruvada 
162729a866aSSrinivas Pandruvada 	ret &= 0xff;
163729a866aSSrinivas Pandruvada 
164729a866aSSrinivas Pandruvada 	if (ret)
165729a866aSSrinivas Pandruvada 		data = BIT(MBOX_DATA_BIT_VALID) | BIT(MBOX_DATA_BIT_AC_DC);
166729a866aSSrinivas Pandruvada 
167729a866aSSrinivas Pandruvada 	data |= ret;
168729a866aSSrinivas Pandruvada 
169*8fdaa9a6SSumeet Pawnikar 	ret = send_mbox_write_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_WRITE, data);
170729a866aSSrinivas Pandruvada 	if (ret)
171729a866aSSrinivas Pandruvada 		return false;
172729a866aSSrinivas Pandruvada 
173729a866aSSrinivas Pandruvada 	return count;
174729a866aSSrinivas Pandruvada }
175729a866aSSrinivas Pandruvada 
workload_type_show(struct device * dev,struct device_attribute * attr,char * buf)176729a866aSSrinivas Pandruvada static ssize_t workload_type_show(struct device *dev,
177729a866aSSrinivas Pandruvada 				   struct device_attribute *attr,
178729a866aSSrinivas Pandruvada 				   char *buf)
179729a866aSSrinivas Pandruvada {
180729a866aSSrinivas Pandruvada 	struct pci_dev *pdev = to_pci_dev(dev);
181786293f6SSrinivas Pandruvada 	u64 cmd_resp;
182729a866aSSrinivas Pandruvada 	int ret;
183729a866aSSrinivas Pandruvada 
184*8fdaa9a6SSumeet Pawnikar 	ret = send_mbox_read_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, &cmd_resp);
185729a866aSSrinivas Pandruvada 	if (ret)
186729a866aSSrinivas Pandruvada 		return false;
187729a866aSSrinivas Pandruvada 
188729a866aSSrinivas Pandruvada 	cmd_resp &= 0xff;
189729a866aSSrinivas Pandruvada 
190729a866aSSrinivas Pandruvada 	if (cmd_resp > ARRAY_SIZE(workload_types) - 1)
191729a866aSSrinivas Pandruvada 		return -EINVAL;
192729a866aSSrinivas Pandruvada 
193729a866aSSrinivas Pandruvada 	return sprintf(buf, "%s\n", workload_types[cmd_resp]);
194729a866aSSrinivas Pandruvada }
195729a866aSSrinivas Pandruvada 
196729a866aSSrinivas Pandruvada static DEVICE_ATTR_RW(workload_type);
197729a866aSSrinivas Pandruvada 
198729a866aSSrinivas Pandruvada static struct attribute *workload_req_attrs[] = {
199729a866aSSrinivas Pandruvada 	&dev_attr_workload_available_types.attr,
200729a866aSSrinivas Pandruvada 	&dev_attr_workload_type.attr,
201729a866aSSrinivas Pandruvada 	NULL
202729a866aSSrinivas Pandruvada };
203729a866aSSrinivas Pandruvada 
204729a866aSSrinivas Pandruvada static const struct attribute_group workload_req_attribute_group = {
205729a866aSSrinivas Pandruvada 	.attrs = workload_req_attrs,
206729a866aSSrinivas Pandruvada 	.name = "workload_request"
207729a866aSSrinivas Pandruvada };
208729a866aSSrinivas Pandruvada 
209729a866aSSrinivas Pandruvada static bool workload_req_created;
210729a866aSSrinivas Pandruvada 
proc_thermal_mbox_add(struct pci_dev * pdev,struct proc_thermal_device * proc_priv)211729a866aSSrinivas Pandruvada int proc_thermal_mbox_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)
212729a866aSSrinivas Pandruvada {
213786293f6SSrinivas Pandruvada 	u64 cmd_resp;
214729a866aSSrinivas Pandruvada 	int ret;
215729a866aSSrinivas Pandruvada 
216729a866aSSrinivas Pandruvada 	/* Check if there is a mailbox support, if fails return success */
217*8fdaa9a6SSumeet Pawnikar 	ret = send_mbox_read_cmd(pdev, MBOX_CMD_WORKLOAD_TYPE_READ, &cmd_resp);
218729a866aSSrinivas Pandruvada 	if (ret)
219729a866aSSrinivas Pandruvada 		return 0;
220729a866aSSrinivas Pandruvada 
221729a866aSSrinivas Pandruvada 	ret = sysfs_create_group(&pdev->dev.kobj, &workload_req_attribute_group);
222729a866aSSrinivas Pandruvada 	if (ret)
223729a866aSSrinivas Pandruvada 		return ret;
224729a866aSSrinivas Pandruvada 
225729a866aSSrinivas Pandruvada 	workload_req_created = true;
226729a866aSSrinivas Pandruvada 
227729a866aSSrinivas Pandruvada 	return 0;
228729a866aSSrinivas Pandruvada }
229729a866aSSrinivas Pandruvada EXPORT_SYMBOL_GPL(proc_thermal_mbox_add);
230729a866aSSrinivas Pandruvada 
proc_thermal_mbox_remove(struct pci_dev * pdev)231729a866aSSrinivas Pandruvada void proc_thermal_mbox_remove(struct pci_dev *pdev)
232729a866aSSrinivas Pandruvada {
233729a866aSSrinivas Pandruvada 	if (workload_req_created)
234729a866aSSrinivas Pandruvada 		sysfs_remove_group(&pdev->dev.kobj, &workload_req_attribute_group);
235729a866aSSrinivas Pandruvada 
236729a866aSSrinivas Pandruvada 	workload_req_created = false;
237729a866aSSrinivas Pandruvada 
238729a866aSSrinivas Pandruvada }
239729a866aSSrinivas Pandruvada EXPORT_SYMBOL_GPL(proc_thermal_mbox_remove);
240729a866aSSrinivas Pandruvada 
241729a866aSSrinivas Pandruvada MODULE_LICENSE("GPL v2");
242