1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Intel Speed Select Interface: Mbox via PCI Interface
4 * Copyright (c) 2019, Intel Corporation.
5 * All rights reserved.
6 *
7 * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
8 */
9
10 #include <linux/cpufeature.h>
11 #include <linux/module.h>
12 #include <linux/pci.h>
13 #include <linux/sched/signal.h>
14 #include <linux/uaccess.h>
15 #include <uapi/linux/isst_if.h>
16
17 #include "isst_if_common.h"
18
19 #define PUNIT_MAILBOX_DATA 0xA0
20 #define PUNIT_MAILBOX_INTERFACE 0xA4
21 #define PUNIT_MAILBOX_BUSY_BIT 31
22
23 /*
24 * Commands has variable amount of processing time. Most of the commands will
25 * be done in 0-3 tries, but some takes up to 50.
26 * The real processing time was observed as 25us for the most of the commands
27 * at 2GHz. It is possible to optimize this count taking samples on customer
28 * systems.
29 */
30 #define OS_MAILBOX_RETRY_COUNT 50
31
32 struct isst_if_device {
33 struct mutex mutex;
34 };
35
isst_if_mbox_cmd(struct pci_dev * pdev,struct isst_if_mbox_cmd * mbox_cmd)36 static int isst_if_mbox_cmd(struct pci_dev *pdev,
37 struct isst_if_mbox_cmd *mbox_cmd)
38 {
39 u32 retries, data;
40 int ret;
41
42 /* Poll for rb bit == 0 */
43 retries = OS_MAILBOX_RETRY_COUNT;
44 do {
45 ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
46 &data);
47 if (ret)
48 return ret;
49
50 if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
51 ret = -EBUSY;
52 continue;
53 }
54 ret = 0;
55 break;
56 } while (--retries);
57
58 if (ret)
59 return ret;
60
61 /* Write DATA register */
62 ret = pci_write_config_dword(pdev, PUNIT_MAILBOX_DATA,
63 mbox_cmd->req_data);
64 if (ret)
65 return ret;
66
67 /* Write command register */
68 data = BIT_ULL(PUNIT_MAILBOX_BUSY_BIT) |
69 (mbox_cmd->parameter & GENMASK_ULL(13, 0)) << 16 |
70 (mbox_cmd->sub_command << 8) |
71 mbox_cmd->command;
72
73 ret = pci_write_config_dword(pdev, PUNIT_MAILBOX_INTERFACE, data);
74 if (ret)
75 return ret;
76
77 /* Poll for rb bit == 0 */
78 retries = OS_MAILBOX_RETRY_COUNT;
79 do {
80 ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
81 &data);
82 if (ret)
83 return ret;
84
85 if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
86 ret = -EBUSY;
87 continue;
88 }
89
90 if (data & 0xff)
91 return -ENXIO;
92
93 ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_DATA, &data);
94 if (ret)
95 return ret;
96
97 mbox_cmd->resp_data = data;
98 ret = 0;
99 break;
100 } while (--retries);
101
102 return ret;
103 }
104
isst_if_mbox_proc_cmd(u8 * cmd_ptr,int * write_only,int resume)105 static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
106 {
107 struct isst_if_mbox_cmd *mbox_cmd;
108 struct isst_if_device *punit_dev;
109 struct pci_dev *pdev;
110 int ret;
111
112 mbox_cmd = (struct isst_if_mbox_cmd *)cmd_ptr;
113
114 if (isst_if_mbox_cmd_invalid(mbox_cmd))
115 return -EINVAL;
116
117 if (isst_if_mbox_cmd_set_req(mbox_cmd) && !capable(CAP_SYS_ADMIN))
118 return -EPERM;
119
120 pdev = isst_if_get_pci_dev(mbox_cmd->logical_cpu, 1, 30, 1);
121 if (!pdev)
122 return -EINVAL;
123
124 punit_dev = pci_get_drvdata(pdev);
125 if (!punit_dev)
126 return -EINVAL;
127
128 /*
129 * Basically we are allowing one complete mailbox transaction on
130 * a mapped PCI device at a time.
131 */
132 mutex_lock(&punit_dev->mutex);
133 ret = isst_if_mbox_cmd(pdev, mbox_cmd);
134 if (!ret && !resume && isst_if_mbox_cmd_set_req(mbox_cmd))
135 ret = isst_store_cmd(mbox_cmd->command,
136 mbox_cmd->sub_command,
137 mbox_cmd->logical_cpu, 1,
138 mbox_cmd->parameter,
139 mbox_cmd->req_data);
140 mutex_unlock(&punit_dev->mutex);
141 if (ret)
142 return ret;
143
144 *write_only = 0;
145
146 return 0;
147 }
148
149 static const struct pci_device_id isst_if_mbox_ids[] = {
150 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CFG_MBOX_DEVID_0)},
151 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CFG_MBOX_DEVID_1)},
152 { 0 },
153 };
154 MODULE_DEVICE_TABLE(pci, isst_if_mbox_ids);
155
isst_if_mbox_probe(struct pci_dev * pdev,const struct pci_device_id * ent)156 static int isst_if_mbox_probe(struct pci_dev *pdev,
157 const struct pci_device_id *ent)
158 {
159 struct isst_if_device *punit_dev;
160 struct isst_if_cmd_cb cb;
161 int ret;
162
163 punit_dev = devm_kzalloc(&pdev->dev, sizeof(*punit_dev), GFP_KERNEL);
164 if (!punit_dev)
165 return -ENOMEM;
166
167 ret = pcim_enable_device(pdev);
168 if (ret)
169 return ret;
170
171 mutex_init(&punit_dev->mutex);
172 pci_set_drvdata(pdev, punit_dev);
173
174 memset(&cb, 0, sizeof(cb));
175 cb.cmd_size = sizeof(struct isst_if_mbox_cmd);
176 cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
177 cb.cmd_callback = isst_if_mbox_proc_cmd;
178 cb.owner = THIS_MODULE;
179 ret = isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
180
181 if (ret)
182 mutex_destroy(&punit_dev->mutex);
183
184 return ret;
185 }
186
isst_if_mbox_remove(struct pci_dev * pdev)187 static void isst_if_mbox_remove(struct pci_dev *pdev)
188 {
189 struct isst_if_device *punit_dev;
190
191 punit_dev = pci_get_drvdata(pdev);
192 isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
193 mutex_destroy(&punit_dev->mutex);
194 }
195
isst_if_resume(struct device * device)196 static int __maybe_unused isst_if_resume(struct device *device)
197 {
198 isst_resume_common();
199 return 0;
200 }
201
202 static SIMPLE_DEV_PM_OPS(isst_if_pm_ops, NULL, isst_if_resume);
203
204 static struct pci_driver isst_if_pci_driver = {
205 .name = "isst_if_mbox_pci",
206 .id_table = isst_if_mbox_ids,
207 .probe = isst_if_mbox_probe,
208 .remove = isst_if_mbox_remove,
209 .driver.pm = &isst_if_pm_ops,
210 };
211
212 module_pci_driver(isst_if_pci_driver);
213
214 MODULE_LICENSE("GPL v2");
215 MODULE_DESCRIPTION("Intel speed select interface pci mailbox driver");
216