diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 58f128d..da6421f 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -50,6 +50,15 @@ config PCIE_DW_MVEBU Armada-8K SoCs. The PCIe controller on Armada-8K is based on DesignWare hardware. +config PCI_RCAR_GEN2 + bool "Renesas RCar Gen2 PCIe driver" + depends on DM_PCI + depends on RCAR_32 + help + Say Y here if you want to enable PCIe controller support on + Renesas RCar Gen2 SoCs. The PCIe controller on RCar Gen2 is + also used to access EHCI USB controller on the SoC. + config PCI_SANDBOX bool "Sandbox PCI support" depends on SANDBOX && DM_PCI diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 5410897..8fbab46 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_PCI_MSC01) += pci_msc01.o obj-$(CONFIG_PCIE_IMX) += pcie_imx.o obj-$(CONFIG_FTPCI100) += pci_ftpci100.o obj-$(CONFIG_PCI_MVEBU) += pci_mvebu.o +obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o obj-$(CONFIG_SH4_PCI) += pci_sh4.o obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o diff --git a/drivers/pci/pci-rcar-gen2.c b/drivers/pci/pci-rcar-gen2.c new file mode 100644 index 0000000..8293a6d --- /dev/null +++ b/drivers/pci/pci-rcar-gen2.c @@ -0,0 +1,264 @@ +/* + * Renesas RCar Gen2 PCIEC driver + * + * Copyright (C) 2018 Marek Vasut + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include +#include +#include +#include + +/* AHB-PCI Bridge PCI communication registers */ +#define RCAR_AHBPCI_PCICOM_OFFSET 0x800 + +#define RCAR_PCIAHB_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x00) +#define RCAR_PCIAHB_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x04) +#define RCAR_PCIAHB_PREFETCH0 0x0 +#define RCAR_PCIAHB_PREFETCH4 0x1 +#define RCAR_PCIAHB_PREFETCH8 0x2 +#define RCAR_PCIAHB_PREFETCH16 0x3 + +#define RCAR_AHBPCI_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x10) +#define RCAR_AHBPCI_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x14) +#define RCAR_AHBPCI_WIN_CTR_MEM (3 << 1) +#define RCAR_AHBPCI_WIN_CTR_CFG (5 << 1) +#define RCAR_AHBPCI_WIN1_HOST BIT(30) +#define RCAR_AHBPCI_WIN1_DEVICE BIT(31) + +#define RCAR_PCI_INT_ENABLE_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x20) +#define RCAR_PCI_INT_STATUS_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x24) +#define RCAR_PCI_INT_SIGTABORT BIT(0) +#define RCAR_PCI_INT_SIGRETABORT BIT(1) +#define RCAR_PCI_INT_REMABORT BIT(2) +#define RCAR_PCI_INT_PERR BIT(3) +#define RCAR_PCI_INT_SIGSERR BIT(4) +#define RCAR_PCI_INT_RESERR BIT(5) +#define RCAR_PCI_INT_WIN1ERR BIT(12) +#define RCAR_PCI_INT_WIN2ERR BIT(13) +#define RCAR_PCI_INT_A BIT(16) +#define RCAR_PCI_INT_B BIT(17) +#define RCAR_PCI_INT_PME BIT(19) +#define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT | \ + RCAR_PCI_INT_SIGRETABORT | \ + RCAR_PCI_INT_SIGRETABORT | \ + RCAR_PCI_INT_REMABORT | \ + RCAR_PCI_INT_PERR | \ + RCAR_PCI_INT_SIGSERR | \ + RCAR_PCI_INT_RESERR | \ + RCAR_PCI_INT_WIN1ERR | \ + RCAR_PCI_INT_WIN2ERR) + +#define RCAR_AHB_BUS_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x30) +#define RCAR_AHB_BUS_MMODE_HTRANS BIT(0) +#define RCAR_AHB_BUS_MMODE_BYTE_BURST BIT(1) +#define RCAR_AHB_BUS_MMODE_WR_INCR BIT(2) +#define RCAR_AHB_BUS_MMODE_HBUS_REQ BIT(7) +#define RCAR_AHB_BUS_SMODE_READYCTR BIT(17) +#define RCAR_AHB_BUS_MODE (RCAR_AHB_BUS_MMODE_HTRANS | \ + RCAR_AHB_BUS_MMODE_BYTE_BURST | \ + RCAR_AHB_BUS_MMODE_WR_INCR | \ + RCAR_AHB_BUS_MMODE_HBUS_REQ | \ + RCAR_AHB_BUS_SMODE_READYCTR) + +#define RCAR_USBCTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x34) +#define RCAR_USBCTR_USBH_RST BIT(0) +#define RCAR_USBCTR_PCICLK_MASK BIT(1) +#define RCAR_USBCTR_PLL_RST BIT(2) +#define RCAR_USBCTR_DIRPD BIT(8) +#define RCAR_USBCTR_PCIAHB_WIN2_EN BIT(9) +#define RCAR_USBCTR_PCIAHB_WIN1_256M (0 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_512M (1 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_1G (2 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_2G (3 << 10) +#define RCAR_USBCTR_PCIAHB_WIN1_MASK (3 << 10) + +#define RCAR_PCI_ARBITER_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x40) +#define RCAR_PCI_ARBITER_PCIREQ0 BIT(0) +#define RCAR_PCI_ARBITER_PCIREQ1 BIT(1) +#define RCAR_PCI_ARBITER_PCIBP_MODE BIT(12) + +#define RCAR_PCI_UNIT_REV_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x48) + +struct rcar_gen2_pci_priv { + fdt_addr_t cfg_base; + fdt_addr_t mem_base; +}; + +static int rcar_gen2_pci_addr_valid(pci_dev_t d, uint offset) +{ + u32 slot; + + if (PCI_FUNC(d)) + return -EINVAL; + + /* Only one EHCI/OHCI device built-in */ + slot = PCI_DEV(d); + if (slot > 2) + return -EINVAL; + + /* bridge logic only has registers to 0x40 */ + if (slot == 0x0 && offset >= 0x40) + return -EINVAL; + + return 0; +} + +static u32 get_bus_address(struct udevice *dev, pci_dev_t bdf, u32 offset) +{ + struct rcar_gen2_pci_priv *priv = dev_get_priv(dev); + + return priv->cfg_base + (PCI_DEV(bdf) >> 1) * 0x100 + (offset & ~3); +} + +static u32 setup_bus_address(struct udevice *dev, pci_dev_t bdf, u32 offset) +{ + struct rcar_gen2_pci_priv *priv = dev_get_priv(dev); + u32 reg; + + reg = PCI_DEV(bdf) ? RCAR_AHBPCI_WIN1_DEVICE : RCAR_AHBPCI_WIN1_HOST; + reg |= RCAR_AHBPCI_WIN_CTR_CFG; + writel(reg, priv->cfg_base + RCAR_AHBPCI_WIN1_CTR_REG); + + return get_bus_address(dev, bdf, offset); +} + +static int rcar_gen2_pci_read_config(struct udevice *dev, pci_dev_t bdf, + uint offset, ulong *value, + enum pci_size_t size) +{ + u32 addr, reg; + int ret; + + ret = rcar_gen2_pci_addr_valid(bdf, offset); + if (ret) { + *value = pci_get_ff(size); + return 0; + } + + addr = get_bus_address(dev, bdf, offset); + reg = readl(addr); + *value = pci_conv_32_to_size(reg, offset, size); + + return 0; +} + +static int rcar_gen2_pci_write_config(struct udevice *dev, pci_dev_t bdf, + uint offset, ulong value, + enum pci_size_t size) +{ + u32 addr, reg, old; + int ret; + + ret = rcar_gen2_pci_addr_valid(bdf, offset); + if (ret) + return ret; + + addr = get_bus_address(dev, bdf, offset); + + old = readl(addr); + reg = pci_conv_size_to_32(old, value, offset, size); + writel(reg, addr); + + return 0; +} + +static int rcar_gen2_pci_probe(struct udevice *dev) +{ + struct rcar_gen2_pci_priv *priv = dev_get_priv(dev); + struct clk pci_clk; + u32 devad; + int ret; + + ret = clk_get_by_index(dev, 0, &pci_clk); + if (ret) + return ret; + + ret = clk_enable(&pci_clk); + if (ret) + return ret; + + /* Clock & Reset & Direct Power Down */ + clrsetbits_le32(priv->cfg_base + RCAR_USBCTR_REG, + RCAR_USBCTR_DIRPD | RCAR_USBCTR_PCICLK_MASK | + RCAR_USBCTR_USBH_RST, + RCAR_USBCTR_PCIAHB_WIN1_1G); + clrbits_le32(priv->cfg_base + RCAR_USBCTR_REG, RCAR_USBCTR_PLL_RST); + + /* AHB-PCI Bridge Communication Registers */ + writel(RCAR_AHB_BUS_MODE, priv->cfg_base + RCAR_AHB_BUS_CTR_REG); + writel((CONFIG_SYS_SDRAM_BASE & 0xf0000000) | RCAR_PCIAHB_PREFETCH16, + priv->cfg_base + RCAR_PCIAHB_WIN1_CTR_REG); + writel(0xf0000000 | RCAR_PCIAHB_PREFETCH16, + priv->cfg_base + RCAR_PCIAHB_WIN2_CTR_REG); + writel(priv->mem_base | RCAR_AHBPCI_WIN_CTR_MEM, + priv->cfg_base + RCAR_AHBPCI_WIN2_CTR_REG); + setbits_le32(priv->cfg_base + RCAR_PCI_ARBITER_CTR_REG, + RCAR_PCI_ARBITER_PCIREQ0 | RCAR_PCI_ARBITER_PCIREQ1 | + RCAR_PCI_ARBITER_PCIBP_MODE); + + /* PCI Configuration Registers for AHBPCI */ + devad = setup_bus_address(dev, PCI_BDF(0, 0, 0), 0); + writel(priv->cfg_base + 0x800, devad + PCI_BASE_ADDRESS_0); + writel(CONFIG_SYS_SDRAM_BASE & 0xf0000000, devad + PCI_BASE_ADDRESS_1); + writel(0xf0000000, devad + PCI_BASE_ADDRESS_2); + writel(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_PARITY | PCI_COMMAND_SERR, + devad + PCI_COMMAND); + + /* PCI Configuration Registers for OHCI */ + devad = setup_bus_address(dev, PCI_BDF(0, 1, 0), 0); + writel(priv->mem_base + 0x0, devad + PCI_BASE_ADDRESS_0); + writel(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_PARITY | PCI_COMMAND_SERR, + devad + PCI_COMMAND); + + /* PCI Configuration Registers for EHCI */ + devad = setup_bus_address(dev, PCI_BDF(0, 2, 0), 0); + writel(priv->mem_base + 0x1000, devad + PCI_BASE_ADDRESS_0); + writel(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_PARITY | PCI_COMMAND_SERR, + devad + PCI_COMMAND); + + /* Enable PCI interrupt */ + setbits_le32(priv->cfg_base + RCAR_PCI_INT_ENABLE_REG, + RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME); + + return 0; +} + +static int rcar_gen2_pci_ofdata_to_platdata(struct udevice *dev) +{ + struct rcar_gen2_pci_priv *priv = dev_get_priv(dev); + + priv->cfg_base = devfdt_get_addr_index(dev, 0); + priv->mem_base = devfdt_get_addr_index(dev, 1); + if (!priv->cfg_base || !priv->mem_base) + return -EINVAL; + + return 0; +} + +static const struct dm_pci_ops rcar_gen2_pci_ops = { + .read_config = rcar_gen2_pci_read_config, + .write_config = rcar_gen2_pci_write_config, +}; + +static const struct udevice_id rcar_gen2_pci_ids[] = { + { .compatible = "renesas,pci-rcar-gen2" }, + { } +}; + +U_BOOT_DRIVER(rcar_gen2_pci) = { + .name = "rcar_gen2_pci", + .id = UCLASS_PCI, + .of_match = rcar_gen2_pci_ids, + .ops = &rcar_gen2_pci_ops, + .probe = rcar_gen2_pci_probe, + .ofdata_to_platdata = rcar_gen2_pci_ofdata_to_platdata, + .priv_auto_alloc_size = sizeof(struct rcar_gen2_pci_priv), +};