• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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