833 lines
24 KiB
C
833 lines
24 KiB
C
/****************************************************************************
|
|
* drivers/pci/pci.c
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership. The
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the
|
|
* License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/pci/pci.h>
|
|
#include <nuttx/virt/qemu_pci.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* For now hard code jailhouse as a flag. In the future we can determine this
|
|
* by looking at the CPUID base for "Jailhouse\0\0\0"
|
|
*/
|
|
|
|
#define JAILHOUSE_ENABLED 1
|
|
|
|
#define PCI_BDF(bus, slot, func) (((uint32_t)bus << 8) | \
|
|
((uint32_t)slot << 3) | \
|
|
func)
|
|
|
|
/****************************************************************************
|
|
* Private Functions Definitions
|
|
****************************************************************************/
|
|
|
|
static void pci_probe_device(FAR struct pci_bus_s *root_bus,
|
|
uint8_t bus_idx, uint8_t slot_idx, uint8_t func,
|
|
FAR const struct pci_dev_type_s **types);
|
|
|
|
static uint8_t pci_check_pci_bridge(FAR struct pci_bus_s *root_bus,
|
|
uint8_t bus_idx, uint8_t slot_idx,
|
|
uint8_t dev_func);
|
|
|
|
static void pci_scan_device(FAR struct pci_bus_s *root_bus,
|
|
uint8_t bus_idx, uint8_t slot_idx,
|
|
FAR const struct pci_dev_type_s **types);
|
|
|
|
static void pci_scan_bus(FAR struct pci_bus_s *root_bus,
|
|
uint8_t bus_idx,
|
|
FAR const struct pci_dev_type_s **types);
|
|
|
|
static void pci_set_cmd_bit(FAR struct pci_dev_s *dev, uint16_t bitmask);
|
|
|
|
static void pci_clear_cmd_bit(FAR struct pci_dev_s *dev, uint16_t bitmask);
|
|
|
|
/****************************************************************************
|
|
* Public Data
|
|
****************************************************************************/
|
|
|
|
const struct pci_dev_type_s *g_pci_device_types[] =
|
|
{
|
|
#ifdef CONFIG_VIRT_QEMU_PCI_TEST
|
|
&g_pci_type_qemu_pci_test,
|
|
#endif
|
|
#ifdef CONFIG_VIRT_QEMU_EDU
|
|
&g_pci_type_qemu_edu,
|
|
#endif
|
|
NULL,
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: pci_probe
|
|
*
|
|
* Description:
|
|
* Checks if the specified device is supported and if so calls probe on it
|
|
*
|
|
* Input Parameters:
|
|
* root_bus - The root bus device that lets us address the whole tree
|
|
* bus - Bus ID
|
|
* slot - Device Slot
|
|
* func - Device Function
|
|
* types - List of pointers to devices types recognized, NULL terminated
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void pci_probe_device(FAR struct pci_bus_s *root_bus,
|
|
uint8_t bus_idx, uint8_t slot_idx, uint8_t func,
|
|
FAR const struct pci_dev_type_s **types)
|
|
{
|
|
struct pci_dev_s tmp_dev;
|
|
uint32_t class_rev;
|
|
uint16_t vid;
|
|
uint16_t id;
|
|
int i;
|
|
|
|
tmp_dev.bus = root_bus;
|
|
tmp_dev.bdf = PCI_BDF(bus_idx, slot_idx, func);
|
|
|
|
vid = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_VENDOR, 2);
|
|
id = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_DEVICE, 2);
|
|
|
|
/* This is reading rev prog_if subclass and class */
|
|
|
|
class_rev = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_REV_ID, 4);
|
|
|
|
pci_dev_dump(&tmp_dev);
|
|
|
|
for (i = 0; types[i] != NULL; i++)
|
|
{
|
|
if (types[i]->vendor == PCI_ID_ANY ||
|
|
types[i]->vendor == vid)
|
|
{
|
|
if (types[i]->device == PCI_ID_ANY ||
|
|
types[i]->device == id)
|
|
{
|
|
if (types[i]->class_rev == PCI_ID_ANY ||
|
|
types[i]->class_rev == class_rev)
|
|
{
|
|
pciinfo("Found: %s\n", types[i]->name);
|
|
if (types[i]->probe)
|
|
{
|
|
pciinfo("[%02x:%02x.%x] Probing\n",
|
|
bus_idx, slot_idx, func);
|
|
types[i]->probe(root_bus, types[i], tmp_dev.bdf);
|
|
}
|
|
else
|
|
{
|
|
pcierr("[%02x:%02x.%x] Error: Invalid"
|
|
"device probe function\n",
|
|
bus_idx, slot_idx, func);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_check_pci_bridge
|
|
*
|
|
* Description:
|
|
* Checks if the specified device is PCI bridge and return the sub-bridge
|
|
* idx if found. Otherwise return 0.
|
|
*
|
|
* Input Parameters:
|
|
* root_bus - The root bus device that lets us address the whole tree
|
|
* bus - Bus ID
|
|
* slot - Device Slot
|
|
* func - Device Function
|
|
*
|
|
****************************************************************************/
|
|
|
|
static uint8_t pci_check_pci_bridge(FAR struct pci_bus_s *root_bus,
|
|
uint8_t bus_idx, uint8_t slot_idx,
|
|
uint8_t dev_func)
|
|
{
|
|
struct pci_dev_s tmp_dev;
|
|
uint8_t base_class;
|
|
uint8_t sub_class;
|
|
uint8_t secondary_bus;
|
|
|
|
tmp_dev.bus = root_bus;
|
|
tmp_dev.bdf = PCI_BDF(bus_idx, slot_idx, dev_func);
|
|
|
|
/* Check if this is a PCI-PCI bridge device */
|
|
|
|
base_class = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_CLASS, 1);
|
|
sub_class = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_SUBCLASS, 1);
|
|
|
|
if ((base_class == PCI_CLASS_BASE_BRG_DEV) &&
|
|
(sub_class == PCI_CLASS_SUB_PCI_BRG))
|
|
{
|
|
/* This is a bridge device we need to determine the bus idx and
|
|
* enumerate it just like we do the root.
|
|
*/
|
|
|
|
pciinfo("[%02x:%02x.%x] Found Bridge\n",
|
|
bus_idx, slot_idx, dev_func);
|
|
|
|
secondary_bus = root_bus->ops->pci_cfg_read(
|
|
&tmp_dev, PCI_CONFIG_SEC_BUS, 1);
|
|
|
|
return secondary_bus;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_scan_device
|
|
*
|
|
* Description:
|
|
* Checks if the specified device is a bus and iterates over it or
|
|
* if it is a real device initializes it if recognized.
|
|
*
|
|
* Input Parameters:
|
|
* root_bus - The root bus device that lets us address the whole tree
|
|
* bus - Bus ID
|
|
* slot - Device Slot
|
|
* types - List of pointers to devices types recognized, NULL terminated
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void pci_scan_device(FAR struct pci_bus_s *root_bus,
|
|
uint8_t bus_idx, uint8_t slot_idx,
|
|
FAR const struct pci_dev_type_s **types)
|
|
{
|
|
struct pci_dev_s tmp_dev;
|
|
uint8_t multi_function;
|
|
uint8_t dev_func = 0;
|
|
uint16_t vid;
|
|
uint8_t sec_bus;
|
|
|
|
tmp_dev.bus = root_bus;
|
|
tmp_dev.bdf = PCI_BDF(bus_idx, slot_idx, dev_func);
|
|
vid = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_VENDOR, 2);
|
|
if (vid == 0xffff)
|
|
{
|
|
return;
|
|
}
|
|
|
|
multi_function = root_bus->ops->pci_cfg_read(
|
|
&tmp_dev, PCI_CONFIG_HEADER_TYPE, 1) & PCI_HEADER_MASK_MULTI;
|
|
|
|
/* Jailhouse breaks the PCI spec by allowing you to pass individual
|
|
* functions of a multi-function device. In this case we need to
|
|
* scan each of the functions not just function 0.
|
|
*/
|
|
|
|
if (multi_function || JAILHOUSE_ENABLED)
|
|
{
|
|
/* This is a multi-function device that we need to iterate over */
|
|
|
|
for (dev_func = 0; dev_func < 8; dev_func++)
|
|
{
|
|
tmp_dev.bdf = PCI_BDF(bus_idx, slot_idx, dev_func);
|
|
vid = root_bus->ops->pci_cfg_read(&tmp_dev, PCI_CONFIG_VENDOR, 2);
|
|
|
|
if (vid != 0xffff)
|
|
{
|
|
sec_bus = pci_check_pci_bridge(
|
|
root_bus, bus_idx, slot_idx, dev_func);
|
|
|
|
if (sec_bus)
|
|
{
|
|
pci_scan_bus(root_bus, sec_bus, types);
|
|
continue;
|
|
}
|
|
|
|
pci_probe_device(root_bus, bus_idx, slot_idx, dev_func, types);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Check if this is a PCI-PCI bridge device with MF=0 */
|
|
|
|
sec_bus = pci_check_pci_bridge(root_bus, bus_idx, slot_idx, dev_func);
|
|
if (sec_bus)
|
|
{
|
|
pci_scan_bus(root_bus, sec_bus, types);
|
|
}
|
|
else
|
|
{
|
|
pci_probe_device(root_bus, bus_idx, slot_idx, dev_func, types);
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_scan_bus
|
|
*
|
|
* Description:
|
|
* Iterates over all slots on bus looking for devices and buses to
|
|
* enumerate.
|
|
*
|
|
* Input Parameters:
|
|
* root_bus - The root bus device that lets us address the whole tree
|
|
* bus - Bus ID
|
|
* types - List of pointers to devices types recognized, NULL terminated
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void pci_scan_bus(FAR struct pci_bus_s *root_bus,
|
|
uint8_t bus_idx,
|
|
FAR const struct pci_dev_type_s **types)
|
|
{
|
|
uint8_t slot_idx;
|
|
|
|
for (slot_idx = 0; slot_idx < 32; slot_idx++)
|
|
{
|
|
pci_scan_device(root_bus, bus_idx, slot_idx, types);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_set_cmd_bit
|
|
*
|
|
* Description:
|
|
* This sets an individual bit in the command register for a device.
|
|
*
|
|
* Input Parameters:
|
|
* dev - device
|
|
* bit - Bit to set
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void pci_set_cmd_bit(FAR struct pci_dev_s *dev, uint16_t bitmask)
|
|
{
|
|
uint16_t cmd;
|
|
|
|
cmd = dev->bus->ops->pci_cfg_read(dev, PCI_CONFIG_COMMAND, 2);
|
|
dev->bus->ops->pci_cfg_write(dev, PCI_CONFIG_COMMAND,
|
|
(cmd | bitmask), 2);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_clear_cmd_bit
|
|
*
|
|
* Description:
|
|
* This clears an individual bit in the command register for a device.
|
|
*
|
|
* Input Parameters:
|
|
* dev - device
|
|
* bit - Bit to set
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void pci_clear_cmd_bit(FAR struct pci_dev_s *dev, uint16_t bitmask)
|
|
{
|
|
uint16_t cmd;
|
|
|
|
cmd = dev->bus->ops->pci_cfg_read(dev, PCI_CONFIG_COMMAND, 2);
|
|
dev->bus->ops->pci_cfg_write(dev, PCI_CONFIG_COMMAND,
|
|
(cmd & ~bitmask), 2);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: pci_enumerate
|
|
*
|
|
* Description:
|
|
* Scan the PCI bus and enumerate the devices.
|
|
* Initialize any recognized devices, given in types.
|
|
*
|
|
* Input Parameters:
|
|
* bus - PCI-E bus structure
|
|
* types - List of pointers to devices types recognized, NULL terminated
|
|
*
|
|
* Returned Value:
|
|
* 0: success, <0: A negated errno
|
|
*
|
|
****************************************************************************/
|
|
|
|
int pci_enumerate(FAR struct pci_bus_s *bus,
|
|
FAR const struct pci_dev_type_s **types)
|
|
{
|
|
if (!bus)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!types)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
pci_scan_bus(bus, 0, types);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_initialize
|
|
*
|
|
* Description:
|
|
* Initialize the PCI-E bus and enumerate the devices with give devices
|
|
* type array
|
|
*
|
|
* Input Parameters:
|
|
* bus - An PCIE bus
|
|
* types - A array of PCIE device types
|
|
* num - Number of device types
|
|
*
|
|
* Returned Value:
|
|
* OK if the driver was successfully register; A negated errno value is
|
|
* returned on any failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int pci_initialize(FAR struct pci_bus_s *bus)
|
|
{
|
|
return pci_enumerate(bus, g_pci_device_types);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_enable_io
|
|
*
|
|
* Description:
|
|
* Enable MMIO or IOPORT
|
|
*
|
|
* Input Parameters:
|
|
* dev - device
|
|
* space - which resource is being enabled
|
|
* PCI_SYS_RES_IOPORT for io port address decoding or
|
|
* PCI_SYS_RES_MEM for memory
|
|
*
|
|
* Return value:
|
|
* -EINVAL: error
|
|
* OK: OK
|
|
*
|
|
****************************************************************************/
|
|
|
|
int pci_enable_io(FAR struct pci_dev_s *dev, int res)
|
|
{
|
|
switch (res)
|
|
{
|
|
case PCI_SYS_RES_IOPORT:
|
|
{
|
|
pci_set_cmd_bit(dev, PCI_CMD_IO_SPACE);
|
|
return OK;
|
|
}
|
|
|
|
case PCI_SYS_RES_MEM:
|
|
{
|
|
pci_set_cmd_bit(dev, PCI_CMD_MEM_SPACE);
|
|
return OK;
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_disable_io
|
|
*
|
|
* Description:
|
|
* Enable MMIO or IOPORT
|
|
*
|
|
* Input Parameters:
|
|
* dev - device
|
|
* space - which resource is being disabled
|
|
* PCI_SYS_RES_IOPORT for io port address decoding or
|
|
* PCI_SYS_RES_MEM for memory
|
|
*
|
|
* Return value:
|
|
* -EINVAL: error
|
|
* OK: OK
|
|
*
|
|
****************************************************************************/
|
|
|
|
int pci_disable_io(FAR struct pci_dev_s *dev, int res)
|
|
{
|
|
switch (res)
|
|
{
|
|
case PCI_SYS_RES_IOPORT:
|
|
{
|
|
pci_clear_cmd_bit(dev, PCI_CMD_IO_SPACE);
|
|
return OK;
|
|
}
|
|
|
|
case PCI_SYS_RES_MEM:
|
|
{
|
|
pci_clear_cmd_bit(dev, PCI_CMD_MEM_SPACE);
|
|
return OK;
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_enable_bus_master
|
|
*
|
|
* Description:
|
|
* Enable bus mastering for device so it can perform PCI accesses
|
|
*
|
|
* Input Parameters:
|
|
* dev - device
|
|
*
|
|
* Return value:
|
|
* -EINVAL: error
|
|
* OK: OK
|
|
*
|
|
****************************************************************************/
|
|
|
|
int pci_enable_bus_master(FAR struct pci_dev_s *dev)
|
|
{
|
|
pci_set_cmd_bit(dev, PCI_CMD_BUS_MSTR);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_disable_bus_master
|
|
*
|
|
* Description:
|
|
* Disable bus mastering for device
|
|
*
|
|
* Input Parameters:
|
|
* dev - device
|
|
*
|
|
* Return value:
|
|
* -EINVAL: error
|
|
* OK: OK
|
|
*
|
|
****************************************************************************/
|
|
|
|
int pci_disable_bus_master(FAR struct pci_dev_s *dev)
|
|
{
|
|
pci_clear_cmd_bit(dev, PCI_CMD_BUS_MSTR);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_bar_valid
|
|
*
|
|
* Description:
|
|
* Determine in if the address in the BAR is valid
|
|
*
|
|
* Input Parameters:
|
|
* dev - device
|
|
* bar_id - bar number
|
|
*
|
|
* Return value:
|
|
* -EINVAL: error
|
|
* OK: OK
|
|
*
|
|
****************************************************************************/
|
|
|
|
int pci_bar_valid(FAR struct pci_dev_s *dev, uint8_t bar_id)
|
|
{
|
|
uint32_t bar = dev->bus->ops->pci_cfg_read(dev,
|
|
PCI_HEADER_NORM_BAR0 + (bar_id * 4), 4);
|
|
|
|
if (bar == PCI_BAR_INVALID)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_bar_is_64
|
|
*
|
|
* Description:
|
|
* Determine in if the bar address is 64 bit. If it is the address includes
|
|
* the address in the next bar location.
|
|
*
|
|
* Input Parameters:
|
|
* dev - device
|
|
* bar_id - bar number
|
|
*
|
|
* Return value:
|
|
* true: 64bit address
|
|
*
|
|
****************************************************************************/
|
|
|
|
bool pci_bar_is_64(FAR struct pci_dev_s *dev, uint8_t bar_id)
|
|
{
|
|
uint32_t bar = dev->bus->ops->pci_cfg_read(dev,
|
|
PCI_HEADER_NORM_BAR0 + (bar_id * 4), 4);
|
|
|
|
/* Check that it is memory and not io port */
|
|
|
|
if ((bar & PCI_BAR_LAYOUT_MASK) != PCI_BAR_LAYOUT_MEM)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (((bar & PCI_BAR_TYPE_MASK) >> PCI_BAR_TYPE_OFFSET) == PCI_BAR_TYPE_64)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_bar_size
|
|
*
|
|
* Description:
|
|
* Determine the size of the address space required by the BAR
|
|
*
|
|
* Input Parameters:
|
|
* dev - device
|
|
* bar_id - bar number
|
|
*
|
|
* Return value:
|
|
* Size of address space
|
|
*
|
|
****************************************************************************/
|
|
|
|
uint64_t pci_bar_size(FAR struct pci_dev_s *dev, uint8_t bar_id)
|
|
{
|
|
FAR const struct pci_bus_ops_s *dev_ops = dev->bus->ops;
|
|
uint32_t bar;
|
|
uint32_t size;
|
|
uint64_t full_size;
|
|
uint8_t bar_offset;
|
|
|
|
bar_offset = PCI_HEADER_NORM_BAR0 + (bar_id * 4);
|
|
bar = dev_ops->pci_cfg_read(dev, bar_offset, 4);
|
|
|
|
/* Write all 1 to the BAR. We are looking for which bits will change */
|
|
|
|
dev_ops->pci_cfg_write(dev, bar_offset, 0xffffffff, 4);
|
|
full_size = dev_ops->pci_cfg_read(dev, bar_offset, 4);
|
|
|
|
/* Resore BAR to original values */
|
|
|
|
dev_ops->pci_cfg_write(dev, bar_offset, bar, 4);
|
|
|
|
if (full_size == 0)
|
|
{
|
|
/* This is not a valid bar */
|
|
|
|
return 0;
|
|
}
|
|
|
|
if ((bar & PCI_BAR_LAYOUT_MASK) == PCI_BAR_LAYOUT_MEM)
|
|
{
|
|
full_size &= PCI_BAR_MEM_BASE_MASK;
|
|
}
|
|
else
|
|
{
|
|
full_size &= PCI_BAR_IO_BASE_MASK;
|
|
}
|
|
|
|
/* If it is 64 bit address check the next bar as well */
|
|
|
|
if (pci_bar_is_64(dev, bar_id))
|
|
{
|
|
bar_offset += 4;
|
|
bar = dev_ops->pci_cfg_read(dev, bar_offset, 4);
|
|
dev_ops->pci_cfg_write(dev, bar_offset, 0xffffffff, 4);
|
|
size = dev_ops->pci_cfg_read(dev, bar_offset, 4);
|
|
dev_ops->pci_cfg_write(dev, bar_offset, bar, 4);
|
|
full_size |= ((uint64_t)size << 32);
|
|
}
|
|
else
|
|
{
|
|
full_size |= (uint64_t)(0xffffffff) << 32;
|
|
}
|
|
|
|
return ~full_size + 1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_bar_addr
|
|
*
|
|
* Description:
|
|
* Determine the size of the address space required by the BAR
|
|
*
|
|
* Input Parameters:
|
|
* dev - device
|
|
* bar_id - bar number
|
|
*
|
|
* Return value:
|
|
* full bar address
|
|
*
|
|
****************************************************************************/
|
|
|
|
uint64_t pci_bar_addr(FAR struct pci_dev_s *dev, uint8_t bar_id)
|
|
{
|
|
FAR const struct pci_bus_ops_s *dev_ops = dev->bus->ops;
|
|
uint64_t addr;
|
|
uint8_t bar_offset;
|
|
|
|
bar_offset = PCI_HEADER_NORM_BAR0 + (bar_id * 4);
|
|
addr = dev_ops->pci_cfg_read(dev, bar_offset, 4);
|
|
|
|
if ((addr & PCI_BAR_LAYOUT_MASK) == PCI_BAR_LAYOUT_MEM)
|
|
{
|
|
addr &= PCI_BAR_MEM_BASE_MASK;
|
|
}
|
|
else
|
|
{
|
|
addr &= PCI_BAR_IO_BASE_MASK;
|
|
}
|
|
|
|
/* If it is 64 bit address check the next bar as well */
|
|
|
|
if (pci_bar_is_64(dev, bar_id))
|
|
{
|
|
bar_offset += 4;
|
|
addr |= (uint64_t)(dev_ops->pci_cfg_read(dev, bar_offset, 4)) << 32;
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: pci_dev_dump
|
|
*
|
|
* Description:
|
|
* Dump the configuration information for the device
|
|
*
|
|
* Input Parameters:
|
|
* dev - device
|
|
*
|
|
****************************************************************************/
|
|
|
|
void pci_dev_dump(FAR struct pci_dev_s *dev)
|
|
{
|
|
FAR const struct pci_bus_ops_s *dev_ops = dev->bus->ops;
|
|
uint8_t bar_mem_type = 0;
|
|
uint8_t bar_id;
|
|
uint32_t bar;
|
|
uint64_t bar_size;
|
|
uint64_t bar_addr;
|
|
uint8_t cap_id;
|
|
uint8_t cap_offset;
|
|
uint32_t bdf;
|
|
uint16_t vid;
|
|
uint16_t pid;
|
|
uint8_t header;
|
|
uint8_t progif;
|
|
uint8_t subclass;
|
|
uint8_t class;
|
|
uint8_t int_pin;
|
|
uint8_t int_line;
|
|
|
|
bdf = dev->bdf;
|
|
vid = dev_ops->pci_cfg_read(dev, PCI_CONFIG_VENDOR, 2);
|
|
pid = dev_ops->pci_cfg_read(dev, PCI_CONFIG_DEVICE, 2);
|
|
header = dev_ops->pci_cfg_read(dev, PCI_CONFIG_HEADER_TYPE, 1);
|
|
progif = dev_ops->pci_cfg_read(dev, PCI_CONFIG_PROG_IF, 1);
|
|
subclass = dev_ops->pci_cfg_read(dev, PCI_CONFIG_SUBCLASS, 1);
|
|
class = dev_ops->pci_cfg_read(dev, PCI_CONFIG_CLASS, 1);
|
|
|
|
pciinfo("[%02x:%02x.%x] %04x:%04x\n",
|
|
bdf >> 8, (bdf & 0xff) >> 3, bdf & 0x7, vid, pid);
|
|
pciinfo("\ttype %02x Prog IF %02x Class %02x Subclass %02x\n",
|
|
header, progif, class, subclass);
|
|
|
|
cap_offset = dev_ops->pci_cfg_read(dev, PCI_HEADER_NORM_CAP, 1);
|
|
while (cap_offset)
|
|
{
|
|
cap_id = dev_ops->pci_cfg_read(dev, cap_offset, 1);
|
|
if (cap_id > PCI_CAP_ID_END)
|
|
{
|
|
pcierr("Invalid PCI Capability Found, Skipping. %d\n", cap_id);
|
|
DEBUGPANIC();
|
|
break;
|
|
}
|
|
|
|
pciinfo("\tCAP %02x\n", cap_id);
|
|
cap_offset = dev_ops->pci_cfg_read(dev, cap_offset + 1, 1);
|
|
}
|
|
|
|
if ((header & PCI_HEADER_TYPE_MASK) != PCI_HEADER_NORMAL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int_pin = dev_ops->pci_cfg_read(dev, PCI_HEADER_NORM_INT_PIN, 1);
|
|
int_line = dev_ops->pci_cfg_read(dev, PCI_HEADER_NORM_INT_LINE, 1);
|
|
pciinfo("\tINT Pin %02x Line %02x\n", int_pin, int_line);
|
|
|
|
for (bar_id = 0; bar_id < PCI_BAR_CNT; bar_id++)
|
|
{
|
|
if (pci_bar_valid(dev, bar_id) != OK)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bar = dev_ops->pci_cfg_read(dev,
|
|
PCI_HEADER_NORM_BAR0 + (bar_id * 4), 4);
|
|
|
|
bar_size = pci_bar_size(dev, bar_id);
|
|
bar_addr = pci_bar_addr(dev, bar_id);
|
|
if ((bar & PCI_BAR_LAYOUT_MASK) == PCI_BAR_LAYOUT_MEM)
|
|
{
|
|
switch ((bar & PCI_BAR_TYPE_MASK) >> PCI_BAR_TYPE_OFFSET)
|
|
{
|
|
case PCI_BAR_TYPE_64:
|
|
bar_mem_type = 64;
|
|
break;
|
|
case PCI_BAR_TYPE_32:
|
|
bar_mem_type = 32;
|
|
break;
|
|
case PCI_BAR_TYPE_16:
|
|
bar_mem_type = 16;
|
|
break;
|
|
default:
|
|
bar_mem_type = 0;
|
|
}
|
|
|
|
pciinfo("\tBAR [%d] MEM %db range %p-%p (%p)\n",
|
|
bar_id, bar_mem_type,
|
|
bar_addr, bar_addr + bar_size - 1, bar_size);
|
|
}
|
|
else
|
|
{
|
|
pciinfo("\tBAR [%d] PIO range %p-%p (%p)\n",
|
|
bar_id,
|
|
bar_addr, bar_addr + bar_size - 1, bar_size);
|
|
}
|
|
|
|
/* Skip next bar if this one was 64bit */
|
|
|
|
if (bar_mem_type == 64)
|
|
{
|
|
bar_id++;
|
|
}
|
|
}
|
|
}
|