2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2021 Alexander Motin <mav@FreeBSD.org>
5 * Copyright 2019 Cisco Systems, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/types.h>
34 #include <sys/param.h>
37 #include <sys/kernel.h>
38 #include <sys/limits.h>
39 #include <sys/module.h>
40 #include <sys/sysctl.h>
41 #include <sys/systm.h>
42 #include <sys/malloc.h>
44 #include <machine/bus.h>
45 #include <machine/resource.h>
46 #include <machine/intr_machdep.h>
49 #include <sys/mutex.h>
51 #include <sys/pciio.h>
52 #include <dev/pci/pcivar.h>
53 #include <dev/pci/pcireg.h>
54 #include <dev/pci/pci_private.h>
55 #include <dev/pci/pcib_private.h>
57 #include <dev/vmd/vmd.h>
66 #define BUS_RESTRICT 1
67 #define VECTOR_OFFSET 2
71 #define VMD_BUS_RESTRICT 0x1
73 #define VMD_CONFIG 0x44
74 #define VMD_BUS_START(x) ((x >> 8) & 0x3)
78 SYSCTL_NODE(_hw, OID_AUTO, vmd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
79 "Intel Volume Management Device tuning parameters");
82 * All MSIs within a group share address, so VMD can't distinguish them.
83 * It makes no sense to use more than one per device, only if required by
84 * some specific device drivers.
86 static int vmd_max_msi = 1;
87 SYSCTL_INT(_hw_vmd, OID_AUTO, max_msi, CTLFLAG_RWTUN, &vmd_max_msi, 0,
88 "Maximum number of MSI vectors per device");
91 * MSI-X can use different addresses, but we have limited number of MSI-X
92 * we can route to, so use conservative default to try to avoid sharing.
94 static int vmd_max_msix = 3;
95 SYSCTL_INT(_hw_vmd, OID_AUTO, max_msix, CTLFLAG_RWTUN, &vmd_max_msix, 0,
96 "Maximum number of MSI-X vectors per device");
98 static struct vmd_type vmd_devs[] = {
99 { 0x8086, 0x201d, "Intel Volume Management Device", 0 },
100 { 0x8086, 0x28c0, "Intel Volume Management Device", BUS_RESTRICT },
101 { 0x8086, 0x467f, "Intel Volume Management Device", BUS_RESTRICT | VECTOR_OFFSET },
102 { 0x8086, 0x4c3d, "Intel Volume Management Device", BUS_RESTRICT | VECTOR_OFFSET },
103 { 0x8086, 0x9a0b, "Intel Volume Management Device", BUS_RESTRICT | VECTOR_OFFSET },
108 vmd_probe(device_t dev)
113 vid = pci_get_vendor(dev);
114 did = pci_get_device(dev);
115 for (t = vmd_devs; t->vmd_name != NULL; t++) {
116 if (vid == t->vmd_vid && did == t->vmd_did) {
117 device_set_desc(dev, t->vmd_name);
118 return (BUS_PROBE_DEFAULT);
125 vmd_free(struct vmd_softc *sc)
128 struct vmd_irq_user *u;
131 if (sc->psc.bus.rman.rm_end != 0)
132 rman_fini(&sc->psc.bus.rman);
133 if (sc->psc.mem.rman.rm_end != 0)
134 rman_fini(&sc->psc.mem.rman);
135 while ((u = LIST_FIRST(&sc->vmd_users)) != NULL) {
136 LIST_REMOVE(u, viu_link);
139 if (sc->vmd_irq != NULL) {
140 for (i = 0; i < sc->vmd_msix_count; i++) {
141 vi = &sc->vmd_irq[i];
142 if (vi->vi_res == NULL)
144 bus_teardown_intr(sc->psc.dev, vi->vi_res,
146 bus_release_resource(sc->psc.dev, SYS_RES_IRQ,
147 vi->vi_rid, vi->vi_res);
150 free(sc->vmd_irq, M_DEVBUF);
152 pci_release_msi(sc->psc.dev);
153 for (i = 0; i < VMD_MAX_BAR; i++) {
154 if (sc->vmd_regs_res[i] != NULL)
155 bus_release_resource(sc->psc.dev, SYS_RES_MEMORY,
156 sc->vmd_regs_rid[i], sc->vmd_regs_res[i]);
160 /* Hidden PCI Roots are hidden in BAR(0). */
163 vmd_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width)
165 struct vmd_softc *sc;
168 sc = device_get_softc(dev);
169 if (b < sc->vmd_bus_start || b > sc->vmd_bus_end)
172 offset = ((b - sc->vmd_bus_start) << 20) + (s << 15) + (f << 12) + reg;
176 return (bus_space_read_4(sc->vmd_btag, sc->vmd_bhandle,
179 return (bus_space_read_2(sc->vmd_btag, sc->vmd_bhandle,
182 return (bus_space_read_1(sc->vmd_btag, sc->vmd_bhandle,
185 __assert_unreachable();
191 vmd_write_config(device_t dev, u_int b, u_int s, u_int f, u_int reg,
192 uint32_t val, int width)
194 struct vmd_softc *sc;
197 sc = device_get_softc(dev);
198 if (b < sc->vmd_bus_start || b > sc->vmd_bus_end)
201 offset = ((b - sc->vmd_bus_start) << 20) + (s << 15) + (f << 12) + reg;
205 return (bus_space_write_4(sc->vmd_btag, sc->vmd_bhandle,
208 return (bus_space_write_2(sc->vmd_btag, sc->vmd_bhandle,
211 return (bus_space_write_1(sc->vmd_btag, sc->vmd_bhandle,
214 __assert_unreachable();
222 * We have nothing to do here, but we have to register some interrupt
223 * handler to make PCI code setup and enable the MSI-X vector.
225 return (FILTER_STRAY);
229 vmd_attach(device_t dev)
231 struct vmd_softc *sc;
232 struct pcib_secbus *bus;
233 struct pcib_window *w;
241 sc = device_get_softc(dev);
242 bzero(sc, sizeof(*sc));
244 sc->psc.domain = PCI_DOMAINMAX - device_get_unit(dev);
246 pci_enable_busmaster(dev);
248 for (i = 0, j = 0; i < VMD_MAX_BAR; i++, j++) {
249 sc->vmd_regs_rid[i] = PCIR_BAR(j);
250 bar = pci_read_config(dev, PCIR_BAR(0), 4);
251 if (PCI_BAR_MEM(bar) && (bar & PCIM_BAR_MEM_TYPE) ==
254 if ((sc->vmd_regs_res[i] = bus_alloc_resource_any(dev,
255 SYS_RES_MEMORY, &sc->vmd_regs_rid[i], RF_ACTIVE)) == NULL) {
256 device_printf(dev, "Cannot allocate resources\n");
261 sc->vmd_btag = rman_get_bustag(sc->vmd_regs_res[0]);
262 sc->vmd_bhandle = rman_get_bushandle(sc->vmd_regs_res[0]);
264 vid = pci_get_vendor(dev);
265 did = pci_get_device(dev);
266 for (t = vmd_devs; t->vmd_name != NULL; t++) {
267 if (vid == t->vmd_vid && did == t->vmd_did)
271 sc->vmd_bus_start = 0;
272 if ((t->flags & BUS_RESTRICT) &&
273 (pci_read_config(dev, VMD_CAP, 2) & VMD_BUS_RESTRICT)) {
274 switch (VMD_BUS_START(pci_read_config(dev, VMD_CONFIG, 2))) {
276 sc->vmd_bus_start = 0;
279 sc->vmd_bus_start = 128;
282 sc->vmd_bus_start = 224;
285 device_printf(dev, "Unknown bus offset\n");
289 sc->vmd_bus_end = MIN(PCI_BUSMAX, sc->vmd_bus_start +
290 (rman_get_size(sc->vmd_regs_res[0]) >> 20) - 1);
293 bus->sec = sc->vmd_bus_start;
294 bus->sub = sc->vmd_bus_end;
296 bus->rman.rm_start = 0;
297 bus->rman.rm_end = PCI_BUSMAX;
298 bus->rman.rm_type = RMAN_ARRAY;
299 snprintf(buf, sizeof(buf), "%s bus numbers", device_get_nameunit(dev));
300 bus->rman.rm_descr = strdup(buf, M_DEVBUF);
301 error = rman_init(&bus->rman);
303 device_printf(dev, "Failed to initialize bus rman\n");
304 bus->rman.rm_end = 0;
307 error = rman_manage_region(&bus->rman, sc->vmd_bus_start,
310 device_printf(dev, "Failed to add resource to bus rman\n");
315 w->rman.rm_type = RMAN_ARRAY;
316 snprintf(buf, sizeof(buf), "%s memory window", device_get_nameunit(dev));
317 w->rman.rm_descr = strdup(buf, M_DEVBUF);
318 error = rman_init(&w->rman);
320 device_printf(dev, "Failed to initialize memory rman\n");
324 error = rman_manage_region(&w->rman,
325 rman_get_start(sc->vmd_regs_res[1]),
326 rman_get_end(sc->vmd_regs_res[1]));
328 device_printf(dev, "Failed to add resource to memory rman\n");
331 error = rman_manage_region(&w->rman,
332 rman_get_start(sc->vmd_regs_res[2]) + 0x2000,
333 rman_get_end(sc->vmd_regs_res[2]));
335 device_printf(dev, "Failed to add resource to memory rman\n");
339 LIST_INIT(&sc->vmd_users);
340 sc->vmd_fist_vector = (t->flags & VECTOR_OFFSET) ? 1 : 0;
341 sc->vmd_msix_count = pci_msix_count(dev);
342 if (pci_alloc_msix(dev, &sc->vmd_msix_count) == 0) {
343 sc->vmd_irq = malloc(sizeof(struct vmd_irq) *
344 sc->vmd_msix_count, M_DEVBUF, M_WAITOK | M_ZERO);
345 for (i = 0; i < sc->vmd_msix_count; i++) {
346 vi = &sc->vmd_irq[i];
348 vi->vi_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
349 &vi->vi_rid, RF_ACTIVE | RF_SHAREABLE);
350 if (vi->vi_res == NULL) {
351 device_printf(dev, "Failed to allocate irq\n");
354 vi->vi_irq = rman_get_start(vi->vi_res);
355 if (bus_setup_intr(dev, vi->vi_res, INTR_TYPE_MISC |
356 INTR_MPSAFE, vmd_intr, NULL, vi, &vi->vi_handle)) {
357 device_printf(dev, "Can't set up interrupt\n");
358 bus_release_resource(dev, SYS_RES_IRQ,
359 vi->vi_rid, vi->vi_res);
366 sc->vmd_dma_tag = bus_get_dma_tag(dev);
368 sc->psc.child = device_add_child(dev, "pci", -1);
369 return (bus_generic_attach(dev));
377 vmd_detach(device_t dev)
379 struct vmd_softc *sc = device_get_softc(dev);
382 error = bus_generic_detach(dev);
385 error = device_delete_children(dev);
393 vmd_get_dma_tag(device_t dev, device_t child)
395 struct vmd_softc *sc = device_get_softc(dev);
397 return (sc->vmd_dma_tag);
400 static struct resource *
401 vmd_alloc_resource(device_t dev, device_t child, int type, int *rid,
402 rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
404 struct vmd_softc *sc = device_get_softc(dev);
405 struct resource *res;
409 /* VMD harwdare does not support legacy interrupts. */
412 return (bus_generic_alloc_resource(dev, child, type, rid,
413 start, end, count, flags | RF_SHAREABLE));
415 res = rman_reserve_resource(&sc->psc.mem.rman, start, end,
416 count, flags, child);
421 "allocated memory range (%#jx-%#jx) for rid %d of %s\n",
422 rman_get_start(res), rman_get_end(res), *rid,
423 pcib_child_name(child));
426 res = rman_reserve_resource(&sc->psc.bus.rman, start, end,
427 count, flags, child);
432 "allocated bus range (%ju-%ju) for rid %d of %s\n",
433 rman_get_start(res), rman_get_end(res), *rid,
434 pcib_child_name(child));
437 /* VMD harwdare does not support I/O ports. */
440 rman_set_rid(res, *rid);
445 vmd_adjust_resource(device_t dev, device_t child, int type,
446 struct resource *r, rman_res_t start, rman_res_t end)
449 if (type == SYS_RES_IRQ) {
450 return (bus_generic_adjust_resource(dev, child, type, r,
453 return (rman_adjust_resource(r, start, end));
457 vmd_release_resource(device_t dev, device_t child, int type, int rid,
461 if (type == SYS_RES_IRQ) {
462 return (bus_generic_release_resource(dev, child, type, rid,
465 return (rman_release_resource(r));
469 vmd_route_interrupt(device_t dev, device_t child, int pin)
472 /* VMD harwdare does not support legacy interrupts. */
473 return (PCI_INVALID_IRQ);
477 vmd_alloc_msi(device_t dev, device_t child, int count, int maxcount,
480 struct vmd_softc *sc = device_get_softc(dev);
481 struct vmd_irq_user *u;
482 int i, ibest = 0, best = INT_MAX;
484 if (count > vmd_max_msi)
486 LIST_FOREACH(u, &sc->vmd_users, viu_link) {
487 if (u->viu_child == child)
491 for (i = sc->vmd_fist_vector; i < sc->vmd_msix_count; i++) {
492 if (best > sc->vmd_irq[i].vi_nusers) {
493 best = sc->vmd_irq[i].vi_nusers;
498 u = malloc(sizeof(*u), M_DEVBUF, M_WAITOK | M_ZERO);
499 u->viu_child = child;
500 u->viu_vector = ibest;
501 LIST_INSERT_HEAD(&sc->vmd_users, u, viu_link);
502 sc->vmd_irq[ibest].vi_nusers += count;
504 for (i = 0; i < count; i++)
505 irqs[i] = sc->vmd_irq[ibest].vi_irq;
510 vmd_release_msi(device_t dev, device_t child, int count, int *irqs)
512 struct vmd_softc *sc = device_get_softc(dev);
513 struct vmd_irq_user *u;
515 LIST_FOREACH(u, &sc->vmd_users, viu_link) {
516 if (u->viu_child == child) {
517 sc->vmd_irq[u->viu_vector].vi_nusers -= count;
518 LIST_REMOVE(u, viu_link);
527 vmd_alloc_msix(device_t dev, device_t child, int *irq)
529 struct vmd_softc *sc = device_get_softc(dev);
530 struct vmd_irq_user *u;
531 int i, ibest = 0, best = INT_MAX;
534 LIST_FOREACH(u, &sc->vmd_users, viu_link) {
535 if (u->viu_child == child)
538 if (i >= vmd_max_msix)
541 for (i = sc->vmd_fist_vector; i < sc->vmd_msix_count; i++) {
542 if (best > sc->vmd_irq[i].vi_nusers) {
543 best = sc->vmd_irq[i].vi_nusers;
548 u = malloc(sizeof(*u), M_DEVBUF, M_WAITOK | M_ZERO);
549 u->viu_child = child;
550 u->viu_vector = ibest;
551 LIST_INSERT_HEAD(&sc->vmd_users, u, viu_link);
552 sc->vmd_irq[ibest].vi_nusers++;
554 *irq = sc->vmd_irq[ibest].vi_irq;
559 vmd_release_msix(device_t dev, device_t child, int irq)
561 struct vmd_softc *sc = device_get_softc(dev);
562 struct vmd_irq_user *u;
564 LIST_FOREACH(u, &sc->vmd_users, viu_link) {
565 if (u->viu_child == child &&
566 sc->vmd_irq[u->viu_vector].vi_irq == irq) {
567 sc->vmd_irq[u->viu_vector].vi_nusers--;
568 LIST_REMOVE(u, viu_link);
577 vmd_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data)
579 struct vmd_softc *sc = device_get_softc(dev);
582 for (i = sc->vmd_fist_vector; i < sc->vmd_msix_count; i++) {
583 if (sc->vmd_irq[i].vi_irq == irq)
586 if (i >= sc->vmd_msix_count)
588 *addr = MSI_INTEL_ADDR_BASE | (i << 12);
593 static device_method_t vmd_pci_methods[] = {
594 /* Device interface */
595 DEVMETHOD(device_probe, vmd_probe),
596 DEVMETHOD(device_attach, vmd_attach),
597 DEVMETHOD(device_detach, vmd_detach),
598 DEVMETHOD(device_suspend, bus_generic_suspend),
599 DEVMETHOD(device_resume, bus_generic_resume),
600 DEVMETHOD(device_shutdown, bus_generic_shutdown),
603 DEVMETHOD(bus_get_dma_tag, vmd_get_dma_tag),
604 DEVMETHOD(bus_read_ivar, pcib_read_ivar),
605 DEVMETHOD(bus_write_ivar, pcib_write_ivar),
606 DEVMETHOD(bus_alloc_resource, vmd_alloc_resource),
607 DEVMETHOD(bus_adjust_resource, vmd_adjust_resource),
608 DEVMETHOD(bus_release_resource, vmd_release_resource),
609 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
610 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
611 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
612 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
615 DEVMETHOD(pcib_maxslots, pcib_maxslots),
616 DEVMETHOD(pcib_read_config, vmd_read_config),
617 DEVMETHOD(pcib_write_config, vmd_write_config),
618 DEVMETHOD(pcib_route_interrupt, vmd_route_interrupt),
619 DEVMETHOD(pcib_alloc_msi, vmd_alloc_msi),
620 DEVMETHOD(pcib_release_msi, vmd_release_msi),
621 DEVMETHOD(pcib_alloc_msix, vmd_alloc_msix),
622 DEVMETHOD(pcib_release_msix, vmd_release_msix),
623 DEVMETHOD(pcib_map_msi, vmd_map_msi),
624 DEVMETHOD(pcib_request_feature, pcib_request_feature_allow),
629 static devclass_t pcib_devclass;
631 DEFINE_CLASS_0(pcib, vmd_pci_driver, vmd_pci_methods, sizeof(struct vmd_softc));
632 DRIVER_MODULE(vmd, pci, vmd_pci_driver, pcib_devclass, NULL, NULL);
633 MODULE_PNP_INFO("U16:vendor;U16:device;D:#", pci, vmd,
634 vmd_devs, nitems(vmd_devs) - 1);