mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-25 14:10:43 +00:00
313360b13f
Only PCI device 1 and 2 is populated on the R-Car Gen2 internal PCIe controller. Ignore all other devices. This fix prevents a duplication of OHCI controller response on slot 0 and 1. Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com>
263 lines
7.7 KiB
C
263 lines
7.7 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Renesas RCar Gen2 PCIEC driver
|
|
*
|
|
* Copyright (C) 2018 Marek Vasut <marek.vasut@gmail.com>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <asm/io.h>
|
|
#include <clk.h>
|
|
#include <dm.h>
|
|
#include <errno.h>
|
|
#include <pci.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(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),
|
|
};
|