xref: /openbsd-src/sys/arch/arm64/dev/pci_machdep.c (revision 35ee0dc20abac1f6f4050bebf66979f24e1ce316)
1*35ee0dc2Spatrick /*	$OpenBSD: pci_machdep.c,v 1.7 2024/07/05 22:53:57 patrick Exp $	*/
2a569ea7cSkettenis 
3a569ea7cSkettenis /*
4a569ea7cSkettenis  * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
5a569ea7cSkettenis  *
6a569ea7cSkettenis  * Permission to use, copy, modify, and distribute this software for any
7a569ea7cSkettenis  * purpose with or without fee is hereby granted, provided that the above
8a569ea7cSkettenis  * copyright notice and this permission notice appear in all copies.
9a569ea7cSkettenis  *
10a569ea7cSkettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a569ea7cSkettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a569ea7cSkettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a569ea7cSkettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a569ea7cSkettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a569ea7cSkettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a569ea7cSkettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a569ea7cSkettenis  */
18a569ea7cSkettenis 
19a569ea7cSkettenis #include <sys/param.h>
20a569ea7cSkettenis #include <sys/systm.h>
21a569ea7cSkettenis 
22a569ea7cSkettenis #include <machine/bus.h>
23a569ea7cSkettenis 
24a569ea7cSkettenis #include <dev/pci/pcivar.h>
25a569ea7cSkettenis #include <dev/pci/pcireg.h>
26a569ea7cSkettenis 
2756d02c00Skettenis int
pci_intr_enable_msivec(struct pci_attach_args * pa,int num_vec)2856d02c00Skettenis pci_intr_enable_msivec(struct pci_attach_args *pa, int num_vec)
2956d02c00Skettenis {
3056d02c00Skettenis 	pci_chipset_tag_t pc = pa->pa_pc;
3156d02c00Skettenis 	pcitag_t tag = pa->pa_tag;
3256d02c00Skettenis 	pcireg_t reg;
3356d02c00Skettenis 	int mmc, mme, off;
3456d02c00Skettenis 
35*35ee0dc2Spatrick 	if ((pa->pa_flags & PCI_FLAGS_MSIVEC_ENABLED) == 0 ||
3656d02c00Skettenis 	    pci_get_capability(pc, tag, PCI_CAP_MSI, &off, &reg) == 0)
3756d02c00Skettenis 		return 1;
3856d02c00Skettenis 
3956d02c00Skettenis 	mmc = ((reg & PCI_MSI_MC_MMC_MASK) >> PCI_MSI_MC_MMC_SHIFT);
4056d02c00Skettenis 	if (num_vec > (1 << mmc))
4156d02c00Skettenis 		return 1;
4256d02c00Skettenis 
4356d02c00Skettenis 	mme = ((reg & PCI_MSI_MC_MME_MASK) >> PCI_MSI_MC_MME_SHIFT);
4456d02c00Skettenis 	while ((1 << mme) < num_vec)
4556d02c00Skettenis 		mme++;
4656d02c00Skettenis 	reg &= ~PCI_MSI_MC_MME_MASK;
4756d02c00Skettenis 	reg |= (mme << PCI_MSI_MC_MME_SHIFT);
4856d02c00Skettenis 	pci_conf_write(pc, tag, off, reg);
4956d02c00Skettenis 
5056d02c00Skettenis 	return 0;
5156d02c00Skettenis }
5256d02c00Skettenis 
53a569ea7cSkettenis void
pci_msi_enable(pci_chipset_tag_t pc,pcitag_t tag,bus_addr_t addr,uint32_t data)54a569ea7cSkettenis pci_msi_enable(pci_chipset_tag_t pc, pcitag_t tag,
55a569ea7cSkettenis     bus_addr_t addr, uint32_t data)
56a569ea7cSkettenis {
57a569ea7cSkettenis 	pcireg_t reg;
5856d02c00Skettenis 	int mme, off;
59a569ea7cSkettenis 
60a569ea7cSkettenis 	if (pci_get_capability(pc, tag, PCI_CAP_MSI, &off, &reg) == 0)
61a569ea7cSkettenis 		panic("%s: no msi capability", __func__);
62a569ea7cSkettenis 
6356d02c00Skettenis 	mme = ((reg & PCI_MSI_MC_MME_MASK) >> PCI_MSI_MC_MME_SHIFT);
6456d02c00Skettenis 	data &= ~((1 << mme) - 1);
6556d02c00Skettenis 
66a569ea7cSkettenis 	if (reg & PCI_MSI_MC_C64) {
67a569ea7cSkettenis 		pci_conf_write(pc, tag, off + PCI_MSI_MA, addr);
68a569ea7cSkettenis 		pci_conf_write(pc, tag, off + PCI_MSI_MAU32, addr >> 32);
69a569ea7cSkettenis 		pci_conf_write(pc, tag, off + PCI_MSI_MD64, data);
70a569ea7cSkettenis 	} else {
71a569ea7cSkettenis 		pci_conf_write(pc, tag, off + PCI_MSI_MA, addr);
72a569ea7cSkettenis 		pci_conf_write(pc, tag, off + PCI_MSI_MD32, data);
73a569ea7cSkettenis 	}
74a569ea7cSkettenis 	pci_conf_write(pc, tag, off, reg | PCI_MSI_MC_MSIE);
75a569ea7cSkettenis }
76a569ea7cSkettenis 
774211e504Skettenis int
pci_msix_table_map(pci_chipset_tag_t pc,pcitag_t tag,bus_space_tag_t memt,bus_space_handle_t * memh)784211e504Skettenis pci_msix_table_map(pci_chipset_tag_t pc, pcitag_t tag,
794211e504Skettenis     bus_space_tag_t memt, bus_space_handle_t *memh)
80a569ea7cSkettenis {
81a569ea7cSkettenis 	bus_addr_t base;
82a569ea7cSkettenis 	pcireg_t reg, table, type;
83a569ea7cSkettenis 	int bir, offset;
84a569ea7cSkettenis 	int off, tblsz;
85a569ea7cSkettenis 
86a569ea7cSkettenis 	if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, &reg) == 0)
87a569ea7cSkettenis 		panic("%s: no msix capability", __func__);
88a569ea7cSkettenis 
89a569ea7cSkettenis 	table = pci_conf_read(pc, tag, off + PCI_MSIX_TABLE);
90a569ea7cSkettenis 	bir = (table & PCI_MSIX_TABLE_BIR);
91a569ea7cSkettenis 	offset = (table & PCI_MSIX_TABLE_OFF);
92a569ea7cSkettenis 	tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
93a569ea7cSkettenis 
94a569ea7cSkettenis 	bir = PCI_MAPREG_START + bir * 4;
95a569ea7cSkettenis 	type = pci_mapreg_type(pc, tag, bir);
96a569ea7cSkettenis 	if (pci_mapreg_info(pc, tag, bir, type, &base, NULL, NULL) ||
974211e504Skettenis 	    bus_space_map(memt, base + offset, tblsz * 16, 0, memh))
984211e504Skettenis 		return -1;
994211e504Skettenis 
1004211e504Skettenis 	return 0;
1014211e504Skettenis }
1024211e504Skettenis 
1034211e504Skettenis void
pci_msix_table_unmap(pci_chipset_tag_t pc,pcitag_t tag,bus_space_tag_t memt,bus_space_handle_t memh)1044211e504Skettenis pci_msix_table_unmap(pci_chipset_tag_t pc, pcitag_t tag,
1054211e504Skettenis     bus_space_tag_t memt, bus_space_handle_t memh)
1064211e504Skettenis {
1074211e504Skettenis 	pcireg_t reg;
1084211e504Skettenis 	int tblsz;
1094211e504Skettenis 
1104211e504Skettenis 	if (pci_get_capability(pc, tag, PCI_CAP_MSIX, NULL, &reg) == 0)
1114211e504Skettenis 		panic("%s: no msix capability", __func__);
1124211e504Skettenis 
1134211e504Skettenis 	tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
1144211e504Skettenis 	bus_space_unmap(memt, memh, tblsz * 16);
1154211e504Skettenis }
1164211e504Skettenis 
1174211e504Skettenis void
pci_msix_enable(pci_chipset_tag_t pc,pcitag_t tag,bus_space_tag_t memt,int vec,bus_addr_t addr,uint32_t data)1184211e504Skettenis pci_msix_enable(pci_chipset_tag_t pc, pcitag_t tag, bus_space_tag_t memt,
1194211e504Skettenis     int vec, bus_addr_t addr, uint32_t data)
1204211e504Skettenis {
1214211e504Skettenis 	bus_space_handle_t memh;
1224211e504Skettenis 	pcireg_t reg;
1234211e504Skettenis 	uint32_t ctrl;
1244211e504Skettenis 	int off;
1254211e504Skettenis 
1264211e504Skettenis 	if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, &reg) == 0)
1274211e504Skettenis 		panic("%s: no msix capability", __func__);
1284211e504Skettenis 
1294211e504Skettenis 	KASSERT(vec <= PCI_MSIX_MC_TBLSZ(reg));
1304211e504Skettenis 
1314211e504Skettenis 	if (pci_msix_table_map(pc, tag, memt, &memh))
132a569ea7cSkettenis 		panic("%s: cannot map registers", __func__);
133a569ea7cSkettenis 
134a569ea7cSkettenis 	bus_space_write_4(memt, memh, PCI_MSIX_MA(vec), addr);
135a569ea7cSkettenis 	bus_space_write_4(memt, memh, PCI_MSIX_MAU32(vec), addr >> 32);
136a569ea7cSkettenis 	bus_space_write_4(memt, memh, PCI_MSIX_MD(vec), data);
137a569ea7cSkettenis 	bus_space_barrier(memt, memh, PCI_MSIX_MA(vec), 16,
138a569ea7cSkettenis 	    BUS_SPACE_BARRIER_WRITE);
139a569ea7cSkettenis 	ctrl = bus_space_read_4(memt, memh, PCI_MSIX_VC(vec));
140a569ea7cSkettenis 	bus_space_write_4(memt, memh, PCI_MSIX_VC(vec),
141a569ea7cSkettenis 	    ctrl & ~PCI_MSIX_VC_MASK);
142a569ea7cSkettenis 
1434211e504Skettenis 	pci_msix_table_unmap(pc, tag, memt, memh);
144a569ea7cSkettenis 
145a569ea7cSkettenis 	pci_conf_write(pc, tag, off, reg | PCI_MSIX_MC_MSIXE);
146a569ea7cSkettenis }
147809fc743Skettenis 
148809fc743Skettenis int
_pci_intr_map_msi(struct pci_attach_args * pa,pci_intr_handle_t * ihp)149809fc743Skettenis _pci_intr_map_msi(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
150809fc743Skettenis {
151809fc743Skettenis 	pci_chipset_tag_t pc = pa->pa_pc;
152809fc743Skettenis 	pcitag_t tag = pa->pa_tag;
153809fc743Skettenis 
154809fc743Skettenis 	if ((pa->pa_flags & PCI_FLAGS_MSI_ENABLED) == 0 ||
155809fc743Skettenis 	    pci_get_capability(pc, tag, PCI_CAP_MSI, NULL, NULL) == 0)
156809fc743Skettenis 		return -1;
157809fc743Skettenis 
158809fc743Skettenis 	ihp->ih_pc = pa->pa_pc;
159809fc743Skettenis 	ihp->ih_tag = pa->pa_tag;
16056d02c00Skettenis 	ihp->ih_intrpin = 0;
16156d02c00Skettenis 	ihp->ih_type = PCI_MSI;
16256d02c00Skettenis 	ihp->ih_dmat = pa->pa_dmat;
16356d02c00Skettenis 
16456d02c00Skettenis 	return 0;
16556d02c00Skettenis }
16656d02c00Skettenis 
16756d02c00Skettenis int
_pci_intr_map_msivec(struct pci_attach_args * pa,int vec,pci_intr_handle_t * ihp)16856d02c00Skettenis _pci_intr_map_msivec(struct pci_attach_args *pa, int vec,
16956d02c00Skettenis     pci_intr_handle_t *ihp)
17056d02c00Skettenis {
17156d02c00Skettenis 	pci_chipset_tag_t pc = pa->pa_pc;
17256d02c00Skettenis 	pcitag_t tag = pa->pa_tag;
17356d02c00Skettenis 	pcireg_t reg;
17456d02c00Skettenis 	int mme, off;
17556d02c00Skettenis 
17656d02c00Skettenis 	if ((pa->pa_flags & PCI_FLAGS_MSIVEC_ENABLED) == 0 ||
17756d02c00Skettenis 	    pci_get_capability(pc, tag, PCI_CAP_MSI, &off, &reg) == 0)
17856d02c00Skettenis 		return -1;
17956d02c00Skettenis 
18056d02c00Skettenis 	mme = ((reg & PCI_MSI_MC_MME_MASK) >> PCI_MSI_MC_MME_SHIFT);
18156d02c00Skettenis 	if (vec >= (1 << mme))
18256d02c00Skettenis 		return -1;
18356d02c00Skettenis 
18456d02c00Skettenis 	ihp->ih_pc = pa->pa_pc;
18556d02c00Skettenis 	ihp->ih_tag = pa->pa_tag;
18656d02c00Skettenis 	ihp->ih_intrpin = vec;
187809fc743Skettenis 	ihp->ih_type = PCI_MSI;
188cc0ede06Spatrick 	ihp->ih_dmat = pa->pa_dmat;
189809fc743Skettenis 
190809fc743Skettenis 	return 0;
191809fc743Skettenis }
192809fc743Skettenis 
193809fc743Skettenis int
_pci_intr_map_msix(struct pci_attach_args * pa,int vec,pci_intr_handle_t * ihp)194809fc743Skettenis _pci_intr_map_msix(struct pci_attach_args *pa, int vec,
195809fc743Skettenis     pci_intr_handle_t *ihp)
196809fc743Skettenis {
197809fc743Skettenis 	pci_chipset_tag_t pc = pa->pa_pc;
198809fc743Skettenis 	pcitag_t tag = pa->pa_tag;
1991bdb1c9cSkettenis 	pcireg_t reg, table, type;
2001bdb1c9cSkettenis 	int bir, off;
201809fc743Skettenis 
202809fc743Skettenis 	if ((pa->pa_flags & PCI_FLAGS_MSI_ENABLED) == 0 ||
2031bdb1c9cSkettenis 	    pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, &reg) == 0)
204809fc743Skettenis 		return -1;
205809fc743Skettenis 
206809fc743Skettenis 	if (vec > PCI_MSIX_MC_TBLSZ(reg))
207809fc743Skettenis 		return -1;
208809fc743Skettenis 
2091bdb1c9cSkettenis 	table = pci_conf_read(pc, tag, off + PCI_MSIX_TABLE);
2101bdb1c9cSkettenis 	bir = PCI_MAPREG_START + (table & PCI_MSIX_TABLE_BIR) * 4;
2111bdb1c9cSkettenis 	type = pci_mapreg_type(pc, tag, bir);
2121bdb1c9cSkettenis 	if (pci_mapreg_assign(pa, bir, type, NULL, NULL))
2131bdb1c9cSkettenis 		return -1;
2141bdb1c9cSkettenis 
215809fc743Skettenis 	ihp->ih_pc = pa->pa_pc;
216809fc743Skettenis 	ihp->ih_tag = pa->pa_tag;
217809fc743Skettenis 	ihp->ih_intrpin = vec;
218809fc743Skettenis 	ihp->ih_type = PCI_MSIX;
219cc0ede06Spatrick 	ihp->ih_dmat = pa->pa_dmat;
220809fc743Skettenis 
221809fc743Skettenis 	return 0;
222809fc743Skettenis }
223