xref: /netbsd-src/sys/arch/arm/pci/pci_msi_machdep.c (revision 96f0215b0bfd5fe05356516951f6358f68d2eeef)
1 /* $NetBSD: pci_msi_machdep.c,v 1.10 2024/06/30 09:38:07 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.10 2024/06/30 09:38:07 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 
45 static SIMPLEQ_HEAD(, arm_pci_msi) arm_pci_msi_list =
46     SIMPLEQ_HEAD_INITIALIZER(arm_pci_msi_list);
47 
48 static struct arm_pci_msi *
arm_pci_msi_find_frame(pci_intr_handle_t ih)49 arm_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 
62 static struct arm_pci_msi *
arm_pci_msi_lookup(const struct pci_attach_args * pa)63 arm_pci_msi_lookup(const struct pci_attach_args *pa)
64 {
65 	struct arm_pci_msi *msip;
66 	uint32_t rid, frameid;
67 	int b, d, f;
68 
69 	pci_decompose_tag(pa->pa_pc, pa->pa_tag, &b, &d, &f);
70 
71 	rid = (b << 8) | (d << 3) | f;
72 	frameid = pci_get_frameid(pa->pa_pc, rid);
73 
74 	SIMPLEQ_FOREACH(msip, &arm_pci_msi_list, msi_link)
75 		if (frameid == msip->msi_id)
76 			return msip;
77 
78 	return NULL;
79 }
80 
81 static int
arm_pci_msi_alloc_common(pci_intr_handle_t ** ihps,int * count,const struct pci_attach_args * pa,bool exact)82 arm_pci_msi_alloc_common(pci_intr_handle_t **ihps, int *count, const struct pci_attach_args *pa, bool exact)
83 {
84 	pci_intr_handle_t *vectors;
85 	struct arm_pci_msi *msi;
86 
87 	if ((pa->pa_flags & PCI_FLAGS_MSI_OKAY) == 0)
88 		return ENODEV;
89 
90 	msi = arm_pci_msi_lookup(pa);
91 	if (msi == NULL || msi->msi_alloc == NULL)
92 		return EINVAL;
93 
94 	vectors = msi->msi_alloc(msi, count, pa, exact);
95 	if (vectors == NULL)
96 		return ENOMEM;
97 
98 	*ihps = vectors;
99 
100 	return 0;
101 }
102 
103 static int
arm_pci_msix_alloc_common(pci_intr_handle_t ** ihps,u_int * table_indexes,int * count,const struct pci_attach_args * pa,bool exact)104 arm_pci_msix_alloc_common(pci_intr_handle_t **ihps, u_int *table_indexes, int *count, const struct pci_attach_args *pa, bool exact)
105 {
106 	pci_intr_handle_t *vectors;
107 	struct arm_pci_msi *msi;
108 
109 	if ((pa->pa_flags & PCI_FLAGS_MSIX_OKAY) == 0)
110 		return ENODEV;
111 
112 	msi = arm_pci_msi_lookup(pa);
113 	if (msi == NULL || msi->msix_alloc == NULL)
114 		return EINVAL;
115 
116 	vectors = msi->msix_alloc(msi, table_indexes, count, pa, exact);
117 	if (vectors == NULL)
118 		return ENOMEM;
119 
120 	*ihps = vectors;
121 
122 	return 0;
123 }
124 
125 /*
126  * arm_pci_msi MD API
127  */
128 
129 int
arm_pci_msi_add(struct arm_pci_msi * msi)130 arm_pci_msi_add(struct arm_pci_msi *msi)
131 {
132 	SIMPLEQ_INSERT_TAIL(&arm_pci_msi_list, msi, msi_link);
133 
134 	return 0;
135 }
136 
137 void *
arm_pci_msi_intr_establish(pci_chipset_tag_t pc,pci_intr_handle_t pih,int ipl,int (* func)(void *),void * arg,const char * xname)138 arm_pci_msi_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t pih,
139     int ipl, int (*func)(void *), void *arg, const char *xname)
140 {
141 	struct arm_pci_msi *msi;
142 
143 	msi = arm_pci_msi_find_frame(pih);
144 	if (msi == NULL)
145 		return NULL;
146 
147 	return msi->msi_intr_establish(msi, pih, ipl, func, arg, xname);
148 }
149 
150 /*
151  * pci_msi(9) implementation
152  */
153 
154 int
pci_msi_alloc(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int * count)155 pci_msi_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count)
156 {
157 	return arm_pci_msi_alloc_common(ihps, count, pa, false);
158 }
159 
160 int
pci_msi_alloc_exact(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int count)161 pci_msi_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int count)
162 {
163 	return arm_pci_msi_alloc_common(ihps, &count, pa, true);
164 }
165 
166 int
pci_msix_alloc(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int * count)167 pci_msix_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count)
168 {
169 	return arm_pci_msix_alloc_common(ihps, NULL, count, pa, false);
170 }
171 
172 int
pci_msix_alloc_exact(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int count)173 pci_msix_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int count)
174 {
175 	return arm_pci_msix_alloc_common(ihps, NULL, &count, pa, true);
176 }
177 
178 int
pci_msix_alloc_map(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,u_int * table_indexes,int count)179 pci_msix_alloc_map(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, u_int *table_indexes, int count)
180 {
181 	return arm_pci_msix_alloc_common(ihps, table_indexes, &count, pa, true);
182 }
183 
184 int
pci_intx_alloc(const struct pci_attach_args * pa,pci_intr_handle_t ** ihp)185 pci_intx_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihp)
186 {
187 	pci_intr_handle_t *pih;
188 
189 	if (ihp == NULL)
190 		return EINVAL;
191 
192 	pih = kmem_alloc(sizeof(*pih), KM_SLEEP);
193 	if (pci_intr_map(pa, pih) != 0) {
194 		kmem_free(pih, sizeof(*pih));
195 		return EINVAL;
196 	}
197 	*ihp = pih;
198 
199 	return 0;
200 }
201 
202 int
pci_intr_alloc(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int * counts,pci_intr_type_t max_type)203 pci_intr_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *counts, pci_intr_type_t max_type)
204 {
205 	int intx_count, msi_count, msix_count, error;
206 
207 	error = EINVAL;
208 
209 	if (counts != NULL) {
210 		intx_count = msi_count = msix_count = 0;
211 
212 		switch (max_type) {
213 		case PCI_INTR_TYPE_MSIX:
214 			msix_count = counts[PCI_INTR_TYPE_MSIX];
215 			/* FALLTHROUGH */
216 		case PCI_INTR_TYPE_MSI:
217 			msi_count = counts[PCI_INTR_TYPE_MSI];
218 			/* FALLTHROUGH */
219 		case PCI_INTR_TYPE_INTX:
220 			intx_count = counts[PCI_INTR_TYPE_INTX];
221 			if (intx_count > 1)
222 				return EINVAL;
223 			break;
224 		default:
225 			return EINVAL;
226 		}
227 		memset(counts, 0, sizeof(*counts) * PCI_INTR_TYPE_SIZE);
228 	} else {
229 		intx_count = msi_count = msix_count = 1;
230 	}
231 
232 	if (msix_count == -1)
233 		msix_count = pci_msix_count(pa->pa_pc, pa->pa_tag);
234 	if (msix_count > 0 && (error = pci_msix_alloc_exact(pa, ihps, msix_count)) == 0) {
235 		if (counts != NULL)
236 			counts[PCI_INTR_TYPE_MSIX] = msix_count;
237 		return 0;
238 	}
239 
240 	if (msi_count == -1)
241 		msi_count = pci_msi_count(pa->pa_pc, pa->pa_tag);
242 	if (msi_count > 0 && (error = pci_msi_alloc_exact(pa, ihps, msi_count)) == 0) {
243 		if (counts != NULL)
244 			counts[PCI_INTR_TYPE_MSI] = msi_count;
245 		return 0;
246 	}
247 
248 	if (intx_count > 0 && (error = pci_intx_alloc(pa, ihps)) == 0) {
249 		if (counts != NULL)
250 			counts[PCI_INTR_TYPE_INTX] = intx_count;
251 		return 0;
252 	}
253 
254 	return error;
255 }
256 
257 void
pci_intr_release(pci_chipset_tag_t pc,pci_intr_handle_t * pih,int count)258 pci_intr_release(pci_chipset_tag_t pc, pci_intr_handle_t *pih, int count)
259 {
260 	struct arm_pci_msi *msi = NULL;
261 
262 	if (pih == NULL || count == 0)
263 		return;
264 
265 	if ((pih[0] & (ARM_PCI_INTR_MSIX|ARM_PCI_INTR_MSI)) != 0) {
266 		msi = arm_pci_msi_find_frame(pih[0]);
267 		KASSERT(msi != NULL);
268 		msi->msi_intr_release(msi, pih, count);
269 	}
270 
271 	kmem_free(pih, sizeof(*pih) * count);
272 }
273 
274 pci_intr_type_t
pci_intr_type(pci_chipset_tag_t pc,pci_intr_handle_t ih)275 pci_intr_type(pci_chipset_tag_t pc, pci_intr_handle_t ih)
276 {
277 	if (ih & ARM_PCI_INTR_MSIX)
278 		return PCI_INTR_TYPE_MSIX;
279 
280 	if (ih & ARM_PCI_INTR_MSI)
281 		return PCI_INTR_TYPE_MSI;
282 
283 	return PCI_INTR_TYPE_INTX;
284 }
285