1 /* $OpenBSD: pci_machdep.c,v 1.7 2024/07/05 22:53:57 patrick 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
27 int
pci_intr_enable_msivec(struct pci_attach_args * pa,int num_vec)28 pci_intr_enable_msivec(struct pci_attach_args *pa, int num_vec)
29 {
30 pci_chipset_tag_t pc = pa->pa_pc;
31 pcitag_t tag = pa->pa_tag;
32 pcireg_t reg;
33 int mmc, mme, off;
34
35 if ((pa->pa_flags & PCI_FLAGS_MSIVEC_ENABLED) == 0 ||
36 pci_get_capability(pc, tag, PCI_CAP_MSI, &off, ®) == 0)
37 return 1;
38
39 mmc = ((reg & PCI_MSI_MC_MMC_MASK) >> PCI_MSI_MC_MMC_SHIFT);
40 if (num_vec > (1 << mmc))
41 return 1;
42
43 mme = ((reg & PCI_MSI_MC_MME_MASK) >> PCI_MSI_MC_MME_SHIFT);
44 while ((1 << mme) < num_vec)
45 mme++;
46 reg &= ~PCI_MSI_MC_MME_MASK;
47 reg |= (mme << PCI_MSI_MC_MME_SHIFT);
48 pci_conf_write(pc, tag, off, reg);
49
50 return 0;
51 }
52
53 void
pci_msi_enable(pci_chipset_tag_t pc,pcitag_t tag,bus_addr_t addr,uint32_t data)54 pci_msi_enable(pci_chipset_tag_t pc, pcitag_t tag,
55 bus_addr_t addr, uint32_t data)
56 {
57 pcireg_t reg;
58 int mme, off;
59
60 if (pci_get_capability(pc, tag, PCI_CAP_MSI, &off, ®) == 0)
61 panic("%s: no msi capability", __func__);
62
63 mme = ((reg & PCI_MSI_MC_MME_MASK) >> PCI_MSI_MC_MME_SHIFT);
64 data &= ~((1 << mme) - 1);
65
66 if (reg & PCI_MSI_MC_C64) {
67 pci_conf_write(pc, tag, off + PCI_MSI_MA, addr);
68 pci_conf_write(pc, tag, off + PCI_MSI_MAU32, addr >> 32);
69 pci_conf_write(pc, tag, off + PCI_MSI_MD64, data);
70 } else {
71 pci_conf_write(pc, tag, off + PCI_MSI_MA, addr);
72 pci_conf_write(pc, tag, off + PCI_MSI_MD32, data);
73 }
74 pci_conf_write(pc, tag, off, reg | PCI_MSI_MC_MSIE);
75 }
76
77 int
pci_msix_table_map(pci_chipset_tag_t pc,pcitag_t tag,bus_space_tag_t memt,bus_space_handle_t * memh)78 pci_msix_table_map(pci_chipset_tag_t pc, pcitag_t tag,
79 bus_space_tag_t memt, bus_space_handle_t *memh)
80 {
81 bus_addr_t base;
82 pcireg_t reg, table, type;
83 int bir, offset;
84 int off, tblsz;
85
86 if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, ®) == 0)
87 panic("%s: no msix capability", __func__);
88
89 table = pci_conf_read(pc, tag, off + PCI_MSIX_TABLE);
90 bir = (table & PCI_MSIX_TABLE_BIR);
91 offset = (table & PCI_MSIX_TABLE_OFF);
92 tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
93
94 bir = PCI_MAPREG_START + bir * 4;
95 type = pci_mapreg_type(pc, tag, bir);
96 if (pci_mapreg_info(pc, tag, bir, type, &base, NULL, NULL) ||
97 bus_space_map(memt, base + offset, tblsz * 16, 0, memh))
98 return -1;
99
100 return 0;
101 }
102
103 void
pci_msix_table_unmap(pci_chipset_tag_t pc,pcitag_t tag,bus_space_tag_t memt,bus_space_handle_t memh)104 pci_msix_table_unmap(pci_chipset_tag_t pc, pcitag_t tag,
105 bus_space_tag_t memt, bus_space_handle_t memh)
106 {
107 pcireg_t reg;
108 int tblsz;
109
110 if (pci_get_capability(pc, tag, PCI_CAP_MSIX, NULL, ®) == 0)
111 panic("%s: no msix capability", __func__);
112
113 tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
114 bus_space_unmap(memt, memh, tblsz * 16);
115 }
116
117 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)118 pci_msix_enable(pci_chipset_tag_t pc, pcitag_t tag, bus_space_tag_t memt,
119 int vec, bus_addr_t addr, uint32_t data)
120 {
121 bus_space_handle_t memh;
122 pcireg_t reg;
123 uint32_t ctrl;
124 int off;
125
126 if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, ®) == 0)
127 panic("%s: no msix capability", __func__);
128
129 KASSERT(vec <= PCI_MSIX_MC_TBLSZ(reg));
130
131 if (pci_msix_table_map(pc, tag, memt, &memh))
132 panic("%s: cannot map registers", __func__);
133
134 bus_space_write_4(memt, memh, PCI_MSIX_MA(vec), addr);
135 bus_space_write_4(memt, memh, PCI_MSIX_MAU32(vec), addr >> 32);
136 bus_space_write_4(memt, memh, PCI_MSIX_MD(vec), data);
137 bus_space_barrier(memt, memh, PCI_MSIX_MA(vec), 16,
138 BUS_SPACE_BARRIER_WRITE);
139 ctrl = bus_space_read_4(memt, memh, PCI_MSIX_VC(vec));
140 bus_space_write_4(memt, memh, PCI_MSIX_VC(vec),
141 ctrl & ~PCI_MSIX_VC_MASK);
142
143 pci_msix_table_unmap(pc, tag, memt, memh);
144
145 pci_conf_write(pc, tag, off, reg | PCI_MSIX_MC_MSIXE);
146 }
147
148 int
_pci_intr_map_msi(struct pci_attach_args * pa,pci_intr_handle_t * ihp)149 _pci_intr_map_msi(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
150 {
151 pci_chipset_tag_t pc = pa->pa_pc;
152 pcitag_t tag = pa->pa_tag;
153
154 if ((pa->pa_flags & PCI_FLAGS_MSI_ENABLED) == 0 ||
155 pci_get_capability(pc, tag, PCI_CAP_MSI, NULL, NULL) == 0)
156 return -1;
157
158 ihp->ih_pc = pa->pa_pc;
159 ihp->ih_tag = pa->pa_tag;
160 ihp->ih_intrpin = 0;
161 ihp->ih_type = PCI_MSI;
162 ihp->ih_dmat = pa->pa_dmat;
163
164 return 0;
165 }
166
167 int
_pci_intr_map_msivec(struct pci_attach_args * pa,int vec,pci_intr_handle_t * ihp)168 _pci_intr_map_msivec(struct pci_attach_args *pa, int vec,
169 pci_intr_handle_t *ihp)
170 {
171 pci_chipset_tag_t pc = pa->pa_pc;
172 pcitag_t tag = pa->pa_tag;
173 pcireg_t reg;
174 int mme, off;
175
176 if ((pa->pa_flags & PCI_FLAGS_MSIVEC_ENABLED) == 0 ||
177 pci_get_capability(pc, tag, PCI_CAP_MSI, &off, ®) == 0)
178 return -1;
179
180 mme = ((reg & PCI_MSI_MC_MME_MASK) >> PCI_MSI_MC_MME_SHIFT);
181 if (vec >= (1 << mme))
182 return -1;
183
184 ihp->ih_pc = pa->pa_pc;
185 ihp->ih_tag = pa->pa_tag;
186 ihp->ih_intrpin = vec;
187 ihp->ih_type = PCI_MSI;
188 ihp->ih_dmat = pa->pa_dmat;
189
190 return 0;
191 }
192
193 int
_pci_intr_map_msix(struct pci_attach_args * pa,int vec,pci_intr_handle_t * ihp)194 _pci_intr_map_msix(struct pci_attach_args *pa, int vec,
195 pci_intr_handle_t *ihp)
196 {
197 pci_chipset_tag_t pc = pa->pa_pc;
198 pcitag_t tag = pa->pa_tag;
199 pcireg_t reg, table, type;
200 int bir, off;
201
202 if ((pa->pa_flags & PCI_FLAGS_MSI_ENABLED) == 0 ||
203 pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, ®) == 0)
204 return -1;
205
206 if (vec > PCI_MSIX_MC_TBLSZ(reg))
207 return -1;
208
209 table = pci_conf_read(pc, tag, off + PCI_MSIX_TABLE);
210 bir = PCI_MAPREG_START + (table & PCI_MSIX_TABLE_BIR) * 4;
211 type = pci_mapreg_type(pc, tag, bir);
212 if (pci_mapreg_assign(pa, bir, type, NULL, NULL))
213 return -1;
214
215 ihp->ih_pc = pa->pa_pc;
216 ihp->ih_tag = pa->pa_tag;
217 ihp->ih_intrpin = vec;
218 ihp->ih_type = PCI_MSIX;
219 ihp->ih_dmat = pa->pa_dmat;
220
221 return 0;
222 }
223