162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCIe host controller driver for Freescale Layerscape SoCs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Freescale Semiconductor. 662306a36Sopenharmony_ci * Copyright 2021 NXP 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Minghuan Lian <Minghuan.Lian@freescale.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/iopoll.h> 1662306a36Sopenharmony_ci#include <linux/of_pci.h> 1762306a36Sopenharmony_ci#include <linux/of_platform.h> 1862306a36Sopenharmony_ci#include <linux/of_address.h> 1962306a36Sopenharmony_ci#include <linux/pci.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/resource.h> 2262306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 2362306a36Sopenharmony_ci#include <linux/regmap.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "../../pci.h" 2662306a36Sopenharmony_ci#include "pcie-designware.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* PEX Internal Configuration Registers */ 2962306a36Sopenharmony_ci#define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */ 3062306a36Sopenharmony_ci#define PCIE_ABSERR 0x8d0 /* Bridge Slave Error Response Register */ 3162306a36Sopenharmony_ci#define PCIE_ABSERR_SETTING 0x9401 /* Forward error of non-posted request */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* PF Message Command Register */ 3462306a36Sopenharmony_ci#define LS_PCIE_PF_MCR 0x2c 3562306a36Sopenharmony_ci#define PF_MCR_PTOMR BIT(0) 3662306a36Sopenharmony_ci#define PF_MCR_EXL2S BIT(1) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define PCIE_IATU_NUM 6 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct ls_pcie_drvdata { 4162306a36Sopenharmony_ci const u32 pf_off; 4262306a36Sopenharmony_ci bool pm_support; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct ls_pcie { 4662306a36Sopenharmony_ci struct dw_pcie *pci; 4762306a36Sopenharmony_ci const struct ls_pcie_drvdata *drvdata; 4862306a36Sopenharmony_ci void __iomem *pf_base; 4962306a36Sopenharmony_ci bool big_endian; 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define ls_pcie_pf_readl_addr(addr) ls_pcie_pf_readl(pcie, addr) 5362306a36Sopenharmony_ci#define to_ls_pcie(x) dev_get_drvdata((x)->dev) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic bool ls_pcie_is_bridge(struct ls_pcie *pcie) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct dw_pcie *pci = pcie->pci; 5862306a36Sopenharmony_ci u32 header_type; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci header_type = ioread8(pci->dbi_base + PCI_HEADER_TYPE); 6162306a36Sopenharmony_ci header_type &= 0x7f; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return header_type == PCI_HEADER_TYPE_BRIDGE; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* Clear multi-function bit */ 6762306a36Sopenharmony_cistatic void ls_pcie_clear_multifunction(struct ls_pcie *pcie) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct dw_pcie *pci = pcie->pci; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci iowrite8(PCI_HEADER_TYPE_BRIDGE, pci->dbi_base + PCI_HEADER_TYPE); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* Drop MSG TLP except for Vendor MSG */ 7562306a36Sopenharmony_cistatic void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci u32 val; 7862306a36Sopenharmony_ci struct dw_pcie *pci = pcie->pci; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci val = ioread32(pci->dbi_base + PCIE_STRFMR1); 8162306a36Sopenharmony_ci val &= 0xDFFFFFFF; 8262306a36Sopenharmony_ci iowrite32(val, pci->dbi_base + PCIE_STRFMR1); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* Forward error response of outbound non-posted requests */ 8662306a36Sopenharmony_cistatic void ls_pcie_fix_error_response(struct ls_pcie *pcie) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct dw_pcie *pci = pcie->pci; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci iowrite32(PCIE_ABSERR_SETTING, pci->dbi_base + PCIE_ABSERR); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic u32 ls_pcie_pf_readl(struct ls_pcie *pcie, u32 off) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci if (pcie->big_endian) 9662306a36Sopenharmony_ci return ioread32be(pcie->pf_base + off); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return ioread32(pcie->pf_base + off); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic void ls_pcie_pf_writel(struct ls_pcie *pcie, u32 off, u32 val) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci if (pcie->big_endian) 10462306a36Sopenharmony_ci iowrite32be(val, pcie->pf_base + off); 10562306a36Sopenharmony_ci else 10662306a36Sopenharmony_ci iowrite32(val, pcie->pf_base + off); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void ls_pcie_send_turnoff_msg(struct dw_pcie_rp *pp) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 11262306a36Sopenharmony_ci struct ls_pcie *pcie = to_ls_pcie(pci); 11362306a36Sopenharmony_ci u32 val; 11462306a36Sopenharmony_ci int ret; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci val = ls_pcie_pf_readl(pcie, LS_PCIE_PF_MCR); 11762306a36Sopenharmony_ci val |= PF_MCR_PTOMR; 11862306a36Sopenharmony_ci ls_pcie_pf_writel(pcie, LS_PCIE_PF_MCR, val); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci ret = readx_poll_timeout(ls_pcie_pf_readl_addr, LS_PCIE_PF_MCR, 12162306a36Sopenharmony_ci val, !(val & PF_MCR_PTOMR), 12262306a36Sopenharmony_ci PCIE_PME_TO_L2_TIMEOUT_US/10, 12362306a36Sopenharmony_ci PCIE_PME_TO_L2_TIMEOUT_US); 12462306a36Sopenharmony_ci if (ret) 12562306a36Sopenharmony_ci dev_err(pcie->pci->dev, "PME_Turn_off timeout\n"); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void ls_pcie_exit_from_l2(struct dw_pcie_rp *pp) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 13162306a36Sopenharmony_ci struct ls_pcie *pcie = to_ls_pcie(pci); 13262306a36Sopenharmony_ci u32 val; 13362306a36Sopenharmony_ci int ret; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* 13662306a36Sopenharmony_ci * Set PF_MCR_EXL2S bit in LS_PCIE_PF_MCR register for the link 13762306a36Sopenharmony_ci * to exit L2 state. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci val = ls_pcie_pf_readl(pcie, LS_PCIE_PF_MCR); 14062306a36Sopenharmony_ci val |= PF_MCR_EXL2S; 14162306a36Sopenharmony_ci ls_pcie_pf_writel(pcie, LS_PCIE_PF_MCR, val); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* 14462306a36Sopenharmony_ci * L2 exit timeout of 10ms is not defined in the specifications, 14562306a36Sopenharmony_ci * it was chosen based on empirical observations. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci ret = readx_poll_timeout(ls_pcie_pf_readl_addr, LS_PCIE_PF_MCR, 14862306a36Sopenharmony_ci val, !(val & PF_MCR_EXL2S), 14962306a36Sopenharmony_ci 1000, 15062306a36Sopenharmony_ci 10000); 15162306a36Sopenharmony_ci if (ret) 15262306a36Sopenharmony_ci dev_err(pcie->pci->dev, "L2 exit timeout\n"); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int ls_pcie_host_init(struct dw_pcie_rp *pp) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 15862306a36Sopenharmony_ci struct ls_pcie *pcie = to_ls_pcie(pci); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci ls_pcie_fix_error_response(pcie); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci dw_pcie_dbi_ro_wr_en(pci); 16362306a36Sopenharmony_ci ls_pcie_clear_multifunction(pcie); 16462306a36Sopenharmony_ci dw_pcie_dbi_ro_wr_dis(pci); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci ls_pcie_drop_msg_tlp(pcie); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic const struct dw_pcie_host_ops ls_pcie_host_ops = { 17262306a36Sopenharmony_ci .host_init = ls_pcie_host_init, 17362306a36Sopenharmony_ci .pme_turn_off = ls_pcie_send_turnoff_msg, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic const struct ls_pcie_drvdata ls1021a_drvdata = { 17762306a36Sopenharmony_ci .pm_support = false, 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic const struct ls_pcie_drvdata layerscape_drvdata = { 18162306a36Sopenharmony_ci .pf_off = 0xc0000, 18262306a36Sopenharmony_ci .pm_support = true, 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic const struct of_device_id ls_pcie_of_match[] = { 18662306a36Sopenharmony_ci { .compatible = "fsl,ls1012a-pcie", .data = &layerscape_drvdata }, 18762306a36Sopenharmony_ci { .compatible = "fsl,ls1021a-pcie", .data = &ls1021a_drvdata }, 18862306a36Sopenharmony_ci { .compatible = "fsl,ls1028a-pcie", .data = &layerscape_drvdata }, 18962306a36Sopenharmony_ci { .compatible = "fsl,ls1043a-pcie", .data = &ls1021a_drvdata }, 19062306a36Sopenharmony_ci { .compatible = "fsl,ls1046a-pcie", .data = &layerscape_drvdata }, 19162306a36Sopenharmony_ci { .compatible = "fsl,ls2080a-pcie", .data = &layerscape_drvdata }, 19262306a36Sopenharmony_ci { .compatible = "fsl,ls2085a-pcie", .data = &layerscape_drvdata }, 19362306a36Sopenharmony_ci { .compatible = "fsl,ls2088a-pcie", .data = &layerscape_drvdata }, 19462306a36Sopenharmony_ci { .compatible = "fsl,ls1088a-pcie", .data = &layerscape_drvdata }, 19562306a36Sopenharmony_ci { }, 19662306a36Sopenharmony_ci}; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int ls_pcie_probe(struct platform_device *pdev) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct device *dev = &pdev->dev; 20162306a36Sopenharmony_ci struct dw_pcie *pci; 20262306a36Sopenharmony_ci struct ls_pcie *pcie; 20362306a36Sopenharmony_ci struct resource *dbi_base; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); 20662306a36Sopenharmony_ci if (!pcie) 20762306a36Sopenharmony_ci return -ENOMEM; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); 21062306a36Sopenharmony_ci if (!pci) 21162306a36Sopenharmony_ci return -ENOMEM; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci pcie->drvdata = of_device_get_match_data(dev); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci pci->dev = dev; 21662306a36Sopenharmony_ci pci->pp.ops = &ls_pcie_host_ops; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci pcie->pci = pci; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); 22162306a36Sopenharmony_ci pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base); 22262306a36Sopenharmony_ci if (IS_ERR(pci->dbi_base)) 22362306a36Sopenharmony_ci return PTR_ERR(pci->dbi_base); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci pcie->big_endian = of_property_read_bool(dev->of_node, "big-endian"); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci pcie->pf_base = pci->dbi_base + pcie->drvdata->pf_off; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (!ls_pcie_is_bridge(pcie)) 23062306a36Sopenharmony_ci return -ENODEV; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci platform_set_drvdata(pdev, pcie); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return dw_pcie_host_init(&pci->pp); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int ls_pcie_suspend_noirq(struct device *dev) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct ls_pcie *pcie = dev_get_drvdata(dev); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (!pcie->drvdata->pm_support) 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return dw_pcie_suspend_noirq(pcie->pci); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int ls_pcie_resume_noirq(struct device *dev) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct ls_pcie *pcie = dev_get_drvdata(dev); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (!pcie->drvdata->pm_support) 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ls_pcie_exit_from_l2(&pcie->pci->pp); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return dw_pcie_resume_noirq(pcie->pci); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic const struct dev_pm_ops ls_pcie_pm_ops = { 26062306a36Sopenharmony_ci NOIRQ_SYSTEM_SLEEP_PM_OPS(ls_pcie_suspend_noirq, ls_pcie_resume_noirq) 26162306a36Sopenharmony_ci}; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic struct platform_driver ls_pcie_driver = { 26462306a36Sopenharmony_ci .probe = ls_pcie_probe, 26562306a36Sopenharmony_ci .driver = { 26662306a36Sopenharmony_ci .name = "layerscape-pcie", 26762306a36Sopenharmony_ci .of_match_table = ls_pcie_of_match, 26862306a36Sopenharmony_ci .suppress_bind_attrs = true, 26962306a36Sopenharmony_ci .pm = &ls_pcie_pm_ops, 27062306a36Sopenharmony_ci }, 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_cibuiltin_platform_driver(ls_pcie_driver); 273