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, ®, 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