Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 105 additions & 44 deletions drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ static int pci_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
ret = pci_bus_read_config(ctrl->bus, devfn, io->pi_reg,
io->pi_width, &io->pi_data);
break;
}
}

case PCIOCWRITE:
{
Expand Down Expand Up @@ -425,6 +425,60 @@ static void pci_change_master(FAR struct pci_device_s *dev, bool enable)
}
}

/****************************************************************************
* Name: pci_enable_parent_bridges
*
* Description:
* Enable memory forwarding and bus mastering on all bridges between a PCI
* device and the root bus.
*
****************************************************************************/

static int pci_enable_parent_bridges(FAR struct pci_device_s *dev)
{
FAR struct pci_device_s *bridge;
FAR struct pci_device_s *candidate;
FAR struct pci_bus_s *bus = dev->bus;
uint16_t command;
int ret;

while (bus != NULL && bus->parent_bus != NULL)
{
bridge = NULL;
list_for_every_entry(&bus->parent_bus->devices, candidate,
struct pci_device_s, bus_list)
{
if (candidate->subordinate == bus)
{
bridge = candidate;
break;
}
}

if (bridge == NULL)
{
return -ENODEV;
}

ret = pci_read_config_word(bridge, PCI_COMMAND, &command);
if (ret < 0)
{
return ret;
}

command |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
ret = pci_write_config_word(bridge, PCI_COMMAND, command);
if (ret < 0)
{
return ret;
}

bus = bus->parent_bus;
}

return OK;
}

/****************************************************************************
* Name: pci_bus_find_start_cap
*
Expand Down Expand Up @@ -456,17 +510,17 @@ static uint8_t pci_bus_find_start_cap(FAR struct pci_bus_s *bus,
/* Ignore MF bit */

switch (hdr_type & 0x7f)
{
case PCI_HEADER_TYPE_NORMAL:
case PCI_HEADER_TYPE_BRIDGE:
return PCI_CAPABILITY_LIST;
{
case PCI_HEADER_TYPE_NORMAL:
case PCI_HEADER_TYPE_BRIDGE:
return PCI_CAPABILITY_LIST;

case PCI_HEADER_TYPE_CARDBUS:
return PCI_CB_CAPABILITY_LIST;
case PCI_HEADER_TYPE_CARDBUS:
return PCI_CB_CAPABILITY_LIST;

default:
return 0;
}
default:
return 0;
}
}

/****************************************************************************
Expand Down Expand Up @@ -1085,53 +1139,53 @@ static void pci_scan_bus(FAR struct pci_bus_s *bus)
dev->vendor, dev->device);

switch (hdr_type & 0x7f)
{
case PCI_HEADER_TYPE_NORMAL:
if (class == PCI_CLASS_BRIDGE_PCI)
{
goto bad;
}
{
case PCI_HEADER_TYPE_NORMAL:
if (class == PCI_CLASS_BRIDGE_PCI)
{
goto bad;
}

pci_setup_device(dev, 6, PCI_ROM_ADDRESS, &io, &mem, &mem_pref);
pci_setup_device(dev, 6, PCI_ROM_ADDRESS, &io, &mem, &mem_pref);

pci_read_config_word(dev, PCI_SUBSYSTEM_ID,
&dev->subsystem_device);
pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID,
&dev->subsystem_vendor);
break;
pci_read_config_word(dev, PCI_SUBSYSTEM_ID,
&dev->subsystem_device);
pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID,
&dev->subsystem_vendor);
break;

case PCI_HEADER_TYPE_BRIDGE:
child_bus = pci_alloc_bus();
case PCI_HEADER_TYPE_BRIDGE:
child_bus = pci_alloc_bus();

/* Inherit parent properties */
/* Inherit parent properties */

child_bus->ctrl = bus->ctrl;
child_bus->parent_bus = bus;
child_bus->ctrl = bus->ctrl;
child_bus->parent_bus = bus;

#ifdef CONFIG_PCI_ASSIGN_ALL_BUSES
child_bus->number = bus->ctrl->busno++;
child_bus->number = bus->ctrl->busno++;
#endif

list_add_tail(&bus->children, &child_bus->node);
dev->subordinate = child_bus;
list_add_tail(&bus->children, &child_bus->node);
dev->subordinate = child_bus;

/* Scan pci hierarchy behind bridge */
/* Scan pci hierarchy behind bridge */

pci_presetup_bridge(dev);
pci_scan_bus(child_bus);
pci_postsetup_bridge(dev);
pci_presetup_bridge(dev);
pci_scan_bus(child_bus);
pci_postsetup_bridge(dev);

pci_setup_device(dev, 2, PCI_ROM_ADDRESS1, &io, &mem, &mem_pref);
break;
pci_setup_device(dev, 2, PCI_ROM_ADDRESS1, &io, &mem, &mem_pref);
break;

default:
bad:
pcierr("PCI: %02x:%02" PRIx32 " [%04x/%04x/%06" PRIx32
"] has unknown header type %02x, ignoring.\n",
bus->number, dev->devfn, dev->vendor,
dev->device, class, hdr_type);
continue;
}
default:
bad:
pcierr("PCI: %02x:%02" PRIx32 " [%04x/%04x/%06" PRIx32
"] has unknown header type %02x, ignoring.\n",
bus->number, dev->devfn, dev->vendor, dev->device, class,
hdr_type);
continue;
}
}
}

Expand Down Expand Up @@ -1649,6 +1703,13 @@ void pci_clear_master(FAR struct pci_device_s *dev)
int pci_enable_device(FAR struct pci_device_s *dev)
{
uint32_t cmd;
int ret;

ret = pci_enable_parent_bridges(dev);
if (ret < 0)
{
return ret;
}

pci_read_config_dword(dev, PCI_COMMAND, &cmd);
return pci_write_config_dword(dev, PCI_COMMAND,
Expand Down
Loading