// SPDX-License-Identifier: GPL-2.0 /* * MediaTek PCIe host controller driver. * * Copyright (c) 2017-2019 MediaTek Inc. * Author: Ryder Lee * Honghui Zhang */ #include #include #include #include #include #include #include #include #include /* PCIe shared registers */ #define PCIE_SYS_CFG 0x00 #define PCIE_INT_ENABLE 0x0c #define PCIE_CFG_ADDR 0x20 #define PCIE_CFG_DATA 0x24 /* PCIe per port registers */ #define PCIE_BAR0_SETUP 0x10 #define PCIE_CLASS 0x34 #define PCIE_LINK_STATUS 0x50 #define PCIE_PORT_INT_EN(x) BIT(20 + (x)) #define PCIE_PORT_PERST(x) BIT(1 + (x)) #define PCIE_PORT_LINKUP BIT(0) #define PCIE_BAR_MAP_MAX GENMASK(31, 16) #define PCIE_BAR_ENABLE BIT(0) #define PCIE_REVISION_ID BIT(0) #define PCIE_CLASS_CODE (0x60400 << 8) #define PCIE_CONF_REG(regn) (((regn) & GENMASK(7, 2)) | \ ((((regn) >> 8) & GENMASK(3, 0)) << 24)) #define PCIE_CONF_ADDR(regn, bdf) \ (PCIE_CONF_REG(regn) | (bdf)) /* MediaTek specific configuration registers */ #define PCIE_FTS_NUM 0x70c #define PCIE_FTS_NUM_MASK GENMASK(15, 8) #define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8) #define PCIE_FC_CREDIT 0x73c #define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16)) #define PCIE_FC_CREDIT_VAL(x) ((x) << 16) struct mtk_pcie_port { void __iomem *base; struct list_head list; struct mtk_pcie *pcie; struct reset_ctl reset; struct clk sys_ck; struct phy phy; u32 slot; }; struct mtk_pcie { void __iomem *base; struct clk free_ck; struct list_head ports; }; static int mtk_pcie_config_address(const struct udevice *udev, pci_dev_t bdf, uint offset, void **paddress) { struct mtk_pcie *pcie = dev_get_priv(udev); writel(PCIE_CONF_ADDR(offset, bdf), pcie->base + PCIE_CFG_ADDR); *paddress = pcie->base + PCIE_CFG_DATA + (offset & 3); return 0; } static int mtk_pcie_read_config(const struct udevice *bus, pci_dev_t bdf, uint offset, ulong *valuep, enum pci_size_t size) { return pci_generic_mmap_read_config(bus, mtk_pcie_config_address, bdf, offset, valuep, size); } static int mtk_pcie_write_config(struct udevice *bus, pci_dev_t bdf, uint offset, ulong value, enum pci_size_t size) { return pci_generic_mmap_write_config(bus, mtk_pcie_config_address, bdf, offset, value, size); } static const struct dm_pci_ops mtk_pcie_ops = { .read_config = mtk_pcie_read_config, .write_config = mtk_pcie_write_config, }; static void mtk_pcie_port_free(struct mtk_pcie_port *port) { list_del(&port->list); free(port); } static int mtk_pcie_startup_port(struct mtk_pcie_port *port) { struct mtk_pcie *pcie = port->pcie; u32 slot = PCI_DEV(port->slot << 11); u32 val; int err; /* assert port PERST_N */ setbits_le32(pcie->base + PCIE_SYS_CFG, PCIE_PORT_PERST(port->slot)); /* de-assert port PERST_N */ clrbits_le32(pcie->base + PCIE_SYS_CFG, PCIE_PORT_PERST(port->slot)); /* 100ms timeout value should be enough for Gen1/2 training */ err = readl_poll_timeout(port->base + PCIE_LINK_STATUS, val, !!(val & PCIE_PORT_LINKUP), 100000); if (err) return -ETIMEDOUT; /* disable interrupt */ clrbits_le32(pcie->base + PCIE_INT_ENABLE, PCIE_PORT_INT_EN(port->slot)); /* map to all DDR region. We need to set it before cfg operation. */ writel(PCIE_BAR_MAP_MAX | PCIE_BAR_ENABLE, port->base + PCIE_BAR0_SETUP); /* configure class code and revision ID */ writel(PCIE_CLASS_CODE | PCIE_REVISION_ID, port->base + PCIE_CLASS); /* configure FC credit */ writel(PCIE_CONF_ADDR(PCIE_FC_CREDIT, slot), pcie->base + PCIE_CFG_ADDR); clrsetbits_le32(pcie->base + PCIE_CFG_DATA, PCIE_FC_CREDIT_MASK, PCIE_FC_CREDIT_VAL(0x806c)); /* configure RC FTS number to 250 when it leaves L0s */ writel(PCIE_CONF_ADDR(PCIE_FTS_NUM, slot), pcie->base + PCIE_CFG_ADDR); clrsetbits_le32(pcie->base + PCIE_CFG_DATA, PCIE_FTS_NUM_MASK, PCIE_FTS_NUM_L0(0x50)); return 0; } static void mtk_pcie_enable_port(struct mtk_pcie_port *port) { int err; err = clk_enable(&port->sys_ck); if (err) goto exit; err = reset_assert(&port->reset); if (err) goto exit; err = reset_deassert(&port->reset); if (err) goto exit; err = generic_phy_init(&port->phy); if (err) goto exit; err = generic_phy_power_on(&port->phy); if (err) goto exit; if (!mtk_pcie_startup_port(port)) return; pr_err("Port%d link down\n", port->slot); exit: mtk_pcie_port_free(port); } static int mtk_pcie_parse_port(struct udevice *dev, u32 slot) { struct mtk_pcie *pcie = dev_get_priv(dev); struct mtk_pcie_port *port; char name[10]; int err; port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); if (!port) return -ENOMEM; snprintf(name, sizeof(name), "port%d", slot); port->base = dev_remap_addr_name(dev, name); if (!port->base) return -ENOENT; snprintf(name, sizeof(name), "sys_ck%d", slot); err = clk_get_by_name(dev, name, &port->sys_ck); if (err) return err; err = reset_get_by_index(dev, slot, &port->reset); if (err) return err; err = generic_phy_get_by_index(dev, slot, &port->phy); if (err) return err; port->slot = slot; port->pcie = pcie; INIT_LIST_HEAD(&port->list); list_add_tail(&port->list, &pcie->ports); return 0; } static int mtk_pcie_probe(struct udevice *dev) { struct mtk_pcie *pcie = dev_get_priv(dev); struct mtk_pcie_port *port, *tmp; ofnode subnode; int err; INIT_LIST_HEAD(&pcie->ports); pcie->base = dev_remap_addr_name(dev, "subsys"); if (!pcie->base) return -ENOENT; err = clk_get_by_name(dev, "free_ck", &pcie->free_ck); if (err) return err; /* enable top level clock */ err = clk_enable(&pcie->free_ck); if (err) return err; dev_for_each_subnode(subnode, dev) { struct fdt_pci_addr addr; u32 slot = 0; if (!ofnode_is_available(subnode)) continue; err = ofnode_read_pci_addr(subnode, 0, "reg", &addr); if (err) return err; slot = PCI_DEV(addr.phys_hi); err = mtk_pcie_parse_port(dev, slot); if (err) return err; } /* enable each port, and then check link status */ list_for_each_entry_safe(port, tmp, &pcie->ports, list) mtk_pcie_enable_port(port); return 0; } static const struct udevice_id mtk_pcie_ids[] = { { .compatible = "mediatek,mt7623-pcie", }, { } }; U_BOOT_DRIVER(pcie_mediatek) = { .name = "pcie_mediatek", .id = UCLASS_PCI, .of_match = mtk_pcie_ids, .ops = &mtk_pcie_ops, .probe = mtk_pcie_probe, .priv_auto_alloc_size = sizeof(struct mtk_pcie), };