Module Name: src Committed By: jmcneill Date: Wed Oct 31 15:43:19 UTC 2018
Modified Files: src/sys/arch/arm/cortex: gic_v2m.c Log Message: Add MSI-X support. To generate a diff of this commit: cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/cortex/gic_v2m.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/cortex/gic_v2m.c diff -u src/sys/arch/arm/cortex/gic_v2m.c:1.2 src/sys/arch/arm/cortex/gic_v2m.c:1.3 --- src/sys/arch/arm/cortex/gic_v2m.c:1.2 Tue Oct 30 23:59:47 2018 +++ src/sys/arch/arm/cortex/gic_v2m.c Wed Oct 31 15:43:19 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: gic_v2m.c,v 1.2 2018/10/30 23:59:47 jmcneill Exp $ */ +/* $NetBSD: gic_v2m.c,v 1.3 2018/10/31 15:43:19 jmcneill Exp $ */ /*- * Copyright (c) 2018 The NetBSD Foundation, Inc. @@ -32,7 +32,7 @@ #define _INTR_PRIVATE #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: gic_v2m.c,v 1.2 2018/10/30 23:59:47 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: gic_v2m.c,v 1.3 2018/10/31 15:43:19 jmcneill Exp $"); #include <sys/param.h> #include <sys/kmem.h> @@ -135,6 +135,48 @@ gic_v2m_msi_disable(struct gic_v2m_frame pci_conf_write(pc, tag, off + PCI_MSI_CTL, ctl); } +static void +gic_v2m_msix_enable(struct gic_v2m_frame *frame, int spi, int msix_vec, + bus_space_tag_t bst, bus_space_handle_t bsh) +{ + const struct pci_attach_args *pa = frame->frame_pa[spi]; + pci_chipset_tag_t pc = pa->pa_pc; + pcitag_t tag = pa->pa_tag; + pcireg_t ctl; + int off; + + if (!pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL)) + panic("gic_v2m_msix_enable: device is not MSI-X-capable"); + + const uint64_t addr = frame->frame_reg + GIC_MSI_SETSPI; + const uint64_t entry_base = PCI_MSIX_TABLE_ENTRY_SIZE * msix_vec; + bus_space_write_4(bst, bsh, entry_base + PCI_MSIX_TABLE_ENTRY_ADDR_LO, (uint32_t)addr); + bus_space_write_4(bst, bsh, entry_base + PCI_MSIX_TABLE_ENTRY_ADDR_HI, (uint32_t)(addr >> 32)); + bus_space_write_4(bst, bsh, entry_base + PCI_MSIX_TABLE_ENTRY_DATA, spi); + bus_space_write_4(bst, bsh, entry_base + PCI_MSIX_TABLE_ENTRY_VECTCTL, 0); + + ctl = pci_conf_read(pc, tag, off + PCI_MSIX_CTL); + ctl |= PCI_MSIX_CTL_ENABLE; + pci_conf_write(pc, tag, off + PCI_MSIX_CTL, ctl); +} + +static void +gic_v2m_msix_disable(struct gic_v2m_frame *frame, int spi) +{ + const struct pci_attach_args *pa = frame->frame_pa[spi]; + pci_chipset_tag_t pc = pa->pa_pc; + pcitag_t tag = pa->pa_tag; + pcireg_t ctl; + int off; + + if (!pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL)) + panic("gic_v2m_msix_disable: device is not MSI-X-capable"); + + ctl = pci_conf_read(pc, tag, off + PCI_MSIX_CTL); + ctl &= ~PCI_MSIX_CTL_ENABLE; + pci_conf_write(pc, tag, off + PCI_MSIX_CTL, ctl); +} + static pci_intr_handle_t * gic_v2m_msi_alloc(struct arm_pci_msi *msi, int *count, const struct pci_attach_args *pa, bool exact) @@ -175,6 +217,69 @@ gic_v2m_msi_alloc(struct arm_pci_msi *ms return vectors; } +static pci_intr_handle_t * +gic_v2m_msix_alloc(struct arm_pci_msi *msi, u_int *table_indexes, int *count, + const struct pci_attach_args *pa, bool exact) +{ + struct gic_v2m_frame * const frame = msi->msi_priv; + pci_intr_handle_t *vectors; + bus_space_tag_t bst; + bus_space_handle_t bsh; + bus_size_t bsz; + uint32_t table_offset, table_size; + int n, off, bar, error; + pcireg_t tbl; + + if (!pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_MSIX, &off, NULL)) + return NULL; + + const int avail = gic_v2m_msi_available_spi(frame); + if (exact && *count > avail) + return NULL; + + while (*count > avail) { + if (avail < *count) + (*count) >>= 1; + } + if (*count == 0) + return NULL; + + tbl = pci_conf_read(pa->pa_pc, pa->pa_tag, off + PCI_MSIX_TBLOFFSET); + bar = PCI_BAR0 + (4 * (tbl & PCI_MSIX_PBABIR_MASK)); + table_offset = tbl & PCI_MSIX_TBLOFFSET_MASK; + table_size = pci_msix_count(pa->pa_pc, pa->pa_tag) * PCI_MSIX_TABLE_ENTRY_SIZE; + if (table_size == 0) + return NULL; + + error = pci_mapreg_submap(pa, bar, pci_mapreg_type(pa->pa_pc, pa->pa_tag, bar), + BUS_SPACE_MAP_LINEAR, roundup(table_size, PAGE_SIZE), table_offset, + &bst, &bsh, NULL, &bsz); + if (error) + return NULL; + + const int spi_base = gic_v2m_msi_alloc_spi(frame, *count, pa); + if (spi_base == -1) { + bus_space_unmap(bst, bsh, bsz); + return NULL; + } + + vectors = kmem_alloc(sizeof(*vectors) * *count, KM_SLEEP); + for (n = 0; n < *count; n++) { + const int spi = spi_base + n; + const int msix_vec = table_indexes ? table_indexes[n] : n; + vectors[msix_vec] = ARM_PCI_INTR_MSIX | + __SHIFTIN(spi, ARM_PCI_INTR_IRQ) | + __SHIFTIN(msix_vec, ARM_PCI_INTR_MSI_VEC) | + __SHIFTIN(msi->msi_id, ARM_PCI_INTR_FRAME); + + gic_v2m_msix_enable(frame, spi, msix_vec, bst, bsh); + } + + bus_space_unmap(bst, bsh, bsz); + + return vectors; +} + static void * gic_v2m_msi_intr_establish(struct arm_pci_msi *msi, pci_intr_handle_t ih, int ipl, int (*func)(void *), void *arg) @@ -197,7 +302,10 @@ gic_v2m_msi_intr_release(struct arm_pci_ for (n = 0; n < count; n++) { const int spi = __SHIFTOUT(pih[n], ARM_PCI_INTR_IRQ); - gic_v2m_msi_disable(frame, spi); + if (pih[n] & ARM_PCI_INTR_MSIX) + gic_v2m_msix_disable(frame, spi); + if (pih[n] & ARM_PCI_INTR_MSI) + gic_v2m_msi_disable(frame, spi); gic_v2m_msi_free_spi(frame, spi); struct intrsource * const is = frame->frame_pic->pic_sources[spi]; @@ -214,6 +322,7 @@ gic_v2m_init(struct gic_v2m_frame *frame msi->msi_dev = dev; msi->msi_priv = frame; msi->msi_alloc = gic_v2m_msi_alloc; + msi->msix_alloc = gic_v2m_msix_alloc; msi->msi_intr_establish = gic_v2m_msi_intr_establish; msi->msi_intr_release = gic_v2m_msi_intr_release;