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