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, ®) == 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, ®) == 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, ®) == 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, ®) == 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, ®) == 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, ®) == 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, ®) == 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