add x86 MD MSI/MSI-X support code.
This commit is contained in:
parent
ec1f187e3f
commit
8ec1d9400a
@ -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
|
||||
|
@ -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
210
share/man/man9/pci_msi.9
Normal 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).
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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_ */
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
737
sys/arch/x86/pci/msipic.c
Normal 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
46
sys/arch/x86/pci/msipic.h
Normal 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_ */
|
@ -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
|
||||
|
@ -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
|
||||
|
686
sys/arch/x86/pci/pci_msi_machdep.c
Normal file
686
sys/arch/x86/pci/pci_msi_machdep.c
Normal 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);
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 *))
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user