Commit dc0352ab authored by Russell King's avatar Russell King Committed by Bjorn Helgaas

PCI: mvebu: Add PCI Express root complex capability block

Add a PCI Express root complex capability block so the PCI layer identifies
the bridge as a PCI Express device.

We expose this as a version 1 PCIe capability block, with slot support.  We
disable the clock power management capability as this depends on boards
wiring the CLKREQ# signal.

Tested-by: Willy Tarreau <w@1wt.eu> (Iomega iConnect Kirkwood, MiraBox Armada 370)
Tested-by: Andrew Lunn <andrew@lunn.ch> (D-Link DIR664 Kirkwood)
Tested-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> (Armada XP GP)
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Reviewed-by: default avatarThomas Petazzoni <thomas.petazzoni@free-electrons.com>
parent d609a8d8
......@@ -30,6 +30,7 @@
#define PCIE_DEV_REV_OFF 0x0008
#define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3))
#define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3))
#define PCIE_CAP_PCIEXP 0x0060
#define PCIE_HEADER_LOG_4_OFF 0x0128
#define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4))
#define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4))
......@@ -57,14 +58,35 @@
#define PCIE_STAT_BUS 0xff00
#define PCIE_STAT_DEV 0x1f0000
#define PCIE_STAT_LINK_DOWN BIT(0)
#define PCIE_RC_RTSTA 0x1a14
#define PCIE_DEBUG_CTRL 0x1a60
#define PCIE_DEBUG_SOFT_RESET BIT(20)
enum {
PCISWCAP = PCI_BRIDGE_CONTROL + 2,
PCISWCAP_EXP_LIST_ID = PCISWCAP + PCI_CAP_LIST_ID,
PCISWCAP_EXP_DEVCAP = PCISWCAP + PCI_EXP_DEVCAP,
PCISWCAP_EXP_DEVCTL = PCISWCAP + PCI_EXP_DEVCTL,
PCISWCAP_EXP_LNKCAP = PCISWCAP + PCI_EXP_LNKCAP,
PCISWCAP_EXP_LNKCTL = PCISWCAP + PCI_EXP_LNKCTL,
PCISWCAP_EXP_SLTCAP = PCISWCAP + PCI_EXP_SLTCAP,
PCISWCAP_EXP_SLTCTL = PCISWCAP + PCI_EXP_SLTCTL,
PCISWCAP_EXP_RTCTL = PCISWCAP + PCI_EXP_RTCTL,
PCISWCAP_EXP_RTSTA = PCISWCAP + PCI_EXP_RTSTA,
PCISWCAP_EXP_DEVCAP2 = PCISWCAP + PCI_EXP_DEVCAP2,
PCISWCAP_EXP_DEVCTL2 = PCISWCAP + PCI_EXP_DEVCTL2,
PCISWCAP_EXP_LNKCAP2 = PCISWCAP + PCI_EXP_LNKCAP2,
PCISWCAP_EXP_LNKCTL2 = PCISWCAP + PCI_EXP_LNKCTL2,
PCISWCAP_EXP_SLTCAP2 = PCISWCAP + PCI_EXP_SLTCAP2,
PCISWCAP_EXP_SLTCTL2 = PCISWCAP + PCI_EXP_SLTCTL2,
};
/* PCI configuration space of a PCI-to-PCI bridge */
struct mvebu_sw_pci_bridge {
u16 vendor;
u16 device;
u16 command;
u16 status;
u16 class;
u8 interface;
u8 revision;
......@@ -84,13 +106,15 @@ struct mvebu_sw_pci_bridge {
u16 memlimit;
u16 iobaseupper;
u16 iolimitupper;
u8 cappointer;
u8 reserved1;
u16 reserved2;
u32 romaddr;
u8 intline;
u8 intpin;
u16 bridgectrl;
/* PCI express capability */
u32 pcie_sltcap;
u16 pcie_devctl;
u16 pcie_rtctl;
};
struct mvebu_pcie_port;
......@@ -451,6 +475,9 @@ static void mvebu_sw_pci_bridge_init(struct mvebu_pcie_port *port)
/* We support 32 bits I/O addressing */
bridge->iobase = PCI_IO_RANGE_TYPE_32;
bridge->iolimit = PCI_IO_RANGE_TYPE_32;
/* Add capabilities */
bridge->status = PCI_STATUS_CAP_LIST;
}
/*
......@@ -468,7 +495,7 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
break;
case PCI_COMMAND:
*value = bridge->command;
*value = bridge->command | bridge->status << 16;
break;
case PCI_CLASS_REVISION:
......@@ -513,6 +540,10 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
*value = (bridge->iolimitupper << 16 | bridge->iobaseupper);
break;
case PCI_CAPABILITY_LIST:
*value = PCISWCAP;
break;
case PCI_ROM_ADDRESS1:
*value = 0;
break;
......@@ -522,6 +553,59 @@ static int mvebu_sw_pci_bridge_read(struct mvebu_pcie_port *port,
*value = 0;
break;
case PCISWCAP_EXP_LIST_ID:
/* Set PCIe v2, root port, slot support */
*value = (PCI_EXP_TYPE_ROOT_PORT << 4 | 2 |
PCI_EXP_FLAGS_SLOT) << 16 | PCI_CAP_ID_EXP;
break;
case PCISWCAP_EXP_DEVCAP:
*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP);
break;
case PCISWCAP_EXP_DEVCTL:
*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL) &
~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE |
PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE);
*value |= bridge->pcie_devctl;
break;
case PCISWCAP_EXP_LNKCAP:
/*
* PCIe requires the clock power management capability to be
* hard-wired to zero for downstream ports
*/
*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) &
~PCI_EXP_LNKCAP_CLKPM;
break;
case PCISWCAP_EXP_LNKCTL:
*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
break;
case PCISWCAP_EXP_SLTCAP:
*value = bridge->pcie_sltcap;
break;
case PCISWCAP_EXP_SLTCTL:
*value = PCI_EXP_SLTSTA_PDS << 16;
break;
case PCISWCAP_EXP_RTCTL:
*value = bridge->pcie_rtctl;
break;
case PCISWCAP_EXP_RTSTA:
*value = mvebu_readl(port, PCIE_RC_RTSTA);
break;
/* PCIe requires the v2 fields to be hard-wired to zero */
case PCISWCAP_EXP_DEVCAP2:
case PCISWCAP_EXP_DEVCTL2:
case PCISWCAP_EXP_LNKCAP2:
case PCISWCAP_EXP_LNKCTL2:
case PCISWCAP_EXP_SLTCAP2:
case PCISWCAP_EXP_SLTCTL2:
default:
/*
* PCI defines configuration read accesses to reserved or
......@@ -614,6 +698,51 @@ static int mvebu_sw_pci_bridge_write(struct mvebu_pcie_port *port,
mvebu_pcie_set_local_bus_nr(port, bridge->secondary_bus);
break;
case PCISWCAP_EXP_DEVCTL:
/*
* Armada370 data says these bits must always
* be zero when in root complex mode.
*/
value &= ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE |
PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE);
/*
* If the mask is 0xffff0000, then we only want to write
* the device control register, rather than clearing the
* RW1C bits in the device status register. Mask out the
* status register bits.
*/
if (mask == 0xffff0000)
value &= 0xffff;
mvebu_writel(port, value, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL);
break;
case PCISWCAP_EXP_LNKCTL:
/*
* If we don't support CLKREQ, we must ensure that the
* CLKREQ enable bit always reads zero. Since we haven't
* had this capability, and it's dependent on board wiring,
* disable it for the time being.
*/
value &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
/*
* If the mask is 0xffff0000, then we only want to write
* the link control register, rather than clearing the
* RW1C bits in the link status register. Mask out the
* status register bits.
*/
if (mask == 0xffff0000)
value &= 0xffff;
mvebu_writel(port, value, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
break;
case PCISWCAP_EXP_RTSTA:
mvebu_writel(port, value, PCIE_RC_RTSTA);
break;
default:
break;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment