Add the start of INTRNG support for ACPI.

This adds a new acpi_bus interface with a map_intr method. This is similar
to the Open Firmware map_intr method and allows us to create the needed
mapping from ACPI space to INTRNG space.

Obtained from:	ABT Systems Ltd
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D8617
This commit is contained in:
andrew 2018-01-11 17:09:12 +00:00
parent 9a35b0521e
commit 048d22eb18
7 changed files with 152 additions and 8 deletions

View File

@ -72,6 +72,7 @@ __FBSDID("$FreeBSD$");
#ifdef DEV_ACPI
#include <contrib/dev/acpica/include/acpi.h>
#include <dev/acpica/acpivar.h>
#include "acpi_bus_if.h"
#endif
#define GT_CTRL_ENABLE (1 << 0)
@ -312,6 +313,15 @@ arm_tmr_fdt_probe(device_t dev)
#endif
#ifdef DEV_ACPI
static void
arm_tmr_acpi_add_irq(device_t parent, device_t dev, int rid, u_int irq)
{
irq = ACPI_BUS_MAP_INTR(parent, dev, irq,
INTR_TRIGGER_LEVEL, INTR_POLARITY_HIGH);
BUS_SET_RESOURCE(parent, dev, SYS_RES_IRQ, rid, irq, 1);
}
static void
arm_tmr_acpi_identify(driver_t *driver, device_t parent)
{
@ -336,12 +346,9 @@ arm_tmr_acpi_identify(driver_t *driver, device_t parent)
goto out;
}
BUS_SET_RESOURCE(parent, dev, SYS_RES_IRQ, 0,
gtdt->SecureEl1Interrupt, 1);
BUS_SET_RESOURCE(parent, dev, SYS_RES_IRQ, 1,
gtdt->NonSecureEl1Interrupt, 1);
BUS_SET_RESOURCE(parent, dev, SYS_RES_IRQ, 2,
gtdt->VirtualTimerInterrupt, 1);
arm_tmr_acpi_add_irq(parent, dev, 0, gtdt->SecureEl1Interrupt);
arm_tmr_acpi_add_irq(parent, dev, 1, gtdt->NonSecureEl1Interrupt);
arm_tmr_acpi_add_irq(parent, dev, 2, gtdt->VirtualTimerInterrupt);
out:
acpi_unmap_table(gtdt);

View File

@ -72,6 +72,7 @@ __FBSDID("$FreeBSD$");
#ifdef DEV_ACPI
#include <contrib/dev/acpica/include/acpi.h>
#include <dev/acpica/acpivar.h>
#include "acpi_bus_if.h"
#endif
extern struct bus_space memmap_bus;
@ -460,11 +461,16 @@ nexus_ofw_map_intr(device_t dev, device_t child, phandle_t iparent, int icells,
#endif
#ifdef DEV_ACPI
static int nexus_acpi_map_intr(device_t dev, device_t child, u_int irq, int trig, int pol);
static device_method_t nexus_acpi_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, nexus_acpi_probe),
DEVMETHOD(device_attach, nexus_acpi_attach),
/* ACPI interface */
DEVMETHOD(acpi_bus_map_intr, nexus_acpi_map_intr),
DEVMETHOD_END,
};
@ -495,4 +501,30 @@ nexus_acpi_attach(device_t dev)
nexus_add_child(dev, 10, "acpi", 0);
return (nexus_attach(dev));
}
static int
nexus_acpi_map_intr(device_t dev, device_t child, u_int irq, int trig, int pol)
{
struct intr_map_data_acpi *acpi_data;
size_t len;
len = sizeof(*acpi_data);
acpi_data = (struct intr_map_data_acpi *)intr_alloc_map_data(
INTR_MAP_DATA_ACPI, len, M_WAITOK | M_ZERO);
acpi_data->irq = irq;
acpi_data->pol = pol;
acpi_data->trig = trig;
/*
* TODO: This will only handle a single interrupt controller.
* ACPI will map multiple controllers into a single virtual IRQ
* space. Each controller has a System Vector Base to hold the
* first irq it handles in this space. As such the correct way
* to handle interrupts with ACPI is to search through the
* controllers for the largest base value that is no larger than
* the IRQ value.
*/
irq = intr_map_irq(NULL, 0, (struct intr_map_data *)acpi_data);
return (irq);
}
#endif

View File

@ -167,6 +167,7 @@ armv8_crypto_wrap.o optional armv8crypto \
clean "armv8_crypto_wrap.o"
crypto/blowfish/bf_enc.c optional crypto | ipsec | ipsec_support
crypto/des/des_enc.c optional crypto | ipsec | ipsec_support | netsmb
dev/acpica/acpi_bus_if.m optional acpi
dev/acpica/acpi_if.m optional acpi
dev/ahci/ahci_generic.c optional ahci
dev/axgbe/if_axgbe.c optional axgbe

View File

