]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
Merge tag 'compiler-attributes-for-linus-v5.4' of git://github.com/ojeda/linux
[linux.git] / drivers / platform / x86 / intel_speed_select_if / isst_if_mbox_pci.c
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
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
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         { 0 },
152 };
153 MODULE_DEVICE_TABLE(pci, isst_if_mbox_ids);
154
155 static int isst_if_mbox_probe(struct pci_dev *pdev,
156                               const struct pci_device_id *ent)
157 {
158         struct isst_if_device *punit_dev;
159         struct isst_if_cmd_cb cb;
160         int ret;
161
162         punit_dev = devm_kzalloc(&pdev->dev, sizeof(*punit_dev), GFP_KERNEL);
163         if (!punit_dev)
164                 return -ENOMEM;
165
166         ret = pcim_enable_device(pdev);
167         if (ret)
168                 return ret;
169
170         mutex_init(&punit_dev->mutex);
171         pci_set_drvdata(pdev, punit_dev);
172
173         memset(&cb, 0, sizeof(cb));
174         cb.cmd_size = sizeof(struct isst_if_mbox_cmd);
175         cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
176         cb.cmd_callback = isst_if_mbox_proc_cmd;
177         cb.owner = THIS_MODULE;
178         ret = isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
179
180         if (ret)
181                 mutex_destroy(&punit_dev->mutex);
182
183         return ret;
184 }
185
186 static void isst_if_mbox_remove(struct pci_dev *pdev)
187 {
188         struct isst_if_device *punit_dev;
189
190         punit_dev = pci_get_drvdata(pdev);
191         isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
192         mutex_destroy(&punit_dev->mutex);
193 }
194
195 static int __maybe_unused isst_if_resume(struct device *device)
196 {
197         isst_resume_common();
198         return 0;
199 }
200
201 static SIMPLE_DEV_PM_OPS(isst_if_pm_ops, NULL, isst_if_resume);
202
203 static struct pci_driver isst_if_pci_driver = {
204         .name                   = "isst_if_mbox_pci",
205         .id_table               = isst_if_mbox_ids,
206         .probe                  = isst_if_mbox_probe,
207         .remove                 = isst_if_mbox_remove,
208         .driver.pm              = &isst_if_pm_ops,
209 };
210
211 module_pci_driver(isst_if_pci_driver);
212
213 MODULE_LICENSE("GPL v2");
214 MODULE_DESCRIPTION("Intel speed select interface pci mailbox driver");