16e0832faSShawn Lin // SPDX-License-Identifier: GPL-2.0
26e0832faSShawn Lin /*
36e0832faSShawn Lin  * pcie-dra7xx - PCIe controller driver for TI DRA7xx SoCs
46e0832faSShawn Lin  *
57ecd4a81SAlexander A. Klimov  * Copyright (C) 2013-2014 Texas Instruments Incorporated - https://www.ti.com
66e0832faSShawn Lin  *
76e0832faSShawn Lin  * Authors: Kishon Vijay Abraham I <kishon@ti.com>
86e0832faSShawn Lin  */
96e0832faSShawn Lin 
105af94053SLuca Ceresoli #include <linux/clk.h>
116e0832faSShawn Lin #include <linux/delay.h>
126e0832faSShawn Lin #include <linux/device.h>
136e0832faSShawn Lin #include <linux/err.h>
146e0832faSShawn Lin #include <linux/interrupt.h>
156e0832faSShawn Lin #include <linux/irq.h>
166e0832faSShawn Lin #include <linux/irqdomain.h>
176e0832faSShawn Lin #include <linux/kernel.h>
183b868d15SLuca Ceresoli #include <linux/module.h>
19*c925cfafSRob Herring #include <linux/of.h>
206e0832faSShawn Lin #include <linux/of_gpio.h>
216e0832faSShawn Lin #include <linux/of_pci.h>
226e0832faSShawn Lin #include <linux/pci.h>
236e0832faSShawn Lin #include <linux/phy/phy.h>
246e0832faSShawn Lin #include <linux/platform_device.h>
256e0832faSShawn Lin #include <linux/pm_runtime.h>
266e0832faSShawn Lin #include <linux/resource.h>
276e0832faSShawn Lin #include <linux/types.h>
286e0832faSShawn Lin #include <linux/mfd/syscon.h>
296e0832faSShawn Lin #include <linux/regmap.h>
30381ed79cSYueHaibing #include <linux/gpio/consumer.h>
316e0832faSShawn Lin 
326e0832faSShawn Lin #include "../../pci.h"
336e0832faSShawn Lin #include "pcie-designware.h"
346e0832faSShawn Lin 
356e0832faSShawn Lin /* PCIe controller wrapper DRA7XX configuration registers */
366e0832faSShawn Lin 
376e0832faSShawn Lin #define	PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN		0x0024
386e0832faSShawn Lin #define	PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN		0x0028
396e0832faSShawn Lin #define	ERR_SYS						BIT(0)
406e0832faSShawn Lin #define	ERR_FATAL					BIT(1)
416e0832faSShawn Lin #define	ERR_NONFATAL					BIT(2)
426e0832faSShawn Lin #define	ERR_COR						BIT(3)
436e0832faSShawn Lin #define	ERR_AXI						BIT(4)
446e0832faSShawn Lin #define	ERR_ECRC					BIT(5)
456e0832faSShawn Lin #define	PME_TURN_OFF					BIT(8)
466e0832faSShawn Lin #define	PME_TO_ACK					BIT(9)
476e0832faSShawn Lin #define	PM_PME						BIT(10)
486e0832faSShawn Lin #define	LINK_REQ_RST					BIT(11)
496e0832faSShawn Lin #define	LINK_UP_EVT					BIT(12)
506e0832faSShawn Lin #define	CFG_BME_EVT					BIT(13)
516e0832faSShawn Lin #define	CFG_MSE_EVT					BIT(14)
526e0832faSShawn Lin #define	INTERRUPTS (ERR_SYS | ERR_FATAL | ERR_NONFATAL | ERR_COR | ERR_AXI | \
536e0832faSShawn Lin 			ERR_ECRC | PME_TURN_OFF | PME_TO_ACK | PM_PME | \
546e0832faSShawn Lin 			LINK_REQ_RST | LINK_UP_EVT | CFG_BME_EVT | CFG_MSE_EVT)
556e0832faSShawn Lin 
566e0832faSShawn Lin #define	PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI		0x0034
576e0832faSShawn Lin #define	PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI		0x0038
586e0832faSShawn Lin #define	INTA						BIT(0)
596e0832faSShawn Lin #define	INTB						BIT(1)
606e0832faSShawn Lin #define	INTC						BIT(2)
616e0832faSShawn Lin #define	INTD						BIT(3)
626e0832faSShawn Lin #define	MSI						BIT(4)
636e0832faSShawn Lin #define	LEG_EP_INTERRUPTS (INTA | INTB | INTC | INTD)
646e0832faSShawn Lin 
656e0832faSShawn Lin #define	PCIECTRL_TI_CONF_DEVICE_TYPE			0x0100
666e0832faSShawn Lin #define	DEVICE_TYPE_EP					0x0
676e0832faSShawn Lin #define	DEVICE_TYPE_LEG_EP				0x1
686e0832faSShawn Lin #define	DEVICE_TYPE_RC					0x4
696e0832faSShawn Lin 
706e0832faSShawn Lin #define	PCIECTRL_DRA7XX_CONF_DEVICE_CMD			0x0104
716e0832faSShawn Lin #define	LTSSM_EN					0x1
726e0832faSShawn Lin 
736e0832faSShawn Lin #define	PCIECTRL_DRA7XX_CONF_PHY_CS			0x010C
746e0832faSShawn Lin #define	LINK_UP						BIT(16)
756e0832faSShawn Lin #define	DRA7XX_CPU_TO_BUS_ADDR				0x0FFFFFFF
766e0832faSShawn Lin 
776e0832faSShawn Lin #define	PCIECTRL_TI_CONF_INTX_ASSERT			0x0124
786e0832faSShawn Lin #define	PCIECTRL_TI_CONF_INTX_DEASSERT			0x0128
796e0832faSShawn Lin 
806e0832faSShawn Lin #define	PCIECTRL_TI_CONF_MSI_XMT			0x012c
816e0832faSShawn Lin #define MSI_REQ_GRANT					BIT(0)
826e0832faSShawn Lin #define MSI_VECTOR_SHIFT				7
836e0832faSShawn Lin 
84c232c0dfSKishon Vijay Abraham I #define PCIE_1LANE_2LANE_SELECTION			BIT(13)
85c232c0dfSKishon Vijay Abraham I #define PCIE_B1C0_MODE_SEL				BIT(2)
86c232c0dfSKishon Vijay Abraham I #define PCIE_B0_B1_TSYNCEN				BIT(0)
87c232c0dfSKishon Vijay Abraham I 
886e0832faSShawn Lin struct dra7xx_pcie {
896e0832faSShawn Lin 	struct dw_pcie		*pci;
906e0832faSShawn Lin 	void __iomem		*base;		/* DT ti_conf */
916e0832faSShawn Lin 	int			phy_count;	/* DT phy-names count */
926e0832faSShawn Lin 	struct phy		**phy;
936e0832faSShawn Lin 	struct irq_domain	*irq_domain;
945af94053SLuca Ceresoli 	struct clk              *clk;
956e0832faSShawn Lin 	enum dw_pcie_device_mode mode;
966e0832faSShawn Lin };
976e0832faSShawn Lin 
986e0832faSShawn Lin struct dra7xx_pcie_of_data {
996e0832faSShawn Lin 	enum dw_pcie_device_mode mode;
100c232c0dfSKishon Vijay Abraham I 	u32 b1co_mode_sel_mask;
1016e0832faSShawn Lin };
1026e0832faSShawn Lin 
1036e0832faSShawn Lin #define to_dra7xx_pcie(x)	dev_get_drvdata((x)->dev)
1046e0832faSShawn Lin 
dra7xx_pcie_readl(struct dra7xx_pcie * pcie,u32 offset)1056e0832faSShawn Lin static inline u32 dra7xx_pcie_readl(struct dra7xx_pcie *pcie, u32 offset)
1066e0832faSShawn Lin {
1076e0832faSShawn Lin 	return readl(pcie->base + offset);
1086e0832faSShawn Lin }
1096e0832faSShawn Lin 
dra7xx_pcie_writel(struct dra7xx_pcie * pcie,u32 offset,u32 value)1106e0832faSShawn Lin static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset,
1116e0832faSShawn Lin 				      u32 value)
1126e0832faSShawn Lin {
1136e0832faSShawn Lin 	writel(value, pcie->base + offset);
1146e0832faSShawn Lin }
1156e0832faSShawn Lin 
dra7xx_pcie_cpu_addr_fixup(struct dw_pcie * pci,u64 pci_addr)1166e0832faSShawn Lin static u64 dra7xx_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr)
1176e0832faSShawn Lin {
1186e0832faSShawn Lin 	return pci_addr & DRA7XX_CPU_TO_BUS_ADDR;
1196e0832faSShawn Lin }
1206e0832faSShawn Lin 
dra7xx_pcie_link_up(struct dw_pcie * pci)1216e0832faSShawn Lin static int dra7xx_pcie_link_up(struct dw_pcie *pci)
1226e0832faSShawn Lin {
1236e0832faSShawn Lin 	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
1246e0832faSShawn Lin 	u32 reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS);
1256e0832faSShawn Lin 
1266e0832faSShawn Lin 	return !!(reg & LINK_UP);
1276e0832faSShawn Lin }
1286e0832faSShawn Lin 
dra7xx_pcie_stop_link(struct dw_pcie * pci)1296e0832faSShawn Lin static void dra7xx_pcie_stop_link(struct dw_pcie *pci)
1306e0832faSShawn Lin {
1316e0832faSShawn Lin 	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
1326e0832faSShawn Lin 	u32 reg;
1336e0832faSShawn Lin 
1346e0832faSShawn Lin 	reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD);
1356e0832faSShawn Lin 	reg &= ~LTSSM_EN;
1366e0832faSShawn Lin 	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
1376e0832faSShawn Lin }
1386e0832faSShawn Lin 
dra7xx_pcie_establish_link(struct dw_pcie * pci)1396e0832faSShawn Lin static int dra7xx_pcie_establish_link(struct dw_pcie *pci)
1406e0832faSShawn Lin {
1416e0832faSShawn Lin 	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
1426e0832faSShawn Lin 	struct device *dev = pci->dev;
1436e0832faSShawn Lin 	u32 reg;
1446e0832faSShawn Lin 
1456e0832faSShawn Lin 	if (dw_pcie_link_up(pci)) {
1466e0832faSShawn Lin 		dev_err(dev, "link is already up\n");
1476e0832faSShawn Lin 		return 0;
1486e0832faSShawn Lin 	}
1496e0832faSShawn Lin 
1506e0832faSShawn Lin 	reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD);
1516e0832faSShawn Lin 	reg |= LTSSM_EN;
1526e0832faSShawn Lin 	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
1536e0832faSShawn Lin 
1546e0832faSShawn Lin 	return 0;
1556e0832faSShawn Lin }
1566e0832faSShawn Lin 
dra7xx_pcie_enable_msi_interrupts(struct dra7xx_pcie * dra7xx)1576e0832faSShawn Lin static void dra7xx_pcie_enable_msi_interrupts(struct dra7xx_pcie *dra7xx)
1586e0832faSShawn Lin {
1596e0832faSShawn Lin 	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI,
1606e0832faSShawn Lin 			   LEG_EP_INTERRUPTS | MSI);
1616e0832faSShawn Lin 
1626e0832faSShawn Lin 	dra7xx_pcie_writel(dra7xx,
1636e0832faSShawn Lin 			   PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI,
1646e0832faSShawn Lin 			   MSI | LEG_EP_INTERRUPTS);
1656e0832faSShawn Lin }
1666e0832faSShawn Lin 
dra7xx_pcie_enable_wrapper_interrupts(struct dra7xx_pcie * dra7xx)1676e0832faSShawn Lin static void dra7xx_pcie_enable_wrapper_interrupts(struct dra7xx_pcie *dra7xx)
1686e0832faSShawn Lin {
1696e0832faSShawn Lin 	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN,
1706e0832faSShawn Lin 			   INTERRUPTS);
1716e0832faSShawn Lin 	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN,
1726e0832faSShawn Lin 			   INTERRUPTS);
1736e0832faSShawn Lin }
1746e0832faSShawn Lin 
dra7xx_pcie_enable_interrupts(struct dra7xx_pcie * dra7xx)1756e0832faSShawn Lin static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx)
1766e0832faSShawn Lin {
1776e0832faSShawn Lin 	dra7xx_pcie_enable_wrapper_interrupts(dra7xx);
1786e0832faSShawn Lin 	dra7xx_pcie_enable_msi_interrupts(dra7xx);
1796e0832faSShawn Lin }
1806e0832faSShawn Lin 
dra7xx_pcie_host_init(struct dw_pcie_rp * pp)18160b3c27fSSerge Semin static int dra7xx_pcie_host_init(struct dw_pcie_rp *pp)
1826e0832faSShawn Lin {
1836e0832faSShawn Lin 	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
1846e0832faSShawn Lin 	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
1856e0832faSShawn Lin 
1866e0832faSShawn Lin 	dra7xx_pcie_enable_interrupts(dra7xx);
1876e0832faSShawn Lin 
1886e0832faSShawn Lin 	return 0;
1896e0832faSShawn Lin }
1906e0832faSShawn Lin 
dra7xx_pcie_intx_map(struct irq_domain * domain,unsigned int irq,irq_hw_number_t hwirq)1916e0832faSShawn Lin static int dra7xx_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
1926e0832faSShawn Lin 				irq_hw_number_t hwirq)
1936e0832faSShawn Lin {
1946e0832faSShawn Lin 	irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
1956e0832faSShawn Lin 	irq_set_chip_data(irq, domain->host_data);
1966e0832faSShawn Lin 
1976e0832faSShawn Lin 	return 0;
1986e0832faSShawn Lin }
1996e0832faSShawn Lin 
2006e0832faSShawn Lin static const struct irq_domain_ops intx_domain_ops = {
2016e0832faSShawn Lin 	.map = dra7xx_pcie_intx_map,
2026e0832faSShawn Lin 	.xlate = pci_irqd_intx_xlate,
2036e0832faSShawn Lin };
2046e0832faSShawn Lin 
dra7xx_pcie_handle_msi(struct dw_pcie_rp * pp,int index)20560b3c27fSSerge Semin static int dra7xx_pcie_handle_msi(struct dw_pcie_rp *pp, int index)
2066e0832faSShawn Lin {
2076e0832faSShawn Lin 	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
2089a5595abSVignesh Raghavendra 	unsigned long val;
209d21faba1SMarc Zyngier 	int pos;
2106e0832faSShawn Lin 
2119a5595abSVignesh Raghavendra 	val = dw_pcie_readl_dbi(pci, PCIE_MSI_INTR0_STATUS +
2129a5595abSVignesh Raghavendra 				   (index * MSI_REG_CTRL_BLOCK_SIZE));
2139a5595abSVignesh Raghavendra 	if (!val)
2146e0832faSShawn Lin 		return 0;
2159a5595abSVignesh Raghavendra 
216b5c7e7ecSYury Norov 	pos = find_first_bit(&val, MAX_MSI_IRQS_PER_CTRL);
2179a5595abSVignesh Raghavendra 	while (pos != MAX_MSI_IRQS_PER_CTRL) {
218d21faba1SMarc Zyngier 		generic_handle_domain_irq(pp->irq_domain,
2199a5595abSVignesh Raghavendra 					  (index * MAX_MSI_IRQS_PER_CTRL) + pos);
2209a5595abSVignesh Raghavendra 		pos++;
2219a5595abSVignesh Raghavendra 		pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL, pos);
2226e0832faSShawn Lin 	}
2236e0832faSShawn Lin 
2249a5595abSVignesh Raghavendra 	return 1;
2259a5595abSVignesh Raghavendra }
2269a5595abSVignesh Raghavendra 
dra7xx_pcie_handle_msi_irq(struct dw_pcie_rp * pp)22760b3c27fSSerge Semin static void dra7xx_pcie_handle_msi_irq(struct dw_pcie_rp *pp)
2286e0832faSShawn Lin {
2299a5595abSVignesh Raghavendra 	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
2309a5595abSVignesh Raghavendra 	int ret, i, count, num_ctrls;
2319a5595abSVignesh Raghavendra 
2329a5595abSVignesh Raghavendra 	num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;
2339a5595abSVignesh Raghavendra 
2349a5595abSVignesh Raghavendra 	/**
2359a5595abSVignesh Raghavendra 	 * Need to make sure all MSI status bits read 0 before exiting.
2369a5595abSVignesh Raghavendra 	 * Else, new MSI IRQs are not registered by the wrapper. Have an
2379a5595abSVignesh Raghavendra 	 * upperbound for the loop and exit the IRQ in case of IRQ flood
2389a5595abSVignesh Raghavendra 	 * to avoid locking up system in interrupt context.
2399a5595abSVignesh Raghavendra 	 */
2409a5595abSVignesh Raghavendra 	count = 0;
2419a5595abSVignesh Raghavendra 	do {
2429a5595abSVignesh Raghavendra 		ret = 0;
2439a5595abSVignesh Raghavendra 
2449a5595abSVignesh Raghavendra 		for (i = 0; i < num_ctrls; i++)
2459a5595abSVignesh Raghavendra 			ret |= dra7xx_pcie_handle_msi(pp, i);
2469a5595abSVignesh Raghavendra 		count++;
2479a5595abSVignesh Raghavendra 	} while (ret && count <= 1000);
2489a5595abSVignesh Raghavendra 
2499a5595abSVignesh Raghavendra 	if (count > 1000)
2509a5595abSVignesh Raghavendra 		dev_warn_ratelimited(pci->dev,
2519a5595abSVignesh Raghavendra 				     "Too many MSI IRQs to handle\n");
2529a5595abSVignesh Raghavendra }
2539a5595abSVignesh Raghavendra 
dra7xx_pcie_msi_irq_handler(struct irq_desc * desc)2549a5595abSVignesh Raghavendra static void dra7xx_pcie_msi_irq_handler(struct irq_desc *desc)
2559a5595abSVignesh Raghavendra {
2569a5595abSVignesh Raghavendra 	struct irq_chip *chip = irq_desc_get_chip(desc);
2579a5595abSVignesh Raghavendra 	struct dra7xx_pcie *dra7xx;
25860b3c27fSSerge Semin 	struct dw_pcie_rp *pp;
2599a5595abSVignesh Raghavendra 	struct dw_pcie *pci;
2606e0832faSShawn Lin 	unsigned long reg;
261d21faba1SMarc Zyngier 	u32 bit;
2626e0832faSShawn Lin 
2639a5595abSVignesh Raghavendra 	chained_irq_enter(chip, desc);
2649a5595abSVignesh Raghavendra 
2659a5595abSVignesh Raghavendra 	pp = irq_desc_get_handler_data(desc);
2669a5595abSVignesh Raghavendra 	pci = to_dw_pcie_from_pp(pp);
2679a5595abSVignesh Raghavendra 	dra7xx = to_dra7xx_pcie(pci);
2689a5595abSVignesh Raghavendra 
2696e0832faSShawn Lin 	reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI);
2709a5595abSVignesh Raghavendra 	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, reg);
2716e0832faSShawn Lin 
2726e0832faSShawn Lin 	switch (reg) {
2736e0832faSShawn Lin 	case MSI:
2749a5595abSVignesh Raghavendra 		dra7xx_pcie_handle_msi_irq(pp);
2756e0832faSShawn Lin 		break;
2766e0832faSShawn Lin 	case INTA:
2776e0832faSShawn Lin 	case INTB:
2786e0832faSShawn Lin 	case INTC:
2796e0832faSShawn Lin 	case INTD:
280d21faba1SMarc Zyngier 		for_each_set_bit(bit, &reg, PCI_NUM_INTX)
281d21faba1SMarc Zyngier 			generic_handle_domain_irq(dra7xx->irq_domain, bit);
2826e0832faSShawn Lin 		break;
2836e0832faSShawn Lin 	}
2846e0832faSShawn Lin 
2859a5595abSVignesh Raghavendra 	chained_irq_exit(chip, desc);
2866e0832faSShawn Lin }
2876e0832faSShawn Lin 
dra7xx_pcie_irq_handler(int irq,void * arg)2886e0832faSShawn Lin static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)
2896e0832faSShawn Lin {
2906e0832faSShawn Lin 	struct dra7xx_pcie *dra7xx = arg;
2916e0832faSShawn Lin 	struct dw_pcie *pci = dra7xx->pci;
2926e0832faSShawn Lin 	struct device *dev = pci->dev;
2936e0832faSShawn Lin 	struct dw_pcie_ep *ep = &pci->ep;
2946e0832faSShawn Lin 	u32 reg;
2956e0832faSShawn Lin 
2966e0832faSShawn Lin 	reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN);
2976e0832faSShawn Lin 
2986e0832faSShawn Lin 	if (reg & ERR_SYS)
2996e0832faSShawn Lin 		dev_dbg(dev, "System Error\n");
3006e0832faSShawn Lin 
3016e0832faSShawn Lin 	if (reg & ERR_FATAL)
3026e0832faSShawn Lin 		dev_dbg(dev, "Fatal Error\n");
3036e0832faSShawn Lin 
3046e0832faSShawn Lin 	if (reg & ERR_NONFATAL)
3056e0832faSShawn Lin 		dev_dbg(dev, "Non Fatal Error\n");
3066e0832faSShawn Lin 
3076e0832faSShawn Lin 	if (reg & ERR_COR)
3086e0832faSShawn Lin 		dev_dbg(dev, "Correctable Error\n");
3096e0832faSShawn Lin 
3106e0832faSShawn Lin 	if (reg & ERR_AXI)
3116e0832faSShawn Lin 		dev_dbg(dev, "AXI tag lookup fatal Error\n");
3126e0832faSShawn Lin 
3136e0832faSShawn Lin 	if (reg & ERR_ECRC)
3146e0832faSShawn Lin 		dev_dbg(dev, "ECRC Error\n");
3156e0832faSShawn Lin 
3166e0832faSShawn Lin 	if (reg & PME_TURN_OFF)
3176e0832faSShawn Lin 		dev_dbg(dev,
3186e0832faSShawn Lin 			"Power Management Event Turn-Off message received\n");
3196e0832faSShawn Lin 
3206e0832faSShawn Lin 	if (reg & PME_TO_ACK)
3216e0832faSShawn Lin 		dev_dbg(dev,
3226e0832faSShawn Lin 			"Power Management Turn-Off Ack message received\n");
3236e0832faSShawn Lin 
3246e0832faSShawn Lin 	if (reg & PM_PME)
3256e0832faSShawn Lin 		dev_dbg(dev, "PM Power Management Event message received\n");
3266e0832faSShawn Lin 
3276e0832faSShawn Lin 	if (reg & LINK_REQ_RST)
3286e0832faSShawn Lin 		dev_dbg(dev, "Link Request Reset\n");
3296e0832faSShawn Lin 
3306e0832faSShawn Lin 	if (reg & LINK_UP_EVT) {
3316e0832faSShawn Lin 		if (dra7xx->mode == DW_PCIE_EP_TYPE)
3326e0832faSShawn Lin 			dw_pcie_ep_linkup(ep);
3336e0832faSShawn Lin 		dev_dbg(dev, "Link-up state change\n");
3346e0832faSShawn Lin 	}
3356e0832faSShawn Lin 
3366e0832faSShawn Lin 	if (reg & CFG_BME_EVT)
3376e0832faSShawn Lin 		dev_dbg(dev, "CFG 'Bus Master Enable' change\n");
3386e0832faSShawn Lin 
3396e0832faSShawn Lin 	if (reg & CFG_MSE_EVT)
3406e0832faSShawn Lin 		dev_dbg(dev, "CFG 'Memory Space Enable' change\n");
3416e0832faSShawn Lin 
3426e0832faSShawn Lin 	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, reg);
3436e0832faSShawn Lin 
3446e0832faSShawn Lin 	return IRQ_HANDLED;
3456e0832faSShawn Lin }
3466e0832faSShawn Lin 
dra7xx_pcie_init_irq_domain(struct dw_pcie_rp * pp)34760b3c27fSSerge Semin static int dra7xx_pcie_init_irq_domain(struct dw_pcie_rp *pp)
3489a5595abSVignesh Raghavendra {
3499a5595abSVignesh Raghavendra 	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
3509a5595abSVignesh Raghavendra 	struct device *dev = pci->dev;
3519a5595abSVignesh Raghavendra 	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
3529a5595abSVignesh Raghavendra 	struct device_node *node = dev->of_node;
3539a5595abSVignesh Raghavendra 	struct device_node *pcie_intc_node =  of_get_next_child(node, NULL);
3549a5595abSVignesh Raghavendra 
3559a5595abSVignesh Raghavendra 	if (!pcie_intc_node) {
3569a5595abSVignesh Raghavendra 		dev_err(dev, "No PCIe Intc node found\n");
3579a5595abSVignesh Raghavendra 		return -ENODEV;
3589a5595abSVignesh Raghavendra 	}
3599a5595abSVignesh Raghavendra 
3609a5595abSVignesh Raghavendra 	irq_set_chained_handler_and_data(pp->irq, dra7xx_pcie_msi_irq_handler,
3619a5595abSVignesh Raghavendra 					 pp);
3629a5595abSVignesh Raghavendra 	dra7xx->irq_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
3639a5595abSVignesh Raghavendra 						   &intx_domain_ops, pp);
3649a5595abSVignesh Raghavendra 	of_node_put(pcie_intc_node);
3659a5595abSVignesh Raghavendra 	if (!dra7xx->irq_domain) {
3669a5595abSVignesh Raghavendra 		dev_err(dev, "Failed to get a INTx IRQ domain\n");
3679a5595abSVignesh Raghavendra 		return -ENODEV;
3689a5595abSVignesh Raghavendra 	}
3699a5595abSVignesh Raghavendra 
3709a5595abSVignesh Raghavendra 	return 0;
3719a5595abSVignesh Raghavendra }
3729a5595abSVignesh Raghavendra 
3739a5595abSVignesh Raghavendra static const struct dw_pcie_host_ops dra7xx_pcie_host_ops = {
3749a5595abSVignesh Raghavendra 	.host_init = dra7xx_pcie_host_init,
3759a5595abSVignesh Raghavendra };
3769a5595abSVignesh Raghavendra 
dra7xx_pcie_ep_init(struct dw_pcie_ep * ep)3776e0832faSShawn Lin static void dra7xx_pcie_ep_init(struct dw_pcie_ep *ep)
3786e0832faSShawn Lin {
3796e0832faSShawn Lin 	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
3806e0832faSShawn Lin 	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
3816e0832faSShawn Lin 	enum pci_barno bar;
3826e0832faSShawn Lin 
383c9c13ba4SDenis Efremov 	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++)
3846e0832faSShawn Lin 		dw_pcie_ep_reset_bar(pci, bar);
3856e0832faSShawn Lin 
3866e0832faSShawn Lin 	dra7xx_pcie_enable_wrapper_interrupts(dra7xx);
3876e0832faSShawn Lin }
3886e0832faSShawn Lin 
dra7xx_pcie_raise_legacy_irq(struct dra7xx_pcie * dra7xx)3896e0832faSShawn Lin static void dra7xx_pcie_raise_legacy_irq(struct dra7xx_pcie *dra7xx)
3906e0832faSShawn Lin {
3916e0832faSShawn Lin 	dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_ASSERT, 0x1);
3926e0832faSShawn Lin 	mdelay(1);
3936e0832faSShawn Lin 	dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_INTX_DEASSERT, 0x1);
3946e0832faSShawn Lin }
3956e0832faSShawn Lin 
dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie * dra7xx,u8 interrupt_num)3966e0832faSShawn Lin static void dra7xx_pcie_raise_msi_irq(struct dra7xx_pcie *dra7xx,
3976e0832faSShawn Lin 				      u8 interrupt_num)
3986e0832faSShawn Lin {
3996e0832faSShawn Lin 	u32 reg;
4006e0832faSShawn Lin 
4016e0832faSShawn Lin 	reg = (interrupt_num - 1) << MSI_VECTOR_SHIFT;
4026e0832faSShawn Lin 	reg |= MSI_REQ_GRANT;
4036e0832faSShawn Lin 	dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_MSI_XMT, reg);
4046e0832faSShawn Lin }
4056e0832faSShawn Lin 
dra7xx_pcie_raise_irq(struct dw_pcie_ep * ep,u8 func_no,enum pci_epc_irq_type type,u16 interrupt_num)4066e0832faSShawn Lin static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
407d3c70a98SGustavo Pimentel 				 enum pci_epc_irq_type type, u16 interrupt_num)
4086e0832faSShawn Lin {
4096e0832faSShawn Lin 	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
4106e0832faSShawn Lin 	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci);
4116e0832faSShawn Lin 
4126e0832faSShawn Lin 	switch (type) {
4136e0832faSShawn Lin 	case PCI_EPC_IRQ_LEGACY:
4146e0832faSShawn Lin 		dra7xx_pcie_raise_legacy_irq(dra7xx);
4156e0832faSShawn Lin 		break;
4166e0832faSShawn Lin 	case PCI_EPC_IRQ_MSI:
4176e0832faSShawn Lin 		dra7xx_pcie_raise_msi_irq(dra7xx, interrupt_num);
4186e0832faSShawn Lin 		break;
4196e0832faSShawn Lin 	default:
4206e0832faSShawn Lin 		dev_err(pci->dev, "UNKNOWN IRQ type\n");
4216e0832faSShawn Lin 	}
4226e0832faSShawn Lin 
4236e0832faSShawn Lin 	return 0;
4246e0832faSShawn Lin }
4256e0832faSShawn Lin 
4264894467eSKishon Vijay Abraham I static const struct pci_epc_features dra7xx_pcie_epc_features = {
4274894467eSKishon Vijay Abraham I 	.linkup_notifier = true,
4284894467eSKishon Vijay Abraham I 	.msi_capable = true,
4294894467eSKishon Vijay Abraham I 	.msix_capable = false,
4304894467eSKishon Vijay Abraham I };
4314894467eSKishon Vijay Abraham I 
4324894467eSKishon Vijay Abraham I static const struct pci_epc_features*
dra7xx_pcie_get_features(struct dw_pcie_ep * ep)4334894467eSKishon Vijay Abraham I dra7xx_pcie_get_features(struct dw_pcie_ep *ep)
4344894467eSKishon Vijay Abraham I {
4354894467eSKishon Vijay Abraham I 	return &dra7xx_pcie_epc_features;
4364894467eSKishon Vijay Abraham I }
4374894467eSKishon Vijay Abraham I 
438626961ddSKishon Vijay Abraham I static const struct dw_pcie_ep_ops pcie_ep_ops = {
4396e0832faSShawn Lin 	.ep_init = dra7xx_pcie_ep_init,
4406e0832faSShawn Lin 	.raise_irq = dra7xx_pcie_raise_irq,
4414894467eSKishon Vijay Abraham I 	.get_features = dra7xx_pcie_get_features,
4426e0832faSShawn Lin };
4436e0832faSShawn Lin 
dra7xx_add_pcie_ep(struct dra7xx_pcie * dra7xx,struct platform_device * pdev)444e259c292STony Lindgren static int dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx,
4456e0832faSShawn Lin 			      struct platform_device *pdev)
4466e0832faSShawn Lin {
4476e0832faSShawn Lin 	int ret;
4486e0832faSShawn Lin 	struct dw_pcie_ep *ep;
4496e0832faSShawn Lin 	struct device *dev = &pdev->dev;
4506e0832faSShawn Lin 	struct dw_pcie *pci = dra7xx->pci;
4516e0832faSShawn Lin 
4526e0832faSShawn Lin 	ep = &pci->ep;
4536e0832faSShawn Lin 	ep->ops = &pcie_ep_ops;
4546e0832faSShawn Lin 
455936fa5cdSDejin Zheng 	pci->dbi_base = devm_platform_ioremap_resource_byname(pdev, "ep_dbics");
4566e0832faSShawn Lin 	if (IS_ERR(pci->dbi_base))
4576e0832faSShawn Lin 		return PTR_ERR(pci->dbi_base);
4586e0832faSShawn Lin 
459936fa5cdSDejin Zheng 	pci->dbi_base2 =
460936fa5cdSDejin Zheng 		devm_platform_ioremap_resource_byname(pdev, "ep_dbics2");
4616e0832faSShawn Lin 	if (IS_ERR(pci->dbi_base2))
4626e0832faSShawn Lin 		return PTR_ERR(pci->dbi_base2);
4636e0832faSShawn Lin 
4646e0832faSShawn Lin 	ret = dw_pcie_ep_init(ep);
4656e0832faSShawn Lin 	if (ret) {
4666e0832faSShawn Lin 		dev_err(dev, "failed to initialize endpoint\n");
4676e0832faSShawn Lin 		return ret;
4686e0832faSShawn Lin 	}
4696e0832faSShawn Lin 
4706e0832faSShawn Lin 	return 0;
4716e0832faSShawn Lin }
4726e0832faSShawn Lin 
dra7xx_add_pcie_port(struct dra7xx_pcie * dra7xx,struct platform_device * pdev)473e259c292STony Lindgren static int dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
4746e0832faSShawn Lin 				struct platform_device *pdev)
4756e0832faSShawn Lin {
4766e0832faSShawn Lin 	int ret;
4776e0832faSShawn Lin 	struct dw_pcie *pci = dra7xx->pci;
47860b3c27fSSerge Semin 	struct dw_pcie_rp *pp = &pci->pp;
4796e0832faSShawn Lin 	struct device *dev = pci->dev;
4806e0832faSShawn Lin 
4816e0832faSShawn Lin 	pp->irq = platform_get_irq(pdev, 1);
482caecb05cSKrzysztof Wilczyński 	if (pp->irq < 0)
4836e0832faSShawn Lin 		return pp->irq;
4846e0832faSShawn Lin 
4855bcb1757SRob Herring 	/* MSI IRQ is muxed */
486db388348SDmitry Baryshkov 	pp->msi_irq[0] = -ENODEV;
4875bcb1757SRob Herring 
4886e0832faSShawn Lin 	ret = dra7xx_pcie_init_irq_domain(pp);
4896e0832faSShawn Lin 	if (ret < 0)
4906e0832faSShawn Lin 		return ret;
4916e0832faSShawn Lin 
492936fa5cdSDejin Zheng 	pci->dbi_base = devm_platform_ioremap_resource_byname(pdev, "rc_dbics");
4936e0832faSShawn Lin 	if (IS_ERR(pci->dbi_base))
4946e0832faSShawn Lin 		return PTR_ERR(pci->dbi_base);
4956e0832faSShawn Lin 
4966e0832faSShawn Lin 	pp->ops = &dra7xx_pcie_host_ops;
4976e0832faSShawn Lin 
4986e0832faSShawn Lin 	ret = dw_pcie_host_init(pp);
4996e0832faSShawn Lin 	if (ret) {
5006e0832faSShawn Lin 		dev_err(dev, "failed to initialize host\n");
5016e0832faSShawn Lin 		return ret;
5026e0832faSShawn Lin 	}
5036e0832faSShawn Lin 
5046e0832faSShawn Lin 	return 0;
5056e0832faSShawn Lin }
5066e0832faSShawn Lin 
5076e0832faSShawn Lin static const struct dw_pcie_ops dw_pcie_ops = {
5086e0832faSShawn Lin 	.cpu_addr_fixup = dra7xx_pcie_cpu_addr_fixup,
5096e0832faSShawn Lin 	.start_link = dra7xx_pcie_establish_link,
5106e0832faSShawn Lin 	.stop_link = dra7xx_pcie_stop_link,
5116e0832faSShawn Lin 	.link_up = dra7xx_pcie_link_up,
5126e0832faSShawn Lin };
5136e0832faSShawn Lin 
dra7xx_pcie_disable_phy(struct dra7xx_pcie * dra7xx)5146e0832faSShawn Lin static void dra7xx_pcie_disable_phy(struct dra7xx_pcie *dra7xx)
5156e0832faSShawn Lin {
5166e0832faSShawn Lin 	int phy_count = dra7xx->phy_count;
5176e0832faSShawn Lin 
5186e0832faSShawn Lin 	while (phy_count--) {
5196e0832faSShawn Lin 		phy_power_off(dra7xx->phy[phy_count]);
5206e0832faSShawn Lin 		phy_exit(dra7xx->phy[phy_count]);
5216e0832faSShawn Lin 	}
5226e0832faSShawn Lin }
5236e0832faSShawn Lin 
dra7xx_pcie_enable_phy(struct dra7xx_pcie * dra7xx)5246e0832faSShawn Lin static int dra7xx_pcie_enable_phy(struct dra7xx_pcie *dra7xx)
5256e0832faSShawn Lin {
5266e0832faSShawn Lin 	int phy_count = dra7xx->phy_count;
5276e0832faSShawn Lin 	int ret;
5286e0832faSShawn Lin 	int i;
5296e0832faSShawn Lin 
5306e0832faSShawn Lin 	for (i = 0; i < phy_count; i++) {
53165c839a1SKishon Vijay Abraham I 		ret = phy_set_mode(dra7xx->phy[i], PHY_MODE_PCIE);
53265c839a1SKishon Vijay Abraham I 		if (ret < 0)
53365c839a1SKishon Vijay Abraham I 			goto err_phy;
53465c839a1SKishon Vijay Abraham I 
5356e0832faSShawn Lin 		ret = phy_init(dra7xx->phy[i]);
5366e0832faSShawn Lin 		if (ret < 0)
5376e0832faSShawn Lin 			goto err_phy;
5386e0832faSShawn Lin 
5396e0832faSShawn Lin 		ret = phy_power_on(dra7xx->phy[i]);
5406e0832faSShawn Lin 		if (ret < 0) {
5416e0832faSShawn Lin 			phy_exit(dra7xx->phy[i]);
5426e0832faSShawn Lin 			goto err_phy;
5436e0832faSShawn Lin 		}
5446e0832faSShawn Lin 	}
5456e0832faSShawn Lin 
5466e0832faSShawn Lin 	return 0;
5476e0832faSShawn Lin 
5486e0832faSShawn Lin err_phy:
5496e0832faSShawn Lin 	while (--i >= 0) {
5506e0832faSShawn Lin 		phy_power_off(dra7xx->phy[i]);
5516e0832faSShawn Lin 		phy_exit(dra7xx->phy[i]);
5526e0832faSShawn Lin 	}
5536e0832faSShawn Lin 
5546e0832faSShawn Lin 	return ret;
5556e0832faSShawn Lin }
5566e0832faSShawn Lin 
5576e0832faSShawn Lin static const struct dra7xx_pcie_of_data dra7xx_pcie_rc_of_data = {
5586e0832faSShawn Lin 	.mode = DW_PCIE_RC_TYPE,
5596e0832faSShawn Lin };
5606e0832faSShawn Lin 
5616e0832faSShawn Lin static const struct dra7xx_pcie_of_data dra7xx_pcie_ep_of_data = {
5626e0832faSShawn Lin 	.mode = DW_PCIE_EP_TYPE,
5636e0832faSShawn Lin };
5646e0832faSShawn Lin 
565c232c0dfSKishon Vijay Abraham I static const struct dra7xx_pcie_of_data dra746_pcie_rc_of_data = {
566c232c0dfSKishon Vijay Abraham I 	.b1co_mode_sel_mask = BIT(2),
567c232c0dfSKishon Vijay Abraham I 	.mode = DW_PCIE_RC_TYPE,
568c232c0dfSKishon Vijay Abraham I };
569c232c0dfSKishon Vijay Abraham I 
570c232c0dfSKishon Vijay Abraham I static const struct dra7xx_pcie_of_data dra726_pcie_rc_of_data = {
571c232c0dfSKishon Vijay Abraham I 	.b1co_mode_sel_mask = GENMASK(3, 2),
572c232c0dfSKishon Vijay Abraham I 	.mode = DW_PCIE_RC_TYPE,
573c232c0dfSKishon Vijay Abraham I };
574c232c0dfSKishon Vijay Abraham I 
575c232c0dfSKishon Vijay Abraham I static const struct dra7xx_pcie_of_data dra746_pcie_ep_of_data = {
576c232c0dfSKishon Vijay Abraham I 	.b1co_mode_sel_mask = BIT(2),
577c232c0dfSKishon Vijay Abraham I 	.mode = DW_PCIE_EP_TYPE,
578c232c0dfSKishon Vijay Abraham I };
579c232c0dfSKishon Vijay Abraham I 
580c232c0dfSKishon Vijay Abraham I static const struct dra7xx_pcie_of_data dra726_pcie_ep_of_data = {
581c232c0dfSKishon Vijay Abraham I 	.b1co_mode_sel_mask = GENMASK(3, 2),
582c232c0dfSKishon Vijay Abraham I 	.mode = DW_PCIE_EP_TYPE,
583c232c0dfSKishon Vijay Abraham I };
584c232c0dfSKishon Vijay Abraham I 
5856e0832faSShawn Lin static const struct of_device_id of_dra7xx_pcie_match[] = {
5866e0832faSShawn Lin 	{
5876e0832faSShawn Lin 		.compatible = "ti,dra7-pcie",
5886e0832faSShawn Lin 		.data = &dra7xx_pcie_rc_of_data,
5896e0832faSShawn Lin 	},
5906e0832faSShawn Lin 	{
5916e0832faSShawn Lin 		.compatible = "ti,dra7-pcie-ep",
5926e0832faSShawn Lin 		.data = &dra7xx_pcie_ep_of_data,
5936e0832faSShawn Lin 	},
594c232c0dfSKishon Vijay Abraham I 	{
595c232c0dfSKishon Vijay Abraham I 		.compatible = "ti,dra746-pcie-rc",
596c232c0dfSKishon Vijay Abraham I 		.data = &dra746_pcie_rc_of_data,
597c232c0dfSKishon Vijay Abraham I 	},
598c232c0dfSKishon Vijay Abraham I 	{
599c232c0dfSKishon Vijay Abraham I 		.compatible = "ti,dra726-pcie-rc",
600c232c0dfSKishon Vijay Abraham I 		.data = &dra726_pcie_rc_of_data,
601c232c0dfSKishon Vijay Abraham I 	},
602c232c0dfSKishon Vijay Abraham I 	{
603c232c0dfSKishon Vijay Abraham I 		.compatible = "ti,dra746-pcie-ep",
604c232c0dfSKishon Vijay Abraham I 		.data = &dra746_pcie_ep_of_data,
605c232c0dfSKishon Vijay Abraham I 	},
606c232c0dfSKishon Vijay Abraham I 	{
607c232c0dfSKishon Vijay Abraham I 		.compatible = "ti,dra726-pcie-ep",
608c232c0dfSKishon Vijay Abraham I 		.data = &dra726_pcie_ep_of_data,
609c232c0dfSKishon Vijay Abraham I 	},
6106e0832faSShawn Lin 	{},
6116e0832faSShawn Lin };
6123b868d15SLuca Ceresoli MODULE_DEVICE_TABLE(of, of_dra7xx_pcie_match);
6136e0832faSShawn Lin 
6146e0832faSShawn Lin /*
615726d75a6SVignesh R  * dra7xx_pcie_unaligned_memaccess: workaround for AM572x/AM571x Errata i870
6166e0832faSShawn Lin  * @dra7xx: the dra7xx device where the workaround should be applied
6176e0832faSShawn Lin  *
6186e0832faSShawn Lin  * Access to the PCIe slave port that are not 32-bit aligned will result
6196e0832faSShawn Lin  * in incorrect mapping to TLP Address and Byte enable fields. Therefore,
6206e0832faSShawn Lin  * byte and half-word accesses are not possible to byte offset 0x1, 0x2, or
6216e0832faSShawn Lin  * 0x3.
6226e0832faSShawn Lin  *
6236e0832faSShawn Lin  * To avoid this issue set PCIE_SS1_AXI2OCP_LEGACY_MODE_ENABLE to 1.
6246e0832faSShawn Lin  */
dra7xx_pcie_unaligned_memaccess(struct device * dev)625726d75a6SVignesh R static int dra7xx_pcie_unaligned_memaccess(struct device *dev)
6266e0832faSShawn Lin {
6276e0832faSShawn Lin 	int ret;
6286e0832faSShawn Lin 	struct device_node *np = dev->of_node;
6296e0832faSShawn Lin 	struct of_phandle_args args;
6306e0832faSShawn Lin 	struct regmap *regmap;
6316e0832faSShawn Lin 
6326e0832faSShawn Lin 	regmap = syscon_regmap_lookup_by_phandle(np,
6336e0832faSShawn Lin 						 "ti,syscon-unaligned-access");
6346e0832faSShawn Lin 	if (IS_ERR(regmap)) {
6356e0832faSShawn Lin 		dev_dbg(dev, "can't get ti,syscon-unaligned-access\n");
6366e0832faSShawn Lin 		return -EINVAL;
6376e0832faSShawn Lin 	}
6386e0832faSShawn Lin 
6396e0832faSShawn Lin 	ret = of_parse_phandle_with_fixed_args(np, "ti,syscon-unaligned-access",
6406e0832faSShawn Lin 					       2, 0, &args);
6416e0832faSShawn Lin 	if (ret) {
6426e0832faSShawn Lin 		dev_err(dev, "failed to parse ti,syscon-unaligned-access\n");
6436e0832faSShawn Lin 		return ret;
6446e0832faSShawn Lin 	}
6456e0832faSShawn Lin 
6466e0832faSShawn Lin 	ret = regmap_update_bits(regmap, args.args[0], args.args[1],
6476e0832faSShawn Lin 				 args.args[1]);
6486e0832faSShawn Lin 	if (ret)
6496e0832faSShawn Lin 		dev_err(dev, "failed to enable unaligned access\n");
6506e0832faSShawn Lin 
6516e0832faSShawn Lin 	of_node_put(args.np);
6526e0832faSShawn Lin 
6536e0832faSShawn Lin 	return ret;
6546e0832faSShawn Lin }
6556e0832faSShawn Lin 
dra7xx_pcie_configure_two_lane(struct device * dev,u32 b1co_mode_sel_mask)656c232c0dfSKishon Vijay Abraham I static int dra7xx_pcie_configure_two_lane(struct device *dev,
657c232c0dfSKishon Vijay Abraham I 					  u32 b1co_mode_sel_mask)
658c232c0dfSKishon Vijay Abraham I {
659c232c0dfSKishon Vijay Abraham I 	struct device_node *np = dev->of_node;
660c232c0dfSKishon Vijay Abraham I 	struct regmap *pcie_syscon;
661c232c0dfSKishon Vijay Abraham I 	unsigned int pcie_reg;
662c232c0dfSKishon Vijay Abraham I 	u32 mask;
663c232c0dfSKishon Vijay Abraham I 	u32 val;
664c232c0dfSKishon Vijay Abraham I 
665c232c0dfSKishon Vijay Abraham I 	pcie_syscon = syscon_regmap_lookup_by_phandle(np, "ti,syscon-lane-sel");
666c232c0dfSKishon Vijay Abraham I 	if (IS_ERR(pcie_syscon)) {
667c232c0dfSKishon Vijay Abraham I 		dev_err(dev, "unable to get ti,syscon-lane-sel\n");
668c232c0dfSKishon Vijay Abraham I 		return -EINVAL;
669c232c0dfSKishon Vijay Abraham I 	}
670c232c0dfSKishon Vijay Abraham I 
671c232c0dfSKishon Vijay Abraham I 	if (of_property_read_u32_index(np, "ti,syscon-lane-sel", 1,
672c232c0dfSKishon Vijay Abraham I 				       &pcie_reg)) {
673c232c0dfSKishon Vijay Abraham I 		dev_err(dev, "couldn't get lane selection reg offset\n");
674c232c0dfSKishon Vijay Abraham I 		return -EINVAL;
675c232c0dfSKishon Vijay Abraham I 	}
676c232c0dfSKishon Vijay Abraham I 
677c232c0dfSKishon Vijay Abraham I 	mask = b1co_mode_sel_mask | PCIE_B0_B1_TSYNCEN;
678c232c0dfSKishon Vijay Abraham I 	val = PCIE_B1C0_MODE_SEL | PCIE_B0_B1_TSYNCEN;
679c232c0dfSKishon Vijay Abraham I 	regmap_update_bits(pcie_syscon, pcie_reg, mask, val);
680c232c0dfSKishon Vijay Abraham I 
681c232c0dfSKishon Vijay Abraham I 	return 0;
682c232c0dfSKishon Vijay Abraham I }
683c232c0dfSKishon Vijay Abraham I 
dra7xx_pcie_probe(struct platform_device * pdev)684e259c292STony Lindgren static int dra7xx_pcie_probe(struct platform_device *pdev)
6856e0832faSShawn Lin {
6866e0832faSShawn Lin 	u32 reg;
6876e0832faSShawn Lin 	int ret;
6886e0832faSShawn Lin 	int irq;
6896e0832faSShawn Lin 	int i;
6906e0832faSShawn Lin 	int phy_count;
6916e0832faSShawn Lin 	struct phy **phy;
6926e0832faSShawn Lin 	struct device_link **link;
6936e0832faSShawn Lin 	void __iomem *base;
6946e0832faSShawn Lin 	struct dw_pcie *pci;
6956e0832faSShawn Lin 	struct dra7xx_pcie *dra7xx;
6966e0832faSShawn Lin 	struct device *dev = &pdev->dev;
6976e0832faSShawn Lin 	struct device_node *np = dev->of_node;
6986e0832faSShawn Lin 	char name[10];
6996e0832faSShawn Lin 	struct gpio_desc *reset;
7006e0832faSShawn Lin 	const struct dra7xx_pcie_of_data *data;
7016e0832faSShawn Lin 	enum dw_pcie_device_mode mode;
702c232c0dfSKishon Vijay Abraham I 	u32 b1co_mode_sel_mask;
7036e0832faSShawn Lin 
704dc078f15SFan Fei 	data = of_device_get_match_data(dev);
705dc078f15SFan Fei 	if (!data)
7066e0832faSShawn Lin 		return -EINVAL;
7076e0832faSShawn Lin 
7086e0832faSShawn Lin 	mode = (enum dw_pcie_device_mode)data->mode;
709c232c0dfSKishon Vijay Abraham I 	b1co_mode_sel_mask = data->b1co_mode_sel_mask;
7106e0832faSShawn Lin 
7116e0832faSShawn Lin 	dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL);
7126e0832faSShawn Lin 	if (!dra7xx)
7136e0832faSShawn Lin 		return -ENOMEM;
7146e0832faSShawn Lin 
7156e0832faSShawn Lin 	pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
7166e0832faSShawn Lin 	if (!pci)
7176e0832faSShawn Lin 		return -ENOMEM;
7186e0832faSShawn Lin 
7196e0832faSShawn Lin 	pci->dev = dev;
7206e0832faSShawn Lin 	pci->ops = &dw_pcie_ops;
7216e0832faSShawn Lin 
7226e0832faSShawn Lin 	irq = platform_get_irq(pdev, 0);
723caecb05cSKrzysztof Wilczyński 	if (irq < 0)
7246e0832faSShawn Lin 		return irq;
7256e0832faSShawn Lin 
726c8a11977SWei Yongjun 	base = devm_platform_ioremap_resource_byname(pdev, "ti_conf");
727c8a11977SWei Yongjun 	if (IS_ERR(base))
728c8a11977SWei Yongjun 		return PTR_ERR(base);
7296e0832faSShawn Lin 
7306e0832faSShawn Lin 	phy_count = of_property_count_strings(np, "phy-names");
7316e0832faSShawn Lin 	if (phy_count < 0) {
7326e0832faSShawn Lin 		dev_err(dev, "unable to find the strings\n");
7336e0832faSShawn Lin 		return phy_count;
7346e0832faSShawn Lin 	}
7356e0832faSShawn Lin 
736b08fc527SLinus Torvalds 	phy = devm_kcalloc(dev, phy_count, sizeof(*phy), GFP_KERNEL);
7376e0832faSShawn Lin 	if (!phy)
7386e0832faSShawn Lin 		return -ENOMEM;
7396e0832faSShawn Lin 
740b08fc527SLinus Torvalds 	link = devm_kcalloc(dev, phy_count, sizeof(*link), GFP_KERNEL);
7416e0832faSShawn Lin 	if (!link)
7426e0832faSShawn Lin 		return -ENOMEM;
7436e0832faSShawn Lin 
7445af94053SLuca Ceresoli 	dra7xx->clk = devm_clk_get_optional(dev, NULL);
7455af94053SLuca Ceresoli 	if (IS_ERR(dra7xx->clk))
7465af94053SLuca Ceresoli 		return dev_err_probe(dev, PTR_ERR(dra7xx->clk),
7475af94053SLuca Ceresoli 				     "clock request failed");
7485af94053SLuca Ceresoli 
7495af94053SLuca Ceresoli 	ret = clk_prepare_enable(dra7xx->clk);
7505af94053SLuca Ceresoli 	if (ret)
7515af94053SLuca Ceresoli 		return ret;
7525af94053SLuca Ceresoli 
7536e0832faSShawn Lin 	for (i = 0; i < phy_count; i++) {
7546e0832faSShawn Lin 		snprintf(name, sizeof(name), "pcie-phy%d", i);
7556e0832faSShawn Lin 		phy[i] = devm_phy_get(dev, name);
7566e0832faSShawn Lin 		if (IS_ERR(phy[i]))
7576e0832faSShawn Lin 			return PTR_ERR(phy[i]);
7586e0832faSShawn Lin 
7596e0832faSShawn Lin 		link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS);
7606e0832faSShawn Lin 		if (!link[i]) {
7616e0832faSShawn Lin 			ret = -EINVAL;
7626e0832faSShawn Lin 			goto err_link;
7636e0832faSShawn Lin 		}
7646e0832faSShawn Lin 	}
7656e0832faSShawn Lin 
7666e0832faSShawn Lin 	dra7xx->base = base;
7676e0832faSShawn Lin 	dra7xx->phy = phy;
7686e0832faSShawn Lin 	dra7xx->pci = pci;
7696e0832faSShawn Lin 	dra7xx->phy_count = phy_count;
7706e0832faSShawn Lin 
771c232c0dfSKishon Vijay Abraham I 	if (phy_count == 2) {
772c232c0dfSKishon Vijay Abraham I 		ret = dra7xx_pcie_configure_two_lane(dev, b1co_mode_sel_mask);
773c232c0dfSKishon Vijay Abraham I 		if (ret < 0)
774c232c0dfSKishon Vijay Abraham I 			dra7xx->phy_count = 1; /* Fallback to x1 lane mode */
775c232c0dfSKishon Vijay Abraham I 	}
776c232c0dfSKishon Vijay Abraham I 
7776e0832faSShawn Lin 	ret = dra7xx_pcie_enable_phy(dra7xx);
7786e0832faSShawn Lin 	if (ret) {
7796e0832faSShawn Lin 		dev_err(dev, "failed to enable phy\n");
7806e0832faSShawn Lin 		return ret;
7816e0832faSShawn Lin 	}
7826e0832faSShawn Lin 
7836e0832faSShawn Lin 	platform_set_drvdata(pdev, dra7xx);
7846e0832faSShawn Lin 
7856e0832faSShawn Lin 	pm_runtime_enable(dev);
7866e0832faSShawn Lin 	ret = pm_runtime_get_sync(dev);
7876e0832faSShawn Lin 	if (ret < 0) {
7886e0832faSShawn Lin 		dev_err(dev, "pm_runtime_get_sync failed\n");
7896e0832faSShawn Lin 		goto err_get_sync;
7906e0832faSShawn Lin 	}
7916e0832faSShawn Lin 
7926e0832faSShawn Lin 	reset = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH);
7936e0832faSShawn Lin 	if (IS_ERR(reset)) {
7946e0832faSShawn Lin 		ret = PTR_ERR(reset);
7956e0832faSShawn Lin 		dev_err(&pdev->dev, "gpio request failed, ret %d\n", ret);
7966e0832faSShawn Lin 		goto err_gpio;
7976e0832faSShawn Lin 	}
7986e0832faSShawn Lin 
7996e0832faSShawn Lin 	reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD);
8006e0832faSShawn Lin 	reg &= ~LTSSM_EN;
8016e0832faSShawn Lin 	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
8026e0832faSShawn Lin 
8036e0832faSShawn Lin 	switch (mode) {
8046e0832faSShawn Lin 	case DW_PCIE_RC_TYPE:
8056e0832faSShawn Lin 		if (!IS_ENABLED(CONFIG_PCI_DRA7XX_HOST)) {
8066e0832faSShawn Lin 			ret = -ENODEV;
8076e0832faSShawn Lin 			goto err_gpio;
8086e0832faSShawn Lin 		}
8096e0832faSShawn Lin 
8106e0832faSShawn Lin 		dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
8116e0832faSShawn Lin 				   DEVICE_TYPE_RC);
812726d75a6SVignesh R 
813726d75a6SVignesh R 		ret = dra7xx_pcie_unaligned_memaccess(dev);
814726d75a6SVignesh R 		if (ret)
815726d75a6SVignesh R 			dev_err(dev, "WA for Errata i870 not applied\n");
816726d75a6SVignesh R 
8176e0832faSShawn Lin 		ret = dra7xx_add_pcie_port(dra7xx, pdev);
8186e0832faSShawn Lin 		if (ret < 0)
8196e0832faSShawn Lin 			goto err_gpio;
8206e0832faSShawn Lin 		break;
8216e0832faSShawn Lin 	case DW_PCIE_EP_TYPE:
8226e0832faSShawn Lin 		if (!IS_ENABLED(CONFIG_PCI_DRA7XX_EP)) {
8236e0832faSShawn Lin 			ret = -ENODEV;
8246e0832faSShawn Lin 			goto err_gpio;
8256e0832faSShawn Lin 		}
8266e0832faSShawn Lin 
8276e0832faSShawn Lin 		dra7xx_pcie_writel(dra7xx, PCIECTRL_TI_CONF_DEVICE_TYPE,
8286e0832faSShawn Lin 				   DEVICE_TYPE_EP);
8296e0832faSShawn Lin 
830726d75a6SVignesh R 		ret = dra7xx_pcie_unaligned_memaccess(dev);
8316e0832faSShawn Lin 		if (ret)
8326e0832faSShawn Lin 			goto err_gpio;
8336e0832faSShawn Lin 
8346e0832faSShawn Lin 		ret = dra7xx_add_pcie_ep(dra7xx, pdev);
8356e0832faSShawn Lin 		if (ret < 0)
8366e0832faSShawn Lin 			goto err_gpio;
8376e0832faSShawn Lin 		break;
8386e0832faSShawn Lin 	default:
8396e0832faSShawn Lin 		dev_err(dev, "INVALID device type %d\n", mode);
8406e0832faSShawn Lin 	}
8416e0832faSShawn Lin 	dra7xx->mode = mode;
8426e0832faSShawn Lin 
843da87d35aSManivannan Sadhasivam 	ret = devm_request_threaded_irq(dev, irq, NULL, dra7xx_pcie_irq_handler,
8446e0832faSShawn Lin 			       IRQF_SHARED, "dra7xx-pcie-main", dra7xx);
8456e0832faSShawn Lin 	if (ret) {
8466e0832faSShawn Lin 		dev_err(dev, "failed to request irq\n");
8476e0832faSShawn Lin 		goto err_gpio;
8486e0832faSShawn Lin 	}
8496e0832faSShawn Lin 
8506e0832faSShawn Lin 	return 0;
8516e0832faSShawn Lin 
8526e0832faSShawn Lin err_gpio:
8536e0832faSShawn Lin err_get_sync:
854c2615d62SDinghao Liu 	pm_runtime_put(dev);
8556e0832faSShawn Lin 	pm_runtime_disable(dev);
8566e0832faSShawn Lin 	dra7xx_pcie_disable_phy(dra7xx);
8576e0832faSShawn Lin 
8586e0832faSShawn Lin err_link:
8596e0832faSShawn Lin 	while (--i >= 0)
8606e0832faSShawn Lin 		device_link_del(link[i]);
8616e0832faSShawn Lin 
8626e0832faSShawn Lin 	return ret;
8636e0832faSShawn Lin }
8646e0832faSShawn Lin 
dra7xx_pcie_suspend(struct device * dev)8656e0832faSShawn Lin static int dra7xx_pcie_suspend(struct device *dev)
8666e0832faSShawn Lin {
8676e0832faSShawn Lin 	struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev);
8686e0832faSShawn Lin 	struct dw_pcie *pci = dra7xx->pci;
8696e0832faSShawn Lin 	u32 val;
8706e0832faSShawn Lin 
8716e0832faSShawn Lin 	if (dra7xx->mode != DW_PCIE_RC_TYPE)
8726e0832faSShawn Lin 		return 0;
8736e0832faSShawn Lin 
8746e0832faSShawn Lin 	/* clear MSE */
8756e0832faSShawn Lin 	val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
8766e0832faSShawn Lin 	val &= ~PCI_COMMAND_MEMORY;
8776e0832faSShawn Lin 	dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
8786e0832faSShawn Lin 
8796e0832faSShawn Lin 	return 0;
8806e0832faSShawn Lin }
8816e0832faSShawn Lin 
dra7xx_pcie_resume(struct device * dev)8826e0832faSShawn Lin static int dra7xx_pcie_resume(struct device *dev)
8836e0832faSShawn Lin {
8846e0832faSShawn Lin 	struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev);
8856e0832faSShawn Lin 	struct dw_pcie *pci = dra7xx->pci;
8866e0832faSShawn Lin 	u32 val;
8876e0832faSShawn Lin 
8886e0832faSShawn Lin 	if (dra7xx->mode != DW_PCIE_RC_TYPE)
8896e0832faSShawn Lin 		return 0;
8906e0832faSShawn Lin 
8916e0832faSShawn Lin 	/* set MSE */
8926e0832faSShawn Lin 	val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
8936e0832faSShawn Lin 	val |= PCI_COMMAND_MEMORY;
8946e0832faSShawn Lin 	dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
8956e0832faSShawn Lin 
8966e0832faSShawn Lin 	return 0;
8976e0832faSShawn Lin }
8986e0832faSShawn Lin 
dra7xx_pcie_suspend_noirq(struct device * dev)8996e0832faSShawn Lin static int dra7xx_pcie_suspend_noirq(struct device *dev)
9006e0832faSShawn Lin {
9016e0832faSShawn Lin 	struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev);
9026e0832faSShawn Lin 
9036e0832faSShawn Lin 	dra7xx_pcie_disable_phy(dra7xx);
9046e0832faSShawn Lin 
9056e0832faSShawn Lin 	return 0;
9066e0832faSShawn Lin }
9076e0832faSShawn Lin 
dra7xx_pcie_resume_noirq(struct device * dev)9086e0832faSShawn Lin static int dra7xx_pcie_resume_noirq(struct device *dev)
9096e0832faSShawn Lin {
9106e0832faSShawn Lin 	struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev);
9116e0832faSShawn Lin 	int ret;
9126e0832faSShawn Lin 
9136e0832faSShawn Lin 	ret = dra7xx_pcie_enable_phy(dra7xx);
9146e0832faSShawn Lin 	if (ret) {
9156e0832faSShawn Lin 		dev_err(dev, "failed to enable phy\n");
9166e0832faSShawn Lin 		return ret;
9176e0832faSShawn Lin 	}
9186e0832faSShawn Lin 
9196e0832faSShawn Lin 	return 0;
9206e0832faSShawn Lin }
9216e0832faSShawn Lin 
dra7xx_pcie_shutdown(struct platform_device * pdev)9226e0832faSShawn Lin static void dra7xx_pcie_shutdown(struct platform_device *pdev)
9236e0832faSShawn Lin {
9246e0832faSShawn Lin 	struct device *dev = &pdev->dev;
9256e0832faSShawn Lin 	struct dra7xx_pcie *dra7xx = dev_get_drvdata(dev);
9266e0832faSShawn Lin 	int ret;
9276e0832faSShawn Lin 
9286e0832faSShawn Lin 	dra7xx_pcie_stop_link(dra7xx->pci);
9296e0832faSShawn Lin 
9306e0832faSShawn Lin 	ret = pm_runtime_put_sync(dev);
9316e0832faSShawn Lin 	if (ret < 0)
9326e0832faSShawn Lin 		dev_dbg(dev, "pm_runtime_put_sync failed\n");
9336e0832faSShawn Lin 
9346e0832faSShawn Lin 	pm_runtime_disable(dev);
9356e0832faSShawn Lin 	dra7xx_pcie_disable_phy(dra7xx);
9365af94053SLuca Ceresoli 
9375af94053SLuca Ceresoli 	clk_disable_unprepare(dra7xx->clk);
9386e0832faSShawn Lin }
9396e0832faSShawn Lin 
9406e0832faSShawn Lin static const struct dev_pm_ops dra7xx_pcie_pm_ops = {
94119b7858cSBjorn Helgaas 	SYSTEM_SLEEP_PM_OPS(dra7xx_pcie_suspend, dra7xx_pcie_resume)
94219b7858cSBjorn Helgaas 	NOIRQ_SYSTEM_SLEEP_PM_OPS(dra7xx_pcie_suspend_noirq,
9436e0832faSShawn Lin 				  dra7xx_pcie_resume_noirq)
9446e0832faSShawn Lin };
9456e0832faSShawn Lin 
9466e0832faSShawn Lin static struct platform_driver dra7xx_pcie_driver = {
947e259c292STony Lindgren 	.probe = dra7xx_pcie_probe,
9486e0832faSShawn Lin 	.driver = {
9496e0832faSShawn Lin 		.name	= "dra7-pcie",
9506e0832faSShawn Lin 		.of_match_table = of_dra7xx_pcie_match,
9516e0832faSShawn Lin 		.suppress_bind_attrs = true,
9526e0832faSShawn Lin 		.pm	= &dra7xx_pcie_pm_ops,
9536e0832faSShawn Lin 	},
9546e0832faSShawn Lin 	.shutdown = dra7xx_pcie_shutdown,
9556e0832faSShawn Lin };
9563b868d15SLuca Ceresoli module_platform_driver(dra7xx_pcie_driver);
9573b868d15SLuca Ceresoli 
9583b868d15SLuca Ceresoli MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
9593b868d15SLuca Ceresoli MODULE_DESCRIPTION("PCIe controller driver for TI DRA7xx SoCs");
9603b868d15SLuca Ceresoli MODULE_LICENSE("GPL v2");
961