162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * pcie-dra7xx - PCIe controller driver for TI DRA7xx SoCs 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013-2014 Texas Instruments Incorporated - https://www.ti.com 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: Kishon Vijay Abraham I <kishon@ti.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/irq.h> 1662306a36Sopenharmony_ci#include <linux/irqdomain.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/of_gpio.h> 2162306a36Sopenharmony_ci#include <linux/of_pci.h> 2262306a36Sopenharmony_ci#include <linux/pci.h> 2362306a36Sopenharmony_ci#include <linux/phy/phy.h> 2462306a36Sopenharmony_ci#include <linux/platform_device.h> 2562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2662306a36Sopenharmony_ci#include <linux/resource.h> 2762306a36Sopenharmony_ci#include <linux/types.h> 2862306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 2962306a36Sopenharmony_ci#include <linux/regmap.h> 3062306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "../../pci.h" 3362306a36Sopenharmony_ci#include "pcie-designware.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* PCIe controller wrapper DRA7XX configuration registers */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN 0x0024 3862306a36Sopenharmony_ci#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN 0x0028 3962306a36Sopenharmony_ci#define ERR_SYS BIT(0) 4062306a36Sopenharmony_ci#define ERR_FATAL BIT(1) 4162306a36Sopenharmony_ci#define ERR_NONFATAL BIT(2) 4262306a36Sopenharmony_ci#define ERR_COR BIT(3) 4362306a36Sopenharmony_ci#define ERR_AXI BIT(4) 4462306a36Sopenharmony_ci#define ERR_ECRC BIT(5) 4562306a36Sopenharmony_ci#define PME_TURN_OFF BIT(8) 4662306a36Sopenharmony_ci#define PME_TO_ACK BIT(9) 4762306a36Sopenharmony_ci#define PM_PME BIT(10) 4862306a36Sopenharmony_ci#define LINK_REQ_RST BIT(11) 4962306a36Sopenharmony_ci#define LINK_UP_EVT BIT(12) 5062306a36Sopenharmony_ci#define CFG_BME_EVT BIT(13) 5162306a36Sopenharmony_ci#define CFG_MSE_EVT BIT(14) 5262306a36Sopenharmony_ci#define INTERRUPTS (ERR_SYS | ERR_FATAL | ERR_NONFATAL | ERR_COR | ERR_AXI | \ 5362306a36Sopenharmony_ci ERR_ECRC | PME_TURN_OFF | PME_TO_ACK | PM_PME | \ 5462306a36Sopenharmony_ci LINK_REQ_RST | LINK_UP_EVT | CFG_BME_EVT | CFG_MSE_EVT) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI 0x0034 5762306a36Sopenharmony_ci#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI 0x0038 5862306a36Sopenharmony_ci#define INTA BIT(0) 5962306a36Sopenharmony_ci#define INTB BIT(1) 6062306a36Sopenharmony_ci#define INTC BIT(2) 6162306a36Sopenharmony_ci#define INTD BIT(3) 6262306a36Sopenharmony_ci#define MSI BIT(4) 6362306a36Sopenharmony_ci#define LEG_EP_INTERRUPTS (INTA | INTB | INTC | INTD) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define PCIECTRL_TI_CONF_DEVICE_TYPE 0x0100 6662306a36Sopenharmony_ci#define DEVICE_TYPE_EP 0x0 6762306a36Sopenharmony_ci#define DEVICE_TYPE_LEG_EP 0x1 6862306a36Sopenharmony_ci#define DEVICE_TYPE_RC 0x4 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define PCIECTRL_DRA7XX_CONF_DEVICE_CMD 0x0104 7162306a36Sopenharmony_ci#define LTSSM_EN 0x1 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define PCIECTRL_DRA7XX_CONF_PHY_CS 0x010C 7462306a36Sopenharmony_ci#define LINK_UP BIT(16) 7562306a36Sopenharmony_ci#define DRA7XX_CPU_TO_BUS_ADDR 0x0FFFFFFF 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define PCIECTRL_TI_CONF_INTX_ASSERT 0x0124 7862306a36Sopenharmony_ci#define PCIECTRL_TI_CONF_INTX_DEASSERT 0x0128 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define PCIECTRL_TI_CONF_MSI_XMT 0x012c 8162306a36Sopenharmony_ci#define MSI_REQ_GRANT BIT(0) 8262306a36Sopenharmony_ci#define MSI_VECTOR_SHIFT 7 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define PCIE_1LANE_2LANE_SELECTION BIT(13) 8562306a36Sopenharmony_ci#define PCIE_B1C0_MODE_SEL BIT(2) 8662306a36Sopenharmony_ci#define PCIE_B0_B1_TSYNCEN BIT(0) 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistruct dra7xx_pcie { 8962306a36Sopenharmony_ci struct dw_pcie *pci; 9062306a36Sopenharmony_ci void __iomem *base; /* DT ti_conf */ 9162306a36Sopenharmony_ci int phy_count; /* DT phy-names count */ 9262306a36Sopenharmony_ci struct phy **phy; 9362306a36Sopenharmony_ci struct irq_domain *irq_domain; 9462306a36Sopenharmony_ci struct clk *clk; 9562306a36Sopenharmony_ci enum dw_pcie_device_mode mode; 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistruct dra7xx_pcie_of_data { 9962306a36Sopenharmony_ci enum dw_pcie_device_mode mode; 10062306a36Sopenharmony_ci u32 b1co_mode_sel_mask; 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define to_dra7xx_pcie(x) dev_get_drvdata((x)->dev) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic inline u32 dra7xx_pcie_readl(struct dra7xx_pcie *pcie, u32 offset) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci return readl(pcie->base + offset); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset, 11162306a36Sopenharmony_ci u32 value) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci writel(value, pcie->base + offset); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic u64 dra7xx_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci return pci_addr & DRA7XX_CPU_TO_BUS_ADDR; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int dra7xx_pcie_link_up(struct dw_pcie *pci) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); 12462306a36Sopenharmony_ci u32 reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return !!(reg & LINK_UP); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void dra7xx_pcie_stop_link(struct dw_pcie *pci) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); 13262306a36Sopenharmony_ci u32 reg; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); 13562306a36Sopenharmony_ci reg &= ~LTSSM_EN; 13662306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic int dra7xx_pcie_establish_link(struct dw_pcie *pci) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); 14262306a36Sopenharmony_ci struct device *dev = pci->dev; 14362306a36Sopenharmony_ci u32 reg; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (dw_pcie_link_up(pci)) { 14662306a36Sopenharmony_ci dev_err(dev, "link is already up\n"); 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); 15162306a36Sopenharmony_ci reg |= LTSSM_EN; 15262306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic void dra7xx_pcie_enable_msi_interrupts(struct dra7xx_pcie *dra7xx) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, 16062306a36Sopenharmony_ci LEG_EP_INTERRUPTS | MSI); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, 16362306a36Sopenharmony_ci PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, 16462306a36Sopenharmony_ci MSI | LEG_EP_INTERRUPTS); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void dra7xx_pcie_enable_wrapper_interrupts(struct dra7xx_pcie *dra7xx) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, 17062306a36Sopenharmony_ci INTERRUPTS); 17162306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN, 17262306a36Sopenharmony_ci INTERRUPTS); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci dra7xx_pcie_enable_wrapper_interrupts(dra7xx); 17862306a36Sopenharmony_ci dra7xx_pcie_enable_msi_interrupts(dra7xx); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int dra7xx_pcie_host_init(struct dw_pcie_rp *pp) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 18462306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci dra7xx_pcie_enable_interrupts(dra7xx); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int dra7xx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, 19262306a36Sopenharmony_ci irq_hw_number_t hwirq) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); 19562306a36Sopenharmony_ci irq_set_chip_data(irq, domain->host_data); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic const struct irq_domain_ops intx_domain_ops = { 20162306a36Sopenharmony_ci .map = dra7xx_pcie_intx_map, 20262306a36Sopenharmony_ci .xlate = pci_irqd_intx_xlate, 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic int dra7xx_pcie_handle_msi(struct dw_pcie_rp *pp, int index) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 20862306a36Sopenharmony_ci unsigned long val; 20962306a36Sopenharmony_ci int pos; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci val = dw_pcie_readl_dbi(pci, PCIE_MSI_INTR0_STATUS + 21262306a36Sopenharmony_ci (index * MSI_REG_CTRL_BLOCK_SIZE)); 21362306a36Sopenharmony_ci if (!val) 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci pos = find_first_bit(&val, MAX_MSI_IRQS_PER_CTRL); 21762306a36Sopenharmony_ci while (pos != MAX_MSI_IRQS_PER_CTRL) { 21862306a36Sopenharmony_ci generic_handle_domain_irq(pp->irq_domain, 21962306a36Sopenharmony_ci (index * MAX_MSI_IRQS_PER_CTRL) + pos); 22062306a36Sopenharmony_ci pos++; 22162306a36Sopenharmony_ci pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL, pos); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return 1; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void dra7xx_pcie_handle_msi_irq(struct dw_pcie_rp *pp) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 23062306a36Sopenharmony_ci int ret, i, count, num_ctrls; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /** 23562306a36Sopenharmony_ci * Need to make sure all MSI status bits read 0 before exiting. 23662306a36Sopenharmony_ci * Else, new MSI IRQs are not registered by the wrapper. Have an 23762306a36Sopenharmony_ci * upperbound for the loop and exit the IRQ in case of IRQ flood 23862306a36Sopenharmony_ci * to avoid locking up system in interrupt context. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci count = 0; 24162306a36Sopenharmony_ci do { 24262306a36Sopenharmony_ci ret = 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci for (i = 0; i < num_ctrls; i++) 24562306a36Sopenharmony_ci ret |= dra7xx_pcie_handle_msi(pp, i); 24662306a36Sopenharmony_ci count++; 24762306a36Sopenharmony_ci } while (ret && count <= 1000); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (count > 1000) 25062306a36Sopenharmony_ci dev_warn_ratelimited(pci->dev, 25162306a36Sopenharmony_ci "Too many MSI IRQs to handle\n"); 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic void dra7xx_pcie_msi_irq_handler(struct irq_desc *desc) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 25762306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx; 25862306a36Sopenharmony_ci struct dw_pcie_rp *pp; 25962306a36Sopenharmony_ci struct dw_pcie *pci; 26062306a36Sopenharmony_ci unsigned long reg; 26162306a36Sopenharmony_ci u32 bit; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci chained_irq_enter(chip, desc); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci pp = irq_desc_get_handler_data(desc); 26662306a36Sopenharmony_ci pci = to_dw_pcie_from_pp(pp); 26762306a36Sopenharmony_ci dra7xx = to_dra7xx_pcie(pci); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI); 27062306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, reg); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci switch (reg) { 27362306a36Sopenharmony_ci case MSI: 27462306a36Sopenharmony_ci dra7xx_pcie_handle_msi_irq(pp); 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci case INTA: 27762306a36Sopenharmony_ci case INTB: 27862306a36Sopenharmony_ci case INTC: 27962306a36Sopenharmony_ci case INTD: 28062306a36Sopenharmony_ci for_each_set_bit(bit, ®, PCI_NUM_INTX) 28162306a36Sopenharmony_ci generic_handle_domain_irq(dra7xx->irq_domain, bit); 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci chained_irq_exit(chip, desc); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx = arg; 29162306a36Sopenharmony_ci struct dw_pcie *pci = dra7xx->pci; 29262306a36Sopenharmony_ci struct device *dev = pci->dev; 29362306a36Sopenharmony_ci struct dw_pcie_ep *ep = &pci->ep; 29462306a36Sopenharmony_ci u32 reg; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (reg & ERR_SYS) 29962306a36Sopenharmony_ci dev_dbg(dev, "System Error\n"); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (reg & ERR_FATAL) 30262306a36Sopenharmony_ci dev_dbg(dev, "Fatal Error\n"); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (reg & ERR_NONFATAL) 30562306a36Sopenharmony_ci dev_dbg(dev, "Non Fatal Error\n"); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (reg & ERR_COR) 30862306a36Sopenharmony_ci dev_dbg(dev, "Correctable Error\n"); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (reg & ERR_AXI) 31162306a36Sopenharmony_ci dev_dbg(dev, "AXI tag lookup fatal Error\n"); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci if (reg & ERR_ECRC) 31462306a36Sopenharmony_ci dev_dbg(dev, "ECRC Error\n"); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (reg & PME_TURN_OFF) 31762306a36Sopenharmony_ci dev_dbg(dev, 31862306a36Sopenharmony_ci "Power Management Event Turn-Off message received\n"); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (reg & PME_TO_ACK) 32162306a36Sopenharmony_ci dev_dbg(dev, 32262306a36Sopenharmony_ci "Power Management Turn-Off Ack message received\n"); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (reg & PM_PME) 32562306a36Sopenharmony_ci dev_dbg(dev, "PM Power Management Event message received\n"); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (reg & LINK_REQ_RST) 32862306a36Sopenharmony_ci dev_dbg(dev, "Link Request Reset\n"); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (reg & LINK_UP_EVT) { 33162306a36Sopenharmony_ci if (dra7xx->mode == DW_PCIE_EP_TYPE) 33262306a36Sopenharmony_ci dw_pcie_ep_linkup(ep); 33362306a36Sopenharmony_ci dev_dbg(dev, "Link-up state change\n"); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (reg & CFG_BME_EVT) 33762306a36Sopenharmony_ci dev_dbg(dev, "CFG 'Bus Master Enable' change\n"); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (reg & CFG_MSE_EVT) 34062306a36Sopenharmony_ci dev_dbg(dev, "CFG 'Memory Space Enable' change\n"); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, reg); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return IRQ_HANDLED; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int dra7xx_pcie_init_irq_domain(struct dw_pcie_rp *pp) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 35062306a36Sopenharmony_ci struct device *dev = pci->dev; 35162306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); 35262306a36Sopenharmony_ci struct device_node *node = dev->of_node; 35362306a36Sopenharmony_ci struct device_node *pcie_intc_node = of_get_next_child(node, NULL); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (!pcie_intc_node) { 35662306a36Sopenharmony_ci dev_err(dev, "No PCIe Intc node found\n"); 35762306a36Sopenharmony_ci return -ENODEV; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci irq_set_chained_handler_and_data(pp->irq, dra7xx_pcie_msi_irq_handler, 36162306a36Sopenharmony_ci pp); 36262306a36Sopenharmony_ci dra7xx->irq_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX, 36362306a36Sopenharmony_ci &intx_domain_ops, pp); 36462306a36Sopenharmony_ci of_node_put(pcie_intc_node); 36562306a36Sopenharmony_ci if (!dra7xx->irq_domain) { 36662306a36Sopenharmony_ci dev_err(dev, "Failed to get a INTx IRQ domain\n"); 36762306a36Sopenharmony_ci return -ENODEV; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic const struct dw_pcie_host_ops dra7xx_pcie_host_ops = { 37462306a36Sopenharmony_ci .host_init = dra7xx_pcie_host_init, 37562306a36Sopenharmony_ci}; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_ep(ep); 38062306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); 38162306a36Sopenharmony_ci enum pci_barno bar; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) 38462306a36Sopenharmony_ci dw_pcie_ep_reset_bar(pci, bar); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci dra7xx_pcie_enable_wrapper_interrupts(dra7xx); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void dra7xx_pcie_raise_legacy_irq(struct dra7xx_pcie *dra7xx) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_ASSERT, 0x1); 39262306a36Sopenharmony_ci mdelay(1); 39362306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_DEASSERT, 0x1); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx, 39762306a36Sopenharmony_ci u8 interrupt_num) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci u32 reg; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci reg = (interrupt_num - 1) << MSI_VECTOR_SHIFT; 40262306a36Sopenharmony_ci reg |= MSI_REQ_GRANT; 40362306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_MSI_XMT, reg); 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, 40762306a36Sopenharmony_ci enum pci_epc_irq_type type, u16 interrupt_num) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_ep(ep); 41062306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci switch (type) { 41362306a36Sopenharmony_ci case PCI_EPC_IRQ_LEGACY: 41462306a36Sopenharmony_ci dra7xx_pcie_raise_legacy_irq(dra7xx); 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci case PCI_EPC_IRQ_MSI: 41762306a36Sopenharmony_ci dra7xx_pcie_raise_msi_irq(dra7xx, interrupt_num); 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci default: 42062306a36Sopenharmony_ci dev_err(pci->dev, "UNKNOWN IRQ type\n"); 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic const struct pci_epc_features dra7xx_pcie_epc_features = { 42762306a36Sopenharmony_ci .linkup_notifier = true, 42862306a36Sopenharmony_ci .msi_capable = true, 42962306a36Sopenharmony_ci .msix_capable = false, 43062306a36Sopenharmony_ci}; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic const struct pci_epc_features* 43362306a36Sopenharmony_cidra7xx_pcie_get_features(struct dw_pcie_ep *ep) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci return &dra7xx_pcie_epc_features; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic const struct dw_pcie_ep_ops pcie_ep_ops = { 43962306a36Sopenharmony_ci .ep_init = dra7xx_pcie_ep_init, 44062306a36Sopenharmony_ci .raise_irq = dra7xx_pcie_raise_irq, 44162306a36Sopenharmony_ci .get_features = dra7xx_pcie_get_features, 44262306a36Sopenharmony_ci}; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx, 44562306a36Sopenharmony_ci struct platform_device *pdev) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci int ret; 44862306a36Sopenharmony_ci struct dw_pcie_ep *ep; 44962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 45062306a36Sopenharmony_ci struct dw_pcie *pci = dra7xx->pci; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci ep = &pci->ep; 45362306a36Sopenharmony_ci ep->ops = &pcie_ep_ops; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci pci->dbi_base = devm_platform_ioremap_resource_byname(pdev, "ep_dbics"); 45662306a36Sopenharmony_ci if (IS_ERR(pci->dbi_base)) 45762306a36Sopenharmony_ci return PTR_ERR(pci->dbi_base); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci pci->dbi_base2 = 46062306a36Sopenharmony_ci devm_platform_ioremap_resource_byname(pdev, "ep_dbics2"); 46162306a36Sopenharmony_ci if (IS_ERR(pci->dbi_base2)) 46262306a36Sopenharmony_ci return PTR_ERR(pci->dbi_base2); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci ret = dw_pcie_ep_init(ep); 46562306a36Sopenharmony_ci if (ret) { 46662306a36Sopenharmony_ci dev_err(dev, "failed to initialize endpoint\n"); 46762306a36Sopenharmony_ci return ret; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return 0; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic int dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx, 47462306a36Sopenharmony_ci struct platform_device *pdev) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci int ret; 47762306a36Sopenharmony_ci struct dw_pcie *pci = dra7xx->pci; 47862306a36Sopenharmony_ci struct dw_pcie_rp *pp = &pci->pp; 47962306a36Sopenharmony_ci struct device *dev = pci->dev; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci pp->irq = platform_get_irq(pdev, 1); 48262306a36Sopenharmony_ci if (pp->irq < 0) 48362306a36Sopenharmony_ci return pp->irq; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* MSI IRQ is muxed */ 48662306a36Sopenharmony_ci pp->msi_irq[0] = -ENODEV; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci ret = dra7xx_pcie_init_irq_domain(pp); 48962306a36Sopenharmony_ci if (ret < 0) 49062306a36Sopenharmony_ci return ret; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci pci->dbi_base = devm_platform_ioremap_resource_byname(pdev, "rc_dbics"); 49362306a36Sopenharmony_ci if (IS_ERR(pci->dbi_base)) 49462306a36Sopenharmony_ci return PTR_ERR(pci->dbi_base); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci pp->ops = &dra7xx_pcie_host_ops; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci ret = dw_pcie_host_init(pp); 49962306a36Sopenharmony_ci if (ret) { 50062306a36Sopenharmony_ci dev_err(dev, "failed to initialize host\n"); 50162306a36Sopenharmony_ci return ret; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic const struct dw_pcie_ops dw_pcie_ops = { 50862306a36Sopenharmony_ci .cpu_addr_fixup = dra7xx_pcie_cpu_addr_fixup, 50962306a36Sopenharmony_ci .start_link = dra7xx_pcie_establish_link, 51062306a36Sopenharmony_ci .stop_link = dra7xx_pcie_stop_link, 51162306a36Sopenharmony_ci .link_up = dra7xx_pcie_link_up, 51262306a36Sopenharmony_ci}; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic void dra7xx_pcie_disable_phy(struct dra7xx_pcie *dra7xx) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci int phy_count = dra7xx->phy_count; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci while (phy_count--) { 51962306a36Sopenharmony_ci phy_power_off(dra7xx->phy[phy_count]); 52062306a36Sopenharmony_ci phy_exit(dra7xx->phy[phy_count]); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic int dra7xx_pcie_enable_phy(struct dra7xx_pcie *dra7xx) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci int phy_count = dra7xx->phy_count; 52762306a36Sopenharmony_ci int ret; 52862306a36Sopenharmony_ci int i; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci for (i = 0; i < phy_count; i++) { 53162306a36Sopenharmony_ci ret = phy_set_mode(dra7xx->phy[i], PHY_MODE_PCIE); 53262306a36Sopenharmony_ci if (ret < 0) 53362306a36Sopenharmony_ci goto err_phy; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci ret = phy_init(dra7xx->phy[i]); 53662306a36Sopenharmony_ci if (ret < 0) 53762306a36Sopenharmony_ci goto err_phy; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci ret = phy_power_on(dra7xx->phy[i]); 54062306a36Sopenharmony_ci if (ret < 0) { 54162306a36Sopenharmony_ci phy_exit(dra7xx->phy[i]); 54262306a36Sopenharmony_ci goto err_phy; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci return 0; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_cierr_phy: 54962306a36Sopenharmony_ci while (--i >= 0) { 55062306a36Sopenharmony_ci phy_power_off(dra7xx->phy[i]); 55162306a36Sopenharmony_ci phy_exit(dra7xx->phy[i]); 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return ret; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_cistatic const struct dra7xx_pcie_of_data dra7xx_pcie_rc_of_data = { 55862306a36Sopenharmony_ci .mode = DW_PCIE_RC_TYPE, 55962306a36Sopenharmony_ci}; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_cistatic const struct dra7xx_pcie_of_data dra7xx_pcie_ep_of_data = { 56262306a36Sopenharmony_ci .mode = DW_PCIE_EP_TYPE, 56362306a36Sopenharmony_ci}; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic const struct dra7xx_pcie_of_data dra746_pcie_rc_of_data = { 56662306a36Sopenharmony_ci .b1co_mode_sel_mask = BIT(2), 56762306a36Sopenharmony_ci .mode = DW_PCIE_RC_TYPE, 56862306a36Sopenharmony_ci}; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cistatic const struct dra7xx_pcie_of_data dra726_pcie_rc_of_data = { 57162306a36Sopenharmony_ci .b1co_mode_sel_mask = GENMASK(3, 2), 57262306a36Sopenharmony_ci .mode = DW_PCIE_RC_TYPE, 57362306a36Sopenharmony_ci}; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistatic const struct dra7xx_pcie_of_data dra746_pcie_ep_of_data = { 57662306a36Sopenharmony_ci .b1co_mode_sel_mask = BIT(2), 57762306a36Sopenharmony_ci .mode = DW_PCIE_EP_TYPE, 57862306a36Sopenharmony_ci}; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic const struct dra7xx_pcie_of_data dra726_pcie_ep_of_data = { 58162306a36Sopenharmony_ci .b1co_mode_sel_mask = GENMASK(3, 2), 58262306a36Sopenharmony_ci .mode = DW_PCIE_EP_TYPE, 58362306a36Sopenharmony_ci}; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic const struct of_device_id of_dra7xx_pcie_match[] = { 58662306a36Sopenharmony_ci { 58762306a36Sopenharmony_ci .compatible = "ti,dra7-pcie", 58862306a36Sopenharmony_ci .data = &dra7xx_pcie_rc_of_data, 58962306a36Sopenharmony_ci }, 59062306a36Sopenharmony_ci { 59162306a36Sopenharmony_ci .compatible = "ti,dra7-pcie-ep", 59262306a36Sopenharmony_ci .data = &dra7xx_pcie_ep_of_data, 59362306a36Sopenharmony_ci }, 59462306a36Sopenharmony_ci { 59562306a36Sopenharmony_ci .compatible = "ti,dra746-pcie-rc", 59662306a36Sopenharmony_ci .data = &dra746_pcie_rc_of_data, 59762306a36Sopenharmony_ci }, 59862306a36Sopenharmony_ci { 59962306a36Sopenharmony_ci .compatible = "ti,dra726-pcie-rc", 60062306a36Sopenharmony_ci .data = &dra726_pcie_rc_of_data, 60162306a36Sopenharmony_ci }, 60262306a36Sopenharmony_ci { 60362306a36Sopenharmony_ci .compatible = "ti,dra746-pcie-ep", 60462306a36Sopenharmony_ci .data = &dra746_pcie_ep_of_data, 60562306a36Sopenharmony_ci }, 60662306a36Sopenharmony_ci { 60762306a36Sopenharmony_ci .compatible = "ti,dra726-pcie-ep", 60862306a36Sopenharmony_ci .data = &dra726_pcie_ep_of_data, 60962306a36Sopenharmony_ci }, 61062306a36Sopenharmony_ci {}, 61162306a36Sopenharmony_ci}; 61262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_dra7xx_pcie_match); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci/* 61562306a36Sopenharmony_ci * dra7xx_pcie_unaligned_memaccess: workaround for AM572x/AM571x Errata i870 61662306a36Sopenharmony_ci * @dra7xx: the dra7xx device where the workaround should be applied 61762306a36Sopenharmony_ci * 61862306a36Sopenharmony_ci * Access to the PCIe slave port that are not 32-bit aligned will result 61962306a36Sopenharmony_ci * in incorrect mapping to TLP Address and Byte enable fields. Therefore, 62062306a36Sopenharmony_ci * byte and half-word accesses are not possible to byte offset 0x1, 0x2, or 62162306a36Sopenharmony_ci * 0x3. 62262306a36Sopenharmony_ci * 62362306a36Sopenharmony_ci * To avoid this issue set PCIE_SS1_AXI2OCP_LEGACY_MODE_ENABLE to 1. 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_cistatic int dra7xx_pcie_unaligned_memaccess(struct device *dev) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci int ret; 62862306a36Sopenharmony_ci struct device_node *np = dev->of_node; 62962306a36Sopenharmony_ci struct of_phandle_args args; 63062306a36Sopenharmony_ci struct regmap *regmap; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci regmap = syscon_regmap_lookup_by_phandle(np, 63362306a36Sopenharmony_ci "ti,syscon-unaligned-access"); 63462306a36Sopenharmony_ci if (IS_ERR(regmap)) { 63562306a36Sopenharmony_ci dev_dbg(dev, "can't get ti,syscon-unaligned-access\n"); 63662306a36Sopenharmony_ci return -EINVAL; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci ret = of_parse_phandle_with_fixed_args(np, "ti,syscon-unaligned-access", 64062306a36Sopenharmony_ci 2, 0, &args); 64162306a36Sopenharmony_ci if (ret) { 64262306a36Sopenharmony_ci dev_err(dev, "failed to parse ti,syscon-unaligned-access\n"); 64362306a36Sopenharmony_ci return ret; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci ret = regmap_update_bits(regmap, args.args[0], args.args[1], 64762306a36Sopenharmony_ci args.args[1]); 64862306a36Sopenharmony_ci if (ret) 64962306a36Sopenharmony_ci dev_err(dev, "failed to enable unaligned access\n"); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci of_node_put(args.np); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci return ret; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic int dra7xx_pcie_configure_two_lane(struct device *dev, 65762306a36Sopenharmony_ci u32 b1co_mode_sel_mask) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci struct device_node *np = dev->of_node; 66062306a36Sopenharmony_ci struct regmap *pcie_syscon; 66162306a36Sopenharmony_ci unsigned int pcie_reg; 66262306a36Sopenharmony_ci u32 mask; 66362306a36Sopenharmony_ci u32 val; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci pcie_syscon = syscon_regmap_lookup_by_phandle(np, "ti,syscon-lane-sel"); 66662306a36Sopenharmony_ci if (IS_ERR(pcie_syscon)) { 66762306a36Sopenharmony_ci dev_err(dev, "unable to get ti,syscon-lane-sel\n"); 66862306a36Sopenharmony_ci return -EINVAL; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (of_property_read_u32_index(np, "ti,syscon-lane-sel", 1, 67262306a36Sopenharmony_ci &pcie_reg)) { 67362306a36Sopenharmony_ci dev_err(dev, "couldn't get lane selection reg offset\n"); 67462306a36Sopenharmony_ci return -EINVAL; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci mask = b1co_mode_sel_mask | PCIE_B0_B1_TSYNCEN; 67862306a36Sopenharmony_ci val = PCIE_B1C0_MODE_SEL | PCIE_B0_B1_TSYNCEN; 67962306a36Sopenharmony_ci regmap_update_bits(pcie_syscon, pcie_reg, mask, val); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic int dra7xx_pcie_probe(struct platform_device *pdev) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci u32 reg; 68762306a36Sopenharmony_ci int ret; 68862306a36Sopenharmony_ci int irq; 68962306a36Sopenharmony_ci int i; 69062306a36Sopenharmony_ci int phy_count; 69162306a36Sopenharmony_ci struct phy **phy; 69262306a36Sopenharmony_ci struct device_link **link; 69362306a36Sopenharmony_ci void __iomem *base; 69462306a36Sopenharmony_ci struct dw_pcie *pci; 69562306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx; 69662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 69762306a36Sopenharmony_ci struct device_node *np = dev->of_node; 69862306a36Sopenharmony_ci char name[10]; 69962306a36Sopenharmony_ci struct gpio_desc *reset; 70062306a36Sopenharmony_ci const struct dra7xx_pcie_of_data *data; 70162306a36Sopenharmony_ci enum dw_pcie_device_mode mode; 70262306a36Sopenharmony_ci u32 b1co_mode_sel_mask; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci data = of_device_get_match_data(dev); 70562306a36Sopenharmony_ci if (!data) 70662306a36Sopenharmony_ci return -EINVAL; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci mode = (enum dw_pcie_device_mode)data->mode; 70962306a36Sopenharmony_ci b1co_mode_sel_mask = data->b1co_mode_sel_mask; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL); 71262306a36Sopenharmony_ci if (!dra7xx) 71362306a36Sopenharmony_ci return -ENOMEM; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); 71662306a36Sopenharmony_ci if (!pci) 71762306a36Sopenharmony_ci return -ENOMEM; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci pci->dev = dev; 72062306a36Sopenharmony_ci pci->ops = &dw_pcie_ops; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 72362306a36Sopenharmony_ci if (irq < 0) 72462306a36Sopenharmony_ci return irq; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci base = devm_platform_ioremap_resource_byname(pdev, "ti_conf"); 72762306a36Sopenharmony_ci if (IS_ERR(base)) 72862306a36Sopenharmony_ci return PTR_ERR(base); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci phy_count = of_property_count_strings(np, "phy-names"); 73162306a36Sopenharmony_ci if (phy_count < 0) { 73262306a36Sopenharmony_ci dev_err(dev, "unable to find the strings\n"); 73362306a36Sopenharmony_ci return phy_count; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci phy = devm_kcalloc(dev, phy_count, sizeof(*phy), GFP_KERNEL); 73762306a36Sopenharmony_ci if (!phy) 73862306a36Sopenharmony_ci return -ENOMEM; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci link = devm_kcalloc(dev, phy_count, sizeof(*link), GFP_KERNEL); 74162306a36Sopenharmony_ci if (!link) 74262306a36Sopenharmony_ci return -ENOMEM; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci dra7xx->clk = devm_clk_get_optional(dev, NULL); 74562306a36Sopenharmony_ci if (IS_ERR(dra7xx->clk)) 74662306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(dra7xx->clk), 74762306a36Sopenharmony_ci "clock request failed"); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci ret = clk_prepare_enable(dra7xx->clk); 75062306a36Sopenharmony_ci if (ret) 75162306a36Sopenharmony_ci return ret; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci for (i = 0; i < phy_count; i++) { 75462306a36Sopenharmony_ci snprintf(name, sizeof(name), "pcie-phy%d", i); 75562306a36Sopenharmony_ci phy[i] = devm_phy_get(dev, name); 75662306a36Sopenharmony_ci if (IS_ERR(phy[i])) 75762306a36Sopenharmony_ci return PTR_ERR(phy[i]); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS); 76062306a36Sopenharmony_ci if (!link[i]) { 76162306a36Sopenharmony_ci ret = -EINVAL; 76262306a36Sopenharmony_ci goto err_link; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci dra7xx->base = base; 76762306a36Sopenharmony_ci dra7xx->phy = phy; 76862306a36Sopenharmony_ci dra7xx->pci = pci; 76962306a36Sopenharmony_ci dra7xx->phy_count = phy_count; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (phy_count == 2) { 77262306a36Sopenharmony_ci ret = dra7xx_pcie_configure_two_lane(dev, b1co_mode_sel_mask); 77362306a36Sopenharmony_ci if (ret < 0) 77462306a36Sopenharmony_ci dra7xx->phy_count = 1; /* Fallback to x1 lane mode */ 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci ret = dra7xx_pcie_enable_phy(dra7xx); 77862306a36Sopenharmony_ci if (ret) { 77962306a36Sopenharmony_ci dev_err(dev, "failed to enable phy\n"); 78062306a36Sopenharmony_ci return ret; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci platform_set_drvdata(pdev, dra7xx); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci pm_runtime_enable(dev); 78662306a36Sopenharmony_ci ret = pm_runtime_get_sync(dev); 78762306a36Sopenharmony_ci if (ret < 0) { 78862306a36Sopenharmony_ci dev_err(dev, "pm_runtime_get_sync failed\n"); 78962306a36Sopenharmony_ci goto err_get_sync; 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci reset = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); 79362306a36Sopenharmony_ci if (IS_ERR(reset)) { 79462306a36Sopenharmony_ci ret = PTR_ERR(reset); 79562306a36Sopenharmony_ci dev_err(&pdev->dev, "gpio request failed, ret %d\n", ret); 79662306a36Sopenharmony_ci goto err_gpio; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); 80062306a36Sopenharmony_ci reg &= ~LTSSM_EN; 80162306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci switch (mode) { 80462306a36Sopenharmony_ci case DW_PCIE_RC_TYPE: 80562306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_PCI_DRA7XX_HOST)) { 80662306a36Sopenharmony_ci ret = -ENODEV; 80762306a36Sopenharmony_ci goto err_gpio; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE, 81162306a36Sopenharmony_ci DEVICE_TYPE_RC); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci ret = dra7xx_pcie_unaligned_memaccess(dev); 81462306a36Sopenharmony_ci if (ret) 81562306a36Sopenharmony_ci dev_err(dev, "WA for Errata i870 not applied\n"); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci ret = dra7xx_add_pcie_port(dra7xx, pdev); 81862306a36Sopenharmony_ci if (ret < 0) 81962306a36Sopenharmony_ci goto err_gpio; 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci case DW_PCIE_EP_TYPE: 82262306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_PCI_DRA7XX_EP)) { 82362306a36Sopenharmony_ci ret = -ENODEV; 82462306a36Sopenharmony_ci goto err_gpio; 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE, 82862306a36Sopenharmony_ci DEVICE_TYPE_EP); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci ret = dra7xx_pcie_unaligned_memaccess(dev); 83162306a36Sopenharmony_ci if (ret) 83262306a36Sopenharmony_ci goto err_gpio; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci ret = dra7xx_add_pcie_ep(dra7xx, pdev); 83562306a36Sopenharmony_ci if (ret < 0) 83662306a36Sopenharmony_ci goto err_gpio; 83762306a36Sopenharmony_ci break; 83862306a36Sopenharmony_ci default: 83962306a36Sopenharmony_ci dev_err(dev, "INVALID device type %d\n", mode); 84062306a36Sopenharmony_ci } 84162306a36Sopenharmony_ci dra7xx->mode = mode; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, irq, NULL, dra7xx_pcie_irq_handler, 84462306a36Sopenharmony_ci IRQF_SHARED, "dra7xx-pcie-main", dra7xx); 84562306a36Sopenharmony_ci if (ret) { 84662306a36Sopenharmony_ci dev_err(dev, "failed to request irq\n"); 84762306a36Sopenharmony_ci goto err_gpio; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci return 0; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cierr_gpio: 85362306a36Sopenharmony_cierr_get_sync: 85462306a36Sopenharmony_ci pm_runtime_put(dev); 85562306a36Sopenharmony_ci pm_runtime_disable(dev); 85662306a36Sopenharmony_ci dra7xx_pcie_disable_phy(dra7xx); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cierr_link: 85962306a36Sopenharmony_ci while (--i >= 0) 86062306a36Sopenharmony_ci device_link_del(link[i]); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci return ret; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic int dra7xx_pcie_suspend(struct device *dev) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); 86862306a36Sopenharmony_ci struct dw_pcie *pci = dra7xx->pci; 86962306a36Sopenharmony_ci u32 val; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (dra7xx->mode != DW_PCIE_RC_TYPE) 87262306a36Sopenharmony_ci return 0; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci /* clear MSE */ 87562306a36Sopenharmony_ci val = dw_pcie_readl_dbi(pci, PCI_COMMAND); 87662306a36Sopenharmony_ci val &= ~PCI_COMMAND_MEMORY; 87762306a36Sopenharmony_ci dw_pcie_writel_dbi(pci, PCI_COMMAND, val); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci return 0; 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_cistatic int dra7xx_pcie_resume(struct device *dev) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); 88562306a36Sopenharmony_ci struct dw_pcie *pci = dra7xx->pci; 88662306a36Sopenharmony_ci u32 val; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (dra7xx->mode != DW_PCIE_RC_TYPE) 88962306a36Sopenharmony_ci return 0; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci /* set MSE */ 89262306a36Sopenharmony_ci val = dw_pcie_readl_dbi(pci, PCI_COMMAND); 89362306a36Sopenharmony_ci val |= PCI_COMMAND_MEMORY; 89462306a36Sopenharmony_ci dw_pcie_writel_dbi(pci, PCI_COMMAND, val); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci return 0; 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic int dra7xx_pcie_suspend_noirq(struct device *dev) 90062306a36Sopenharmony_ci{ 90162306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci dra7xx_pcie_disable_phy(dra7xx); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci return 0; 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic int dra7xx_pcie_resume_noirq(struct device *dev) 90962306a36Sopenharmony_ci{ 91062306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); 91162306a36Sopenharmony_ci int ret; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci ret = dra7xx_pcie_enable_phy(dra7xx); 91462306a36Sopenharmony_ci if (ret) { 91562306a36Sopenharmony_ci dev_err(dev, "failed to enable phy\n"); 91662306a36Sopenharmony_ci return ret; 91762306a36Sopenharmony_ci } 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci return 0; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic void dra7xx_pcie_shutdown(struct platform_device *pdev) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 92562306a36Sopenharmony_ci struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev); 92662306a36Sopenharmony_ci int ret; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci dra7xx_pcie_stop_link(dra7xx->pci); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci ret = pm_runtime_put_sync(dev); 93162306a36Sopenharmony_ci if (ret < 0) 93262306a36Sopenharmony_ci dev_dbg(dev, "pm_runtime_put_sync failed\n"); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci pm_runtime_disable(dev); 93562306a36Sopenharmony_ci dra7xx_pcie_disable_phy(dra7xx); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci clk_disable_unprepare(dra7xx->clk); 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic const struct dev_pm_ops dra7xx_pcie_pm_ops = { 94162306a36Sopenharmony_ci SYSTEM_SLEEP_PM_OPS(dra7xx_pcie_suspend, dra7xx_pcie_resume) 94262306a36Sopenharmony_ci NOIRQ_SYSTEM_SLEEP_PM_OPS(dra7xx_pcie_suspend_noirq, 94362306a36Sopenharmony_ci dra7xx_pcie_resume_noirq) 94462306a36Sopenharmony_ci}; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic struct platform_driver dra7xx_pcie_driver = { 94762306a36Sopenharmony_ci .probe = dra7xx_pcie_probe, 94862306a36Sopenharmony_ci .driver = { 94962306a36Sopenharmony_ci .name = "dra7-pcie", 95062306a36Sopenharmony_ci .of_match_table = of_dra7xx_pcie_match, 95162306a36Sopenharmony_ci .suppress_bind_attrs = true, 95262306a36Sopenharmony_ci .pm = &dra7xx_pcie_pm_ops, 95362306a36Sopenharmony_ci }, 95462306a36Sopenharmony_ci .shutdown = dra7xx_pcie_shutdown, 95562306a36Sopenharmony_ci}; 95662306a36Sopenharmony_cimodule_platform_driver(dra7xx_pcie_driver); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ciMODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>"); 95962306a36Sopenharmony_ciMODULE_DESCRIPTION("PCIe controller driver for TI DRA7xx SoCs"); 96062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 961