diff options
Diffstat (limited to 'arch/sh/drivers/pci')
-rw-r--r-- | arch/sh/drivers/pci/ops-sh7786.c | 35 | ||||
-rw-r--r-- | arch/sh/drivers/pci/pcie-sh7786.c | 29 |
2 files changed, 55 insertions, 9 deletions
diff --git a/arch/sh/drivers/pci/ops-sh7786.c b/arch/sh/drivers/pci/ops-sh7786.c index 4728199..01cb70f 100644 --- a/arch/sh/drivers/pci/ops-sh7786.c +++ b/arch/sh/drivers/pci/ops-sh7786.c @@ -25,23 +25,49 @@ static int sh7786_pcie_config_access(unsigned char access_type, struct pci_bus *bus, unsigned int devfn, int where, u32 *data) { struct pci_channel *chan = bus->sysdata; - int dev, func, type; + int dev, func, type, reg; dev = PCI_SLOT(devfn); func = PCI_FUNC(devfn); type = !!bus->parent; + reg = where & ~3; if (bus->number > 255 || dev > 31 || func > 7) return PCIBIOS_FUNC_NOT_SUPPORTED; - if (bus->parent == NULL && dev) - return PCIBIOS_DEVICE_NOT_FOUND; + + /* + * While each channel has its own memory-mapped extended config + * space, it's generally only accessible when in endpoint mode. + * When in root complex mode, the controller is unable to target + * itself with either type 0 or type 1 accesses, and indeed, any + * controller initiated target transfer to its own config space + * result in a completer abort. + * + * Each channel effectively only supports a single device, but as + * the same channel <-> device access works for any PCI_SLOT() + * value, we cheat a bit here and bind the controller's config + * space to devfn 0 in order to enable self-enumeration. In this + * case the regular PAR/PDR path is sidelined and the mangled + * config access itself is initiated as a SuperHyway transaction. + */ + if (pci_is_root_bus(bus)) { + if (dev == 0) { + if (access_type == PCI_ACCESS_READ) + *data = pci_read_reg(chan, PCI_REG(reg)); + else + pci_write_reg(chan, *data, PCI_REG(reg)); + + return PCIBIOS_SUCCESSFUL; + } else if (dev > 1) + return PCIBIOS_DEVICE_NOT_FOUND; + } /* Clear errors */ pci_write_reg(chan, pci_read_reg(chan, SH4A_PCIEERRFR), SH4A_PCIEERRFR); /* Set the PIO address */ pci_write_reg(chan, (bus->number << 24) | (dev << 19) | - (func << 16) | (where & ~3), SH4A_PCIEPAR); + (func << 16) | reg, SH4A_PCIEPAR); /* Enable the configuration access */ pci_write_reg(chan, (1 << 31) | (type << 8), SH4A_PCIEPCTLR); @@ -49,6 +75,7 @@ static int sh7786_pcie_config_access(unsigned char access_type, /* Check for errors */ if (pci_read_reg(chan, SH4A_PCIEERRFR) & 0x10) return PCIBIOS_DEVICE_NOT_FOUND; + /* Check for master and target aborts */ if (pci_read_reg(chan, SH4A_PCIEPCICONF1) & ((1 << 29) | (1 << 28))) return PCIBIOS_DEVICE_NOT_FOUND; diff --git a/arch/sh/drivers/pci/pcie-sh7786.c b/arch/sh/drivers/pci/pcie-sh7786.c index 4e6cf88..3dfc250 100644 --- a/arch/sh/drivers/pci/pcie-sh7786.c +++ b/arch/sh/drivers/pci/pcie-sh7786.c @@ -121,6 +121,24 @@ static struct pci_channel sh7786_pci_channels[] = { DEFINE_CONTROLLER(0xfcc00000, 2), }; +static void __devinit sh7786_pci_fixup(struct pci_dev *dev) +{ + /* + * Prevent enumeration of root complex resources. + */ + if (pci_is_root_bus(dev->bus) && dev->devfn == 0) { + int i; + + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + dev->resource[i].start = 0; + dev->resource[i].end = 0; + dev->resource[i].flags = 0; + } + } +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_RENESAS, PCI_DEVICE_ID_RENESAS_SH7786, + sh7786_pci_fixup); + static int phy_wait_for_ack(struct pci_channel *chan) { unsigned int timeout = 100; @@ -229,11 +247,12 @@ static int pcie_init(struct sh7786_pcie_port *port) /* Begin initialization */ pcie_reset(port); - /* Initialize as type1. */ - data = pci_read_reg(chan, SH4A_PCIEPCICONF3); - data &= ~(0x7f << 16); - data |= PCI_HEADER_TYPE_BRIDGE << 16; - pci_write_reg(chan, data, SH4A_PCIEPCICONF3); + /* + * Initial header for port config space is type 1, set the device + * class to match. Hardware takes care of propagating the IDSETR + * settings, so there is no need to bother with a quirk. + */ + pci_write_reg(chan, PCI_CLASS_BRIDGE_PCI << 16, SH4A_PCIEIDSETR1); /* Initialize default capabilities. */ data = pci_read_reg(chan, SH4A_PCIEEXPCAP0); |