X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=drivers%2Fpci%2Fpci-aardvark.c;h=22b6d8b628653270992f5c0f752e2251e4bab06b;hb=4afab30caea3211032710c4298a8839d3254e7f7;hp=74797e984cb8633315964555652ff2e79ba9b958;hpb=d9ac6e28afae08f4d0c2116db4279fa59ea7c838;p=platform%2Fkernel%2Fu-boot.git diff --git a/drivers/pci/pci-aardvark.c b/drivers/pci/pci-aardvark.c index 74797e9..22b6d8b 100644 --- a/drivers/pci/pci-aardvark.c +++ b/drivers/pci/pci-aardvark.c @@ -21,6 +21,7 @@ * * Author: Victor Gu * Hezi Shahmoon + * Pali Rohár * */ @@ -39,6 +40,9 @@ #define PCIE_CORE_CMD_IO_ACCESS_EN BIT(0) #define PCIE_CORE_CMD_MEM_ACCESS_EN BIT(1) #define PCIE_CORE_CMD_MEM_IO_REQ_EN BIT(2) +#define PCIE_CORE_DEV_REV_REG 0x8 +#define PCIE_CORE_EXP_ROM_BAR_REG 0x30 +#define PCIE_CORE_PCIEXP_CAP_OFF 0xc0 #define PCIE_CORE_DEV_CTRL_STATS_REG 0xc8 #define PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE (0 << 4) #define PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE (0 << 11) @@ -145,6 +149,7 @@ #define LTSSM_SHIFT 24 #define LTSSM_MASK 0x3f #define LTSSM_L0 0x10 +#define LTSSM_DISABLED 0x20 #define VENDOR_ID_REG (LMI_BASE_ADDR + 0x44) /* PCIe core controller registers */ @@ -161,16 +166,6 @@ #define PCIE_CONFIG_WR_TYPE0 0xa #define PCIE_CONFIG_WR_TYPE1 0xb -/* PCI_BDF shifts 8bit, so we need extra 4bit shift */ -#define PCIE_BDF(dev) (dev << 4) -#define PCIE_CONF_BUS(bus) (((bus) & 0xff) << 20) -#define PCIE_CONF_DEV(dev) (((dev) & 0x1f) << 15) -#define PCIE_CONF_FUNC(fun) (((fun) & 0x7) << 12) -#define PCIE_CONF_REG(reg) ((reg) & 0xffc) -#define PCIE_CONF_ADDR(bus, devfn, where) \ - (PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn)) | \ - PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where)) - /* PCIe Retries & Timeout definitions */ #define PIO_MAX_RETRIES 1500 #define PIO_WAIT_TIMEOUT 1000 @@ -182,18 +177,24 @@ /** * struct pcie_advk - Advk PCIe controller state * - * @reg_base: The base address of the register space. - * @first_busno: This driver supports multiple PCIe controllers. - * first_busno stores the bus number of the PCIe root-port - * number which may vary depending on the PCIe setup - * (PEX switches etc). - * @device: The pointer to PCI uclass device. + * @base: The base address of the register space. + * @first_busno: Bus number of the PCIe root-port. + * This may vary depending on the PCIe setup. + * @sec_busno: Bus number for the device behind the PCIe root-port. + * @dev: The pointer to PCI uclass device. + * @reset_gpio: GPIO descriptor for PERST. + * @cfgcache: Buffer for emulation of PCIe Root Port's PCI Bridge registers + * that are not available on Aardvark. + * @cfgcrssve: For CRSSVE emulation. */ struct pcie_advk { - void *base; - int first_busno; - struct udevice *dev; - struct gpio_desc reset_gpio; + void *base; + int first_busno; + int sec_busno; + struct udevice *dev; + struct gpio_desc reset_gpio; + u32 cfgcache[(0x3c - 0x10) / 4]; + bool cfgcrssve; }; static inline void advk_writel(struct pcie_advk *pcie, uint val, uint reg) @@ -209,22 +210,30 @@ static inline uint advk_readl(struct pcie_advk *pcie, uint reg) /** * pcie_advk_addr_valid() - Check for valid bus address * + * @pcie: Pointer to the PCI bus + * @busno: Bus number of PCI device + * @dev: Device number of PCI device + * @func: Function number of PCI device * @bdf: The PCI device to access - * @first_busno: Bus number of the PCIe controller root complex * - * Return: 1 on valid, 0 on invalid + * Return: true on valid, false on invalid */ -static int pcie_advk_addr_valid(pci_dev_t bdf, int first_busno) +static bool pcie_advk_addr_valid(struct pcie_advk *pcie, + int busno, u8 dev, u8 func) { + /* On the primary (local) bus there is only one PCI Bridge */ + if (busno == pcie->first_busno && (dev != 0 || func != 0)) + return false; + /* - * In PCIE-E only a single device (0) can exist - * on the local bus. Beyound the local bus, there might be - * a Switch and everything is possible. + * In PCI-E only a single device (0) can exist on the secondary bus. + * Beyond the secondary bus, there might be a Switch and anything is + * possible. */ - if ((PCI_BUS(bdf) == first_busno) && (PCI_DEV(bdf) > 0)) - return 0; + if (busno == pcie->sec_busno && dev != 0) + return false; - return 1; + return true; } /** @@ -327,7 +336,7 @@ static int pcie_advk_check_pio_status(struct pcie_advk *pcie, else str_posted = "Posted"; - dev_err(pcie->dev, "%s PIO Response Status: %s, %#x @ %#x\n", + dev_dbg(pcie->dev, "%s PIO Response Status: %s, %#x @ %#x\n", str_posted, strcomp_status, reg, advk_readl(pcie, PIO_ADDR_LS)); @@ -354,32 +363,79 @@ static int pcie_advk_read_config(const struct udevice *bus, pci_dev_t bdf, enum pci_size_t size) { struct pcie_advk *pcie = dev_get_priv(bus); + int busno = PCI_BUS(bdf) - dev_seq(bus); int retry_count; bool allow_crs; + ulong data; uint reg; int ret; dev_dbg(pcie->dev, "PCIE CFG read: (b,d,f)=(%2d,%2d,%2d) ", PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); - if (!pcie_advk_addr_valid(bdf, pcie->first_busno)) { + if (!pcie_advk_addr_valid(pcie, busno, PCI_DEV(bdf), PCI_FUNC(bdf))) { dev_dbg(pcie->dev, "- out of range\n"); *valuep = pci_get_ff(size); return 0; } /* + * The configuration space of the PCI Bridge on primary (first) bus is + * not accessible via PIO transfers like all other PCIe devices. PCI + * Bridge config registers are available directly in Aardvark memory + * space starting at offset zero. The PCI Bridge config space is of + * Type 0, but the BAR registers (including ROM BAR) don't have the same + * meaning as in the PCIe specification. Therefore do not access BAR + * registers and non-common registers (those which have different + * meaning for Type 0 and Type 1 config space) of the primary PCI Bridge + * and instead read their content from driver virtual cfgcache[]. + */ + if (busno == pcie->first_busno) { + if ((offset >= 0x10 && offset < 0x34) || (offset >= 0x38 && offset < 0x3c)) + data = pcie->cfgcache[(offset - 0x10) / 4]; + else + data = advk_readl(pcie, offset & ~3); + + if ((offset & ~3) == (PCI_HEADER_TYPE & ~3)) { + /* + * Change Header Type of PCI Bridge device to Type 1 + * (0x01, used by PCI Bridges) because hardwired value + * is Type 0 (0x00, used by Endpoint devices). + */ + data &= ~0x007f0000; + data |= PCI_HEADER_TYPE_BRIDGE << 16; + } + + if ((offset & ~3) == PCIE_CORE_PCIEXP_CAP_OFF + PCI_EXP_RTCTL) { + /* CRSSVE bit is stored only in cache */ + if (pcie->cfgcrssve) + data |= PCI_EXP_RTCTL_CRSSVE; + } + + if ((offset & ~3) == PCIE_CORE_PCIEXP_CAP_OFF + + (PCI_EXP_RTCAP & ~3)) { + /* CRS is emulated below, so set CRSVIS capability */ + data |= PCI_EXP_RTCAP_CRSVIS << 16; + } + + *valuep = pci_conv_32_to_size(data, offset, size); + + return 0; + } + + /* * Returning fabricated CRS value (0xFFFF0001) by PCIe Root Complex to * OS is allowed only for 4-byte PCI_VENDOR_ID config read request and * only when CRSSVE bit in Root Port PCIe device is enabled. In all * other error PCIe Root Complex must return all-ones. - * Aardvark HW does not have Root Port PCIe device and U-Boot does not - * implement emulation of this device. + * * U-Boot currently does not support handling of CRS return value for * PCI_VENDOR_ID config read request and also does not set CRSSVE bit. - * Therefore disable returning CRS response for now. + * So it means that pcie->cfgcrssve is false. But the code is prepared + * for returning CRS, so that if U-Boot does support CRS in the future, + * it will work for Aardvark. */ - allow_crs = false; + allow_crs = (offset == PCI_VENDOR_ID) && (size == PCI_SIZE_32) && pcie->cfgcrssve; if (advk_readl(pcie, PIO_START)) { dev_err(pcie->dev, @@ -395,17 +451,20 @@ static int pcie_advk_read_config(const struct udevice *bus, pci_dev_t bdf, /* Program the control register */ reg = advk_readl(pcie, PIO_CTRL); reg &= ~PIO_CTRL_TYPE_MASK; - if (PCI_BUS(bdf) == pcie->first_busno) + if (busno == pcie->sec_busno) reg |= PCIE_CONFIG_RD_TYPE0; else reg |= PCIE_CONFIG_RD_TYPE1; advk_writel(pcie, reg, PIO_CTRL); /* Program the address registers */ - reg = PCIE_BDF(bdf) | PCIE_CONF_REG(offset); + reg = PCIE_ECAM_OFFSET(busno, PCI_DEV(bdf), PCI_FUNC(bdf), (offset & ~0x3)); advk_writel(pcie, reg, PIO_ADDR_LS); advk_writel(pcie, 0, PIO_ADDR_MS); + /* Program the data strobe */ + advk_writel(pcie, 0xf, PIO_WR_DATA_STRB); + retry_count = 0; retry: @@ -490,7 +549,9 @@ static int pcie_advk_write_config(struct udevice *bus, pci_dev_t bdf, enum pci_size_t size) { struct pcie_advk *pcie = dev_get_priv(bus); + int busno = PCI_BUS(bdf) - dev_seq(bus); int retry_count; + ulong data; uint reg; int ret; @@ -499,11 +560,47 @@ static int pcie_advk_write_config(struct udevice *bus, pci_dev_t bdf, dev_dbg(pcie->dev, "(addr,size,val)=(0x%04x, %d, 0x%08lx)\n", offset, size, value); - if (!pcie_advk_addr_valid(bdf, pcie->first_busno)) { + if (!pcie_advk_addr_valid(pcie, busno, PCI_DEV(bdf), PCI_FUNC(bdf))) { dev_dbg(pcie->dev, "- out of range\n"); return 0; } + /* + * As explained in pcie_advk_read_config(), PCI Bridge config registers + * are available directly in Aardvark memory space starting at offset + * zero. Type 1 specific registers are not available, so we write their + * content only into driver virtual cfgcache[]. + */ + if (busno == pcie->first_busno) { + if ((offset >= 0x10 && offset < 0x34) || + (offset >= 0x38 && offset < 0x3c)) { + data = pcie->cfgcache[(offset - 0x10) / 4]; + data = pci_conv_size_to_32(data, value, offset, size); + /* This PCI bridge does not have configurable bars */ + if ((offset & ~3) == PCI_BASE_ADDRESS_0 || + (offset & ~3) == PCI_BASE_ADDRESS_1 || + (offset & ~3) == PCI_ROM_ADDRESS1) + data = 0x0; + pcie->cfgcache[(offset - 0x10) / 4] = data; + } else { + data = advk_readl(pcie, offset & ~3); + data = pci_conv_size_to_32(data, value, offset, size); + advk_writel(pcie, data, offset & ~3); + } + + if (offset == PCI_PRIMARY_BUS) + pcie->first_busno = data & 0xff; + + if (offset == PCI_SECONDARY_BUS || + (offset == PCI_PRIMARY_BUS && size != PCI_SIZE_8)) + pcie->sec_busno = (data >> 8) & 0xff; + + if ((offset & ~3) == PCIE_CORE_PCIEXP_CAP_OFF + PCI_EXP_RTCTL) + pcie->cfgcrssve = data & PCI_EXP_RTCTL_CRSSVE; + + return 0; + } + if (advk_readl(pcie, PIO_START)) { dev_err(pcie->dev, "Previous PIO read/write transfer is still running\n"); @@ -513,14 +610,14 @@ static int pcie_advk_write_config(struct udevice *bus, pci_dev_t bdf, /* Program the control register */ reg = advk_readl(pcie, PIO_CTRL); reg &= ~PIO_CTRL_TYPE_MASK; - if (PCI_BUS(bdf) == pcie->first_busno) + if (busno == pcie->sec_busno) reg |= PCIE_CONFIG_WR_TYPE0; else reg |= PCIE_CONFIG_WR_TYPE1; advk_writel(pcie, reg, PIO_CTRL); /* Program the address registers */ - reg = PCIE_BDF(bdf) | PCIE_CONF_REG(offset); + reg = PCIE_ECAM_OFFSET(busno, PCI_DEV(bdf), PCI_FUNC(bdf), (offset & ~0x3)); advk_writel(pcie, reg, PIO_ADDR_LS); advk_writel(pcie, 0, PIO_ADDR_MS); dev_dbg(pcie->dev, "\tPIO req. - addr = 0x%08x\n", reg); @@ -569,7 +666,7 @@ static int pcie_advk_link_up(struct pcie_advk *pcie) val = advk_readl(pcie, CFG_REG); ltssm_state = (val >> LTSSM_SHIFT) & LTSSM_MASK; - return ltssm_state >= LTSSM_L0; + return ltssm_state >= LTSSM_L0 && ltssm_state < LTSSM_DISABLED; } /** @@ -589,14 +686,14 @@ static int pcie_advk_wait_for_link(struct pcie_advk *pcie) /* check if the link is up or not */ for (retries = 0; retries < LINK_MAX_RETRIES; retries++) { if (pcie_advk_link_up(pcie)) { - printf("PCIE-%d: Link up\n", pcie->first_busno); + printf("PCIe: Link up\n"); return 0; } udelay(LINK_WAIT_TIMEOUT); } - printf("PCIE-%d: Link down\n", pcie->first_busno); + printf("PCIe: Link down\n"); return -ETIMEDOUT; } @@ -715,6 +812,33 @@ static int pcie_advk_setup_hw(struct pcie_advk *pcie) */ advk_writel(pcie, 0x11ab11ab, VENDOR_ID_REG); + /* + * Change Class Code of PCI Bridge device to PCI Bridge (0x600400), + * because default value is Mass Storage Controller (0x010400), causing + * U-Boot to fail to recognize it as P2P Bridge. + * + * Note that this Aardvark PCI Bridge does not have a compliant Type 1 + * Configuration Space and it even cannot be accessed via Aardvark's + * PCI config space access method. Aardvark PCI Bridge Config space is + * available in internal Aardvark registers starting at offset 0x0 + * and has format of Type 0 config space. + * + * Moreover Type 0 BAR registers (ranges 0x10 - 0x28 and 0x30 - 0x34) + * have the same format in Marvell's specification as in PCIe + * specification, but their meaning is totally different (and not even + * the same meaning as explained in the corresponding comment in the + * pci_mvebu driver; aardvark is still different). + * + * So our driver converts Type 0 config space to Type 1 and reports + * Header Type as Type 1. Access to BAR registers and to non-existent + * Type 1 registers is redirected to the virtual cfgcache[] buffer, + * which avoids changing unrelated registers. + */ + reg = advk_readl(pcie, PCIE_CORE_DEV_REV_REG); + reg &= ~0xffffff00; + reg |= (PCI_CLASS_BRIDGE_PCI << 8) << 8; + advk_writel(pcie, reg, PCIE_CORE_DEV_REV_REG); + /* Set Advanced Error Capabilities and Control PF0 register */ reg = PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX | PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN | @@ -809,12 +933,6 @@ static int pcie_advk_setup_hw(struct pcie_advk *pcie) if (pcie_advk_wait_for_link(pcie)) return -ENXIO; - reg = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG); - reg |= PCIE_CORE_CMD_MEM_ACCESS_EN | - PCIE_CORE_CMD_IO_ACCESS_EN | - PCIE_CORE_CMD_MEM_IO_REQ_EN; - advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG); - return 0; } @@ -856,9 +974,14 @@ static int pcie_advk_probe(struct udevice *dev) dev_warn(dev, "PCIE Reset on GPIO support is missing\n"); } - pcie->first_busno = dev_seq(dev); pcie->dev = pci_get_controller(dev); + /* PCI Bridge support 32-bit I/O and 64-bit prefetch mem addressing */ + pcie->cfgcache[(PCI_IO_BASE - 0x10) / 4] = + PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8); + pcie->cfgcache[(PCI_PREF_MEMORY_BASE - 0x10) / 4] = + PCI_PREF_RANGE_TYPE_64 | (PCI_PREF_RANGE_TYPE_64 << 16); + return pcie_advk_setup_hw(pcie); }