Commit | Line | Data |
---|---|---|
dd0f3683 TS |
1 | /* |
2 | This file is provided under a dual BSD/GPLv2 license. When using or | |
3 | redistributing this file, you may do so under either license. | |
4 | ||
5 | GPL LICENSE SUMMARY | |
6 | Copyright(c) 2014 Intel Corporation. | |
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of version 2 of the GNU General Public License as | |
9 | published by the Free Software Foundation. | |
10 | ||
11 | This program is distributed in the hope that it will be useful, but | |
12 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | General Public License for more details. | |
15 | ||
16 | Contact Information: | |
17 | qat-linux@intel.com | |
18 | ||
19 | BSD LICENSE | |
20 | Copyright(c) 2014 Intel Corporation. | |
21 | Redistribution and use in source and binary forms, with or without | |
22 | modification, are permitted provided that the following conditions | |
23 | are met: | |
24 | ||
25 | * Redistributions of source code must retain the above copyright | |
26 | notice, this list of conditions and the following disclaimer. | |
27 | * Redistributions in binary form must reproduce the above copyright | |
28 | notice, this list of conditions and the following disclaimer in | |
29 | the documentation and/or other materials provided with the | |
30 | distribution. | |
31 | * Neither the name of Intel Corporation nor the names of its | |
32 | contributors may be used to endorse or promote products derived | |
33 | from this software without specific prior written permission. | |
34 | ||
35 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
36 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
37 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
38 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
39 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
40 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
41 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
42 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
43 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
44 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
45 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
46 | */ | |
47 | #include <linux/kernel.h> | |
48 | #include <linux/init.h> | |
49 | #include <linux/types.h> | |
50 | #include <linux/pci.h> | |
51 | #include <linux/slab.h> | |
52 | #include <linux/errno.h> | |
53 | #include <linux/interrupt.h> | |
d6064165 | 54 | #include <linux/workqueue.h> |
1a72d3a6 TS |
55 | #include "adf_accel_devices.h" |
56 | #include "adf_common_drv.h" | |
57 | #include "adf_cfg.h" | |
58 | #include "adf_cfg_strings.h" | |
59 | #include "adf_cfg_common.h" | |
60 | #include "adf_transport_access_macros.h" | |
61 | #include "adf_transport_internal.h" | |
62 | #include "adf_pf2vf_msg.h" | |
63 | ||
64 | #define ADF_VINTSOU_OFFSET 0x204 | |
65 | #define ADF_VINTSOU_BUN BIT(0) | |
66 | #define ADF_VINTSOU_PF2VF BIT(1) | |
dd0f3683 | 67 | |
d6064165 TS |
68 | static struct workqueue_struct *adf_vf_stop_wq; |
69 | ||
70 | struct adf_vf_stop_data { | |
71 | struct adf_accel_dev *accel_dev; | |
72 | struct work_struct work; | |
73 | }; | |
74 | ||
dd0f3683 TS |
75 | static int adf_enable_msi(struct adf_accel_dev *accel_dev) |
76 | { | |
77 | struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev; | |
78 | int stat = pci_enable_msi(pci_dev_info->pci_dev); | |
79 | ||
80 | if (stat) { | |
81 | dev_err(&GET_DEV(accel_dev), | |
82 | "Failed to enable MSI interrupts\n"); | |
83 | return stat; | |
84 | } | |
85 | ||
86 | accel_dev->vf.irq_name = kzalloc(ADF_MAX_MSIX_VECTOR_NAME, GFP_KERNEL); | |
87 | if (!accel_dev->vf.irq_name) | |
88 | return -ENOMEM; | |
89 | ||
90 | return stat; | |
91 | } | |
92 | ||
93 | static void adf_disable_msi(struct adf_accel_dev *accel_dev) | |
94 | { | |
95 | struct pci_dev *pdev = accel_to_pci_dev(accel_dev); | |
96 | ||
97 | kfree(accel_dev->vf.irq_name); | |
98 | pci_disable_msi(pdev); | |
99 | } | |
100 | ||
d6064165 TS |
101 | static void adf_dev_stop_async(struct work_struct *work) |
102 | { | |
103 | struct adf_vf_stop_data *stop_data = | |
104 | container_of(work, struct adf_vf_stop_data, work); | |
105 | struct adf_accel_dev *accel_dev = stop_data->accel_dev; | |
106 | ||
107 | adf_dev_stop(accel_dev); | |
108 | adf_dev_shutdown(accel_dev); | |
109 | ||
110 | /* Re-enable PF2VF interrupts */ | |
111 | adf_enable_pf2vf_interrupts(accel_dev); | |
112 | kfree(stop_data); | |
113 | } | |
114 | ||
dd0f3683 TS |
115 | static void adf_pf2vf_bh_handler(void *data) |
116 | { | |
117 | struct adf_accel_dev *accel_dev = data; | |
1a72d3a6 TS |
118 | struct adf_hw_device_data *hw_data = accel_dev->hw_device; |
119 | struct adf_bar *pmisc = | |
120 | &GET_BARS(accel_dev)[hw_data->get_misc_bar_id(hw_data)]; | |
121 | void __iomem *pmisc_bar_addr = pmisc->virt_addr; | |
dd0f3683 TS |
122 | u32 msg; |
123 | ||
124 | /* Read the message from PF */ | |
1a72d3a6 | 125 | msg = ADF_CSR_RD(pmisc_bar_addr, hw_data->get_pf2vf_offset(0)); |
dd0f3683 TS |
126 | |
127 | if (!(msg & ADF_PF2VF_MSGORIGIN_SYSTEM)) | |
128 | /* Ignore legacy non-system (non-kernel) PF2VF messages */ | |
129 | goto err; | |
130 | ||
131 | switch ((msg & ADF_PF2VF_MSGTYPE_MASK) >> ADF_PF2VF_MSGTYPE_SHIFT) { | |
d6064165 TS |
132 | case ADF_PF2VF_MSGTYPE_RESTARTING: { |
133 | struct adf_vf_stop_data *stop_data; | |
134 | ||
dd0f3683 TS |
135 | dev_dbg(&GET_DEV(accel_dev), |
136 | "Restarting msg received from PF 0x%x\n", msg); | |
d6064165 | 137 | |
25c6ffb2 TS |
138 | clear_bit(ADF_STATUS_PF_RUNNING, &accel_dev->status); |
139 | ||
d6064165 TS |
140 | stop_data = kzalloc(sizeof(*stop_data), GFP_ATOMIC); |
141 | if (!stop_data) { | |
142 | dev_err(&GET_DEV(accel_dev), | |
143 | "Couldn't schedule stop for vf_%d\n", | |
144 | accel_dev->accel_id); | |
145 | return; | |
146 | } | |
147 | stop_data->accel_dev = accel_dev; | |
148 | INIT_WORK(&stop_data->work, adf_dev_stop_async); | |
149 | queue_work(adf_vf_stop_wq, &stop_data->work); | |
150 | /* To ack, clear the PF2VFINT bit */ | |
151 | msg &= ~BIT(0); | |
152 | ADF_CSR_WR(pmisc_bar_addr, hw_data->get_pf2vf_offset(0), msg); | |
153 | return; | |
154 | } | |
dd0f3683 TS |
155 | case ADF_PF2VF_MSGTYPE_VERSION_RESP: |
156 | dev_dbg(&GET_DEV(accel_dev), | |
157 | "Version resp received from PF 0x%x\n", msg); | |
158 | accel_dev->vf.pf_version = | |
159 | (msg & ADF_PF2VF_VERSION_RESP_VERS_MASK) >> | |
160 | ADF_PF2VF_VERSION_RESP_VERS_SHIFT; | |
161 | accel_dev->vf.compatible = | |
162 | (msg & ADF_PF2VF_VERSION_RESP_RESULT_MASK) >> | |
163 | ADF_PF2VF_VERSION_RESP_RESULT_SHIFT; | |
164 | complete(&accel_dev->vf.iov_msg_completion); | |
165 | break; | |
166 | default: | |
167 | goto err; | |
168 | } | |
169 | ||
170 | /* To ack, clear the PF2VFINT bit */ | |
1a72d3a6 TS |
171 | msg &= ~BIT(0); |
172 | ADF_CSR_WR(pmisc_bar_addr, hw_data->get_pf2vf_offset(0), msg); | |
dd0f3683 TS |
173 | |
174 | /* Re-enable PF2VF interrupts */ | |
175 | adf_enable_pf2vf_interrupts(accel_dev); | |
176 | return; | |
177 | err: | |
178 | dev_err(&GET_DEV(accel_dev), | |
179 | "Unknown message from PF (0x%x); leaving PF2VF ints disabled\n", | |
180 | msg); | |
181 | } | |
182 | ||
183 | static int adf_setup_pf2vf_bh(struct adf_accel_dev *accel_dev) | |
184 | { | |
185 | tasklet_init(&accel_dev->vf.pf2vf_bh_tasklet, | |
186 | (void *)adf_pf2vf_bh_handler, (unsigned long)accel_dev); | |
187 | ||
188 | mutex_init(&accel_dev->vf.vf2pf_lock); | |
189 | return 0; | |
190 | } | |
191 | ||
192 | static void adf_cleanup_pf2vf_bh(struct adf_accel_dev *accel_dev) | |
193 | { | |
194 | tasklet_disable(&accel_dev->vf.pf2vf_bh_tasklet); | |
195 | tasklet_kill(&accel_dev->vf.pf2vf_bh_tasklet); | |
196 | mutex_destroy(&accel_dev->vf.vf2pf_lock); | |
197 | } | |
198 | ||
199 | static irqreturn_t adf_isr(int irq, void *privdata) | |
200 | { | |
201 | struct adf_accel_dev *accel_dev = privdata; | |
1a72d3a6 TS |
202 | struct adf_hw_device_data *hw_data = accel_dev->hw_device; |
203 | struct adf_bar *pmisc = | |
204 | &GET_BARS(accel_dev)[hw_data->get_misc_bar_id(hw_data)]; | |
205 | void __iomem *pmisc_bar_addr = pmisc->virt_addr; | |
dd0f3683 TS |
206 | u32 v_int; |
207 | ||
208 | /* Read VF INT source CSR to determine the source of VF interrupt */ | |
1a72d3a6 | 209 | v_int = ADF_CSR_RD(pmisc_bar_addr, ADF_VINTSOU_OFFSET); |
dd0f3683 TS |
210 | |
211 | /* Check for PF2VF interrupt */ | |
1a72d3a6 | 212 | if (v_int & ADF_VINTSOU_PF2VF) { |
dd0f3683 TS |
213 | /* Disable PF to VF interrupt */ |
214 | adf_disable_pf2vf_interrupts(accel_dev); | |
215 | ||
216 | /* Schedule tasklet to handle interrupt BH */ | |
217 | tasklet_hi_schedule(&accel_dev->vf.pf2vf_bh_tasklet); | |
218 | return IRQ_HANDLED; | |
219 | } | |
220 | ||
221 | /* Check bundle interrupt */ | |
1a72d3a6 | 222 | if (v_int & ADF_VINTSOU_BUN) { |
dd0f3683 TS |
223 | struct adf_etr_data *etr_data = accel_dev->transport; |
224 | struct adf_etr_bank_data *bank = &etr_data->banks[0]; | |
225 | ||
226 | /* Disable Flag and Coalesce Ring Interrupts */ | |
227 | WRITE_CSR_INT_FLAG_AND_COL(bank->csr_addr, bank->bank_number, | |
228 | 0); | |
229 | tasklet_hi_schedule(&bank->resp_handler); | |
230 | return IRQ_HANDLED; | |
231 | } | |
232 | ||
233 | return IRQ_NONE; | |
234 | } | |
235 | ||
236 | static int adf_request_msi_irq(struct adf_accel_dev *accel_dev) | |
237 | { | |
238 | struct pci_dev *pdev = accel_to_pci_dev(accel_dev); | |
239 | unsigned int cpu; | |
240 | int ret; | |
241 | ||
242 | snprintf(accel_dev->vf.irq_name, ADF_MAX_MSIX_VECTOR_NAME, | |
243 | "qat_%02x:%02d.%02d", pdev->bus->number, PCI_SLOT(pdev->devfn), | |
244 | PCI_FUNC(pdev->devfn)); | |
245 | ret = request_irq(pdev->irq, adf_isr, 0, accel_dev->vf.irq_name, | |
246 | (void *)accel_dev); | |
247 | if (ret) { | |
248 | dev_err(&GET_DEV(accel_dev), "failed to enable irq for %s\n", | |
249 | accel_dev->vf.irq_name); | |
250 | return ret; | |
251 | } | |
252 | cpu = accel_dev->accel_id % num_online_cpus(); | |
253 | irq_set_affinity_hint(pdev->irq, get_cpu_mask(cpu)); | |
254 | ||
255 | return ret; | |
256 | } | |
257 | ||
258 | static int adf_setup_bh(struct adf_accel_dev *accel_dev) | |
259 | { | |
260 | struct adf_etr_data *priv_data = accel_dev->transport; | |
261 | ||
262 | tasklet_init(&priv_data->banks[0].resp_handler, adf_response_handler, | |
263 | (unsigned long)priv_data->banks); | |
264 | return 0; | |
265 | } | |
266 | ||
267 | static void adf_cleanup_bh(struct adf_accel_dev *accel_dev) | |
268 | { | |
269 | struct adf_etr_data *priv_data = accel_dev->transport; | |
270 | ||
271 | tasklet_disable(&priv_data->banks[0].resp_handler); | |
272 | tasklet_kill(&priv_data->banks[0].resp_handler); | |
273 | } | |
274 | ||
1a72d3a6 TS |
275 | /** |
276 | * adf_vf_isr_resource_free() - Free IRQ for acceleration device | |
277 | * @accel_dev: Pointer to acceleration device. | |
278 | * | |
279 | * Function frees interrupts for acceleration device virtual function. | |
280 | */ | |
d5cf4023 | 281 | void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev) |
dd0f3683 TS |
282 | { |
283 | struct pci_dev *pdev = accel_to_pci_dev(accel_dev); | |
284 | ||
285 | irq_set_affinity_hint(pdev->irq, NULL); | |
286 | free_irq(pdev->irq, (void *)accel_dev); | |
287 | adf_cleanup_bh(accel_dev); | |
288 | adf_cleanup_pf2vf_bh(accel_dev); | |
289 | adf_disable_msi(accel_dev); | |
290 | } | |
1a72d3a6 TS |
291 | EXPORT_SYMBOL_GPL(adf_vf_isr_resource_free); |
292 | ||
293 | /** | |
294 | * adf_vf_isr_resource_alloc() - Allocate IRQ for acceleration device | |
295 | * @accel_dev: Pointer to acceleration device. | |
296 | * | |
297 | * Function allocates interrupts for acceleration device virtual function. | |
298 | * | |
299 | * Return: 0 on success, error code otherwise. | |
300 | */ | |
d5cf4023 | 301 | int adf_vf_isr_resource_alloc(struct adf_accel_dev *accel_dev) |
dd0f3683 TS |
302 | { |
303 | if (adf_enable_msi(accel_dev)) | |
304 | goto err_out; | |
305 | ||
306 | if (adf_setup_pf2vf_bh(accel_dev)) | |
307 | goto err_out; | |
308 | ||
309 | if (adf_setup_bh(accel_dev)) | |
310 | goto err_out; | |
311 | ||
312 | if (adf_request_msi_irq(accel_dev)) | |
313 | goto err_out; | |
314 | ||
315 | return 0; | |
316 | err_out: | |
d5cf4023 | 317 | adf_vf_isr_resource_free(accel_dev); |
dd0f3683 TS |
318 | return -EFAULT; |
319 | } | |
1a72d3a6 | 320 | EXPORT_SYMBOL_GPL(adf_vf_isr_resource_alloc); |
d6064165 TS |
321 | |
322 | int __init adf_init_vf_wq(void) | |
323 | { | |
324 | adf_vf_stop_wq = create_workqueue("adf_vf_stop_wq"); | |
325 | ||
326 | return !adf_vf_stop_wq ? -EFAULT : 0; | |
327 | } | |
328 | ||
329 | void __exit adf_exit_vf_wq(void) | |
330 | { | |
331 | if (adf_vf_stop_wq) | |
332 | destroy_workqueue(adf_vf_stop_wq); | |
333 | ||
334 | adf_vf_stop_wq = NULL; | |
335 | } |