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>
54#include <adf_accel_devices.h>
55#include <adf_common_drv.h>
56#include <adf_cfg.h>
57#include <adf_cfg_strings.h>
58#include <adf_cfg_common.h>
59#include <adf_transport_access_macros.h>
60#include <adf_transport_internal.h>
61#include <adf_pf2vf_msg.h>
62#include "adf_drv.h"
63#include "adf_dh895xccvf_hw_data.h"
64
65static int adf_enable_msi(struct adf_accel_dev *accel_dev)
66{
67	struct adf_accel_pci *pci_dev_info = &accel_dev->accel_pci_dev;
68	int stat = pci_enable_msi(pci_dev_info->pci_dev);
69
70	if (stat) {
71		dev_err(&GET_DEV(accel_dev),
72			"Failed to enable MSI interrupts\n");
73		return stat;
74	}
75
76	accel_dev->vf.irq_name = kzalloc(ADF_MAX_MSIX_VECTOR_NAME, GFP_KERNEL);
77	if (!accel_dev->vf.irq_name)
78		return -ENOMEM;
79
80	return stat;
81}
82
83static void adf_disable_msi(struct adf_accel_dev *accel_dev)
84{
85	struct pci_dev *pdev = accel_to_pci_dev(accel_dev);
86
87	kfree(accel_dev->vf.irq_name);
88	pci_disable_msi(pdev);
89}
90
91static void adf_pf2vf_bh_handler(void *data)
92{
93	struct adf_accel_dev *accel_dev = data;
94	void __iomem *pmisc_bar_addr =
95		(&GET_BARS(accel_dev)[ADF_DH895XCCIOV_PMISC_BAR])->virt_addr;
96	u32 msg;
97
98	/* Read the message from PF */
99	msg = ADF_CSR_RD(pmisc_bar_addr, ADF_DH895XCCIOV_PF2VF_OFFSET);
100
101	if (!(msg & ADF_PF2VF_MSGORIGIN_SYSTEM))
102		/* Ignore legacy non-system (non-kernel) PF2VF messages */
103		goto err;
104
105	switch ((msg & ADF_PF2VF_MSGTYPE_MASK) >> ADF_PF2VF_MSGTYPE_SHIFT) {
106	case ADF_PF2VF_MSGTYPE_RESTARTING:
107		dev_dbg(&GET_DEV(accel_dev),
108			"Restarting msg received from PF 0x%x\n", msg);
109		adf_dev_stop(accel_dev);
110		break;
111	case ADF_PF2VF_MSGTYPE_VERSION_RESP:
112		dev_dbg(&GET_DEV(accel_dev),
113			"Version resp received from PF 0x%x\n", msg);
114		accel_dev->vf.pf_version =
115			(msg & ADF_PF2VF_VERSION_RESP_VERS_MASK) >>
116			ADF_PF2VF_VERSION_RESP_VERS_SHIFT;
117		accel_dev->vf.compatible =
118			(msg & ADF_PF2VF_VERSION_RESP_RESULT_MASK) >>
119			ADF_PF2VF_VERSION_RESP_RESULT_SHIFT;
120		complete(&accel_dev->vf.iov_msg_completion);
121		break;
122	default:
123		goto err;
124	}
125
126	/* To ack, clear the PF2VFINT bit */
127	msg &= ~ADF_DH895XCC_PF2VF_PF2VFINT;
128	ADF_CSR_WR(pmisc_bar_addr, ADF_DH895XCCIOV_PF2VF_OFFSET, msg);
129
130	/* Re-enable PF2VF interrupts */
131	adf_enable_pf2vf_interrupts(accel_dev);
132	return;
133err:
134	dev_err(&GET_DEV(accel_dev),
135		"Unknown message from PF (0x%x); leaving PF2VF ints disabled\n",
136		msg);
137}
138
139static int adf_setup_pf2vf_bh(struct adf_accel_dev *accel_dev)
140{
141	tasklet_init(&accel_dev->vf.pf2vf_bh_tasklet,
142		     (void *)adf_pf2vf_bh_handler, (unsigned long)accel_dev);
143
144	mutex_init(&accel_dev->vf.vf2pf_lock);
145	return 0;
146}
147
148static void adf_cleanup_pf2vf_bh(struct adf_accel_dev *accel_dev)
149{
150	tasklet_disable(&accel_dev->vf.pf2vf_bh_tasklet);
151	tasklet_kill(&accel_dev->vf.pf2vf_bh_tasklet);
152	mutex_destroy(&accel_dev->vf.vf2pf_lock);
153}
154
155static irqreturn_t adf_isr(int irq, void *privdata)
156{
157	struct adf_accel_dev *accel_dev = privdata;
158	void __iomem *pmisc_bar_addr =
159		(&GET_BARS(accel_dev)[ADF_DH895XCCIOV_PMISC_BAR])->virt_addr;
160	u32 v_int;
161
162	/* Read VF INT source CSR to determine the source of VF interrupt */
163	v_int = ADF_CSR_RD(pmisc_bar_addr, ADF_DH895XCCIOV_VINTSOU_OFFSET);
164
165	/* Check for PF2VF interrupt */
166	if (v_int & ADF_DH895XCC_VINTSOU_PF2VF) {
167		/* Disable PF to VF interrupt */
168		adf_disable_pf2vf_interrupts(accel_dev);
169
170		/* Schedule tasklet to handle interrupt BH */
171		tasklet_hi_schedule(&accel_dev->vf.pf2vf_bh_tasklet);
172		return IRQ_HANDLED;
173	}
174
175	/* Check bundle interrupt */
176	if (v_int & ADF_DH895XCC_VINTSOU_BUN) {
177		struct adf_etr_data *etr_data = accel_dev->transport;
178		struct adf_etr_bank_data *bank = &etr_data->banks[0];
179
180		/* Disable Flag and Coalesce Ring Interrupts */
181		WRITE_CSR_INT_FLAG_AND_COL(bank->csr_addr, bank->bank_number,
182					   0);
183		tasklet_hi_schedule(&bank->resp_handler);
184		return IRQ_HANDLED;
185	}
186
187	return IRQ_NONE;
188}
189
190static int adf_request_msi_irq(struct adf_accel_dev *accel_dev)
191{
192	struct pci_dev *pdev = accel_to_pci_dev(accel_dev);
193	unsigned int cpu;
194	int ret;
195
196	snprintf(accel_dev->vf.irq_name, ADF_MAX_MSIX_VECTOR_NAME,
197		 "qat_%02x:%02d.%02d", pdev->bus->number, PCI_SLOT(pdev->devfn),
198		 PCI_FUNC(pdev->devfn));
199	ret = request_irq(pdev->irq, adf_isr, 0, accel_dev->vf.irq_name,
200			  (void *)accel_dev);
201	if (ret) {
202		dev_err(&GET_DEV(accel_dev), "failed to enable irq for %s\n",
203			accel_dev->vf.irq_name);
204		return ret;
205	}
206	cpu = accel_dev->accel_id % num_online_cpus();
207	irq_set_affinity_hint(pdev->irq, get_cpu_mask(cpu));
208
209	return ret;
210}
211
212static int adf_setup_bh(struct adf_accel_dev *accel_dev)
213{
214	struct adf_etr_data *priv_data = accel_dev->transport;
215
216	tasklet_init(&priv_data->banks[0].resp_handler, adf_response_handler,
217		     (unsigned long)priv_data->banks);
218	return 0;
219}
220
221static void adf_cleanup_bh(struct adf_accel_dev *accel_dev)
222{
223	struct adf_etr_data *priv_data = accel_dev->transport;
224
225	tasklet_disable(&priv_data->banks[0].resp_handler);
226	tasklet_kill(&priv_data->banks[0].resp_handler);
227}
228
229void adf_vf_isr_resource_free(struct adf_accel_dev *accel_dev)
230{
231	struct pci_dev *pdev = accel_to_pci_dev(accel_dev);
232
233	irq_set_affinity_hint(pdev->irq, NULL);
234	free_irq(pdev->irq, (void *)accel_dev);
235	adf_cleanup_bh(accel_dev);
236	adf_cleanup_pf2vf_bh(accel_dev);
237	adf_disable_msi(accel_dev);
238}
239
240int adf_vf_isr_resource_alloc(struct adf_accel_dev *accel_dev)
241{
242	if (adf_enable_msi(accel_dev))
243		goto err_out;
244
245	if (adf_setup_pf2vf_bh(accel_dev))
246		goto err_out;
247
248	if (adf_setup_bh(accel_dev))
249		goto err_out;
250
251	if (adf_request_msi_irq(accel_dev))
252		goto err_out;
253
254	return 0;
255err_out:
256	adf_vf_isr_resource_free(accel_dev);
257	return -EFAULT;
258}
259