add x86 MD MSI/MSI-X support code.

This commit is contained in:
knakahara 2015-04-27 07:03:57 +00:00
parent ec1f187e3f
commit 8ec1d9400a
23 changed files with 2121 additions and 145 deletions

View File

@ -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

View File

@ -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_http.9 \
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 \

210
share/man/man9/pci_msi.9 Normal file
View File

@ -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).

View File

@ -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 1.34 2013/07/31 14:05:33 soren Exp $");
#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 self, void *aux)
#endif
#if NPCI > 0
msipic_init();
/*
* ACPI needs to be able to access PCI configuration space.
*/

View File

@ -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_simple_lock_t;
#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

View File

@ -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 1.98 2013/11/08 03:12:48 christos Exp $"
#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 self, void *aux)
#endif
#if NPCI > 0
msipic_init();
/*
* ACPI needs to be able to access PCI configuration space.
*/

View File

@ -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_simple_lock_t;
#if defined(_KERNEL)
#define __HAVE_RAS
#if !defined(XEN)
#define __HAVE_PCI_MSI_MSIX
#endif
#endif
#endif /* _I386_MACHTYPES_H_ */

View File

@ -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_amdpmi
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

View File

@ -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);

View File

@ -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>

View File

@ -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_tag_t, pci_intr_handle_t,
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

View File

@ -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;

737
sys/arch/x86/pci/msipic.c Normal file
View File

@ -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);
}

46
sys/arch/x86/pci/msipic.h Normal file
View File

@ -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_ */

View File

@ -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_machdep.c,v 1.29 2015/04/27 06:51:40 knakah
#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, pci_intr_handle_t ih, char *buf,
{
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 kcpuset_t *newset, kcpuset_t *oldset)
}
#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)
int
pci_intx_alloc(const struct pci_attach_args *pa, pci_intr_handle_t **pih)
{
}
static void
msipic_addroute(struct pic *pic, struct cpu_info *ci,
int pin, int vec, int type)
{
}
struct intrsource *isp;
pci_intr_handle_t *handle;
int error;
char intrstr_buf[INTRIDBUF];
const char *intrstr;
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;
};
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);
handle = kmem_zalloc(sizeof(*handle), KM_SLEEP);
if (handle == NULL) {
aprint_normal("cannot allocate pci_intr_handle_t\n");
return ENOMEM;
}
pci_conf_write(pa->pa_pc, pa->pa_tag, co + PCI_MSI_CTL,
PCI_MSI_CTL_MSI_ENABLE);
return msih;
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;
}
*pih = handle;
return 0;
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;
pci_conf_write(msih->pc, msih->tag, msih->co + PCI_MSI_CTL, 0);
intr_disestablish(msih->ih);
free(msih, M_DEVBUF);
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);
kmem_free(pih, sizeof(*pih));
}
#endif

View File

@ -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.c,v 1.69 2014/11/07 12:48:21 christos Ex
#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_t self, struct pcibus_attach_args *pba)
#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

View File

@ -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);
}

View File

@ -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.80 2015/04/27 06:51:40 knakahara Exp $");
#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 *, 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 *pic, int pin, char *buf, size_t len)
{
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 *pic, int pin, char *buf, size_t len)
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 *intrid)
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 intrsource *isp)
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 *ci, struct pic *pic, int pin,
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 *ci, struct pic *pic, int pin,
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 pic *pic, int pin, int type, int level,
/* 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));
}

View File

@ -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 regid, int val)
}
struct ioapic_softc *
ioapic_find(intr_handle_t apicid)
ioapic_find(int apicid)
{
struct ioapic_softc *sc;

View File

@ -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

View File

@ -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, pcitag_t tag,
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, pcitag_t tag,
}
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)
@ -507,6 +539,35 @@ pci_get_capability(pci_chipset_tag_t pc, pcitag_t tag, int capid,
return 0;
}
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 *))

View File

@ -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

View File

@ -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_args *, bus_space_tag_t,
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.