1/*	$OpenBSD: pci_machdep.c,v 1.2 2024/02/03 10:37:26 kettenis Exp $	*/
2
3/*
4 * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/param.h>
20#include <sys/systm.h>
21
22#include <machine/bus.h>
23
24#include <dev/pci/pcivar.h>
25#include <dev/pci/pcireg.h>
26
27void
28pci_msi_enable(pci_chipset_tag_t pc, pcitag_t tag,
29    bus_addr_t addr, uint32_t data)
30{
31	pcireg_t reg;
32	int off;
33
34	if (pci_get_capability(pc, tag, PCI_CAP_MSI, &off, &reg) == 0)
35		panic("%s: no msi capability", __func__);
36
37	if (reg & PCI_MSI_MC_C64) {
38		pci_conf_write(pc, tag, off + PCI_MSI_MA, addr);
39		pci_conf_write(pc, tag, off + PCI_MSI_MAU32, addr >> 32);
40		pci_conf_write(pc, tag, off + PCI_MSI_MD64, data);
41	} else {
42		pci_conf_write(pc, tag, off + PCI_MSI_MA, addr);
43		pci_conf_write(pc, tag, off + PCI_MSI_MD32, data);
44	}
45	pci_conf_write(pc, tag, off, reg | PCI_MSI_MC_MSIE);
46}
47
48int
49pci_msix_table_map(pci_chipset_tag_t pc, pcitag_t tag,
50    bus_space_tag_t memt, bus_space_handle_t *memh)
51{
52	bus_addr_t base;
53	pcireg_t reg, table, type;
54	int bir, offset;
55	int off, tblsz;
56
57	if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, &reg) == 0)
58		panic("%s: no msix capability", __func__);
59
60	table = pci_conf_read(pc, tag, off + PCI_MSIX_TABLE);
61	bir = (table & PCI_MSIX_TABLE_BIR);
62	offset = (table & PCI_MSIX_TABLE_OFF);
63	tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
64
65	bir = PCI_MAPREG_START + bir * 4;
66	type = pci_mapreg_type(pc, tag, bir);
67	if (pci_mapreg_info(pc, tag, bir, type, &base, NULL, NULL) ||
68	    bus_space_map(memt, base + offset, tblsz * 16, 0, memh))
69		return -1;
70
71	return 0;
72}
73
74void
75pci_msix_table_unmap(pci_chipset_tag_t pc, pcitag_t tag,
76    bus_space_tag_t memt, bus_space_handle_t memh)
77{
78	pcireg_t reg;
79	int tblsz;
80
81	if (pci_get_capability(pc, tag, PCI_CAP_MSIX, NULL, &reg) == 0)
82		panic("%s: no msix capability", __func__);
83
84	tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
85	bus_space_unmap(memt, memh, tblsz * 16);
86}
87
88void
89pci_msix_enable(pci_chipset_tag_t pc, pcitag_t tag, bus_space_tag_t memt,
90    int vec, bus_addr_t addr, uint32_t data)
91{
92	bus_space_handle_t memh;
93	pcireg_t reg;
94	uint32_t ctrl;
95	int off;
96
97	if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, &reg) == 0)
98		panic("%s: no msix capability", __func__);
99
100	KASSERT(vec <= PCI_MSIX_MC_TBLSZ(reg));
101
102	if (pci_msix_table_map(pc, tag, memt, &memh))
103		panic("%s: cannot map registers", __func__);
104
105	bus_space_write_4(memt, memh, PCI_MSIX_MA(vec), addr);
106	bus_space_write_4(memt, memh, PCI_MSIX_MAU32(vec), addr >> 32);
107	bus_space_write_4(memt, memh, PCI_MSIX_MD(vec), data);
108	bus_space_barrier(memt, memh, PCI_MSIX_MA(vec), 16,
109	    BUS_SPACE_BARRIER_WRITE);
110	ctrl = bus_space_read_4(memt, memh, PCI_MSIX_VC(vec));
111	bus_space_write_4(memt, memh, PCI_MSIX_VC(vec),
112	    ctrl & ~PCI_MSIX_VC_MASK);
113
114	pci_msix_table_unmap(pc, tag, memt, memh);
115
116	pci_conf_write(pc, tag, off, reg | PCI_MSIX_MC_MSIXE);
117}
118
119int
120_pci_intr_map_msi(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
121{
122	pci_chipset_tag_t pc = pa->pa_pc;
123	pcitag_t tag = pa->pa_tag;
124
125	if ((pa->pa_flags & PCI_FLAGS_MSI_ENABLED) == 0 ||
126	    pci_get_capability(pc, tag, PCI_CAP_MSI, NULL, NULL) == 0)
127		return -1;
128
129	ihp->ih_pc = pa->pa_pc;
130	ihp->ih_tag = pa->pa_tag;
131	ihp->ih_type = PCI_MSI;
132	ihp->ih_dmat = pa->pa_dmat;
133
134	return 0;
135}
136
137int
138_pci_intr_map_msivec(struct pci_attach_args *pa, int vec,
139    pci_intr_handle_t *ihp)
140{
141	return -1;
142}
143
144int
145_pci_intr_map_msix(struct pci_attach_args *pa, int vec,
146    pci_intr_handle_t *ihp)
147{
148	pci_chipset_tag_t pc = pa->pa_pc;
149	pcitag_t tag = pa->pa_tag;
150	pcireg_t reg, table, type;
151	int bir, off;
152
153	if ((pa->pa_flags & PCI_FLAGS_MSI_ENABLED) == 0 ||
154	    pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, &reg) == 0)
155		return -1;
156
157	if (vec > PCI_MSIX_MC_TBLSZ(reg))
158		return -1;
159
160	table = pci_conf_read(pc, tag, off + PCI_MSIX_TABLE);
161	bir = PCI_MAPREG_START + (table & PCI_MSIX_TABLE_BIR) * 4;
162	type = pci_mapreg_type(pc, tag, bir);
163	if (pci_mapreg_assign(pa, bir, type, NULL, NULL))
164		return -1;
165
166	ihp->ih_pc = pa->pa_pc;
167	ihp->ih_tag = pa->pa_tag;
168	ihp->ih_intrpin = vec;
169	ihp->ih_type = PCI_MSIX;
170	ihp->ih_dmat = pa->pa_dmat;
171
172	return 0;
173}
174
175