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