blob: 4137aecf48e748d3d285e1d82ea4c7f4f644e408 [file] [log] [blame]
/*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <zephyr/shell/shell.h>
#include <zephyr/drivers/pcie/pcie.h>
#ifdef CONFIG_PCIE_MSI
#include <zephyr/drivers/pcie/msi.h>
#include <zephyr/drivers/pcie/cap.h>
#endif
static void show_msi(const struct shell *sh, pcie_bdf_t bdf)
{
#ifdef CONFIG_PCIE_MSI
uint32_t msi;
uint32_t data;
msi = pcie_get_cap(bdf, PCI_CAP_ID_MSI);
if (msi) {
data = pcie_conf_read(bdf, msi + PCIE_MSI_MCR);
shell_fprintf(sh, SHELL_NORMAL, " MSI support%s%s\n",
(data & PCIE_MSI_MCR_64) ? ", 64-bit" : "",
(data & PCIE_MSI_MCR_EN) ?
", enabled" : ", disabled");
}
msi = pcie_get_cap(bdf, PCI_CAP_ID_MSIX);
if (msi) {
uint32_t offset, table_size;
uint8_t bir;
data = pcie_conf_read(bdf, msi + PCIE_MSIX_MCR);
table_size = ((data & PCIE_MSIX_MCR_TSIZE) >>
PCIE_MSIX_MCR_TSIZE_SHIFT) + 1;
shell_fprintf(sh, SHELL_NORMAL,
" MSI-X support%s table size %d\n",
(data & PCIE_MSIX_MCR_EN) ?
", enabled" : ", disabled",
table_size);
offset = pcie_conf_read(bdf, msi + PCIE_MSIX_TR);
bir = offset & PCIE_MSIX_TR_BIR;
offset &= PCIE_MSIX_TR_OFFSET;
shell_fprintf(sh, SHELL_NORMAL,
"\tTable offset 0x%x BAR %d\n",
offset, bir);
offset = pcie_conf_read(bdf, msi + PCIE_MSIX_PBA);
bir = offset & PCIE_MSIX_PBA_BIR;
offset &= PCIE_MSIX_PBA_OFFSET;
shell_fprintf(sh, SHELL_NORMAL,
"\tPBA offset 0x%x BAR %d\n",
offset, bir);
}
#endif
}
static void show_bars(const struct shell *sh, pcie_bdf_t bdf)
{
uint32_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(sh, SHELL_NORMAL, " bar %d: %s%s",
bar - PCIE_CONF_BAR0,
PCIE_CONF_BAR_IO(data) ? "I/O" : "MEM",
PCIE_CONF_BAR_64(data) ? ", 64-bit" : "");
shell_fprintf(sh, SHELL_NORMAL, " addr 0x");
if (PCIE_CONF_BAR_64(data)) {
++bar;
shell_fprintf(sh, SHELL_NORMAL, "%08x",
pcie_conf_read(bdf, bar));
}
shell_fprintf(sh, SHELL_NORMAL, "%08x\n",
(uint32_t)PCIE_CONF_BAR_ADDR(data));
}
}
static void pcie_dump(const struct shell *sh, pcie_bdf_t bdf)
{
for (int i = 0; i < 16; i++) {
uint32_t val = pcie_conf_read(bdf, i);
for (int j = 0; j < 4; j++) {
shell_fprintf(sh, SHELL_NORMAL, "%02x ",
(uint8_t)val);
val >>= 8;
}
if (((i + 1) % 4) == 0) {
shell_fprintf(sh, SHELL_NORMAL, "\n");
}
}
}
static pcie_bdf_t get_bdf(char *str)
{
int bus, dev, func;
char *tok, *state;
tok = strtok_r(str, ":", &state);
if (tok == NULL) {
return PCIE_BDF_NONE;
}
bus = strtoul(tok, NULL, 16);
tok = strtok_r(NULL, ".", &state);
if (tok == NULL) {
return PCIE_BDF_NONE;
}
dev = strtoul(tok, NULL, 16);
tok = strtok_r(NULL, ".", &state);
if (tok == NULL) {
return PCIE_BDF_NONE;
}
func = strtoul(tok, NULL, 16);
return PCIE_BDF(bus, dev, func);
}
static void show(const struct shell *sh, pcie_bdf_t bdf, bool dump)
{
uint32_t data;
unsigned int irq;
data = pcie_conf_read(bdf, PCIE_CONF_ID);
if (!PCIE_ID_IS_VALID(data)) {
return;
}
shell_fprintf(sh, 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(sh, 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(sh, SHELL_NORMAL, " [bridge]\n");
} else {
shell_fprintf(sh, SHELL_NORMAL, "\n");
show_bars(sh, bdf);
show_msi(sh, bdf);
irq = pcie_get_irq(bdf);
if (irq != PCIE_CONF_INTR_IRQ_NONE) {
shell_fprintf(sh, SHELL_NORMAL,
" wired interrupt on IRQ %d\n", irq);
}
}
if (dump) {
pcie_dump(sh, bdf);
}
}
struct scan_cb_data {
const struct shell *sh;
bool dump;
};
static bool scan_cb(pcie_bdf_t bdf, pcie_id_t id, void *cb_data)
{
struct scan_cb_data *data = cb_data;
show(data->sh, bdf, data->dump);
return true;
}
static int cmd_pcie_ls(const struct shell *sh, size_t argc, char **argv)
{
pcie_bdf_t bdf = PCIE_BDF_NONE;
struct scan_cb_data data = {
.sh = sh,
.dump = false,
};
struct pcie_scan_opt scan_opt = {
.cb = scan_cb,
.cb_data = &data,
.flags = (PCIE_SCAN_RECURSIVE | PCIE_SCAN_CB_ALL),
};
for (int i = 1; i < argc; i++) {
/* Check dump argument */
if (strncmp(argv[i], "dump", 4) == 0) {
data.dump = true;
continue;
}
/* Check BDF string of PCI device */
if (bdf == PCIE_BDF_NONE) {
bdf = get_bdf(argv[i]);
}
if (bdf == PCIE_BDF_NONE) {
shell_error(sh, "Unknown parameter: %s", argv[i]);
return -EINVAL;
}
}
/* Show only specified device */
if (bdf != PCIE_BDF_NONE) {
show(sh, bdf, data.dump);
return 0;
}
pcie_scan(&scan_opt);
return 0;
}
SHELL_STATIC_SUBCMD_SET_CREATE(sub_pcie_cmds,
SHELL_CMD_ARG(ls, NULL,
"List PCIE devices\n"
"Usage: ls [bus:device:function] [dump]",
cmd_pcie_ls, 1, 2),
SHELL_SUBCMD_SET_END /* Array terminated. */
);
SHELL_CMD_REGISTER(pcie, &sub_pcie_cmds, "PCI(e) device information", cmd_pcie_ls);