pci_msi_machdep.c revision 1.8
1/* $NetBSD: pci_msi_machdep.c,v 1.8 2020/02/13 00:02:21 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2018 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jared McNeill <jmcneill@invisible.ca>.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: pci_msi_machdep.c,v 1.8 2020/02/13 00:02:21 jmcneill Exp $");
34
35#include <sys/kernel.h>
36#include <sys/kmem.h>
37
38#include <dev/pci/pcireg.h>
39#include <dev/pci/pcivar.h>
40
41#include <arm/pic/picvar.h>
42
43#include <arm/pci/pci_msi_machdep.h>
44
45static SIMPLEQ_HEAD(, arm_pci_msi) arm_pci_msi_list =
46    SIMPLEQ_HEAD_INITIALIZER(arm_pci_msi_list);
47
48static struct arm_pci_msi *
49arm_pci_msi_find_frame(pci_intr_handle_t ih)
50{
51	struct arm_pci_msi *msip;
52
53	const int id = __SHIFTOUT(ih, ARM_PCI_INTR_FRAME);
54
55	SIMPLEQ_FOREACH(msip, &arm_pci_msi_list, msi_link)
56		if (id == msip->msi_id)
57			return msip;
58
59	return NULL;
60}
61
62static struct arm_pci_msi *
63arm_pci_msi_lookup(const struct pci_attach_args *pa)
64{
65	struct arm_pci_msi *msip;
66	uint32_t devid, frameid;
67	int b, d, f;
68
69	pci_decompose_tag(pa->pa_pc, pa->pa_tag, &b, &d, &f);
70
71	devid = (b << 8) | (d << 3) | f;
72        devid = pci_get_devid(pa->pa_pc, devid);
73	frameid = pci_get_frameid(pa->pa_pc, devid);
74
75	SIMPLEQ_FOREACH(msip, &arm_pci_msi_list, msi_link)
76		if (frameid == msip->msi_id)
77			return msip;
78
79	return NULL;
80}
81
82static int
83arm_pci_msi_alloc_common(pci_intr_handle_t **ihps, int *count, const struct pci_attach_args *pa, bool exact)
84{
85	pci_intr_handle_t *vectors;
86	struct arm_pci_msi *msi;
87
88	if ((pa->pa_flags & PCI_FLAGS_MSI_OKAY) == 0)
89		return ENODEV;
90
91	msi = arm_pci_msi_lookup(pa);
92	if (msi == NULL || msi->msi_alloc == NULL)
93		return EINVAL;
94
95	vectors = msi->msi_alloc(msi, count, pa, exact);
96	if (vectors == NULL)
97		return ENOMEM;
98
99	*ihps = vectors;
100
101	return 0;
102}
103
104static int
105arm_pci_msix_alloc_common(pci_intr_handle_t **ihps, u_int *table_indexes, int *count, const struct pci_attach_args *pa, bool exact)
106{
107	pci_intr_handle_t *vectors;
108	struct arm_pci_msi *msi;
109
110	if ((pa->pa_flags & PCI_FLAGS_MSIX_OKAY) == 0)
111		return ENODEV;
112
113	msi = arm_pci_msi_lookup(pa);
114	if (msi == NULL || msi->msix_alloc == NULL)
115		return EINVAL;
116
117	vectors = msi->msix_alloc(msi, table_indexes, count, pa, exact);
118	if (vectors == NULL)
119		return ENOMEM;
120
121	*ihps = vectors;
122
123	return 0;
124}
125
126/*
127 * arm_pci_msi MD API
128 */
129
130int
131arm_pci_msi_add(struct arm_pci_msi *msi)
132{
133	SIMPLEQ_INSERT_TAIL(&arm_pci_msi_list, msi, msi_link);
134
135	return 0;
136}
137
138void *
139arm_pci_msi_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t pih,
140    int ipl, int (*func)(void *), void *arg, const char *xname)
141{
142	struct arm_pci_msi *msi;
143
144	msi = arm_pci_msi_find_frame(pih);
145	if (msi == NULL)
146		return NULL;
147
148	return msi->msi_intr_establish(msi, pih, ipl, func, arg, xname);
149}
150
151/*
152 * pci_msi(9) implementation
153 */
154
155int
156pci_msi_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count)
157{
158	return arm_pci_msi_alloc_common(ihps, count, pa, false);
159}
160
161int
162pci_msi_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int count)
163{
164	return arm_pci_msi_alloc_common(ihps, &count, pa, true);
165}
166
167int
168pci_msix_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count)
169{
170	return arm_pci_msix_alloc_common(ihps, NULL, count, pa, false);
171}
172
173int
174pci_msix_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int count)
175{
176	return arm_pci_msix_alloc_common(ihps, NULL, &count, pa, true);
177}
178
179int
180pci_msix_alloc_map(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, u_int *table_indexes, int count)
181{
182	return arm_pci_msix_alloc_common(ihps, table_indexes, &count, pa, true);
183}
184
185int
186pci_intx_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihp)
187{
188	pci_intr_handle_t *pih;
189
190	if (ihp == NULL)
191		return EINVAL;
192
193	pih = kmem_alloc(sizeof(*pih), KM_SLEEP);
194	if (pci_intr_map(pa, pih) != 0) {
195		kmem_free(pih, sizeof(*pih));
196		return EINVAL;
197	}
198	*ihp = pih;
199
200	return 0;
201}
202
203int
204pci_intr_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *counts, pci_intr_type_t max_type)
205{
206	int intx_count, msi_count, msix_count, error;
207
208	error = EINVAL;
209
210	if (counts != NULL) {
211		intx_count = msi_count = msix_count = 0;
212
213		switch (max_type) {
214		case PCI_INTR_TYPE_MSIX:
215			msix_count = counts[PCI_INTR_TYPE_MSIX];
216			/* FALLTHROUGH */
217		case PCI_INTR_TYPE_MSI:
218			msi_count = counts[PCI_INTR_TYPE_MSI];
219			/* FALLTHROUGH */
220		case PCI_INTR_TYPE_INTX:
221			intx_count = counts[PCI_INTR_TYPE_INTX];
222			if (intx_count > 1)
223				return EINVAL;
224			break;
225		default:
226			return EINVAL;
227		}
228		memset(counts, 0, sizeof(*counts) * PCI_INTR_TYPE_SIZE);
229	} else {
230		intx_count = msi_count = msix_count = 1;
231	}
232
233	if (msix_count == -1)
234		msix_count = pci_msix_count(pa->pa_pc, pa->pa_tag);
235	if (msix_count > 0 && (error = pci_msix_alloc_exact(pa, ihps, msix_count)) == 0) {
236		if (counts != NULL)
237			counts[PCI_INTR_TYPE_MSIX] = msix_count;
238		return 0;
239	}
240
241	if (msi_count == -1)
242		msi_count = pci_msi_count(pa->pa_pc, pa->pa_tag);
243	if (msi_count > 0 && (error = pci_msi_alloc_exact(pa, ihps, msi_count)) == 0) {
244		if (counts != NULL)
245			counts[PCI_INTR_TYPE_MSI] = msi_count;
246		return 0;
247	}
248
249	if (intx_count > 0 && (error = pci_intx_alloc(pa, ihps)) == 0) {
250		if (counts != NULL)
251			counts[PCI_INTR_TYPE_INTX] = intx_count;
252		return 0;
253	}
254
255	return error;
256}
257
258void
259pci_intr_release(pci_chipset_tag_t pc, pci_intr_handle_t *pih, int count)
260{
261	struct arm_pci_msi *msi = NULL;
262
263	if (pih == NULL || count == 0)
264		return;
265
266	if ((pih[0] & (ARM_PCI_INTR_MSIX|ARM_PCI_INTR_MSI)) != 0) {
267		msi = arm_pci_msi_find_frame(pih[0]);
268		KASSERT(msi != NULL);
269		msi->msi_intr_release(msi, pih, count);
270	}
271
272	kmem_free(pih, sizeof(*pih) * count);
273}
274
275pci_intr_type_t
276pci_intr_type(pci_chipset_tag_t pc, pci_intr_handle_t ih)
277{
278	if (ih & ARM_PCI_INTR_MSIX)
279		return PCI_INTR_TYPE_MSIX;
280
281	if (ih & ARM_PCI_INTR_MSI)
282		return PCI_INTR_TYPE_MSI;
283
284	return PCI_INTR_TYPE_INTX;
285}
286