@ -1358,7 +1358,9 @@ static struct resource *
acpi_alloc_resource(device_t bus, device_t child, int type, int *rid,
rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
{
#ifndef INTRNG
ACPI_RESOURCE ares;
#endif
struct acpi_device *ad;
struct resource_list_entry *rle;
struct resource_list *rl;
@ -1385,6 +1387,7 @@ acpi_alloc_resource(device_t bus, device_t child, int type, int *rid,
resource_list_add(rl, type, *rid, start, end, count);
res = resource_list_alloc(rl, bus, child, type, rid, start, end, count,
flags);
#ifndef INTRNG
if (res != NULL && type == SYS_RES_IRQ) {
/*
* Since bus_config_intr() takes immediate effect, we cannot
@ -1397,6 +1400,7 @@ acpi_alloc_resource(device_t bus, device_t child, int type, int *rid,
if (ACPI_SUCCESS(acpi_lookup_irq_resource(child, *rid, res, &ares)))
acpi_config_intr(child, &ares);
}
#endif
/*
* If this is an allocation of the "default" range for a given

View File

@ -0,0 +1,67 @@
#-
# Copyright (c) 2016 The FreeBSD Foundation
# All rights reserved.
#
# This software was developed by Andrew Turner under
# sponsorship from the FreeBSD Foundation.
#
# 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 AUTHOR 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 AUTHOR 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.
#
# $FreeBSD$
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/bus.h>
INTERFACE acpi_bus;
CODE {
static acpi_bus_map_intr_t acpi_bus_default_map_intr;
int
acpi_bus_default_map_intr(device_t bus, device_t dev, u_int irq,
int trig, int pol)
{
device_t parent;
/* Pass up the hierarchy */
parent = device_get_parent(bus);
if (parent != NULL)
return (ACPI_BUS_MAP_INTR(parent, dev, irq, trig, pol));
panic("Unable to map interrupt %u", irq);
}
};
# Map an interrupt from ACPI space to the FreeBSD IRQ space. Note that
# both of these may be different than the pysical interrupt space as this
# may be local to each interrupt controller.
#
# This method also associates interrupt metadata with the interrupt,
# removing the need for a post hoc configure step.
METHOD int map_intr {
device_t bus;
device_t dev;
u_int irq;
int trig;
int pol;
} DEFAULT acpi_bus_default_map_intr;

View File

@ -45,6 +45,10 @@ __FBSDID("$FreeBSD$");
#include <dev/acpica/acpivar.h>
#ifdef INTRNG
#include "acpi_bus_if.h"
#endif
/* Hooks for the ACPI CA debugging infrastructure */
#define _COMPONENT ACPI_BUS
ACPI_MODULE_NAME("RESOURCE")
@ -556,6 +560,7 @@ acpi_res_set_irq(device_t dev, void *context, uint8_t *irq, int count,
int trig, int pol)
{
struct acpi_res_context *cp = (struct acpi_res_context *)context;
rman_res_t intr;
if (cp == NULL || irq == NULL)
return;
@ -564,7 +569,14 @@ acpi_res_set_irq(device_t dev, void *context, uint8_t *irq, int count,
if (count != 1)
return;
bus_set_resource(dev, SYS_RES_IRQ, cp->ar_nirq++, *irq, 1);
#ifdef INTRNG
intr = ACPI_BUS_MAP_INTR(device_get_parent(dev), dev, *irq,
(trig == ACPI_EDGE_SENSITIVE) ? INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL,
(pol == ACPI_ACTIVE_HIGH) ? INTR_POLARITY_HIGH : INTR_POLARITY_LOW);
#else
intr = *irq;
#endif
bus_set_resource(dev, SYS_RES_IRQ, cp->ar_nirq++, intr, 1);
}
static void
@ -572,6 +584,7 @@ acpi_res_set_ext_irq(device_t dev, void *context, uint32_t *irq, int count,
int trig, int pol)
{
struct acpi_res_context *cp = (struct acpi_res_context *)context;
rman_res_t intr;
if (cp == NULL || irq == NULL)
return;
@ -580,7 +593,14 @@ acpi_res_set_ext_irq(device_t dev, void *context, uint32_t *irq, int count,
if (count != 1)
return;
bus_set_resource(dev, SYS_RES_IRQ, cp->ar_nirq++, *irq, 1);
#ifdef INTRNG
intr = ACPI_BUS_MAP_INTR(device_get_parent(dev), dev, *irq,
(trig == ACPI_EDGE_SENSITIVE) ? INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL,
(pol == ACPI_ACTIVE_HIGH) ? INTR_POLARITY_HIGH : INTR_POLARITY_LOW);
#else
intr = *irq;
#endif
bus_set_resource(dev, SYS_RES_IRQ, cp->ar_nirq++, intr, 1);
}
static void

View File

@ -36,6 +36,9 @@
#include "acpi_if.h"
#include "bus_if.h"
#include <sys/eventhandler.h>
#ifdef INTRNG
#include <sys/intr.h>
#endif
#include <sys/ktr.h>
#include <sys/lock.h>
#include <sys/mutex.h>
@ -91,6 +94,16 @@ struct acpi_device {
struct resource_list ad_rl;
};
#ifdef INTRNG
struct intr_map_data_acpi {
struct intr_map_data hdr;
u_int irq;
u_int pol;
u_int trig;
};
#endif
/* Track device (/dev/{apm,apmctl} and /dev/acpi) notification status. */
struct apm_clone_data {
STAILQ_ENTRY(apm_clone_data) entries;