| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Renesas RCar Gen2 PCIEC driver |
| * |
| * Copyright (C) 2018 Marek Vasut <marek.vasut@gmail.com> |
| */ |
| |
| #include <config.h> |
| #include <asm/io.h> |
| #include <clk.h> |
| #include <dm.h> |
| #include <errno.h> |
| #include <pci.h> |
| #include <linux/bitops.h> |
| |
| /* 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 != 1 && 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(const 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(const 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((CFG_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(CFG_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_of_to_plat(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, |
| .of_to_plat = rcar_gen2_pci_of_to_plat, |
| .priv_auto = sizeof(struct rcar_gen2_pci_priv), |
| }; |