Module Name: src Committed By: knakahara Date: Mon Apr 27 07:03:58 UTC 2015
Modified Files: src/distrib/sets/lists/comp: mi src/share/man/man9: Makefile src/sys/arch/amd64/amd64: mainbus.c src/sys/arch/amd64/include: types.h src/sys/arch/i386/i386: mainbus.c src/sys/arch/i386/include: types.h src/sys/arch/x86/conf: files.x86 src/sys/arch/x86/include: i82093var.h pci_machdep.h pci_machdep_common.h pic.h src/sys/arch/x86/pci: pci_intr_machdep.c pci_machdep.c src/sys/arch/x86/x86: intr.c ioapic.c src/sys/arch/xen/include: intr.h src/sys/dev/pci: pci.c pcireg.h pcivar.h Added Files: src/share/man/man9: pci_msi.9 src/sys/arch/x86/pci: msipic.c msipic.h pci_msi_machdep.c Log Message: add x86 MD MSI/MSI-X support code. To generate a diff of this commit: cvs rdiff -u -r1.1955 -r1.1956 src/distrib/sets/lists/comp/mi cvs rdiff -u -r1.385 -r1.386 src/share/man/man9/Makefile cvs rdiff -u -r0 -r1.1 src/share/man/man9/pci_msi.9 cvs rdiff -u -r1.34 -r1.35 src/sys/arch/amd64/amd64/mainbus.c cvs rdiff -u -r1.45 -r1.46 src/sys/arch/amd64/include/types.h cvs rdiff -u -r1.98 -r1.99 src/sys/arch/i386/i386/mainbus.c cvs rdiff -u -r1.80 -r1.81 src/sys/arch/i386/include/types.h cvs rdiff -u -r1.83 -r1.84 src/sys/arch/x86/conf/files.x86 cvs rdiff -u -r1.13 -r1.14 src/sys/arch/x86/include/i82093var.h cvs rdiff -u -r1.17 -r1.18 src/sys/arch/x86/include/pci_machdep.h cvs rdiff -u -r1.14 -r1.15 src/sys/arch/x86/include/pci_machdep_common.h cvs rdiff -u -r1.7 -r1.8 src/sys/arch/x86/include/pic.h cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/pci/msipic.c \ src/sys/arch/x86/pci/msipic.h src/sys/arch/x86/pci/pci_msi_machdep.c cvs rdiff -u -r1.29 -r1.30 src/sys/arch/x86/pci/pci_intr_machdep.c cvs rdiff -u -r1.69 -r1.70 src/sys/arch/x86/pci/pci_machdep.c cvs rdiff -u -r1.80 -r1.81 src/sys/arch/x86/x86/intr.c cvs rdiff -u -r1.49 -r1.50 src/sys/arch/x86/x86/ioapic.c cvs rdiff -u -r1.35 -r1.36 src/sys/arch/xen/include/intr.h cvs rdiff -u -r1.145 -r1.146 src/sys/dev/pci/pci.c cvs rdiff -u -r1.101 -r1.102 src/sys/dev/pci/pcireg.h \ src/sys/dev/pci/pcivar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/comp/mi diff -u src/distrib/sets/lists/comp/mi:1.1955 src/distrib/sets/lists/comp/mi:1.1956 --- src/distrib/sets/lists/comp/mi:1.1955 Mon Apr 27 06:42:52 2015 +++ src/distrib/sets/lists/comp/mi Mon Apr 27 07:03:57 2015 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1955 2015/04/27 06:42:52 knakahara Exp $ +# $NetBSD: mi,v 1.1956 2015/04/27 07:03:57 knakahara Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -17367,10 +17367,27 @@ ./usr/share/man/html9/pci_intr_evcnt.html comp-sys-htmlman html ./usr/share/man/html9/pci_intr_map.html comp-sys-htmlman html ./usr/share/man/html9/pci_intr_string.html comp-sys-htmlman html +./usr/share/man/html9/pci_intx_alloc.html comp-sys-htmlman html +./usr/share/man/html9/pci_intx_release.html comp-sys-htmlman html ./usr/share/man/html9/pci_make_tag.html comp-sys-htmlman html ./usr/share/man/html9/pci_mapreg_info.html comp-sys-htmlman html ./usr/share/man/html9/pci_mapreg_map.html comp-sys-htmlman html ./usr/share/man/html9/pci_mapreg_type.html comp-sys-htmlman html +./usr/share/man/html9/pci_msi.html comp-sys-htmlman html +./usr/share/man/html9/pci_msi_alloc.html comp-sys-htmlman html +./usr/share/man/html9/pci_msi_alloc_exact.html comp-sys-htmlman html +./usr/share/man/html9/pci_msi_count.html comp-sys-htmlman html +./usr/share/man/html9/pci_msi_disestablish.html comp-sys-htmlman html +./usr/share/man/html9/pci_msi_establish.html comp-sys-htmlman html +./usr/share/man/html9/pci_msi_release.html comp-sys-htmlman html +./usr/share/man/html9/pci_msix.html comp-sys-htmlman html +./usr/share/man/html9/pci_msix_alloc.html comp-sys-htmlman html +./usr/share/man/html9/pci_msix_alloc_exact.html comp-sys-htmlman html +./usr/share/man/html9/pci_msix_alloc_map.html comp-sys-htmlman html +./usr/share/man/html9/pci_msix_count.html comp-sys-htmlman html +./usr/share/man/html9/pci_msix_disestablish.html comp-sys-htmlman html +./usr/share/man/html9/pci_msix_establish.html comp-sys-htmlman html +./usr/share/man/html9/pci_msix_release.html comp-sys-htmlman html ./usr/share/man/html9/pci_set_powerstate.html comp-sys-htmlman html ./usr/share/man/html9/pci_vpd_read.html comp-sys-htmlman html ./usr/share/man/html9/pci_vpd_write.html comp-sys-htmlman html @@ -24306,10 +24323,27 @@ ./usr/share/man/man9/pci_intr_evcnt.9 comp-sys-man .man ./usr/share/man/man9/pci_intr_map.9 comp-sys-man .man ./usr/share/man/man9/pci_intr_string.9 comp-sys-man .man +./usr/share/man/man9/pci_intx_alloc.9 comp-sys-man .man +./usr/share/man/man9/pci_intx_release.9 comp-sys-man .man ./usr/share/man/man9/pci_make_tag.9 comp-sys-man .man ./usr/share/man/man9/pci_mapreg_info.9 comp-sys-man .man ./usr/share/man/man9/pci_mapreg_map.9 comp-sys-man .man ./usr/share/man/man9/pci_mapreg_type.9 comp-sys-man .man +./usr/share/man/man9/pci_msi.9 comp-sys-man .man +./usr/share/man/man9/pci_msi_alloc.9 comp-sys-man .man +./usr/share/man/man9/pci_msi_alloc_exact.9 comp-sys-man .man +./usr/share/man/man9/pci_msi_count.9 comp-sys-man .man +./usr/share/man/man9/pci_msi_disestablish.9 comp-sys-man .man +./usr/share/man/man9/pci_msi_establish.9 comp-sys-man .man +./usr/share/man/man9/pci_msi_release.9 comp-sys-man .man +./usr/share/man/man9/pci_msix.9 comp-sys-man .man +./usr/share/man/man9/pci_msix_alloc.9 comp-sys-man .man +./usr/share/man/man9/pci_msix_alloc_exact.9 comp-sys-man .man +./usr/share/man/man9/pci_msix_alloc_map.9 comp-sys-man .man +./usr/share/man/man9/pci_msix_count.9 comp-sys-man .man +./usr/share/man/man9/pci_msix_disestablish.9 comp-sys-man .man +./usr/share/man/man9/pci_msix_establish.9 comp-sys-man .man +./usr/share/man/man9/pci_msix_release.9 comp-sys-man .man ./usr/share/man/man9/pci_set_powerstate.9 comp-sys-man .man ./usr/share/man/man9/pci_vpd_read.9 comp-sys-man .man ./usr/share/man/man9/pci_vpd_write.9 comp-sys-man .man Index: src/share/man/man9/Makefile diff -u src/share/man/man9/Makefile:1.385 src/share/man/man9/Makefile:1.386 --- src/share/man/man9/Makefile:1.385 Mon Apr 27 06:42:52 2015 +++ src/share/man/man9/Makefile Mon Apr 27 07:03:57 2015 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.385 2015/04/27 06:42:52 knakahara Exp $ +# $NetBSD: Makefile,v 1.386 2015/04/27 07:03:57 knakahara Exp $ # Makefile for section 9 (kernel function and variable) manual pages. @@ -40,7 +40,7 @@ MAN= accept_filter.9 accf_data.9 accf_ht mstohz.9 mutex.9 m_tag.9 namecache.9 \ namei.9 nullop.9 opencrypto.9 optstr.9 \ panic.9 pathbuf.9 pci.9 pci_configure_bus.9 pci_intr.9 \ - pci_intr_distribute.9 pckbport.9 pcmcia.9 pcq.9 pcu.9 \ + pci_intr_distribute.9 pci_msi.9 pckbport.9 pcmcia.9 pcq.9 pcu.9 \ percpu.9 pfil.9 physio.9 pmap.9 pmatch.9 pmc.9 pmf.9 pool.9 \ pool_cache.9 powerhook_establish.9 ppi.9 ppsratecheck.9 preempt.9 \ proc_find.9 pserialize.9 putter.9 \ @@ -572,6 +572,22 @@ MLINKS+=pci.9 pci_conf_read.9 \ pci.9 PCI_VENDOR.9 \ pci.9 PCI_PRODUCT.9 \ pci.9 PCI_REVISION.9 +MLINKS+=pci_msi.9 pci_msix.9 \ + pci_msi.9 pci_intx_alloc.9 \ + pci_msi.9 pci_intx_release.9 \ + pci_msi.9 pci_msi_count.9 \ + pci_msi.9 pci_msi_alloc.9 \ + pci_msi.9 pci_msi_alloc_exact.9 \ + pci_msi.9 pci_msi_release.9 \ + pci_msi.9 pci_msi_establish.9 \ + pci_msi.9 pci_msi_disestablish.9 \ + pci_msi.9 pci_msix_count.9 \ + pci_msi.9 pci_msix_alloc.9 \ + pci_msi.9 pci_msix_alloc_exact.9 \ + pci_msi.9 pci_msix_alloc_map.9 \ + pci_msi.9 pci_msix_release.9 \ + pci_msi.9 pci_msix_establish.9 \ + pci_msi.9 pci_msix_disestablish.9 MLINKS+=pci_configure_bus.9 pci_conf_hook.9 \ pci_configure_bus.9 pci_conf_interrupt.9 MLINKS+=pckbport.9 pckbport_attach.9 \ Index: src/sys/arch/amd64/amd64/mainbus.c diff -u src/sys/arch/amd64/amd64/mainbus.c:1.34 src/sys/arch/amd64/amd64/mainbus.c:1.35 --- src/sys/arch/amd64/amd64/mainbus.c:1.34 Wed Jul 31 14:05:33 2013 +++ src/sys/arch/amd64/amd64/mainbus.c Mon Apr 27 07:03:57 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: mainbus.c,v 1.34 2013/07/31 14:05:33 soren Exp $ */ +/* $NetBSD: mainbus.c,v 1.35 2015/04/27 07:03:57 knakahara Exp $ */ /* * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved. @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 1.34 2013/07/31 14:05:33 soren Exp $"); +__KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 1.35 2015/04/27 07:03:57 knakahara Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -74,6 +74,7 @@ __KERNEL_RCSID(0, "$NetBSD: mainbus.c,v #include <arch/x86/pci/pci_addr_fixup.h> #endif #endif +#include <arch/x86/pci/msipic.h> #endif /* @@ -175,6 +176,8 @@ mainbus_attach(device_t parent, device_t #endif #if NPCI > 0 + msipic_init(); + /* * ACPI needs to be able to access PCI configuration space. */ Index: src/sys/arch/amd64/include/types.h diff -u src/sys/arch/amd64/include/types.h:1.45 src/sys/arch/amd64/include/types.h:1.46 --- src/sys/arch/amd64/include/types.h:1.45 Thu Apr 3 15:22:36 2014 +++ src/sys/arch/amd64/include/types.h Mon Apr 27 07:03:57 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: types.h,v 1.45 2014/04/03 15:22:36 christos Exp $ */ +/* $NetBSD: types.h,v 1.46 2015/04/27 07:03:57 knakahara Exp $ */ /*- * Copyright (c) 1990 The Regents of the University of California. @@ -103,6 +103,7 @@ typedef volatile unsigned char __cpu_si #define __HAVE_MM_MD_DIRECT_MAPPED_IO #define __HAVE_MM_MD_DIRECT_MAPPED_PHYS #define __HAVE_CPU_UAREA_ROUTINES +#define __HAVE_PCI_MSI_MSIX #endif #endif Index: src/sys/arch/i386/i386/mainbus.c diff -u src/sys/arch/i386/i386/mainbus.c:1.98 src/sys/arch/i386/i386/mainbus.c:1.99 --- src/sys/arch/i386/i386/mainbus.c:1.98 Fri Nov 8 03:12:48 2013 +++ src/sys/arch/i386/i386/mainbus.c Mon Apr 27 07:03:57 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: mainbus.c,v 1.98 2013/11/08 03:12:48 christos Exp $ */ +/* $NetBSD: mainbus.c,v 1.99 2015/04/27 07:03:57 knakahara Exp $ */ /* * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved. @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 1.98 2013/11/08 03:12:48 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 1.99 2015/04/27 07:03:57 knakahara Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -86,6 +86,7 @@ __KERNEL_RCSID(0, "$NetBSD: mainbus.c,v #include <arch/x86/pci/pci_addr_fixup.h> #endif #endif +#include <archx86/pci/msipic.h> #endif void mainbus_childdetached(device_t, device_t); @@ -227,6 +228,8 @@ mainbus_attach(device_t parent, device_t #endif #if NPCI > 0 + msipic_init(); + /* * ACPI needs to be able to access PCI configuration space. */ Index: src/sys/arch/i386/include/types.h diff -u src/sys/arch/i386/include/types.h:1.80 src/sys/arch/i386/include/types.h:1.81 --- src/sys/arch/i386/include/types.h:1.80 Wed Apr 22 19:48:01 2015 +++ src/sys/arch/i386/include/types.h Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: types.h,v 1.80 2015/04/22 19:48:01 pooka Exp $ */ +/* $NetBSD: types.h,v 1.81 2015/04/27 07:03:58 knakahara Exp $ */ /*- * Copyright (c) 1990 The Regents of the University of California. @@ -129,6 +129,10 @@ typedef volatile unsigned char __cpu_si #if defined(_KERNEL) #define __HAVE_RAS + +#if !defined(XEN) +#define __HAVE_PCI_MSI_MSIX +#endif #endif #endif /* _I386_MACHTYPES_H_ */ Index: src/sys/arch/x86/conf/files.x86 diff -u src/sys/arch/x86/conf/files.x86:1.83 src/sys/arch/x86/conf/files.x86:1.84 --- src/sys/arch/x86/conf/files.x86:1.83 Fri Oct 10 17:44:17 2014 +++ src/sys/arch/x86/conf/files.x86 Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -# $NetBSD: files.x86,v 1.83 2014/10/10 17:44:17 uebayasi Exp $ +# $NetBSD: files.x86,v 1.84 2015/04/27 07:03:58 knakahara Exp $ # options for MP configuration through the MP spec defflag opt_mpbios.h MPBIOS MPVERBOSE MPDEBUG MPBIOS_SCANPCI @@ -141,6 +141,8 @@ file arch/x86/x86/tprof_amdpmi.c tprof_a file arch/x86/pci/pci_machdep.c pci #file arch/x86/pci/pci_ranges.c pci file arch/x86/pci/pci_intr_machdep.c pci +file arch/x86/pci/pci_msi_machdep.c pci +file arch/x86/pci/msipic.c pci file arch/x86/pci/pciide_machdep.c pciide_common Index: src/sys/arch/x86/include/i82093var.h diff -u src/sys/arch/x86/include/i82093var.h:1.13 src/sys/arch/x86/include/i82093var.h:1.14 --- src/sys/arch/x86/include/i82093var.h:1.13 Mon Apr 27 06:51:40 2015 +++ src/sys/arch/x86/include/i82093var.h Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: i82093var.h,v 1.13 2015/04/27 06:51:40 knakahara Exp $ */ +/* $NetBSD: i82093var.h,v 1.14 2015/04/27 07:03:58 knakahara Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. @@ -68,10 +68,13 @@ struct ioapic_softc { * (ih&0xff0000)>>16 -> ioapic id. * (ih&0x00ff00)>>8 -> ioapic pin. * - * 0x80000000 is used by pci_intr_machdep.c for MPSAFE_MASK + * MSI/MSI-X: + * (ih&0x000ff80000000000)>>43 -> MSI/MSI-X device id. + * (ih&0x000007ff00000000)>>32 -> MSI/MSI-X vector id in a device. */ - +#define MPSAFE_MASK 0x80000000ULL #define APIC_INT_VIA_APIC 0x10000000ULL +#define APIC_INT_VIA_MSI 0x20000000ULL #define APIC_INT_APIC_MASK 0x00ff0000ULL #define APIC_INT_APIC_SHIFT 16 #define APIC_INT_PIN_MASK 0x0000ff00ULL @@ -82,9 +85,21 @@ struct ioapic_softc { #define APIC_IRQ_ISLEGACY(x) (bool)(!((x) & APIC_INT_VIA_APIC)) #define APIC_IRQ_LEGACY_IRQ(x) (int)((x) & 0xff) +#define INT_VIA_MSI(x) (bool)(((x) & APIC_INT_VIA_MSI) != 0) + +#define MSI_INT_MSIX 0x1000000000000000ULL +#define MSI_INT_DEV_MASK 0x000ff80000000000ULL +#define MSI_INT_VEC_MASK 0x000007ff00000000ULL + +#define MSI_INT_IS_MSIX(x) (bool)((((x) & MSI_INT_MSIX) != 0)) +#define MSI_INT_MAKE_MSI(x) ((x) &= ~MSI_INT_MSIX) +#define MSI_INT_MAKE_MSIX(x) ((x) |= MSI_INT_MSIX) +#define MSI_INT_DEV(x) __SHIFTOUT((x), MSI_INT_DEV_MASK) +#define MSI_INT_VEC(x) __SHIFTOUT((x), MSI_INT_VEC_MASK) + void ioapic_print_redir(struct ioapic_softc *, const char *, int); void ioapic_format_redir(char *, const char *, int, uint32_t, uint32_t); -struct ioapic_softc *ioapic_find(intr_handle_t); +struct ioapic_softc *ioapic_find(int); struct ioapic_softc *ioapic_find_bybase(int); void ioapic_enable(void); Index: src/sys/arch/x86/include/pci_machdep.h diff -u src/sys/arch/x86/include/pci_machdep.h:1.17 src/sys/arch/x86/include/pci_machdep.h:1.18 --- src/sys/arch/x86/include/pci_machdep.h:1.17 Mon Apr 27 06:51:40 2015 +++ src/sys/arch/x86/include/pci_machdep.h Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: pci_machdep.h,v 1.17 2015/04/27 06:51:40 knakahara Exp $ */ +/* $NetBSD: pci_machdep.h,v 1.18 2015/04/27 07:03:58 knakahara Exp $ */ /* * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved. @@ -41,6 +41,7 @@ * Types provided to machine-independent PCI code * See also i82093var.h to find out pci_intr_handle_t's bitfield. */ + typedef intr_handle_t pci_intr_handle_t; #include <x86/pci_machdep_common.h> Index: src/sys/arch/x86/include/pci_machdep_common.h diff -u src/sys/arch/x86/include/pci_machdep_common.h:1.14 src/sys/arch/x86/include/pci_machdep_common.h:1.15 --- src/sys/arch/x86/include/pci_machdep_common.h:1.14 Mon Apr 27 06:42:52 2015 +++ src/sys/arch/x86/include/pci_machdep_common.h Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: pci_machdep_common.h,v 1.14 2015/04/27 06:42:52 knakahara Exp $ */ +/* $NetBSD: pci_machdep_common.h,v 1.15 2015/04/27 07:03:58 knakahara Exp $ */ /* * Copyright (c) 1996 Christopher G. Demetriou. All rights reserved. @@ -120,9 +120,34 @@ void *pci_intr_establish(pci_chipset_ta void pci_intr_disestablish(pci_chipset_tag_t, void *); int pci_intr_distribute(void *, const kcpuset_t *, kcpuset_t *); +/* + * If device drivers use MSI/MSI-X, they should use these API for INTx + * instead of pci_intr_map(), because of conforming the pci_intr_handle + * ownership to MSI/MSI-X. + */ +int pci_intx_alloc(const struct pci_attach_args *, + pci_intr_handle_t **); +void pci_intx_release(pci_chipset_tag_t, pci_intr_handle_t *); + /* experimental MSI support */ -void *pci_msi_establish(struct pci_attach_args *, int, int (*)(void *), void *); -void pci_msi_disestablish(void *); +const char *pci_msi_string(pci_chipset_tag_t, pci_intr_handle_t, char *, size_t); +int pci_msi_count(struct pci_attach_args *); +int pci_msi_alloc(struct pci_attach_args *, pci_intr_handle_t **, int *); +int pci_msi_alloc_exact(struct pci_attach_args *, pci_intr_handle_t **, int); +void pci_msi_release(pci_chipset_tag_t, pci_intr_handle_t **, int); +void *pci_msi_establish(pci_chipset_tag_t, pci_intr_handle_t, + int, int (*)(void *), void *); +void pci_msi_disestablish(pci_chipset_tag_t, void *); + +/* experimental MSI-X support */ +int pci_msix_count(struct pci_attach_args *); +int pci_msix_alloc(struct pci_attach_args *, pci_intr_handle_t **, int *); +int pci_msix_alloc_exact(struct pci_attach_args *, pci_intr_handle_t **, int); +int pci_msix_alloc_map(struct pci_attach_args *, pci_intr_handle_t **, u_int *, int); +void pci_msix_release(pci_chipset_tag_t, pci_intr_handle_t **, int); +void *pci_msix_establish(pci_chipset_tag_t, pci_intr_handle_t, + int, int (*)(void *), void *); +void pci_msix_disestablish(pci_chipset_tag_t, void *); /* * ALL OF THE FOLLOWING ARE MACHINE-DEPENDENT, AND SHOULD NOT BE USED Index: src/sys/arch/x86/include/pic.h diff -u src/sys/arch/x86/include/pic.h:1.7 src/sys/arch/x86/include/pic.h:1.8 --- src/sys/arch/x86/include/pic.h:1.7 Sun Apr 19 14:11:37 2009 +++ src/sys/arch/x86/include/pic.h Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: pic.h,v 1.7 2009/04/19 14:11:37 ad Exp $ */ +/* $NetBSD: pic.h,v 1.8 2015/04/27 07:03:58 knakahara Exp $ */ #ifndef _X86_PIC_H #define _X86_PIC_H @@ -22,6 +22,7 @@ struct pic { struct intrstub *pic_level_stubs; struct intrstub *pic_edge_stubs; struct ioapic_softc *pic_ioapic; /* if pic_type == PIC_IOAPIC */ + struct msipic *pic_msipic; /* if (pic_type == PIC_MSI) || (pic_type == PIC_MSIX) */ }; /* @@ -30,7 +31,9 @@ struct pic { #define PIC_I8259 0 #define PIC_IOAPIC 1 #define PIC_LAPIC 2 -#define PIC_SOFT 3 +#define PIC_MSI 3 +#define PIC_MSIX 4 +#define PIC_SOFT 5 extern struct pic i8259_pic; extern struct pic local_pic; Index: src/sys/arch/x86/pci/pci_intr_machdep.c diff -u src/sys/arch/x86/pci/pci_intr_machdep.c:1.29 src/sys/arch/x86/pci/pci_intr_machdep.c:1.30 --- src/sys/arch/x86/pci/pci_intr_machdep.c:1.29 Mon Apr 27 06:51:40 2015 +++ src/sys/arch/x86/pci/pci_intr_machdep.c Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: pci_intr_machdep.c,v 1.29 2015/04/27 06:51:40 knakahara Exp $ */ +/* $NetBSD: pci_intr_machdep.c,v 1.30 2015/04/27 07:03:58 knakahara Exp $ */ /*- * Copyright (c) 1997, 1998, 2009 The NetBSD Foundation, Inc. @@ -73,7 +73,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pci_intr_machdep.c,v 1.29 2015/04/27 06:51:40 knakahara Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pci_intr_machdep.c,v 1.30 2015/04/27 07:03:58 knakahara Exp $"); #include <sys/types.h> #include <sys/param.h> @@ -112,8 +112,6 @@ __KERNEL_RCSID(0, "$NetBSD: pci_intr_mac #include <machine/mpacpi.h> #endif -#define MPSAFE_MASK 0x80000000 - int pci_intr_map(const struct pci_attach_args *pa, pci_intr_handle_t *ihp) { @@ -227,6 +225,9 @@ pci_intr_string(pci_chipset_tag_t pc, pc { pci_chipset_tag_t ipc; + if (INT_VIA_MSI(ih)) + return pci_msi_string(pc, ih, buf, len); + for (ipc = pc; ipc != NULL; ipc = ipc->pc_super) { if ((ipc->pc_present & PCI_OVERRIDE_INTR_STRING) == 0) continue; @@ -340,105 +341,60 @@ pci_intr_distribute(void *cookie, const } #if NIOAPIC > 0 -/* - * experimental support for MSI, does support a single vector, - * no MSI-X, 8-bit APIC IDs - * (while it doesn't need the ioapic technically, it borrows - * from its kernel support) - */ - -/* dummies, needed by common intr_establish code */ -static void -msipic_hwmask(struct pic *pic, int pin) -{ -} -static void -msipic_addroute(struct pic *pic, struct cpu_info *ci, - int pin, int vec, int type) +int +pci_intx_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **pih) { -} + struct intrsource *isp; + pci_intr_handle_t *handle; + int error; + char intrstr_buf[INTRIDBUF]; + const char *intrstr; + + handle = kmem_zalloc(sizeof(*handle), KM_SLEEP); + if (handle == NULL) { + aprint_normal("cannot allocate pci_intr_handle_t\n"); + return ENOMEM; + } + + if (pci_intr_map(pa, handle) != 0) { + aprint_normal("cannot set up pci_intr_handle_t\n"); + error = EINVAL; + goto error; + } + + intrstr = pci_intr_string(pa->pa_pc, *handle, + intrstr_buf, sizeof(intrstr_buf)); + mutex_enter(&cpu_lock); + isp = intr_allocate_io_intrsource(intrstr); + mutex_exit(&cpu_lock); + if (isp == NULL) { + aprint_normal("can't allocate io_intersource\n"); + error = ENOMEM; + goto error; + } -static struct pic msi_pic = { - .pic_name = "msi", - .pic_type = PIC_SOFT, - .pic_vecbase = 0, - .pic_apicid = 0, - .pic_lock = __SIMPLELOCK_UNLOCKED, - .pic_hwmask = msipic_hwmask, - .pic_hwunmask = msipic_hwmask, - .pic_addroute = msipic_addroute, - .pic_delroute = msipic_addroute, - .pic_edge_stubs = ioapic_edge_stubs, -}; - -struct msi_hdl { - struct intrhand *ih; - pci_chipset_tag_t pc; - pcitag_t tag; - int co; -}; + *pih = handle; + return 0; -void * -pci_msi_establish(struct pci_attach_args *pa, int level, - int (*func)(void *), void *arg) -{ - int co; - struct intrhand *ih; - struct msi_hdl *msih; - struct cpu_info *ci; - struct intrsource *is; - pcireg_t reg; - - if (!pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_MSI, &co, 0)) - return NULL; - - ih = intr_establish(-1, &msi_pic, -1, IST_EDGE, level, func, arg, 0); - if (ih == NULL) - return NULL; - - msih = malloc(sizeof(*msih), M_DEVBUF, M_WAITOK); - msih->ih = ih; - msih->pc = pa->pa_pc; - msih->tag = pa->pa_tag; - msih->co = co; - - ci = ih->ih_cpu; - is = ci->ci_isources[ih->ih_slot]; - reg = pci_conf_read(pa->pa_pc, pa->pa_tag, co + PCI_MSI_CTL); - pci_conf_write(pa->pa_pc, pa->pa_tag, co + PCI_MSI_MADDR64_LO, - LAPIC_MSIADDR_BASE | - __SHIFTIN(ci->ci_cpuid, LAPIC_MSIADDR_DSTID_MASK)); - if (reg & PCI_MSI_CTL_64BIT_ADDR) { - pci_conf_write(pa->pa_pc, pa->pa_tag, co + PCI_MSI_MADDR64_HI, - 0); - /* XXX according to the manual, ASSERT is unnecessary if - * EDGE - */ - pci_conf_write(pa->pa_pc, pa->pa_tag, co + PCI_MSI_MDATA64, - __SHIFTIN(is->is_idtvec, LAPIC_MSIDATA_VECTOR_MASK) | - LAPIC_MSIDATA_TRGMODE_EDGE | LAPIC_MSIDATA_LEVEL_ASSERT | - LAPIC_MSIDATA_DM_FIXED); - } else { - /* XXX according to the manual, ASSERT is unnecessary if - * EDGE - */ - pci_conf_write(pa->pa_pc, pa->pa_tag, co + PCI_MSI_MDATA, - __SHIFTIN(is->is_idtvec, LAPIC_MSIDATA_VECTOR_MASK) | - LAPIC_MSIDATA_TRGMODE_EDGE | LAPIC_MSIDATA_LEVEL_ASSERT | - LAPIC_MSIDATA_DM_FIXED); - } - pci_conf_write(pa->pa_pc, pa->pa_tag, co + PCI_MSI_CTL, - PCI_MSI_CTL_MSI_ENABLE); - return msih; +error: + kmem_free(handle, sizeof(*handle)); + return error; } void -pci_msi_disestablish(void *ih) +pci_intx_release(pci_chipset_tag_t pc, pci_intr_handle_t *pih) { - struct msi_hdl *msih = ih; + char intrstr_buf[INTRIDBUF]; + const char *intrstr; + + if (pih == NULL) + return; + + intrstr = pci_intr_string(NULL, *pih, intrstr_buf, sizeof(intrstr_buf)); + mutex_enter(&cpu_lock); + intr_free_io_intrsource(intrstr); + mutex_exit(&cpu_lock); - pci_conf_write(msih->pc, msih->tag, msih->co + PCI_MSI_CTL, 0); - intr_disestablish(msih->ih); - free(msih, M_DEVBUF); + kmem_free(pih, sizeof(*pih)); } #endif Index: src/sys/arch/x86/pci/pci_machdep.c diff -u src/sys/arch/x86/pci/pci_machdep.c:1.69 src/sys/arch/x86/pci/pci_machdep.c:1.70 --- src/sys/arch/x86/pci/pci_machdep.c:1.69 Fri Nov 7 12:48:21 2014 +++ src/sys/arch/x86/pci/pci_machdep.c Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: pci_machdep.c,v 1.69 2014/11/07 12:48:21 christos Exp $ */ +/* $NetBSD: pci_machdep.c,v 1.70 2015/04/27 07:03:58 knakahara Exp $ */ /*- * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. @@ -73,7 +73,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pci_machdep.c,v 1.69 2014/11/07 12:48:21 christos Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pci_machdep.c,v 1.70 2015/04/27 07:03:58 knakahara Exp $"); #include <sys/types.h> #include <sys/param.h> @@ -126,6 +126,8 @@ __KERNEL_RCSID(0, "$NetBSD: pci_machdep. #include <x86/vga_post.h> #endif +#include <x86/cpuvar.h> + #include <machine/autoconf.h> #include <machine/bootinfo.h> @@ -210,6 +212,58 @@ const struct { #undef _tag #undef _qe +/* arch/xen does not support MSI/MSI-X yet. */ +#ifdef __HAVE_PCI_MSI_MSIX +#define PCI_QUIRK_DISABLE_MSI 1 /* Neigher MSI nor MSI-X work */ +#define PCI_QUIRK_DISABLE_MSIX 2 /* MSI-X does not work */ +#define PCI_QUIRK_ENABLE_MSI_VM 3 /* Older chipset in VM where MSI and MSI-X works */ + +#define _dme(vend, prod) \ + { PCI_QUIRK_DISABLE_MSI, PCI_ID_CODE(vend, prod) } +#define _dmxe(vend, prod) \ + { PCI_QUIRK_DISABLE_MSIX, PCI_ID_CODE(vend, prod) } +#define _emve(vend, prod) \ + { PCI_QUIRK_ENABLE_MSI_VM, PCI_ID_CODE(vend, prod) } +const struct { + int type; + pcireg_t id; +} pci_msi_quirk_tbl[] = { + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_PCMC), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82437FX), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82437MX), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82437VX), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82439HX), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82439TX), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443GX), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443GX_AGP), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82440MX), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82441FX), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443BX), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443BX_AGP), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443BX_NOAGP), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443GX_NOAGP), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443LX), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443LX_AGP), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82810_MCH), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82810E_MCH), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82815_FULL_HUB), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82820_MCH), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82830MP_IO_1), + _dme(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82840_HB), + _dme(PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE_PCHB), + _dme(PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE2_PCHB), + _dme(PCI_VENDOR_AMD, PCI_PRODUCT_AMD_SC751_SC), + _dme(PCI_VENDOR_AMD, PCI_PRODUCT_AMD_SC761_SC), + _dme(PCI_VENDOR_AMD, PCI_PRODUCT_AMD_SC762_NB), + + _emve(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82441FX), /* QEMU */ + _emve(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82443BX), /* VMWare */ +}; +#undef _dme +#undef _dmxe +#undef _emve +#endif /* __HAVE_PCI_MSI_MSIX */ + /* * PCI doesn't have any special needs; just use the generic versions * of these functions. @@ -370,9 +424,30 @@ pci_conf_select(uint32_t sel) } } +#ifdef __HAVE_PCI_MSI_MSIX +static int +pci_has_msi_quirk(pcireg_t id, int type) +{ + int i; + + for (i = 0; i < __arraycount(pci_msi_quirk_tbl); i++) { + if (id == pci_msi_quirk_tbl[i].id && + type == pci_msi_quirk_tbl[i].type) + return 1; + } + + return 0; +} +#endif + void pci_attach_hook(device_t parent, device_t self, struct pcibus_attach_args *pba) { +#ifdef __HAVE_PCI_MSI_MSIX + pci_chipset_tag_t pc = pba->pba_pc; + pcitag_t tag; + pcireg_t id, class; +#endif if (pba->pba_bus == 0) aprint_normal(": configuration mode %d", pci_mode); @@ -382,6 +457,58 @@ pci_attach_hook(device_t parent, device_ #if NACPICA > 0 mpacpi_pci_attach_hook(parent, self, pba); #endif + +#ifdef __HAVE_PCI_MSI_MSIX + /* + * In order to decide whether the system supports MSI we look + * at the host bridge, which should be device 0 function 0 on + * bus 0. It is better to not enable MSI on systems that + * support it than the other way around, so be conservative + * here. So we don't enable MSI if we don't find a host + * bridge there. We also deliberately don't enable MSI on + * chipsets from low-end manifacturers like VIA and SiS. + */ + tag = pci_make_tag(pc, 0, 0, 0); + id = pci_conf_read(pc, tag, PCI_ID_REG); + class = pci_conf_read(pc, tag, PCI_CLASS_REG); + + if (PCI_CLASS(class) != PCI_CLASS_BRIDGE || + PCI_SUBCLASS(class) != PCI_SUBCLASS_BRIDGE_HOST) + return; + + if (pci_has_msi_quirk(id, PCI_QUIRK_DISABLE_MSI)) { + pba->pba_flags &= ~PCI_FLAGS_MSI_OKAY; + pba->pba_flags &= ~PCI_FLAGS_MSIX_OKAY; + } else if (pci_has_msi_quirk(id, PCI_QUIRK_DISABLE_MSIX)) { + pba->pba_flags |= PCI_FLAGS_MSI_OKAY; + pba->pba_flags &= ~PCI_FLAGS_MSIX_OKAY; + } else { + pba->pba_flags |= PCI_FLAGS_MSI_OKAY; + pba->pba_flags |= PCI_FLAGS_MSIX_OKAY; + } + + /* VMware and KVM use old chipset, but they can use MSI/MSI-X */ + if (cpu_feature[1] & CPUID2_RAZ) { + if (pci_has_msi_quirk(id, PCI_QUIRK_ENABLE_MSI_VM)) { + pba->pba_flags |= PCI_FLAGS_MSI_OKAY; + pba->pba_flags |= PCI_FLAGS_MSIX_OKAY; + } + } + + /* + * Don't enable MSI on a HyperTransport bus. In order to + * determine that bus 0 is a HyperTransport bus, we look at + * device 24 function 0, which is the HyperTransport + * host/primary interface integrated on most 64-bit AMD CPUs. + * If that device has a HyperTransport capability, bus 0 must + * be a HyperTransport bus and we disable MSI. + */ + tag = pci_make_tag(pc, 0, 24, 0); + if (pci_get_capability(pc, tag, PCI_CAP_LDT, NULL, NULL)) { + pba->pba_flags &= ~PCI_FLAGS_MSI_OKAY; + pba->pba_flags &= ~PCI_FLAGS_MSIX_OKAY; + } +#endif /* __HAVE_PCI_MSI_MSIX */ } int Index: src/sys/arch/x86/x86/intr.c diff -u src/sys/arch/x86/x86/intr.c:1.80 src/sys/arch/x86/x86/intr.c:1.81 --- src/sys/arch/x86/x86/intr.c:1.80 Mon Apr 27 06:51:40 2015 +++ src/sys/arch/x86/x86/intr.c Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: intr.c,v 1.80 2015/04/27 06:51:40 knakahara Exp $ */ +/* $NetBSD: intr.c,v 1.81 2015/04/27 07:03:58 knakahara Exp $ */ /*- * Copyright (c) 2007, 2008, 2009 The NetBSD Foundation, Inc. @@ -133,7 +133,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.80 2015/04/27 06:51:40 knakahara Exp $"); +__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.81 2015/04/27 07:03:58 knakahara Exp $"); #include "opt_intrdebug.h" #include "opt_multiprocessor.h" @@ -176,6 +176,8 @@ __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.8 #include <dev/pci/ppbreg.h> #endif +#include <x86/pci/msipic.h> + #ifdef DDB #include <ddb/db_output.h> #endif @@ -225,6 +227,12 @@ static void intr_redistribute_xc_s1(void static void intr_redistribute_xc_s2(void *, void *); static bool intr_redistribute(struct cpu_info *); +static const char *create_intrid(int, struct pic *, int, char *, size_t); + +static struct intrsource *intr_get_io_intrsource(const char *); +static void intr_free_io_intrsource_direct(struct intrsource *); +static int intr_num_handlers(struct intrsource *); + static const char *legacy_intr_string(int, char *, size_t, struct pic *); static int intr_find_unused_slot(struct cpu_info *, int *); @@ -460,6 +468,23 @@ create_intrid(int legacy_irq, struct pic { int ih; + if ((pic->pic_type == PIC_MSI) || (pic->pic_type == PIC_MSIX)) { + uint64_t pih; + int dev, vec; + + dev = msipic_get_devid(pic); + vec = pin; + pih = __SHIFTIN((uint64_t)dev, MSI_INT_DEV_MASK) + | __SHIFTIN((uint64_t)vec, MSI_INT_VEC_MASK) + | APIC_INT_VIA_MSI; + if (pic->pic_type == PIC_MSI) + MSI_INT_MAKE_MSI(pih); + else if (pic->pic_type == PIC_MSIX) + MSI_INT_MAKE_MSIX(pih); + + return pci_msi_string(NULL, pih, buf, len); + } + /* * If the device is pci, "legacy_irq" is alway -1. Least 8 bit of "ih" * is only used in intr_string() to show the irq number. @@ -469,15 +494,15 @@ create_intrid(int legacy_irq, struct pic if (pic->pic_type == PIC_I8259) { ih = legacy_irq; return legacy_intr_string(ih, buf, len, pic); - } else { - ih = ((pic->pic_apicid << APIC_INT_APIC_SHIFT) & APIC_INT_APIC_MASK) | - ((pin << APIC_INT_PIN_SHIFT) & APIC_INT_PIN_MASK); - if (pic->pic_type == PIC_IOAPIC) { - ih |= APIC_INT_VIA_APIC; - } - ih |= pin; - return intr_string(ih, buf, len); } + + ih = ((pic->pic_apicid << APIC_INT_APIC_SHIFT) & APIC_INT_APIC_MASK) + | ((pin << APIC_INT_PIN_SHIFT) & APIC_INT_PIN_MASK); + if (pic->pic_type == PIC_IOAPIC) { + ih |= APIC_INT_VIA_APIC; + } + ih |= pin; + return intr_string(ih, buf, len); } /* @@ -492,9 +517,8 @@ intr_get_io_intrsource(const char *intri SIMPLEQ_FOREACH(isp, &io_interrupt_sources, is_list) { KASSERT(isp->is_intrid != NULL); - if (strncmp(intrid, isp->is_intrid, INTRIDBUF - 1) == 0) { + if (strncmp(intrid, isp->is_intrid, INTRIDBUF - 1) == 0) return isp; - } } return NULL; } @@ -553,6 +577,7 @@ intr_free_io_intrsource_direct(struct in kmem_free(isp->is_saved_evcnt, sizeof(*(isp->is_saved_evcnt)) * ncpu); + kmem_free(isp, sizeof(*isp)); } @@ -590,13 +615,17 @@ intr_allocate_slot_cpu(struct cpu_info * KASSERT(CPU_IS_PRIMARY(ci)); slot = pin; } else { + int start = 0; slot = -1; + /* avoid reserved slots for legacy interrupts. */ + if (CPU_IS_PRIMARY(ci) && msipic_is_msi_pic(pic)) + start = NUM_LEGACY_IRQS; /* * intr_allocate_slot has checked for an existing mapping. * Now look for a free slot. */ - for (i = 0; i < MAX_INTR_SOURCES ; i++) { + for (i = start; i < MAX_INTR_SOURCES ; i++) { if (ci->ci_isources[i] == NULL) { slot = i; break; @@ -609,10 +638,16 @@ intr_allocate_slot_cpu(struct cpu_info * isp = ci->ci_isources[slot]; if (isp == NULL) { + const char *via; + isp = chained; KASSERT(isp != NULL); + if (pic->pic_type == PIC_MSI || pic->pic_type == PIC_MSIX) + via = "vec"; + else + via = "pin"; snprintf(isp->is_evname, sizeof (isp->is_evname), - "pin %d", pin); + "%s %d", via, pin); evcnt_attach_dynamic(&isp->is_evcnt, EVCNT_TYPE_INTR, NULL, pic->pic_name, isp->is_evname); isp->is_active_cpu = ci->ci_cpuid; @@ -877,6 +912,11 @@ intr_establish(int legacy_irq, struct pi /* allocate intrsource pool, if not yet. */ chained = intr_get_io_intrsource(intrstr); if (chained == NULL) { + if (msipic_is_msi_pic(pic)) { + mutex_exit(&cpu_lock); + printf("%s: %s has no intrsource\n", __func__, intrstr); + return NULL; + } chained = intr_allocate_io_intrsource(intrstr); if (chained == NULL) { mutex_exit(&cpu_lock); @@ -1104,13 +1144,10 @@ intr_disestablish(struct intrhand *ih) where = xc_unicast(0, intr_disestablish_xcall, ih, NULL, ci); xc_wait(where); } - - if (intr_num_handlers(isp) < 1) { + if (!msipic_is_msi_pic(isp->is_pic) && intr_num_handlers(isp) < 1) { intr_free_io_intrsource_direct(isp); } - mutex_exit(&cpu_lock); - kmem_free(ih, sizeof(*ih)); } Index: src/sys/arch/x86/x86/ioapic.c diff -u src/sys/arch/x86/x86/ioapic.c:1.49 src/sys/arch/x86/x86/ioapic.c:1.50 --- src/sys/arch/x86/x86/ioapic.c:1.49 Mon Apr 27 06:51:40 2015 +++ src/sys/arch/x86/x86/ioapic.c Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: ioapic.c,v 1.49 2015/04/27 06:51:40 knakahara Exp $ */ +/* $NetBSD: ioapic.c,v 1.50 2015/04/27 07:03:58 knakahara Exp $ */ /*- * Copyright (c) 2000, 2009 The NetBSD Foundation, Inc. @@ -64,7 +64,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ioapic.c,v 1.49 2015/04/27 06:51:40 knakahara Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ioapic.c,v 1.50 2015/04/27 07:03:58 knakahara Exp $"); #include "opt_ddb.h" @@ -183,7 +183,7 @@ ioapic_write(struct ioapic_softc *sc,int } struct ioapic_softc * -ioapic_find(intr_handle_t apicid) +ioapic_find(int apicid) { struct ioapic_softc *sc; Index: src/sys/arch/xen/include/intr.h diff -u src/sys/arch/xen/include/intr.h:1.35 src/sys/arch/xen/include/intr.h:1.36 --- src/sys/arch/xen/include/intr.h:1.35 Thu Dec 27 06:42:14 2012 +++ src/sys/arch/xen/include/intr.h Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: intr.h,v 1.35 2012/12/27 06:42:14 cherry Exp $ */ +/* $NetBSD: intr.h,v 1.36 2015/04/27 07:03:58 knakahara Exp $ */ /* NetBSD intr.h,v 1.15 2004/10/31 10:39:34 yamt Exp */ /*- @@ -154,6 +154,8 @@ splraiseipl(ipl_cookie_t icookie) struct pcibus_attach_args; +typedef int intr_handle_t; + #ifdef MULTIPROCESSOR int intr_biglock_wrapper(void *); #endif @@ -163,7 +165,7 @@ int x86_nmi(void); void *intr_establish(int, struct pic *, int, int, int, int (*)(void *), void *, bool); void intr_disestablish(struct intrhand *); -const char *intr_string(int); +const char *intr_string(intr_handle_t); void cpu_intr_init(struct cpu_info *); int xen_intr_map(int *, int); #ifdef INTRDEBUG Index: src/sys/dev/pci/pci.c diff -u src/sys/dev/pci/pci.c:1.145 src/sys/dev/pci/pci.c:1.146 --- src/sys/dev/pci/pci.c:1.145 Fri Sep 5 05:29:16 2014 +++ src/sys/dev/pci/pci.c Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: pci.c,v 1.145 2014/09/05 05:29:16 matt Exp $ */ +/* $NetBSD: pci.c,v 1.146 2015/04/27 07:03:58 knakahara Exp $ */ /* * Copyright (c) 1995, 1996, 1997, 1998 @@ -36,7 +36,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: pci.c,v 1.145 2014/09/05 05:29:16 matt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: pci.c,v 1.146 2015/04/27 07:03:58 knakahara Exp $"); #include "opt_pci.h" @@ -275,6 +275,10 @@ pci_probe_device(struct pci_softc *sc, p pci_chipset_tag_t pc = sc->sc_pc; struct pci_attach_args pa; pcireg_t id, /* csr, */ pciclass, intr, bhlcr, bar, endbar; +#ifdef __HAVE_PCI_MSI_MSIX + pcireg_t cap; + int off; +#endif int ret, pin, bus, device, function, i, width; int locs[PCICF_NLOCS]; @@ -406,6 +410,34 @@ pci_probe_device(struct pci_softc *sc, p } pa.pa_intrline = PCI_INTERRUPT_LINE(intr); +#ifdef __HAVE_PCI_MSI_MSIX + if (pci_get_ht_capability(pc, tag, PCI_HT_CAP_MSIMAP, &off, &cap)) { + /* + * XXX Should we enable MSI mapping ourselves on + * systems that have it disabled? + */ + if (cap & PCI_HT_MSI_ENABLED) { + uint64_t addr; + if ((cap & PCI_HT_MSI_FIXED) == 0) { + addr = pci_conf_read(pc, tag, + off + PCI_HT_MSI_ADDR_LO); + addr |= (uint64_t)pci_conf_read(pc, tag, + off + PCI_HT_MSI_ADDR_HI) << 32; + } else + addr = PCI_HT_MSI_FIXED_ADDR; + + /* + * XXX This will fail to enable MSI on systems + * that don't use the canonical address. + */ + if (addr == PCI_HT_MSI_FIXED_ADDR) { + pa.pa_flags |= PCI_FLAGS_MSI_OKAY; + pa.pa_flags |= PCI_FLAGS_MSIX_OKAY; + } + } + } +#endif + if (match != NULL) { ret = (*match)(&pa); if (ret != 0 && pap != NULL) @@ -508,6 +540,35 @@ pci_get_capability(pci_chipset_tag_t pc, } int +pci_get_ht_capability(pci_chipset_tag_t pc, pcitag_t tag, int capid, + int *offset, pcireg_t *value) +{ + pcireg_t reg; + unsigned int ofs; + + if (pci_get_capability(pc, tag, PCI_CAP_LDT, &ofs, NULL) == 0) + return 0; + + while (ofs != 0) { +#ifdef DIAGNOSTIC + if ((ofs & 3) || (ofs < 0x40)) + panic("pci_get_ht_capability"); +#endif + reg = pci_conf_read(pc, tag, ofs); + if (PCI_HT_CAP(reg) == capid) { + if (offset) + *offset = ofs; + if (value) + *value = reg; + return 1; + } + ofs = PCI_CAPLIST_NEXT(reg); + } + + return 0; +} + +int pci_find_device(struct pci_attach_args *pa, int (*match)(const struct pci_attach_args *)) { Index: src/sys/dev/pci/pcireg.h diff -u src/sys/dev/pci/pcireg.h:1.101 src/sys/dev/pci/pcireg.h:1.102 --- src/sys/dev/pci/pcireg.h:1.101 Mon Feb 23 04:16:17 2015 +++ src/sys/dev/pci/pcireg.h Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: pcireg.h,v 1.101 2015/02/23 04:16:17 knakahara Exp $ */ +/* $NetBSD: pcireg.h,v 1.102 2015/04/27 07:03:58 knakahara Exp $ */ /* * Copyright (c) 1995, 1996, 1999, 2000 @@ -653,6 +653,9 @@ typedef u_int8_t pci_revision_t; * MSI Pending Bits (32 bit field) */ + /* Max number of MSI vectors. See PCI-SIG specification. */ +#define PCI_MSI_MAX_VECTORS 32 + /* * Capability ID: 0x07 * PCI-X capability. @@ -1069,6 +1072,9 @@ struct pci_msix_table_entry { }; #define PCI_MSIX_VECTCTL_HWMASK_MASK 0x00000001 + /* Max number of MSI-X vectors. See PCI-SIG specification. */ +#define PCI_MSIX_MAX_VECTORS 2048 + /* * Capability ID: 0x12 * SATA Index: src/sys/dev/pci/pcivar.h diff -u src/sys/dev/pci/pcivar.h:1.101 src/sys/dev/pci/pcivar.h:1.102 --- src/sys/dev/pci/pcivar.h:1.101 Fri Dec 26 05:09:03 2014 +++ src/sys/dev/pci/pcivar.h Mon Apr 27 07:03:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: pcivar.h,v 1.101 2014/12/26 05:09:03 msaitoh Exp $ */ +/* $NetBSD: pcivar.h,v 1.102 2015/04/27 07:03:58 knakahara Exp $ */ /* * Copyright (c) 1996, 1997 Christopher G. Demetriou. All rights reserved. @@ -279,6 +279,7 @@ int pci_find_rom(const struct pci_attach int, bus_space_handle_t *, bus_size_t *); int pci_get_capability(pci_chipset_tag_t, pcitag_t, int, int *, pcireg_t *); +int pci_get_ht_capability(pci_chipset_tag_t, pcitag_t, int, int *, pcireg_t *); /* * Helper functions for autoconfiguration. Added files: Index: src/share/man/man9/pci_msi.9 diff -u /dev/null src/share/man/man9/pci_msi.9:1.1 --- /dev/null Mon Apr 27 07:03:58 2015 +++ src/share/man/man9/pci_msi.9 Mon Apr 27 07:03:57 2015 @@ -0,0 +1,210 @@ +.\" $NetBSD: pci_msi.9,v 1.1 2015/04/27 07:03:57 knakahara Exp $ +.\" +.\" Copyright (c) 2015 Internet Initiative Japan Inc. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd April 8, 2015 +.Dt PCI_MSI 9 (DRAFT) +.Os +.Sh NAME (DRAFT) +.Nm pci_msi , +.Nm pci_msix, +.Nm pci_msi_count , +.Nm pci_msi_alloc , +.Nm pci_msi_alloc_exact , +.Nm pci_msi_release , +.Nm pci_msi_establish , +.Nm pci_msi_disestablish , +.Nm pci_msi_string +.Nm pci_msix_count , +.Nm pci_msix_alloc , +.Nm pci_msix_alloc_exact , +.Nm pci_msix_alloc_map , +.Nm pci_msix_release , +.Nm pci_msix_establish , +.Nm pci_msix_disestablish , +.Nm pci_intx_alloc , +.Nm pci_intx_release +.Nd PCI MSI{,-X} manipulation functions +.Sh SYNOPSIS +.In NOTYET +.Ft int +.Fn pci_msi_count "struct pci_attach_args *pa" +.Ft int +.Fn pci_msi_alloc "struct pci_attach_args *pa" \ +"pci_intr_handle_t **ihps" "int *count" +.Ft int +.Fn pci_msi_alloc_exect "struct pci_attach_args *pa" \ +"pci_intr_handle_t **ihps" "int count" +.Ft void +.Fn pci_msi_release "pci_intr_handle_t **pihs" "int count" +.Ft void * +.Fn pci_msi_establish "pci_chipset_tag_t pc" "pci_intr_handle_t ih" \ +"int level" "int (*func)(void *)" "void *arg" +.Ft void +.Fn pci_msi_disestablish "pci_chipset_tag_t pc" "void *cookie" +.Ft const char * +.Ft pci_msi_string "pci_chipset_tag_t pc" \ +"pci_intr_handle_t, char *buf" "size_t len" +.Ft int +.Fn pci_msix_count "struct pci_attach_args *pa" +.Ft int +.Fn pci_msix_alloc "struct pci_attach_args *pa" \ +"pci_intr_handle_t **ihps" "int *count" +.Ft int +.Fn pci_msix_alloc_exect "struct pci_attach_args *pa" \ +"pci_intr_handle_t **ihps" "int count" +.Ft int +.Fn pci_msix_alloc_map "struct pci_attach_args *pa" \ +"pci_intr_handle_t **ihps" "u_int *table_indexes" "int count" +.Ft void +.Fn pci_msix_release "pci_intr_handle_t **pihs" "int count" +.Ft void * +.Fn pci_msix_establish "pci_chipset_tag_t pc" "pci_intr_handle_t ih" \ +"int level" "int (*func)(void *)" "void *arg" +.Fn pci_msix_disestablish "pci_chipset_tag_t pc" "void *cookie" +.Ft int +.Fn pci_intx_alloc "struct pci_attach_args *pa" \ +"pci_intr_handle_t **ihp" +.Ft void +.Fn pci_intx_release "pci_intr_handle_t *pih" +.Sh DESCRIPTION +XXX This decument describes draft APIs. These APIs may change later. +.Pp +The +.Nm +functions exist to allow device drivers to use MSI/MSI-X. +When the system use MSI/MSI-X, it must define a __HAVE_PCI_MSI_MSIX +build option. +.Pp +Each driver has an +.Fn attach +function which has a bus-specific +.Ft attach_args +structure. +Each driver for a PCI device is passed a pointer to an object of type +.Ft struct pci_attach_args +which contains, among other things, information about the location +of the device in the PCI bus topology sufficient to allow interrupts +from the device to be handled. +.Pp +If a driver wishes to establish an MSI handler for the device, +it should pass the +.Ft struct pci_attach_args * +and +.Ft count +.Fn pci_msi_alloc +or +.Fn pci_msi_alloc_exact +functions, which returns zero on success, and nonzero on failure. +When the functions successed, set the pointer to allocated handle +array to +.Ft pihs +whose size is +.Ft count +or less. The difference between +.Fn pci_msi_alloc +and +.Fn pci_msi_alloc_exact +is +.Ft count +can be decremented or not. +.Fn pci_msi_alloc +can decrement +.Ft count , +and +which is similar to FreeBSD's +.Fn pci_alloc_msi . +In contrast, +.Fn pci_msi_alloc_exact +can not decrement +.Ft count . +.Pp +If the driver wishes to refer to the MSI source in an attach or +error message, it should use the value returned by +.Fn pci_msi_string . +The buffer passed to +.Fn pci_msi_string +should be at least +.Dv PCI_INTRSTR_LEN +bytes. +.Pp +Subsequently, when the driver is prepared to receive MSIs, it +should call +.Fn pci_msi_establish +to actually establish the handler; when the device interrupts, +.Fa intrhand +will be called with a single argument +.Fa intrarg , +and will run at the interrupt priority level +.Fa ipl . +This is the same as +.Fn pci_intr_establish . +.Pp +The return value of +.Fn pci_msi_establish +may be saved and passed to +.Fn pci_msi_disestablish +to disable the interrupt handler +when the driver is no longer interested in MSIs from the device. +After that, the driver should also +.Fn pci_msi_release +to free resources about MSI. +.Pp +If a driver wishes to establish an MSI-X handler for the device, +it is alomost the same as MSI. +The only differences is +.Fn pci_msix_alloc_map . +This function can assign each handles to MSI-X table entries. +e.g. If the driver want assign each handler to +.Bd -literal + msix_handler0 => MSI-X table index: 4 + msix_handler1 => MSI-X table index: 5 + msix_handler2 => MSI-X table index: 0 +.Ed +, the driver should set +.Bd -literal + table_indexes[0] = 4; + table_indexes[1] = 5; + table_indexes[2] = 0; +.Ed +to +.Ft table_indexes . +.Pp +If the driver want to fallback to INTx, the driver should use +.Fn pci_intx_alloc +and +.Fn pci_intx_release +instead of +.Fn pci_intr_map +to resolve contradiction of the interrupt handler ownership. +i.e. +.Fn pci_intr_map +does not have the ownership (the function just calcurates value), +in contrast, +.Fn pci_msi_alloc +and +.Fn pci_msix_alloc +has the owneship (the functions allocate memory for interrupt +handlers). Index: src/sys/arch/x86/pci/msipic.c diff -u /dev/null src/sys/arch/x86/pci/msipic.c:1.1 --- /dev/null Mon Apr 27 07:03:58 2015 +++ src/sys/arch/x86/pci/msipic.c Mon Apr 27 07:03:58 2015 @@ -0,0 +1,737 @@ +/* $NetBSD: msipic.c,v 1.1 2015/04/27 07:03:58 knakahara Exp $ */ + +/* + * Copyright (c) 2015 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: msipic.c,v 1.1 2015/04/27 07:03:58 knakahara Exp $"); + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/kmem.h> +#include <sys/malloc.h> +#include <sys/mutex.h> + +#include <dev/pci/pcivar.h> + +#include <machine/i82489reg.h> +#include <machine/i82093reg.h> +#include <machine/i82093var.h> +#include <machine/pic.h> +#include <machine/lock.h> + +#include <x86/pci/msipic.h> + +#ifdef INTRDEBUG +#define MSIPICDEBUG +#endif + +#ifdef MSIPICDEBUG +#define DPRINTF(msg) printf msg +#else +#define DPRINTF(msg) +#endif + +#define BUS_SPACE_WRITE_FLUSH(pc, tag) (void)bus_space_read_4(pc, tag, 0) + +#define MSIPICNAMEBUF 16 + +/* + * A Pseudo pic for single MSI/MSI-X device. + * The pic and MSI/MSI-X device are distinbuished by "devid". The "devid" + * is managed by below "dev_seqs". + */ +struct msipic { + int mp_bus; + int mp_dev; + int mp_fun; + + int mp_devid; /* The device id for the MSI/MSI-X device. */ + int mp_veccnt; /* The number of MSI/MSI-X vectors. */ + + char mp_pic_name[MSIPICNAMEBUF]; /* The MSI/MSI-X device's name. */ + + struct pci_attach_args mp_pa; + bus_space_tag_t mp_bstag; + bus_space_handle_t mp_bshandle; + bus_size_t mp_bssize; + struct pic *mp_pic; + + LIST_ENTRY(msipic) mp_list; +}; + +static kmutex_t msipic_list_lock; + +static LIST_HEAD(, msipic) msipic_list = + LIST_HEAD_INITIALIZER(msipic_list); + +/* + * This struct managements "devid" to use the same "devid" for the device + * re-attached. If the device's bus number and device numer and function + * number are equal, it is assumed re-attached. + */ +struct dev_last_used_seq { + bool ds_using; + int ds_bus; + int ds_dev; + int ds_fun; +}; +/* The number of MSI/MSI-X devices supported by system. */ +#define NUM_MSI_DEVS 256 +/* Record devids to use the same devid when the device is re-attached. */ +static struct dev_last_used_seq dev_seqs[NUM_MSI_DEVS]; + +static int msipic_allocate_common_msi_devid(struct pci_attach_args *); +static void msipic_release_common_msi_devid(int); + +static struct pic *msipic_find_msi_pic_locked(int); +static struct pic *msipic_construct_common_msi_pic(struct pci_attach_args *, + struct pic *); +static void msipic_destruct_common_msi_pic(struct pic *); + +static void msi_set_msictl_enablebit(struct pic *, int, int); +static void msi_hwmask(struct pic *, int); +static void msi_hwunmask(struct pic *, int); +static void msi_addroute(struct pic *, struct cpu_info *, int, int, int); +static void msi_delroute(struct pic *, struct cpu_info *, int, int, int); + +static void msix_set_vecctl_mask(struct pic *, int, int); +static void msix_hwmask(struct pic *, int); +static void msix_hwunmask(struct pic *, int); +static void msix_addroute(struct pic *, struct cpu_info *, int, int, int); +static void msix_delroute(struct pic *, struct cpu_info *, int, int, int); + +/* + * Return new "devid" for the device attached first. + * Return the same "devid" for the device re-attached after dettached once. + * Return -1 if the number of attached MSI/MSI-X devices is over NUM_MSI_DEVS. + */ +static int +msipic_allocate_common_msi_devid(struct pci_attach_args *pa) +{ + pci_chipset_tag_t pc; + pcitag_t tag; + int bus, dev, fun, i; + + KASSERT(mutex_owned(&msipic_list_lock)); + + pc = pa->pa_pc; + tag = pa->pa_tag; + pci_decompose_tag(pc, tag, &bus, &dev, &fun); + + /* if the device was once attached, use same devid */ + for (i = 0; i < NUM_MSI_DEVS; i++) { + /* skip host bridge */ + if (dev_seqs[i].ds_bus == 0 + && dev_seqs[i].ds_dev == 0 + && dev_seqs[i].ds_fun == 0) + break; + + if (dev_seqs[i].ds_bus == bus + && dev_seqs[i].ds_dev == dev + && dev_seqs[i].ds_fun == fun) { + dev_seqs[i].ds_using = true; + return i; + } + } + + for (i = 0; i < NUM_MSI_DEVS; i++) { + if (dev_seqs[i].ds_using == 0) { + dev_seqs[i].ds_using = true; + dev_seqs[i].ds_bus = bus; + dev_seqs[i].ds_dev = dev; + dev_seqs[i].ds_fun = fun; + return i; + } + } + + DPRINTF(("too many MSI devices.\n")); + return -1; +} + +/* + * Set the "devid" unused, but keep reserving the "devid" to reuse when + * the device is re-attached. + */ +static void +msipic_release_common_msi_devid(int devid) +{ + + KASSERT(mutex_owned(&msipic_list_lock)); + + if (devid < 0 || NUM_MSI_DEVS <= devid) { + DPRINTF(("%s: invalid devid.\n", __func__)); + return; + } + + dev_seqs[devid].ds_using = false; + /* Keep ds_* to reuse the same devid for the same device. */ +} + +static struct pic * +msipic_find_msi_pic_locked(int devid) +{ + struct msipic *mpp; + + KASSERT(mutex_owned(&msipic_list_lock)); + + LIST_FOREACH(mpp, &msipic_list, mp_list) { + if(mpp->mp_devid == devid) + return mpp->mp_pic; + } + return NULL; +} + +/* + * Return the msi_pic whose device is already registered. + * If the device is not registered yet, return NULL. + */ +struct pic * +msipic_find_msi_pic(int devid) +{ + struct pic *msipic; + + mutex_enter(&msipic_list_lock); + msipic = msipic_find_msi_pic_locked(devid); + mutex_exit(&msipic_list_lock); + + return msipic; +} + +/* + * A common construct process of MSI and MSI-X. + */ +static struct pic * +msipic_construct_common_msi_pic(struct pci_attach_args *pa, + struct pic *pic_tmpl) +{ + struct pic *pic; + struct msipic *msipic; + int devid; + + pic = kmem_alloc(sizeof(*pic), KM_SLEEP); + if (pic == NULL) + return NULL; + + msipic = kmem_zalloc(sizeof(*msipic), KM_SLEEP); + if (msipic == NULL) { + kmem_free(pic, sizeof(*pic)); + return NULL; + } + + mutex_enter(&msipic_list_lock); + + devid = msipic_allocate_common_msi_devid(pa); + if (devid == -1) { + mutex_exit(&msipic_list_lock); + kmem_free(pic, sizeof(*pic)); + kmem_free(msipic, sizeof(*msipic)); + return NULL; + } + + memcpy(pic, pic_tmpl, sizeof(*pic)); + pic->pic_msipic = msipic; + msipic->mp_pic = pic; + pci_decompose_tag(pa->pa_pc, pa->pa_tag, + &msipic->mp_bus, &msipic->mp_dev, &msipic->mp_fun); + memcpy(&msipic->mp_pa, pa, sizeof(msipic->mp_pa)); + msipic->mp_devid = devid; + /* + * pci_msi{,x}_alloc() must be called only once in the device driver. + */ + KASSERT(msipic_find_msi_pic_locked(msipic->mp_devid) == NULL); + + LIST_INSERT_HEAD(&msipic_list, msipic, mp_list); + + mutex_exit(&msipic_list_lock); + + return pic; +} + +static void +msipic_destruct_common_msi_pic(struct pic *msi_pic) +{ + struct msipic *msipic; + + if (msi_pic == NULL) + return; + + msipic = msi_pic->pic_msipic; + mutex_enter(&msipic_list_lock); + LIST_REMOVE(msipic, mp_list); + msipic_release_common_msi_devid(msipic->mp_devid); + mutex_exit(&msipic_list_lock); + + kmem_free(msipic, sizeof(*msipic)); + kmem_free(msi_pic, sizeof(*msi_pic)); +} + +/* + * The pic is MSI/MSI-X pic or not. + */ +bool +msipic_is_msi_pic(struct pic *pic) +{ + + return (pic->pic_msipic != NULL); +} + +/* + * Return the MSI/MSI-X devid which is unique for each devices. + */ +int +msipic_get_devid(struct pic *pic) +{ + + KASSERT(msipic_is_msi_pic(pic)); + + return pic->pic_msipic->mp_devid; +} + +#define MSI_MSICTL_ENABLE 1 +#define MSI_MSICTL_DISABLE 0 +static void +msi_set_msictl_enablebit(struct pic *pic, int msi_vec, int flag) +{ + pci_chipset_tag_t pc; + struct pci_attach_args *pa; + pcitag_t tag; + pcireg_t ctl; + int off; + + pc = NULL; + pa = &pic->pic_msipic->mp_pa; + tag = pa->pa_tag; + KASSERT(pci_get_capability(pc, tag, PCI_CAP_MSI, &off, NULL) != 0); + + /* + * MSI can establish only one vector at once. + * So, use whole device mask bit instead of a vector mask bit. + */ + ctl = pci_conf_read(pc, tag, off + PCI_MSI_CTL); + if (flag == MSI_MSICTL_ENABLE) + ctl |= PCI_MSI_CTL_MSI_ENABLE; + else + ctl &= ~PCI_MSI_CTL_MSI_ENABLE; + + pci_conf_write(pc, tag, off, ctl); +} + +static void +msi_hwmask(struct pic *pic, int msi_vec) +{ + + msi_set_msictl_enablebit(pic, msi_vec, MSI_MSICTL_DISABLE); +} + +/* + * Do not use pic->hwunmask() immediately after pic->delroute(). + * It is required to use pic->addroute() before pic->hwunmask(). + */ +static void +msi_hwunmask(struct pic *pic, int msi_vec) +{ + + msi_set_msictl_enablebit(pic, msi_vec, MSI_MSICTL_ENABLE); +} + +static void +msi_addroute(struct pic *pic, struct cpu_info *ci, + int unused, int idt_vec, int type) +{ + pci_chipset_tag_t pc; + struct pci_attach_args *pa; + pcitag_t tag; + pcireg_t addr, data, ctl; + int off; + + pc = NULL; + pa = &pic->pic_msipic->mp_pa; + tag = pa->pa_tag; + KASSERT(pci_get_capability(pc, tag, PCI_CAP_MSI, &off, NULL) != 0); + + /* + * See Intel 64 and IA-32 Architectures Software Developer's Manual + * Volume 3 10.11 Message Signalled Interrupts. + */ + /* + * "cpuid" for MSI address is local APIC ID. In NetBSD, the ID is + * the same as ci->ci_cpuid. + */ + addr = LAPIC_MSIADDR_BASE | __SHIFTIN(ci->ci_cpuid, + LAPIC_MSIADDR_DSTID_MASK); + /* If trigger mode is edge, it don't care level for trigger mode. */ + data = __SHIFTIN(idt_vec, LAPIC_MSIDATA_VECTOR_MASK) + | LAPIC_MSIDATA_TRGMODE_EDGE | LAPIC_MSIDATA_DM_FIXED; + + ctl = pci_conf_read(pc, tag, off + PCI_MSI_CTL); + if (ctl & PCI_MSI_CTL_64BIT_ADDR) { + pci_conf_write(pc, tag, off + PCI_MSI_MADDR64_LO, addr); + pci_conf_write(pc, tag, off + PCI_MSI_MADDR64_HI, 0); + pci_conf_write(pc, tag, off + PCI_MSI_MDATA64, data); + } else { + pci_conf_write(pc, tag, off + PCI_MSI_MADDR, addr); + pci_conf_write(pc, tag, off + PCI_MSI_MDATA, data); + } + ctl |= PCI_MSI_CTL_MSI_ENABLE; + pci_conf_write(pc, tag, off + PCI_MSI_CTL, ctl); +} + +/* + * Do not use pic->hwunmask() immediately after pic->delroute(). + * It is required to use pic->addroute() before pic->hwunmask(). + */ +static void +msi_delroute(struct pic *pic, struct cpu_info *ci, + int msi_vec, int idt_vec, int type) +{ + + msi_hwmask(pic, msi_vec); +} + +/* + * Template for MSI pic. + * .pic_msipic is set later in construct_msi_pic(). + */ +static struct pic msi_pic_tmpl = { + .pic_type = PIC_MSI, + .pic_vecbase = 0, + .pic_apicid = 0, + .pic_lock = __SIMPLELOCK_UNLOCKED, /* not used for msi_pic */ + .pic_hwmask = msi_hwmask, + .pic_hwunmask = msi_hwunmask, + .pic_addroute = msi_addroute, + .pic_delroute = msi_delroute, + .pic_edge_stubs = ioapic_edge_stubs, + .pic_ioapic = NULL, +}; + +/* + * Create pseudo pic for a MSI device. + */ +struct pic * +msipic_construct_msi_pic(struct pci_attach_args *pa) +{ + struct pic *msi_pic; + char pic_name_buf[MSIPICNAMEBUF]; + + msi_pic = msipic_construct_common_msi_pic(pa, &msi_pic_tmpl); + if (msi_pic == NULL) { + DPRINTF(("cannot allocate MSI pic.\n")); + return NULL; + } + + memset(pic_name_buf, 0, MSIPICNAMEBUF); + snprintf(pic_name_buf, MSIPICNAMEBUF, "msi%d", + msi_pic->pic_msipic->mp_devid); + strncpy(msi_pic->pic_msipic->mp_pic_name, pic_name_buf, + MSIPICNAMEBUF - 1); + msi_pic->pic_name = msi_pic->pic_msipic->mp_pic_name; + + return msi_pic; +} + +/* + * Delete pseudo pic for a MSI device. + */ +void +msipic_destruct_msi_pic(struct pic *msi_pic) +{ + + msipic_destruct_common_msi_pic(msi_pic); +} + +#define MSIX_VECCTL_HWMASK 1 +#define MSIX_VECCTL_HWUNMASK 0 +static void +msix_set_vecctl_mask(struct pic *pic, int msix_vec, int flag) +{ + bus_space_tag_t bstag; + bus_space_handle_t bshandle; + uint64_t entry_base; + uint32_t vecctl; + + if (msix_vec < 0) { + DPRINTF(("%s: invalid MSI-X table index, devid=%d vecid=%d", + __func__, msi_get_devid(pic), msix_vec)); + return; + } + + entry_base = PCI_MSIX_TABLE_ENTRY_SIZE * msix_vec; + + bstag = pic->pic_msipic->mp_bstag; + bshandle = pic->pic_msipic->mp_bshandle; + vecctl = bus_space_read_4(bstag, bshandle, + entry_base + PCI_MSIX_TABLE_ENTRY_VECTCTL); + if (flag == MSIX_VECCTL_HWMASK) + vecctl |= PCI_MSIX_VECTCTL_HWMASK_MASK; + else + vecctl &= ~PCI_MSIX_VECTCTL_HWMASK_MASK; + + bus_space_write_4(bstag, bshandle, + entry_base + PCI_MSIX_TABLE_ENTRY_VECTCTL, vecctl); + BUS_SPACE_WRITE_FLUSH(bstag, bshandle); +} + +static void +msix_hwmask(struct pic *pic, int msix_vec) +{ + + msix_set_vecctl_mask(pic, msix_vec, MSIX_VECCTL_HWMASK); +} + +/* + * Do not use pic->hwunmask() immediately after pic->delroute(). + * It is required to use pic->addroute() before pic->hwunmask(). + */ +static void +msix_hwunmask(struct pic *pic, int msix_vec) +{ + + msix_set_vecctl_mask(pic, msix_vec, MSIX_VECCTL_HWUNMASK); +} + +static void +msix_addroute(struct pic *pic, struct cpu_info *ci, + int msix_vec, int idt_vec, int type) +{ + pci_chipset_tag_t pc; + struct pci_attach_args *pa; + pcitag_t tag; + bus_space_tag_t bstag; + bus_space_handle_t bshandle; + uint64_t entry_base; + pcireg_t addr, data, ctl; + int off; + + if (msix_vec < 0) { + DPRINTF(("%s: invalid MSI-X table index, devid=%d vecid=%d", + __func__, msi_get_devid(pic), msix_vec)); + return; + } + + pa = &pic->pic_msipic->mp_pa; + pc = pa->pa_pc; + tag = pa->pa_tag; + KASSERT(pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL) != 0); + + entry_base = PCI_MSIX_TABLE_ENTRY_SIZE * msix_vec; + + /* + * See Intel 64 and IA-32 Architectures Software Developer's Manual + * Volume 3 10.11 Message Signalled Interrupts. + */ + /* + * "cpuid" for MSI-X address is local APIC ID. In NetBSD, the ID is + * the same as ci->ci_cpuid. + */ + addr = LAPIC_MSIADDR_BASE | __SHIFTIN(ci->ci_cpuid, + LAPIC_MSIADDR_DSTID_MASK); + /* If trigger mode is edge, it don't care level for trigger mode. */ + data = __SHIFTIN(idt_vec, LAPIC_MSIDATA_VECTOR_MASK) + | LAPIC_MSIDATA_TRGMODE_EDGE | LAPIC_MSIDATA_DM_FIXED; + + bstag = pic->pic_msipic->mp_bstag; + bshandle = pic->pic_msipic->mp_bshandle; + bus_space_write_4(bstag, bshandle, + entry_base + PCI_MSIX_TABLE_ENTRY_ADDR_LO, addr); + bus_space_write_4(bstag, bshandle, + entry_base + PCI_MSIX_TABLE_ENTRY_ADDR_HI, 0); + bus_space_write_4(bstag, bshandle, + entry_base + PCI_MSIX_TABLE_ENTRY_DATA, data); + bus_space_write_4(bstag, bshandle, + entry_base + PCI_MSIX_TABLE_ENTRY_VECTCTL, 0); + BUS_SPACE_WRITE_FLUSH(bstag, bshandle); + + 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); +} + +/* + * Do not use pic->hwunmask() immediately after pic->delroute(). + * It is required to use pic->addroute() before pic->hwunmask(). + */ +static void +msix_delroute(struct pic *pic, struct cpu_info *ci, + int msix_vec, int vec, int type) +{ + + msix_hwmask(pic, msix_vec); +} + +/* + * Template for MSI-X pic. + * .pic_msipic is set later in construct_msix_pic(). + */ +static struct pic msix_pic_tmpl = { + .pic_type = PIC_MSIX, + .pic_vecbase = 0, + .pic_apicid = 0, + .pic_lock = __SIMPLELOCK_UNLOCKED, /* not used for msix_pic */ + .pic_hwmask = msix_hwmask, + .pic_hwunmask = msix_hwunmask, + .pic_addroute = msix_addroute, + .pic_delroute = msix_delroute, + .pic_edge_stubs = ioapic_edge_stubs, +}; + +struct pic * +msipic_construct_msix_pic(struct pci_attach_args *pa) +{ + struct pic *msix_pic; + pci_chipset_tag_t pc; + pcitag_t tag; + pcireg_t tbl; + bus_space_tag_t bstag; + bus_space_handle_t bshandle; + bus_size_t bssize; + size_t table_size; + uint32_t table_offset; + u_int memtype; + int bir, bar, err, off, table_nentry; + char pic_name_buf[MSIPICNAMEBUF]; + + table_nentry = pci_msix_count(pa); + if (table_nentry == 0) { + DPRINTF(("MSI-X table entry is 0.\n")); + return NULL; + } + + pc = pa->pa_pc; + tag = pa->pa_tag; + if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, NULL) == 0) { + DPRINTF(("%s: no msix capability", __func__)); + return NULL; + } + + msix_pic = msipic_construct_common_msi_pic(pa, &msix_pic_tmpl); + if (msix_pic == NULL) { + DPRINTF(("cannot allocate MSI-X pic.\n")); + return NULL; + } + + memset(pic_name_buf, 0, MSIPICNAMEBUF); + snprintf(pic_name_buf, MSIPICNAMEBUF, "msix%d", + msix_pic->pic_msipic->mp_devid); + strncpy(msix_pic->pic_msipic->mp_pic_name, pic_name_buf, + MSIPICNAMEBUF - 1); + msix_pic->pic_name = msix_pic->pic_msipic->mp_pic_name; + + tbl = pci_conf_read(pc, tag, off + PCI_MSIX_TBLOFFSET); + table_offset = tbl & PCI_MSIX_TBLOFFSET_MASK; + bir = tbl & PCI_MSIX_PBABIR_MASK; + switch(bir) { + case 0: + bar = PCI_BAR0; + break; + case 1: + bar = PCI_BAR1; + break; + case 2: + bar = PCI_BAR2; + break; + case 3: + bar = PCI_BAR3; + break; + case 4: + bar = PCI_BAR4; + break; + case 5: + bar = PCI_BAR5; + break; + default: + aprint_error("detect an illegal device! The device use reserved BIR values.\n"); + msipic_destruct_common_msi_pic(msix_pic); + return NULL; + } + memtype = pci_mapreg_type(pc, tag, bar); + /* + * PCI_MSIX_TABLE_ENTRY_SIZE consists below + * - Vector Control (32bit) + * - Message Data (32bit) + * - Message Upper Address (32bit) + * - Message Lower Address (32bit) + */ + table_size = table_nentry * PCI_MSIX_TABLE_ENTRY_SIZE; + err = pci_mapreg_submap(pa, bar, memtype, BUS_SPACE_MAP_LINEAR, + roundup(table_size, PAGE_SIZE), table_offset, + &bstag, &bshandle, NULL, &bssize); + if (err) { + DPRINTF(("cannot map msix table.\n")); + msipic_destruct_common_msi_pic(msix_pic); + return NULL; + } + msix_pic->pic_msipic->mp_bstag = bstag; + msix_pic->pic_msipic->mp_bshandle = bshandle; + msix_pic->pic_msipic->mp_bssize = bssize; + + return msix_pic; +} + +/* + * Delete pseudo pic for a MSI-X device. + */ +void +msipic_destruct_msix_pic(struct pic *msix_pic) +{ + struct msipic *msipic; + + KASSERT(msipic_is_msi_pic(msix_pic)); + KASSERT(msix_pic->pic_type == PIC_MSIX); + + msipic = msix_pic->pic_msipic; + bus_space_unmap(msipic->mp_bstag, msipic->mp_bshandle, + msipic->mp_bssize); + + msipic_destruct_common_msi_pic(msix_pic); +} + +/* + * Set the number of MSI vectors for pseudo MSI pic. + */ +int +msipic_set_msi_vectors(struct pic *msi_pic, pci_intr_handle_t *pihs, + int count) +{ + + KASSERT(msipic_is_msi_pic(msi_pic)); + + msi_pic->pic_msipic->mp_veccnt = count; + return 0; +} + +/* + * Initialize the system to use MSI/MSI-X. + */ +void +msipic_init(void) +{ + + mutex_init(&msipic_list_lock, MUTEX_DEFAULT, IPL_NONE); +} Index: src/sys/arch/x86/pci/msipic.h diff -u /dev/null src/sys/arch/x86/pci/msipic.h:1.1 --- /dev/null Mon Apr 27 07:03:58 2015 +++ src/sys/arch/x86/pci/msipic.h Mon Apr 27 07:03:58 2015 @@ -0,0 +1,46 @@ +/* $NetBSD: msipic.h,v 1.1 2015/04/27 07:03:58 knakahara Exp $ */ + +/* + * Copyright (c) 2015 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _X86_PCI_MSIPIC_H_ +#define _X86_PCI_MSIPIC_H_ + +#include <dev/pci/pcivar.h> + +struct pic *msipic_construct_msi_pic(struct pci_attach_args *); +void msipic_destruct_msi_pic(struct pic *); +struct pic *msipic_construct_msix_pic(struct pci_attach_args *); +void msipic_destruct_msix_pic(struct pic *); +struct pic *msipic_find_msi_pic(int); +int msipic_set_msi_vectors(struct pic *, pci_intr_handle_t *, int); + +bool msipic_is_msi_pic(struct pic *); +int msipic_get_devid(struct pic *); + +void msipic_init(void); + +#endif /* _X86_PCI_MSIPIC_H_ */ Index: src/sys/arch/x86/pci/pci_msi_machdep.c diff -u /dev/null src/sys/arch/x86/pci/pci_msi_machdep.c:1.1 --- /dev/null Mon Apr 27 07:03:58 2015 +++ src/sys/arch/x86/pci/pci_msi_machdep.c Mon Apr 27 07:03:58 2015 @@ -0,0 +1,686 @@ +/* $NetBSD: pci_msi_machdep.c,v 1.1 2015/04/27 07:03:58 knakahara Exp $ */ + +/* + * Copyright (c) 2015 Internet Initiative Japan Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * TODO + * + * - PBA (Pending Bit Array) support + * - HyperTransport mapping support + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: pci_msi_machdep.c,v 1.1 2015/04/27 07:03:58 knakahara Exp $"); + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/systm.h> +#include <sys/cpu.h> +#include <sys/errno.h> +#include <sys/device.h> +#include <sys/intr.h> +#include <sys/kmem.h> +#include <sys/malloc.h> + +#include <dev/pci/pcivar.h> + +#include <machine/i82093var.h> +#include <machine/pic.h> + +#include <x86/pci/msipic.h> + +#ifdef INTRDEBUG +#define MSIDEBUG +#endif + +#ifdef MSIDEBUG +#define DPRINTF(msg) printf msg +#else +#define DPRINTF(msg) +#endif + +/* + * Return intrid for a MSI/MSI-X device. + * "buf" must be allocated by caller. + */ +const char * +pci_msi_string(pci_chipset_tag_t pc, pci_intr_handle_t ih, char *buf, + size_t len) +{ + int dev, vec; + + KASSERT(INT_VIA_MSI(ih)); + + dev = MSI_INT_DEV(ih); + vec = MSI_INT_VEC(ih); + if (MSI_INT_IS_MSIX(ih)) + snprintf(buf, len, "msix%d vec %d", dev, vec); + else + snprintf(buf, len, "msi%d vec %d", dev, vec); + + return buf; +} + +static pci_intr_handle_t +pci_msi_calculate_handle(struct pic *msi_pic, int vector) +{ + pci_intr_handle_t pih; + + KASSERT(msipic_is_msi_pic(msi_pic)); + + pih = __SHIFTIN((uint64_t)msipic_get_devid(msi_pic), MSI_INT_DEV_MASK) + | __SHIFTIN((uint64_t)vector, MSI_INT_VEC_MASK) + | APIC_INT_VIA_MSI; + if (msi_pic->pic_type == PIC_MSI) + MSI_INT_MAKE_MSI(pih); + else if (msi_pic->pic_type == PIC_MSIX) + MSI_INT_MAKE_MSIX(pih); + else + panic("%s: Unexpected pic_type: %d\n", __func__, + msi_pic->pic_type); + + return pih; +} + +static pci_intr_handle_t * +pci_msi_alloc_vectors(struct pic *msi_pic, uint *table_indexes, int *count) +{ + struct intrsource *isp; + pci_intr_handle_t *vectors, pih; + int i; + const char *intrstr; + char intrstr_buf[INTRIDBUF]; + + vectors = kmem_zalloc(sizeof(vectors[0]) * (*count), KM_SLEEP); + if (vectors == NULL) { + DPRINTF(("cannot allocate vectors\n")); + return NULL; + } + + mutex_enter(&cpu_lock); + for (i = 0; i < *count; i++) { + u_int table_index; + + if (table_indexes == NULL) + table_index = i; + else + table_index = table_indexes[i]; + + pih = pci_msi_calculate_handle(msi_pic, table_index); + + intrstr = pci_msi_string(NULL, pih, intrstr_buf, + sizeof(intrstr_buf)); + isp = intr_allocate_io_intrsource(intrstr); + if (isp == NULL) { + mutex_exit(&cpu_lock); + DPRINTF(("can't allocate io_intersource\n")); + kmem_free(vectors, sizeof(vectors[0]) * (*count)); + return NULL; + } + + vectors[i] = pih; + } + mutex_exit(&cpu_lock); + + return vectors; +} + +static void +pci_msi_free_vectors(struct pic *msi_pic, pci_intr_handle_t *pihs, int count) +{ + pci_intr_handle_t pih; + int i; + const char *intrstr; + char intrstr_buf[INTRIDBUF]; + + mutex_enter(&cpu_lock); + for (i = 0; i < count; i++) { + pih = pci_msi_calculate_handle(msi_pic, i); + intrstr = pci_msi_string(NULL, pih, intrstr_buf, + sizeof(intrstr_buf)); + intr_free_io_intrsource(intrstr); + } + mutex_exit(&cpu_lock); + + kmem_free(pihs, sizeof(pihs[0]) * count); +} + +static int +pci_msi_alloc_md_common(pci_intr_handle_t **ihps, int *count, + struct pci_attach_args *pa, bool exact) +{ + struct pic *msi_pic; + pci_intr_handle_t *vectors; + int error, i; + + if ((pa->pa_flags & PCI_FLAGS_MSI_OKAY) == 0) { + DPRINTF(("PCI host bridge does not support MSI.\n")); + return ENODEV; + } + + msi_pic = msipic_construct_msi_pic(pa); + if (msi_pic == NULL) { + DPRINTF(("cannot allocate MSI pic.\n")); + return EINVAL; + } + + vectors = NULL; + while (*count > 0) { + vectors = pci_msi_alloc_vectors(msi_pic, NULL, count); + if (vectors != NULL) + break; + + if (exact) { + DPRINTF(("cannot allocate MSI vectors.\n")); + msipic_destruct_msi_pic(msi_pic); + return ENOMEM; + } else { + (*count) >>= 1; /* must be power of 2. */ + continue; + } + } + if (vectors == NULL) { + DPRINTF(("cannot allocate MSI vectors.\n")); + msipic_destruct_msi_pic(msi_pic); + return ENOMEM; + } + + for (i = 0; i < *count; i++) { + MSI_INT_MAKE_MSI(vectors[i]); + } + + error = msipic_set_msi_vectors(msi_pic, NULL, *count); + if (error) { + pci_msi_free_vectors(msi_pic, vectors, *count); + msipic_destruct_msi_pic(msi_pic); + return error; + } + + *ihps = vectors; + return 0; +} + +static int +pci_msi_alloc_md(pci_intr_handle_t **ihps, int *count, + struct pci_attach_args *pa) +{ + + return pci_msi_alloc_md_common(ihps, count, pa, false); +} + +static int +pci_msi_alloc_exact_md(pci_intr_handle_t **ihps, int count, + struct pci_attach_args *pa) +{ + + return pci_msi_alloc_md_common(ihps, &count, pa, true); +} + +static void +pci_msi_release_md(pci_intr_handle_t **pihs, int count) +{ + struct pic *pic; + pci_intr_handle_t *vectors; + + vectors = *pihs; + pic = msipic_find_msi_pic(MSI_INT_DEV(vectors[0])); + if (pic == NULL) + return; + + pci_msi_free_vectors(pic, vectors, count); + msipic_destruct_msi_pic(pic); +} + +static void * +pci_msi_common_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, + int level, int (*func)(void *), void *arg, struct pic *pic) +{ + int irq, pin; + bool mpsafe; + + KASSERT(INT_VIA_MSI(ih)); + + irq = -1; + pin = MSI_INT_VEC(ih); + mpsafe = ((ih & MPSAFE_MASK) != 0); + + return intr_establish(irq, pic, pin, IST_EDGE, level, func, arg, + mpsafe); +} + +static void +pci_msi_common_disestablish(pci_chipset_tag_t pc, void *cookie) +{ + + intr_disestablish(cookie); +} + +static int +pci_msix_alloc_md_common(pci_intr_handle_t **ihps, u_int *table_indexes, + int *count, struct pci_attach_args *pa, bool exact) +{ + struct pic *msix_pic; + pci_intr_handle_t *vectors; + int error, i; + + if (((pa->pa_flags & PCI_FLAGS_MSI_OKAY) == 0) + || ((pa->pa_flags & PCI_FLAGS_MSIX_OKAY) == 0)) { + DPRINTF(("PCI host bridge does not support MSI-X.\n")); + return ENODEV; + } + + msix_pic = msipic_construct_msix_pic(pa); + if (msix_pic == NULL) + return EINVAL; + + vectors = NULL; + while (*count > 0) { + vectors = pci_msi_alloc_vectors(msix_pic, table_indexes, count); + if (vectors != NULL) + break; + + if (exact) { + DPRINTF(("cannot allocate MSI-X vectors.\n")); + msipic_destruct_msix_pic(msix_pic); + return ENOMEM; + } else { + (*count)--; + continue; + } + } + if (vectors == NULL) { + DPRINTF(("cannot allocate MSI-X vectors.\n")); + msipic_destruct_msix_pic(msix_pic); + return ENOMEM; + } + + for (i = 0; i < *count; i++) { + MSI_INT_MAKE_MSIX(vectors[i]); + } + + error = msipic_set_msi_vectors(msix_pic, vectors, *count); + if (error) { + pci_msi_free_vectors(msix_pic, vectors, *count); + msipic_destruct_msix_pic(msix_pic); + return error; + } + + *ihps = vectors; + return 0; +} + +static int +pci_msix_alloc_md(pci_intr_handle_t **ihps, int *count, + struct pci_attach_args *pa) +{ + + return pci_msix_alloc_md_common(ihps, NULL, count, pa, false); +} + +static int +pci_msix_alloc_exact_md(pci_intr_handle_t **ihps, int count, + struct pci_attach_args *pa) +{ + + return pci_msix_alloc_md_common(ihps, NULL, &count, pa, true); +} + +static int +pci_msix_alloc_map_md(pci_intr_handle_t **ihps, u_int *table_indexes, int count, + struct pci_attach_args *pa) +{ + + return pci_msix_alloc_md_common(ihps, table_indexes, &count, pa, true); +} + +static void +pci_msix_release_md(pci_intr_handle_t **pihs, int count) +{ + struct pic *pic; + pci_intr_handle_t *vectors; + + vectors = *pihs; + pic = msipic_find_msi_pic(MSI_INT_DEV(vectors[0])); + if (pic == NULL) + return; + + pci_msi_free_vectors(pic, vectors, count); + msipic_destruct_msix_pic(pic); +} + +/*****************************************************************************/ +/* + * these APIs may be MI code. + */ + +/* + * return number of the devices's MSI vectors + * return 0 if the device does not support MSI + */ +int +pci_msi_count(struct pci_attach_args *pa) +{ + pci_chipset_tag_t pc; + pcitag_t tag; + pcireg_t reg; + uint32_t mmc; + int count, offset; + + pc = pa->pa_pc; + tag = pa->pa_tag; + if (pci_get_capability(pc, tag, PCI_CAP_MSI, &offset, NULL) == 0) + return 0; + + reg = pci_conf_read(pc, tag, offset + PCI_MSI_CTL); + mmc = PCI_MSI_CTL_MMC(reg); + count = 1 << mmc; + if (count > PCI_MSI_MAX_VECTORS) { + aprint_error("detect an illegal device! The device use reserved MMC values.\n"); + return 0; + } + + return count; +} + +/* + * This function is used by device drivers like pci_intr_map(). + * + * "ihps" is the array of vector numbers which MSI used instead of IRQ number. + * "count" must be power of 2. + * "count" can decrease if struct intrsource cannot be allocated. + * if count == 0, return non-zero value. + */ +int +pci_msi_alloc(struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count) +{ + int hw_max; + + /* MSI vector count must be power of 2. */ + KASSERT(*count > 0); + KASSERT(((*count - 1) & *count) == 0); + + hw_max = pci_msi_count(pa); + if (hw_max == 0) + return ENODEV; + + if (*count > hw_max) { + DPRINTF(("cut off MSI count to %d\n", hw_max)); + *count = hw_max; /* cut off hw_max */ + } + + return pci_msi_alloc_md(ihps, count, pa); +} + +/* + * This function is used by device drivers like pci_intr_map(). + * + * "ihps" is the array of vector numbers which MSI used instead of IRQ number. + * "count" must be power of 2. + * "count" can not decrease. + * If "count" struct intrsources cannot be allocated, return non-zero value. + */ +int +pci_msi_alloc_exact(struct pci_attach_args *pa, pci_intr_handle_t **ihps, + int count) +{ + int hw_max; + + /* MSI vector count must be power of 2. */ + KASSERT(count > 0); + KASSERT(((count - 1) & count) == 0); + + hw_max = pci_msi_count(pa); + if (hw_max == 0) + return ENODEV; + + if (count > hw_max) { + DPRINTF(("over hardware max MSI count %d\n", hw_max)); + return EINVAL; + } + + return pci_msi_alloc_exact_md(ihps, count, pa); +} + +/* + * Release MSI handles. + */ +void +pci_msi_release(pci_chipset_tag_t pc, pci_intr_handle_t **pihs, int count) +{ + + if (count < 1) + return; + + return pci_msi_release_md(pihs, count); +} + +/* + * Establish a MSI handle. + * If multiple MSI handle is requied to establish, device driver must call + * this function for each handle. + */ +void * +pci_msi_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, + int level, int (*func)(void *), void *arg) +{ + struct pic *pic; + + pic = msipic_find_msi_pic(MSI_INT_DEV(ih)); + if (pic == NULL) { + DPRINTF(("pci_intr_handler has no msi_pic\n")); + return NULL; + } + + return pci_msi_common_establish(pc, ih, level, func, arg, pic); +} + +/* + * Disestablish a MSI handle. + * If multiple MSI handle is requied to disestablish, device driver must call + * this function for each handle. + */ +void +pci_msi_disestablish(pci_chipset_tag_t pc, void *cookie) +{ + + pci_msi_common_disestablish(pc, cookie); +} + +/* + * return number of the devices's MSI-X vectors + * return 0 if the device does not support MSI-X + */ +int +pci_msix_count(struct pci_attach_args *pa) +{ + pci_chipset_tag_t pc; + pcitag_t tag; + pcireg_t reg; + int offset; + + pc = pa->pa_pc; + tag = pa->pa_tag; + if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &offset, NULL) == 0) + return 0; + + reg = pci_conf_read(pc, tag, offset + PCI_MSIX_CTL); + + return PCI_MSIX_CTL_TBLSIZE(reg); +} + +/* + * This function is used by device drivers like pci_intr_map(). + * + * "ihps" is the array of vector numbers which MSI-X used instead of IRQ number. + * "count" can decrease if enough struct intrsources cannot be allocated. + * if count == 0, return non-zero value. + */ +int +pci_msix_alloc(struct pci_attach_args *pa, pci_intr_handle_t **ihps, int *count) +{ + int hw_max; + + KASSERT(*count > 0); + + hw_max = pci_msix_count(pa); + if (hw_max == 0) + return ENODEV; + + if (*count > hw_max) { + DPRINTF(("cut off MSI-X count to %d\n", hw_max)); + *count = hw_max; /* cut off hw_max */ + } + + return pci_msix_alloc_md(ihps, count, pa); +} + +/* + * This function is used by device drivers like pci_intr_map(). + * + * "ihps" is the array of vector numbers which MSI-X used instead of IRQ number. + * "count" can not decrease. + * If "count" struct intrsource cannot be allocated, return non-zero value. + */ +int +pci_msix_alloc_exact(struct pci_attach_args *pa, pci_intr_handle_t **ihps, + int count) +{ + int hw_max; + + KASSERT(count > 0); + + hw_max = pci_msix_count(pa); + if (hw_max == 0) + return ENODEV; + + if (count > hw_max) { + DPRINTF(("over hardware max MSI-X count %d\n", hw_max)); + return EINVAL; + } + + return pci_msix_alloc_exact_md(ihps, count, pa); +} + + +/* + * This function is used by device drivers like pci_intr_map(). + * Futhermore, this function can map each handle to a MSI-X table index. + * + * "ihps" is the array of vector numbers which MSI-X used instead of IRQ number. + * "count" can not decrease. + * "map" size must be equal to "count". + * If "count" struct intrsource cannot be allocated, return non-zero value. + * e.g. + * If "map" = { 1, 4, 0 }, + * 1st handle is bound to MSI-X index 1 + * 2nd handle is bound to MSI-X index 4 + * 3rd handle is bound to MSI-X index 0 + */ +int +pci_msix_alloc_map(struct pci_attach_args *pa, pci_intr_handle_t **ihps, + u_int *table_indexes, int count) +{ + int hw_max, i, j; + + KASSERT(count > 0); + + hw_max = pci_msix_count(pa); + if (hw_max == 0) + return ENODEV; + + if (count > hw_max) { + DPRINTF(("over hardware max MSI-X count %d\n", hw_max)); + return EINVAL; + } + + /* check not to duplicate table_index */ + for (i = 0; i < count; i++) { + u_int basing = table_indexes[i]; + + KASSERT(table_indexes[i] < PCI_MSIX_MAX_VECTORS); + if (basing >= hw_max) { + DPRINTF(("table index is over hardware max MSI-X index %d\n", + hw_max - 1)); + return EINVAL; + } + + for (j = i + 1; j < count; j++) { + if (basing == table_indexes[j]) { + DPRINTF(("MSI-X table index duplicated\n")); + return EINVAL; + } + } + } + + return pci_msix_alloc_map_md(ihps, table_indexes, count, pa); +} + +/* + * Release MSI-X handles. + */ +void +pci_msix_release(pci_chipset_tag_t pc, pci_intr_handle_t **pihs, int count) +{ + + if (count < 1) + return; + + return pci_msix_release_md(pihs, count); +} + +/* + * Establish a MSI-X handle. + * If multiple MSI-X handle is requied to establish, device driver must call + * this function for each handle. + */ +void * +pci_msix_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, + int level, int (*func)(void *), void *arg) +{ + struct pic *pic; + + pic = msipic_find_msi_pic(MSI_INT_DEV(ih)); + if (pic == NULL) { + DPRINTF(("pci_intr_handler has no msi_pic\n")); + return NULL; + } + + return pci_msi_common_establish(pc, ih, level, func, arg, pic); +} + +/* + * Disestablish a MSI-X handle. + * If multiple MSI-X handle is requied to disestablish, device driver must call + * this function for each handle. + */ +void +pci_msix_disestablish(pci_chipset_tag_t pc, void *cookie) +{ + + pci_msi_common_disestablish(pc, cookie); +}