xref: /netbsd-src/sys/arch/arm/pci/pci_msi_machdep.c (revision 96f0215b0bfd5fe05356516951f6358f68d2eeef)
1*96f0215bSjmcneill /* $NetBSD: pci_msi_machdep.c,v 1.10 2024/06/30 09:38:07 jmcneill Exp $ */
2187fa5a9Sjmcneill 
3187fa5a9Sjmcneill /*-
4187fa5a9Sjmcneill  * Copyright (c) 2018 The NetBSD Foundation, Inc.
5187fa5a9Sjmcneill  * All rights reserved.
6187fa5a9Sjmcneill  *
7187fa5a9Sjmcneill  * This code is derived from software contributed to The NetBSD Foundation
8187fa5a9Sjmcneill  * by Jared McNeill <jmcneill@invisible.ca>.
9187fa5a9Sjmcneill  *
10187fa5a9Sjmcneill  * Redistribution and use in source and binary forms, with or without
11187fa5a9Sjmcneill  * modification, are permitted provided that the following conditions
12187fa5a9Sjmcneill  * are met:
13187fa5a9Sjmcneill  * 1. Redistributions of source code must retain the above copyright
14187fa5a9Sjmcneill  *    notice, this list of conditions and the following disclaimer.
15187fa5a9Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
16187fa5a9Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
17187fa5a9Sjmcneill  *    documentation and/or other materials provided with the distribution.
18187fa5a9Sjmcneill  *
19187fa5a9Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20187fa5a9Sjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21187fa5a9Sjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22187fa5a9Sjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23187fa5a9Sjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24187fa5a9Sjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25187fa5a9Sjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26187fa5a9Sjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27187fa5a9Sjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28187fa5a9Sjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29187fa5a9Sjmcneill  * POSSIBILITY OF SUCH DAMAGE.
30187fa5a9Sjmcneill  */
31187fa5a9Sjmcneill 
32187fa5a9Sjmcneill #include <sys/cdefs.h>
33*96f0215bSjmcneill __KERNEL_RCSID(0, "$NetBSD: pci_msi_machdep.c,v 1.10 2024/06/30 09:38:07 jmcneill Exp $");
34187fa5a9Sjmcneill 
35187fa5a9Sjmcneill #include <sys/kernel.h>
36187fa5a9Sjmcneill #include <sys/kmem.h>
37187fa5a9Sjmcneill 
38187fa5a9Sjmcneill #include <dev/pci/pcireg.h>
39187fa5a9Sjmcneill #include <dev/pci/pcivar.h>
40187fa5a9Sjmcneill 
41187fa5a9Sjmcneill #include <arm/pic/picvar.h>
42187fa5a9Sjmcneill 
43187fa5a9Sjmcneill #include <arm/pci/pci_msi_machdep.h>
44187fa5a9Sjmcneill 
45187fa5a9Sjmcneill static SIMPLEQ_HEAD(, arm_pci_msi) arm_pci_msi_list =
46187fa5a9Sjmcneill     SIMPLEQ_HEAD_INITIALIZER(arm_pci_msi_list);
47187fa5a9Sjmcneill 
48187fa5a9Sjmcneill static struct arm_pci_msi *
arm_pci_msi_find_frame(pci_intr_handle_t ih)49187fa5a9Sjmcneill arm_pci_msi_find_frame(pci_intr_handle_t ih)
50187fa5a9Sjmcneill {
51187fa5a9Sjmcneill 	struct arm_pci_msi *msip;
52187fa5a9Sjmcneill 
53187fa5a9Sjmcneill 	const int id = __SHIFTOUT(ih, ARM_PCI_INTR_FRAME);
54187fa5a9Sjmcneill 
55187fa5a9Sjmcneill 	SIMPLEQ_FOREACH(msip, &arm_pci_msi_list, msi_link)
56187fa5a9Sjmcneill 		if (id == msip->msi_id)
57187fa5a9Sjmcneill 			return msip;
58187fa5a9Sjmcneill 
59187fa5a9Sjmcneill 	return NULL;
60187fa5a9Sjmcneill }
61187fa5a9Sjmcneill 
628b7fb0a6Sjmcneill static struct arm_pci_msi *
arm_pci_msi_lookup(const struct pci_attach_args * pa)638b7fb0a6Sjmcneill arm_pci_msi_lookup(const struct pci_attach_args *pa)
648b7fb0a6Sjmcneill {
658b7fb0a6Sjmcneill 	struct arm_pci_msi *msip;
66*96f0215bSjmcneill 	uint32_t rid, frameid;
678b7fb0a6Sjmcneill 	int b, d, f;
688b7fb0a6Sjmcneill 
698b7fb0a6Sjmcneill 	pci_decompose_tag(pa->pa_pc, pa->pa_tag, &b, &d, &f);
708b7fb0a6Sjmcneill 
71*96f0215bSjmcneill 	rid = (b << 8) | (d << 3) | f;
72*96f0215bSjmcneill 	frameid = pci_get_frameid(pa->pa_pc, rid);
738b7fb0a6Sjmcneill 
748b7fb0a6Sjmcneill 	SIMPLEQ_FOREACH(msip, &arm_pci_msi_list, msi_link)
758b7fb0a6Sjmcneill 		if (frameid == msip->msi_id)
768b7fb0a6Sjmcneill 			return msip;
778b7fb0a6Sjmcneill 
788b7fb0a6Sjmcneill 	return NULL;
798b7fb0a6Sjmcneill }
808b7fb0a6Sjmcneill 
81187fa5a9Sjmcneill static int
arm_pci_msi_alloc_common(pci_intr_handle_t ** ihps,int * count,const struct pci_attach_args * pa,bool exact)82187fa5a9Sjmcneill arm_pci_msi_alloc_common(pci_intr_handle_t **ihps, int *count, const struct pci_attach_args *pa, bool exact)
83187fa5a9Sjmcneill {
84187fa5a9Sjmcneill 	pci_intr_handle_t *vectors;
85187fa5a9Sjmcneill 	struct arm_pci_msi *msi;
86187fa5a9Sjmcneill 
87187fa5a9Sjmcneill 	if ((pa->pa_flags & PCI_FLAGS_MSI_OKAY) == 0)
88187fa5a9Sjmcneill 		return ENODEV;
89187fa5a9Sjmcneill 
908b7fb0a6Sjmcneill 	msi = arm_pci_msi_lookup(pa);
91187fa5a9Sjmcneill 	if (msi == NULL || msi->msi_alloc == NULL)
92187fa5a9Sjmcneill 		return EINVAL;
93187fa5a9Sjmcneill 
94187fa5a9Sjmcneill 	vectors = msi->msi_alloc(msi, count, pa, exact);
95187fa5a9Sjmcneill 	if (vectors == NULL)
96187fa5a9Sjmcneill 		return ENOMEM;
97187fa5a9Sjmcneill 
98187fa5a9Sjmcneill 	*ihps = vectors;
99187fa5a9Sjmcneill 
100187fa5a9Sjmcneill 	return 0;
101187fa5a9Sjmcneill }
102187fa5a9Sjmcneill 
1031ddc0ea0Sjmcneill 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)1041ddc0ea0Sjmcneill arm_pci_msix_alloc_common(pci_intr_handle_t **ihps, u_int *table_indexes, int *count, const struct pci_attach_args *pa, bool exact)
1051ddc0ea0Sjmcneill {
1061ddc0ea0Sjmcneill 	pci_intr_handle_t *vectors;
1071ddc0ea0Sjmcneill 	struct arm_pci_msi *msi;
1081ddc0ea0Sjmcneill 
1091ddc0ea0Sjmcneill 	if ((pa->pa_flags & PCI_FLAGS_MSIX_OKAY) == 0)
1101ddc0ea0Sjmcneill 		return ENODEV;
1111ddc0ea0Sjmcneill 
1128b7fb0a6Sjmcneill 	msi = arm_pci_msi_lookup(pa);
1131ddc0ea0Sjmcneill 	if (msi == NULL || msi->msix_alloc == NULL)
1141ddc0ea0Sjmcneill 		return EINVAL;
1151ddc0ea0Sjmcneill 
1161ddc0ea0Sjmcneill 	vectors = msi->msix_alloc(msi, table_indexes, count, pa, exact);
1171ddc0ea0Sjmcneill 	if (vectors == NULL)
1181ddc0ea0Sjmcneill 		return ENOMEM;
1191ddc0ea0Sjmcneill 
1201ddc0ea0Sjmcneill 	*ihps = vectors;
1211ddc0ea0Sjmcneill 
1221ddc0ea0Sjmcneill 	return 0;
1231ddc0ea0Sjmcneill }
1241ddc0ea0Sjmcneill 
125187fa5a9Sjmcneill /*
126187fa5a9Sjmcneill  * arm_pci_msi MD API
127187fa5a9Sjmcneill  */
128187fa5a9Sjmcneill 
129187fa5a9Sjmcneill int
arm_pci_msi_add(struct arm_pci_msi * msi)130187fa5a9Sjmcneill arm_pci_msi_add(struct arm_pci_msi *msi)
131187fa5a9Sjmcneill {
132187fa5a9Sjmcneill 	SIMPLEQ_INSERT_TAIL(&arm_pci_msi_list, msi, msi_link);
133187fa5a9Sjmcneill 
134187fa5a9Sjmcneill 	return 0;
135187fa5a9Sjmcneill }
136187fa5a9Sjmcneill 
137187fa5a9Sjmcneill 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)138187fa5a9Sjmcneill arm_pci_msi_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t pih,
139cce19cc2Sjmcneill     int ipl, int (*func)(void *), void *arg, const char *xname)
140187fa5a9Sjmcneill {
141187fa5a9Sjmcneill 	struct arm_pci_msi *msi;
142187fa5a9Sjmcneill 
143187fa5a9Sjmcneill 	msi = arm_pci_msi_find_frame(pih);
144187fa5a9Sjmcneill 	if (msi == NULL)
145187fa5a9Sjmcneill 		return NULL;
146187fa5a9Sjmcneill 
147cce19cc2Sjmcneill 	return msi->msi_intr_establish(msi, pih, ipl, func, arg, xname);
148187fa5a9Sjmcneill }
149187fa5a9Sjmcneill 
150187fa5a9Sjmcneill /*
151187fa5a9Sjmcneill  * pci_msi(9) implementation
152187fa5a9Sjmcneill  */
153187fa5a9Sjmcneill 
154187fa5a9Sjmcneill int
pci_msi_alloc(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int * count)155187fa5a9Sjmcneill pci_msi_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count)
156187fa5a9Sjmcneill {
157187fa5a9Sjmcneill 	return arm_pci_msi_alloc_common(ihps, count, pa, false);
158187fa5a9Sjmcneill }
159187fa5a9Sjmcneill 
160187fa5a9Sjmcneill int
pci_msi_alloc_exact(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int count)161187fa5a9Sjmcneill pci_msi_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int count)
162187fa5a9Sjmcneill {
163187fa5a9Sjmcneill 	return arm_pci_msi_alloc_common(ihps, &count, pa, true);
164187fa5a9Sjmcneill }
165187fa5a9Sjmcneill 
166187fa5a9Sjmcneill int
pci_msix_alloc(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int * count)167187fa5a9Sjmcneill pci_msix_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count)
168187fa5a9Sjmcneill {
1691ddc0ea0Sjmcneill 	return arm_pci_msix_alloc_common(ihps, NULL, count, pa, false);
170187fa5a9Sjmcneill }
171187fa5a9Sjmcneill 
172187fa5a9Sjmcneill int
pci_msix_alloc_exact(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int count)173187fa5a9Sjmcneill pci_msix_alloc_exact(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int count)
174187fa5a9Sjmcneill {
1751ddc0ea0Sjmcneill 	return arm_pci_msix_alloc_common(ihps, NULL, &count, pa, true);
176187fa5a9Sjmcneill }
177187fa5a9Sjmcneill 
178187fa5a9Sjmcneill int
pci_msix_alloc_map(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,u_int * table_indexes,int count)179187fa5a9Sjmcneill pci_msix_alloc_map(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, u_int *table_indexes, int count)
180187fa5a9Sjmcneill {
1811ddc0ea0Sjmcneill 	return arm_pci_msix_alloc_common(ihps, table_indexes, &count, pa, true);
182187fa5a9Sjmcneill }
183187fa5a9Sjmcneill 
184187fa5a9Sjmcneill int
pci_intx_alloc(const struct pci_attach_args * pa,pci_intr_handle_t ** ihp)185187fa5a9Sjmcneill pci_intx_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihp)
186187fa5a9Sjmcneill {
187187fa5a9Sjmcneill 	pci_intr_handle_t *pih;
188187fa5a9Sjmcneill 
189187fa5a9Sjmcneill 	if (ihp == NULL)
190187fa5a9Sjmcneill 		return EINVAL;
191187fa5a9Sjmcneill 
192187fa5a9Sjmcneill 	pih = kmem_alloc(sizeof(*pih), KM_SLEEP);
193187fa5a9Sjmcneill 	if (pci_intr_map(pa, pih) != 0) {
194187fa5a9Sjmcneill 		kmem_free(pih, sizeof(*pih));
195187fa5a9Sjmcneill 		return EINVAL;
196187fa5a9Sjmcneill 	}
197187fa5a9Sjmcneill 	*ihp = pih;
198187fa5a9Sjmcneill 
199187fa5a9Sjmcneill 	return 0;
200187fa5a9Sjmcneill }
201187fa5a9Sjmcneill 
202187fa5a9Sjmcneill int
pci_intr_alloc(const struct pci_attach_args * pa,pci_intr_handle_t ** ihps,int * counts,pci_intr_type_t max_type)203187fa5a9Sjmcneill pci_intr_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *counts, pci_intr_type_t max_type)
204187fa5a9Sjmcneill {
205187fa5a9Sjmcneill 	int intx_count, msi_count, msix_count, error;
206187fa5a9Sjmcneill 
207187fa5a9Sjmcneill 	error = EINVAL;
208187fa5a9Sjmcneill 
209187fa5a9Sjmcneill 	if (counts != NULL) {
2101dc95d4fSjmcneill 		intx_count = msi_count = msix_count = 0;
2111dc95d4fSjmcneill 
212187fa5a9Sjmcneill 		switch (max_type) {
213187fa5a9Sjmcneill 		case PCI_INTR_TYPE_MSIX:
214187fa5a9Sjmcneill 			msix_count = counts[PCI_INTR_TYPE_MSIX];
215187fa5a9Sjmcneill 			/* FALLTHROUGH */
216187fa5a9Sjmcneill 		case PCI_INTR_TYPE_MSI:
217187fa5a9Sjmcneill 			msi_count = counts[PCI_INTR_TYPE_MSI];
218187fa5a9Sjmcneill 			/* FALLTHROUGH */
219187fa5a9Sjmcneill 		case PCI_INTR_TYPE_INTX:
220187fa5a9Sjmcneill 			intx_count = counts[PCI_INTR_TYPE_INTX];
221187fa5a9Sjmcneill 			if (intx_count > 1)
222187fa5a9Sjmcneill 				return EINVAL;
223187fa5a9Sjmcneill 			break;
224187fa5a9Sjmcneill 		default:
225187fa5a9Sjmcneill 			return EINVAL;
226187fa5a9Sjmcneill 		}
227187fa5a9Sjmcneill 		memset(counts, 0, sizeof(*counts) * PCI_INTR_TYPE_SIZE);
2281dc95d4fSjmcneill 	} else {
2291dc95d4fSjmcneill 		intx_count = msi_count = msix_count = 1;
230187fa5a9Sjmcneill 	}
231187fa5a9Sjmcneill 
232187fa5a9Sjmcneill 	if (msix_count == -1)
233187fa5a9Sjmcneill 		msix_count = pci_msix_count(pa->pa_pc, pa->pa_tag);
234187fa5a9Sjmcneill 	if (msix_count > 0 && (error = pci_msix_alloc_exact(pa, ihps, msix_count)) == 0) {
235d0b9a6d3Sskrll 		if (counts != NULL)
236187fa5a9Sjmcneill 			counts[PCI_INTR_TYPE_MSIX] = msix_count;
237187fa5a9Sjmcneill 		return 0;
238187fa5a9Sjmcneill 	}
239187fa5a9Sjmcneill 
240187fa5a9Sjmcneill 	if (msi_count == -1)
241187fa5a9Sjmcneill 		msi_count = pci_msi_count(pa->pa_pc, pa->pa_tag);
242187fa5a9Sjmcneill 	if (msi_count > 0 && (error = pci_msi_alloc_exact(pa, ihps, msi_count)) == 0) {
243187fa5a9Sjmcneill 		if (counts != NULL)
244187fa5a9Sjmcneill 			counts[PCI_INTR_TYPE_MSI] = msi_count;
245187fa5a9Sjmcneill 		return 0;
246187fa5a9Sjmcneill 	}
247187fa5a9Sjmcneill 
248187fa5a9Sjmcneill 	if (intx_count > 0 && (error = pci_intx_alloc(pa, ihps)) == 0) {
249187fa5a9Sjmcneill 		if (counts != NULL)
250187fa5a9Sjmcneill 			counts[PCI_INTR_TYPE_INTX] = intx_count;
251187fa5a9Sjmcneill 		return 0;
252187fa5a9Sjmcneill 	}
253187fa5a9Sjmcneill 
254187fa5a9Sjmcneill 	return error;
255187fa5a9Sjmcneill }
256187fa5a9Sjmcneill 
257187fa5a9Sjmcneill void
pci_intr_release(pci_chipset_tag_t pc,pci_intr_handle_t * pih,int count)258187fa5a9Sjmcneill pci_intr_release(pci_chipset_tag_t pc, pci_intr_handle_t *pih, int count)
259187fa5a9Sjmcneill {
2605cb5ed74Sjmcneill 	struct arm_pci_msi *msi = NULL;
261187fa5a9Sjmcneill 
262187fa5a9Sjmcneill 	if (pih == NULL || count == 0)
263187fa5a9Sjmcneill 		return;
264187fa5a9Sjmcneill 
2655cb5ed74Sjmcneill 	if ((pih[0] & (ARM_PCI_INTR_MSIX|ARM_PCI_INTR_MSI)) != 0) {
266187fa5a9Sjmcneill 		msi = arm_pci_msi_find_frame(pih[0]);
267187fa5a9Sjmcneill 		KASSERT(msi != NULL);
268187fa5a9Sjmcneill 		msi->msi_intr_release(msi, pih, count);
2695cb5ed74Sjmcneill 	}
270187fa5a9Sjmcneill 
271187fa5a9Sjmcneill 	kmem_free(pih, sizeof(*pih) * count);
272187fa5a9Sjmcneill }
273187fa5a9Sjmcneill 
274187fa5a9Sjmcneill pci_intr_type_t
pci_intr_type(pci_chipset_tag_t pc,pci_intr_handle_t ih)275187fa5a9Sjmcneill pci_intr_type(pci_chipset_tag_t pc, pci_intr_handle_t ih)
276187fa5a9Sjmcneill {
277187fa5a9Sjmcneill 	if (ih & ARM_PCI_INTR_MSIX)
278187fa5a9Sjmcneill 		return PCI_INTR_TYPE_MSIX;
279187fa5a9Sjmcneill 
280187fa5a9Sjmcneill 	if (ih & ARM_PCI_INTR_MSI)
281187fa5a9Sjmcneill 		return PCI_INTR_TYPE_MSI;
282187fa5a9Sjmcneill 
283187fa5a9Sjmcneill 	return PCI_INTR_TYPE_INTX;
284187fa5a9Sjmcneill }
285