zephyr/drivers/pcie/shell.c
Charles E. Youse 18833ac0ef drivers/pcie: verify PCI(e) assigned interrupts
A new function pcie_irq_enable() is added to be used in lieu of
irq_enable() when the target device is PCI(e)-attached. The function
attempts to use MSI, when configured in the kernel and supported by
the endpoint; failing that, it will verify that IRQ requested is in
fact routed to the device by the boot firmware before enabling it.

The NS16550 UART driver is updated to use pcie_irq_enable().

The PCI(e) shell is extended to dump information about wired IRQs.

The up_squared devicetree is fixed (reverted?) to IRQ5 for UART1.

The galileo enables MSI by default.

Signed-off-by: Charles E. Youse <charles.youse@intel.com>
2019-04-28 13:36:28 -04:00

118 lines
2.7 KiB
C

/*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <shell/shell.h>
#include <drivers/pcie/pcie.h>
#ifdef CONFIG_PCIE_MSI
#include <drivers/pcie/msi.h>
#endif
#define MAX_BUS (0xFFFFFFFF & PCIE_BDF_BUS_MASK)
#define MAX_DEV (0xFFFFFFFF & PCIE_BDF_DEV_MASK)
#define MAX_FUNC (0xFFFFFFFF & PCIE_BDF_FUNC_MASK)
static void show_msi(const struct shell *shell, pcie_bdf_t bdf)
{
#ifdef CONFIG_PCIE_MSI
u32_t msi;
u32_t data;
msi = pcie_get_cap(bdf, PCIE_MSI_CAP_ID);
if (msi) {
data = pcie_conf_read(bdf, msi + PCIE_MSI_MCR);
shell_fprintf(shell, SHELL_NORMAL, " MSI support%s%s\n",
(data & PCIE_MSI_MCR_64) ? ", 64-bit" : "",
(data & PCIE_MSI_MCR_EN) ? ", enabled" : "");
}
#endif
}
static void show_bars(const struct shell *shell, pcie_bdf_t bdf)
{
u32_t data;
int bar;
for (bar = PCIE_CONF_BAR0; bar <= PCIE_CONF_BAR5; ++bar) {
data = pcie_conf_read(bdf, bar);
if (data == PCIE_CONF_BAR_NONE) {
continue;
}
shell_fprintf(shell, SHELL_NORMAL, " bar %d: %s%s %x\n",
bar - PCIE_CONF_BAR0,
PCIE_CONF_BAR_IO(data) ? "I/O" : "MEM",
PCIE_CONF_BAR_64(data) ? ", 64-bit" : "",
PCIE_CONF_BAR_ADDR(data));
if (PCIE_CONF_BAR_64(data)) {
++bar;
}
}
}
static void show(const struct shell *shell, pcie_bdf_t bdf)
{
u32_t data;
data = pcie_conf_read(bdf, PCIE_CONF_ID);
if (data == PCIE_ID_NONE) {
return;
}
shell_fprintf(shell, SHELL_NORMAL, "%d:%x.%d ID %x:%x ",
PCIE_BDF_TO_BUS(bdf),
PCIE_BDF_TO_DEV(bdf),
PCIE_BDF_TO_FUNC(bdf),
PCIE_ID_TO_VEND(data),
PCIE_ID_TO_DEV(data));
data = pcie_conf_read(bdf, PCIE_CONF_CLASSREV);
shell_fprintf(shell, SHELL_NORMAL,
"class %x subclass %x prog i/f %x rev %x",
PCIE_CONF_CLASSREV_CLASS(data),
PCIE_CONF_CLASSREV_SUBCLASS(data),
PCIE_CONF_CLASSREV_PROGIF(data),
PCIE_CONF_CLASSREV_REV(data));
data = pcie_conf_read(bdf, PCIE_CONF_TYPE);
if (PCIE_CONF_TYPE_BRIDGE(data)) {
shell_fprintf(shell, SHELL_NORMAL, " [bridge]\n");
} else {
shell_fprintf(shell, SHELL_NORMAL, "\n");
show_bars(shell, bdf);
show_msi(shell, bdf);
data = pcie_conf_read(bdf, PCIE_CONF_INTR);
if (PCIE_CONF_INTR_IRQ(data) != PCIE_CONF_INTR_IRQ_NONE) {
shell_fprintf(shell, SHELL_NORMAL,
" wired interrupt on IRQ %d\n",
PCIE_CONF_INTR_IRQ(data));
}
}
}
static int cmd_lspcie(const struct shell *shell, size_t argc, char **argv)
{
int bus;
int dev;
int func;
for (bus = 0; bus <= MAX_BUS; ++bus) {
for (dev = 0; dev <= MAX_DEV; ++dev) {
for (func = 0; func <= MAX_FUNC; ++func) {
show(shell, PCIE_BDF(bus, dev, func));
}
}
}
return 0;
}
SHELL_CMD_REGISTER(lspcie, NULL, "List PCI(e) devices", cmd_lspcie);