1de80f95cSTom Joseph // SPDX-License-Identifier: GPL-2.0
2de80f95cSTom Joseph // Copyright (c) 2017 Cadence
3de80f95cSTom Joseph // Cadence PCIe host controller driver.
4de80f95cSTom Joseph // Author: Cyrille Pitchen <cyrille.pitchen@free-electrons.com>
5de80f95cSTom Joseph
640d957e6SKishon Vijay Abraham I #include <linux/delay.h>
7de80f95cSTom Joseph #include <linux/kernel.h>
85d3d063aSKishon Vijay Abraham I #include <linux/list_sort.h>
9de80f95cSTom Joseph #include <linux/of_address.h>
10de80f95cSTom Joseph #include <linux/of_pci.h>
11de80f95cSTom Joseph #include <linux/platform_device.h>
12de80f95cSTom Joseph
13de80f95cSTom Joseph #include "pcie-cadence.h"
14de80f95cSTom Joseph
155d3d063aSKishon Vijay Abraham I #define LINK_RETRAIN_TIMEOUT HZ
165d3d063aSKishon Vijay Abraham I
175d3d063aSKishon Vijay Abraham I static u64 bar_max_size[] = {
185d3d063aSKishon Vijay Abraham I [RP_BAR0] = _ULL(128 * SZ_2G),
195d3d063aSKishon Vijay Abraham I [RP_BAR1] = SZ_2G,
205d3d063aSKishon Vijay Abraham I [RP_NO_BAR] = _BITULL(63),
215d3d063aSKishon Vijay Abraham I };
225d3d063aSKishon Vijay Abraham I
235d3d063aSKishon Vijay Abraham I static u8 bar_aperture_mask[] = {
245d3d063aSKishon Vijay Abraham I [RP_BAR0] = 0x1F,
255d3d063aSKishon Vijay Abraham I [RP_BAR1] = 0xF,
26f3e25911SKishon Vijay Abraham I };
27de80f95cSTom Joseph
cdns_pci_map_bus(struct pci_bus * bus,unsigned int devfn,int where)28de80f95cSTom Joseph void __iomem *cdns_pci_map_bus(struct pci_bus *bus, unsigned int devfn,
29de80f95cSTom Joseph int where)
30de80f95cSTom Joseph {
31de80f95cSTom Joseph struct pci_host_bridge *bridge = pci_find_host_bridge(bus);
32de80f95cSTom Joseph struct cdns_pcie_rc *rc = pci_host_bridge_priv(bridge);
33de80f95cSTom Joseph struct cdns_pcie *pcie = &rc->pcie;
34de80f95cSTom Joseph unsigned int busn = bus->number;
35ec64e279SRob Herring u32 addr0, desc0;
36de80f95cSTom Joseph
37de80f95cSTom Joseph if (pci_is_root_bus(bus)) {
38de80f95cSTom Joseph /*
39de80f95cSTom Joseph * Only the root port (devfn == 0) is connected to this bus.
40de80f95cSTom Joseph * All other PCI devices are behind some bridge hence on another
41de80f95cSTom Joseph * bus.
42de80f95cSTom Joseph */
43de80f95cSTom Joseph if (devfn)
44de80f95cSTom Joseph return NULL;
45de80f95cSTom Joseph
46de80f95cSTom Joseph return pcie->reg_base + (where & 0xfff);
47de80f95cSTom Joseph }
48de80f95cSTom Joseph /* Check that the link is up */
49de80f95cSTom Joseph if (!(cdns_pcie_readl(pcie, CDNS_PCIE_LM_BASE) & 0x1))
50de80f95cSTom Joseph return NULL;
51de80f95cSTom Joseph /* Clear AXI link-down status */
52de80f95cSTom Joseph cdns_pcie_writel(pcie, CDNS_PCIE_AT_LINKDOWN, 0x0);
53de80f95cSTom Joseph
54de80f95cSTom Joseph /* Update Output registers for AXI region 0. */
55de80f95cSTom Joseph addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(12) |
56de80f95cSTom Joseph CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) |
57de80f95cSTom Joseph CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_BUS(busn);
58de80f95cSTom Joseph cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(0), addr0);
59de80f95cSTom Joseph
60de80f95cSTom Joseph /* Configuration Type 0 or Type 1 access. */
61de80f95cSTom Joseph desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID |
62de80f95cSTom Joseph CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(0);
63de80f95cSTom Joseph /*
64de80f95cSTom Joseph * The bus number was already set once for all in desc1 by
65ec64e279SRob Herring * cdns_pcie_host_init_address_translation().
66de80f95cSTom Joseph */
67de80f95cSTom Joseph if (busn == bridge->busnr + 1)
68de80f95cSTom Joseph desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0;
69de80f95cSTom Joseph else
70de80f95cSTom Joseph desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1;
71de80f95cSTom Joseph cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(0), desc0);
72de80f95cSTom Joseph
73de80f95cSTom Joseph return rc->cfg_base + (where & 0xfff);
74de80f95cSTom Joseph }
75de80f95cSTom Joseph
76de80f95cSTom Joseph static struct pci_ops cdns_pcie_host_ops = {
77de80f95cSTom Joseph .map_bus = cdns_pci_map_bus,
78de80f95cSTom Joseph .read = pci_generic_config_read,
79de80f95cSTom Joseph .write = pci_generic_config_write,
804740b969SNadeem Athani };
814740b969SNadeem Athani
cdns_pcie_host_training_complete(struct cdns_pcie * pcie)824740b969SNadeem Athani static int cdns_pcie_host_training_complete(struct cdns_pcie *pcie)
834740b969SNadeem Athani {
844740b969SNadeem Athani u32 pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET;
854740b969SNadeem Athani unsigned long end_jiffies;
864740b969SNadeem Athani u16 lnk_stat;
874740b969SNadeem Athani
884740b969SNadeem Athani /* Wait for link training to complete. Exit after timeout. */
894740b969SNadeem Athani end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT;
904740b969SNadeem Athani do {
914740b969SNadeem Athani lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA);
924740b969SNadeem Athani if (!(lnk_stat & PCI_EXP_LNKSTA_LT))
934740b969SNadeem Athani break;
944740b969SNadeem Athani usleep_range(0, 1000);
954740b969SNadeem Athani } while (time_before(jiffies, end_jiffies));
964740b969SNadeem Athani
974740b969SNadeem Athani if (!(lnk_stat & PCI_EXP_LNKSTA_LT))
984740b969SNadeem Athani return 0;
994740b969SNadeem Athani
1004740b969SNadeem Athani return -ETIMEDOUT;
1014740b969SNadeem Athani }
1024740b969SNadeem Athani
cdns_pcie_host_wait_for_link(struct cdns_pcie * pcie)1034740b969SNadeem Athani static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie)
1044740b969SNadeem Athani {
1054740b969SNadeem Athani struct device *dev = pcie->dev;
1064740b969SNadeem Athani int retries;
1074740b969SNadeem Athani
1084740b969SNadeem Athani /* Check if the link is up or not */
1094740b969SNadeem Athani for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
1104740b969SNadeem Athani if (cdns_pcie_link_up(pcie)) {
1114740b969SNadeem Athani dev_info(dev, "Link up\n");
1124740b969SNadeem Athani return 0;
1134740b969SNadeem Athani }
1144740b969SNadeem Athani usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
1154740b969SNadeem Athani }
1164740b969SNadeem Athani
1174740b969SNadeem Athani return -ETIMEDOUT;
1184740b969SNadeem Athani }
1194740b969SNadeem Athani
cdns_pcie_retrain(struct cdns_pcie * pcie)1204740b969SNadeem Athani static int cdns_pcie_retrain(struct cdns_pcie *pcie)
1214740b969SNadeem Athani {
1224740b969SNadeem Athani u32 lnk_cap_sls, pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET;
1234740b969SNadeem Athani u16 lnk_stat, lnk_ctl;
1244740b969SNadeem Athani int ret = 0;
1254740b969SNadeem Athani
126*a1f67bc1SChristian Gmeiner /*
127*a1f67bc1SChristian Gmeiner * Set retrain bit if current speed is 2.5 GB/s,
128*a1f67bc1SChristian Gmeiner * but the PCIe root port support is > 2.5 GB/s.
129*a1f67bc1SChristian Gmeiner */
130*a1f67bc1SChristian Gmeiner
131*a1f67bc1SChristian Gmeiner lnk_cap_sls = cdns_pcie_readl(pcie, (CDNS_PCIE_RP_BASE + pcie_cap_off +
132*a1f67bc1SChristian Gmeiner PCI_EXP_LNKCAP));
133*a1f67bc1SChristian Gmeiner if ((lnk_cap_sls & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
1344740b969SNadeem Athani return ret;
1354740b969SNadeem Athani
1364740b969SNadeem Athani lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA);
1374740b969SNadeem Athani if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) {
1384740b969SNadeem Athani lnk_ctl = cdns_pcie_rp_readw(pcie,
1394740b969SNadeem Athani pcie_cap_off + PCI_EXP_LNKCTL);
1404740b969SNadeem Athani lnk_ctl |= PCI_EXP_LNKCTL_RL;
1414740b969SNadeem Athani cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL,
1424740b969SNadeem Athani lnk_ctl);
1434740b969SNadeem Athani
1444740b969SNadeem Athani ret = cdns_pcie_host_training_complete(pcie);
1454740b969SNadeem Athani if (ret)
1464740b969SNadeem Athani return ret;
1474740b969SNadeem Athani
1484740b969SNadeem Athani ret = cdns_pcie_host_wait_for_link(pcie);
1494740b969SNadeem Athani }
150de80f95cSTom Joseph return ret;
151de80f95cSTom Joseph }
152de80f95cSTom Joseph
cdns_pcie_host_enable_ptm_response(struct cdns_pcie * pcie)153de80f95cSTom Joseph static void cdns_pcie_host_enable_ptm_response(struct cdns_pcie *pcie)
154de80f95cSTom Joseph {
155e3bca37dSKishon Vijay Abraham I u32 val;
156de80f95cSTom Joseph
157de80f95cSTom Joseph val = cdns_pcie_readl(pcie, CDNS_PCIE_LM_PTM_CTRL);
158de80f95cSTom Joseph cdns_pcie_writel(pcie, CDNS_PCIE_LM_PTM_CTRL, val | CDNS_PCIE_LM_TPM_CTRL_PTMRSEN);
159de80f95cSTom Joseph }
160de80f95cSTom Joseph
cdns_pcie_host_start_link(struct cdns_pcie_rc * rc)161de80f95cSTom Joseph static int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc)
162de80f95cSTom Joseph {
163de80f95cSTom Joseph struct cdns_pcie *pcie = &rc->pcie;
164de80f95cSTom Joseph int ret;
165de80f95cSTom Joseph
166de80f95cSTom Joseph ret = cdns_pcie_host_wait_for_link(pcie);
167de80f95cSTom Joseph
168de80f95cSTom Joseph /*
169de80f95cSTom Joseph * Retrain link for Gen2 training defect
170de80f95cSTom Joseph * if quirk flag is set.
171de80f95cSTom Joseph */
172de80f95cSTom Joseph if (!ret && rc->quirk_retrain_flag)
173de80f95cSTom Joseph ret = cdns_pcie_retrain(pcie);
174de80f95cSTom Joseph
175e3bca37dSKishon Vijay Abraham I return ret;
176e3bca37dSKishon Vijay Abraham I }
177e3bca37dSKishon Vijay Abraham I
cdns_pcie_host_init_root_port(struct cdns_pcie_rc * rc)178e3bca37dSKishon Vijay Abraham I static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc)
179e3bca37dSKishon Vijay Abraham I {
180e3bca37dSKishon Vijay Abraham I struct cdns_pcie *pcie = &rc->pcie;
181de80f95cSTom Joseph u32 value, ctrl;
182de80f95cSTom Joseph u32 id;
183de80f95cSTom Joseph
184de80f95cSTom Joseph /*
185de80f95cSTom Joseph * Set the root complex BAR configuration register:
186de80f95cSTom Joseph * - disable both BAR0 and BAR1.
187de80f95cSTom Joseph * - enable Prefetchable Memory Base and Limit registers in type 1
188de80f95cSTom Joseph * config space (64 bits).
189de80f95cSTom Joseph * - enable IO Base and Limit registers in type 1 config
190de80f95cSTom Joseph * space (32 bits).
1915d3d063aSKishon Vijay Abraham I */
1925d3d063aSKishon Vijay Abraham I ctrl = CDNS_PCIE_LM_BAR_CFG_CTRL_DISABLED;
1935d3d063aSKishon Vijay Abraham I value = CDNS_PCIE_LM_RC_BAR_CFG_BAR0_CTRL(ctrl) |
1945d3d063aSKishon Vijay Abraham I CDNS_PCIE_LM_RC_BAR_CFG_BAR1_CTRL(ctrl) |
1955d3d063aSKishon Vijay Abraham I CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE |
1965d3d063aSKishon Vijay Abraham I CDNS_PCIE_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS |
1975d3d063aSKishon Vijay Abraham I CDNS_PCIE_LM_RC_BAR_CFG_IO_ENABLE |
1985d3d063aSKishon Vijay Abraham I CDNS_PCIE_LM_RC_BAR_CFG_IO_32BITS;
1995d3d063aSKishon Vijay Abraham I cdns_pcie_writel(pcie, CDNS_PCIE_LM_RC_BAR_CFG, value);
2005d3d063aSKishon Vijay Abraham I
2015d3d063aSKishon Vijay Abraham I /* Set root port configuration space */
2025d3d063aSKishon Vijay Abraham I if (rc->vendor_id != 0xffff) {
2035d3d063aSKishon Vijay Abraham I id = CDNS_PCIE_LM_ID_VENDOR(rc->vendor_id) |
2045d3d063aSKishon Vijay Abraham I CDNS_PCIE_LM_ID_SUBSYS(rc->vendor_id);
2055d3d063aSKishon Vijay Abraham I cdns_pcie_writel(pcie, CDNS_PCIE_LM_ID, id);
2065d3d063aSKishon Vijay Abraham I }
2075d3d063aSKishon Vijay Abraham I
2085d3d063aSKishon Vijay Abraham I if (rc->device_id != 0xffff)
2095d3d063aSKishon Vijay Abraham I cdns_pcie_rp_writew(pcie, PCI_DEVICE_ID, rc->device_id);
2105d3d063aSKishon Vijay Abraham I
2115d3d063aSKishon Vijay Abraham I cdns_pcie_rp_writeb(pcie, PCI_CLASS_REVISION, 0);
2125d3d063aSKishon Vijay Abraham I cdns_pcie_rp_writeb(pcie, PCI_CLASS_PROG, 0);
2135d3d063aSKishon Vijay Abraham I cdns_pcie_rp_writew(pcie, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI);
2145d3d063aSKishon Vijay Abraham I
2155d3d063aSKishon Vijay Abraham I return 0;
2165d3d063aSKishon Vijay Abraham I }
2175d3d063aSKishon Vijay Abraham I
cdns_pcie_host_bar_ib_config(struct cdns_pcie_rc * rc,enum cdns_pcie_rp_bar bar,u64 cpu_addr,u64 size,unsigned long flags)2185d3d063aSKishon Vijay Abraham I static int cdns_pcie_host_bar_ib_config(struct cdns_pcie_rc *rc,
2195d3d063aSKishon Vijay Abraham I enum cdns_pcie_rp_bar bar,
2205d3d063aSKishon Vijay Abraham I u64 cpu_addr, u64 size,
2215d3d063aSKishon Vijay Abraham I unsigned long flags)
2225d3d063aSKishon Vijay Abraham I {
2235d3d063aSKishon Vijay Abraham I struct cdns_pcie *pcie = &rc->pcie;
2245d3d063aSKishon Vijay Abraham I u32 addr0, addr1, aperture, value;
2255d3d063aSKishon Vijay Abraham I
2265d3d063aSKishon Vijay Abraham I if (!rc->avail_ib_bar[bar])
2275d3d063aSKishon Vijay Abraham I return -EBUSY;
2285d3d063aSKishon Vijay Abraham I
2295d3d063aSKishon Vijay Abraham I rc->avail_ib_bar[bar] = false;
2305d3d063aSKishon Vijay Abraham I
2315d3d063aSKishon Vijay Abraham I aperture = ilog2(size);
2325d3d063aSKishon Vijay Abraham I addr0 = CDNS_PCIE_AT_IB_RP_BAR_ADDR0_NBITS(aperture) |
2335d3d063aSKishon Vijay Abraham I (lower_32_bits(cpu_addr) & GENMASK(31, 8));
2345d3d063aSKishon Vijay Abraham I addr1 = upper_32_bits(cpu_addr);
2355d3d063aSKishon Vijay Abraham I cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_RP_BAR_ADDR0(bar), addr0);
2365d3d063aSKishon Vijay Abraham I cdns_pcie_writel(pcie, CDNS_PCIE_AT_IB_RP_BAR_ADDR1(bar), addr1);
2375d3d063aSKishon Vijay Abraham I
2385d3d063aSKishon Vijay Abraham I if (bar == RP_NO_BAR)
2395d3d063aSKishon Vijay Abraham I return 0;
2405d3d063aSKishon Vijay Abraham I
2415d3d063aSKishon Vijay Abraham I value = cdns_pcie_readl(pcie, CDNS_PCIE_LM_RC_BAR_CFG);
2425d3d063aSKishon Vijay Abraham I value &= ~(LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar) |
2435d3d063aSKishon Vijay Abraham I LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar) |
2445d3d063aSKishon Vijay Abraham I LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar) |
2455d3d063aSKishon Vijay Abraham I LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar) |
2465d3d063aSKishon Vijay Abraham I LM_RC_BAR_CFG_APERTURE(bar, bar_aperture_mask[bar] + 2));
2475d3d063aSKishon Vijay Abraham I if (size + cpu_addr >= SZ_4G) {
2485d3d063aSKishon Vijay Abraham I if (!(flags & IORESOURCE_PREFETCH))
2495d3d063aSKishon Vijay Abraham I value |= LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar);
2505d3d063aSKishon Vijay Abraham I value |= LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar);
2515d3d063aSKishon Vijay Abraham I } else {
2525d3d063aSKishon Vijay Abraham I if (!(flags & IORESOURCE_PREFETCH))
2535d3d063aSKishon Vijay Abraham I value |= LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar);
2545d3d063aSKishon Vijay Abraham I value |= LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar);
2555d3d063aSKishon Vijay Abraham I }
2565d3d063aSKishon Vijay Abraham I
2575d3d063aSKishon Vijay Abraham I value |= LM_RC_BAR_CFG_APERTURE(bar, aperture);
2585d3d063aSKishon Vijay Abraham I cdns_pcie_writel(pcie, CDNS_PCIE_LM_RC_BAR_CFG, value);
2595d3d063aSKishon Vijay Abraham I
2605d3d063aSKishon Vijay Abraham I return 0;
2615d3d063aSKishon Vijay Abraham I }
2625d3d063aSKishon Vijay Abraham I
2635d3d063aSKishon Vijay Abraham I static enum cdns_pcie_rp_bar
cdns_pcie_host_find_min_bar(struct cdns_pcie_rc * rc,u64 size)2645d3d063aSKishon Vijay Abraham I cdns_pcie_host_find_min_bar(struct cdns_pcie_rc *rc, u64 size)
2655d3d063aSKishon Vijay Abraham I {
2665d3d063aSKishon Vijay Abraham I enum cdns_pcie_rp_bar bar, sel_bar;
2675d3d063aSKishon Vijay Abraham I
2685d3d063aSKishon Vijay Abraham I sel_bar = RP_BAR_UNDEFINED;
2695d3d063aSKishon Vijay Abraham I for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) {
2705d3d063aSKishon Vijay Abraham I if (!rc->avail_ib_bar[bar])
2715d3d063aSKishon Vijay Abraham I continue;
2725d3d063aSKishon Vijay Abraham I
2735d3d063aSKishon Vijay Abraham I if (size <= bar_max_size[bar]) {
2745d3d063aSKishon Vijay Abraham I if (sel_bar == RP_BAR_UNDEFINED) {
2755d3d063aSKishon Vijay Abraham I sel_bar = bar;
2765d3d063aSKishon Vijay Abraham I continue;
2775d3d063aSKishon Vijay Abraham I }
2785d3d063aSKishon Vijay Abraham I
2795d3d063aSKishon Vijay Abraham I if (bar_max_size[bar] < bar_max_size[sel_bar])
2805d3d063aSKishon Vijay Abraham I sel_bar = bar;
2815d3d063aSKishon Vijay Abraham I }
2825d3d063aSKishon Vijay Abraham I }
2835d3d063aSKishon Vijay Abraham I
2845d3d063aSKishon Vijay Abraham I return sel_bar;
2855d3d063aSKishon Vijay Abraham I }
2865d3d063aSKishon Vijay Abraham I
2875d3d063aSKishon Vijay Abraham I static enum cdns_pcie_rp_bar
cdns_pcie_host_find_max_bar(struct cdns_pcie_rc * rc,u64 size)2885d3d063aSKishon Vijay Abraham I cdns_pcie_host_find_max_bar(struct cdns_pcie_rc *rc, u64 size)
2895d3d063aSKishon Vijay Abraham I {
2905d3d063aSKishon Vijay Abraham I enum cdns_pcie_rp_bar bar, sel_bar;
2915d3d063aSKishon Vijay Abraham I
2925d3d063aSKishon Vijay Abraham I sel_bar = RP_BAR_UNDEFINED;
2935d3d063aSKishon Vijay Abraham I for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) {
2945d3d063aSKishon Vijay Abraham I if (!rc->avail_ib_bar[bar])
2955d3d063aSKishon Vijay Abraham I continue;
2965d3d063aSKishon Vijay Abraham I
2975d3d063aSKishon Vijay Abraham I if (size >= bar_max_size[bar]) {
2985d3d063aSKishon Vijay Abraham I if (sel_bar == RP_BAR_UNDEFINED) {
2995d3d063aSKishon Vijay Abraham I sel_bar = bar;
3005d3d063aSKishon Vijay Abraham I continue;
3015d3d063aSKishon Vijay Abraham I }
3025d3d063aSKishon Vijay Abraham I
3035d3d063aSKishon Vijay Abraham I if (bar_max_size[bar] > bar_max_size[sel_bar])
3045d3d063aSKishon Vijay Abraham I sel_bar = bar;
3055d3d063aSKishon Vijay Abraham I }
3065d3d063aSKishon Vijay Abraham I }
3075d3d063aSKishon Vijay Abraham I
3085d3d063aSKishon Vijay Abraham I return sel_bar;
3095d3d063aSKishon Vijay Abraham I }
3105d3d063aSKishon Vijay Abraham I
cdns_pcie_host_bar_config(struct cdns_pcie_rc * rc,struct resource_entry * entry)3115d3d063aSKishon Vijay Abraham I static int cdns_pcie_host_bar_config(struct cdns_pcie_rc *rc,
3125d3d063aSKishon Vijay Abraham I struct resource_entry *entry)
3135d3d063aSKishon Vijay Abraham I {
3145d3d063aSKishon Vijay Abraham I u64 cpu_addr, pci_addr, size, winsize;
3155d3d063aSKishon Vijay Abraham I struct cdns_pcie *pcie = &rc->pcie;
3165d3d063aSKishon Vijay Abraham I struct device *dev = pcie->dev;
3175d3d063aSKishon Vijay Abraham I enum cdns_pcie_rp_bar bar;
3185d3d063aSKishon Vijay Abraham I unsigned long flags;
3195d3d063aSKishon Vijay Abraham I int ret;
3205d3d063aSKishon Vijay Abraham I
3215d3d063aSKishon Vijay Abraham I cpu_addr = entry->res->start;
3225d3d063aSKishon Vijay Abraham I pci_addr = entry->res->start - entry->offset;
3235d3d063aSKishon Vijay Abraham I flags = entry->res->flags;
3245d3d063aSKishon Vijay Abraham I size = resource_size(entry->res);
3255d3d063aSKishon Vijay Abraham I
3265d3d063aSKishon Vijay Abraham I if (entry->offset) {
3275d3d063aSKishon Vijay Abraham I dev_err(dev, "PCI addr: %llx must be equal to CPU addr: %llx\n",
3285d3d063aSKishon Vijay Abraham I pci_addr, cpu_addr);
3295d3d063aSKishon Vijay Abraham I return -EINVAL;
3305d3d063aSKishon Vijay Abraham I }
3315d3d063aSKishon Vijay Abraham I
3325d3d063aSKishon Vijay Abraham I while (size > 0) {
3335d3d063aSKishon Vijay Abraham I /*
3345d3d063aSKishon Vijay Abraham I * Try to find a minimum BAR whose size is greater than
3355d3d063aSKishon Vijay Abraham I * or equal to the remaining resource_entry size. This will
3365d3d063aSKishon Vijay Abraham I * fail if the size of each of the available BARs is less than
3375d3d063aSKishon Vijay Abraham I * the remaining resource_entry size.
3385d3d063aSKishon Vijay Abraham I * If a minimum BAR is found, IB ATU will be configured and
3395d3d063aSKishon Vijay Abraham I * exited.
3405d3d063aSKishon Vijay Abraham I */
3415d3d063aSKishon Vijay Abraham I bar = cdns_pcie_host_find_min_bar(rc, size);
3425d3d063aSKishon Vijay Abraham I if (bar != RP_BAR_UNDEFINED) {
3435d3d063aSKishon Vijay Abraham I ret = cdns_pcie_host_bar_ib_config(rc, bar, cpu_addr,
3445d3d063aSKishon Vijay Abraham I size, flags);
3455d3d063aSKishon Vijay Abraham I if (ret)
3465d3d063aSKishon Vijay Abraham I dev_err(dev, "IB BAR: %d config failed\n", bar);
3475d3d063aSKishon Vijay Abraham I return ret;
3485d3d063aSKishon Vijay Abraham I }
3495d3d063aSKishon Vijay Abraham I
3505d3d063aSKishon Vijay Abraham I /*
3515d3d063aSKishon Vijay Abraham I * If the control reaches here, it would mean the remaining
3525d3d063aSKishon Vijay Abraham I * resource_entry size cannot be fitted in a single BAR. So we
3535d3d063aSKishon Vijay Abraham I * find a maximum BAR whose size is less than or equal to the
3545d3d063aSKishon Vijay Abraham I * remaining resource_entry size and split the resource entry
3555d3d063aSKishon Vijay Abraham I * so that part of resource entry is fitted inside the maximum
3564f0f586bSSami Tolvanen * BAR. The remaining size would be fitted during the next
3574f0f586bSSami Tolvanen * iteration of the loop.
3585d3d063aSKishon Vijay Abraham I * If a maximum BAR is not found, there is no way we can fit
3595d3d063aSKishon Vijay Abraham I * this resource_entry, so we error out.
3605d3d063aSKishon Vijay Abraham I */
3615d3d063aSKishon Vijay Abraham I bar = cdns_pcie_host_find_max_bar(rc, size);
3625d3d063aSKishon Vijay Abraham I if (bar == RP_BAR_UNDEFINED) {
3635d3d063aSKishon Vijay Abraham I dev_err(dev, "No free BAR to map cpu_addr %llx\n",
3645d3d063aSKishon Vijay Abraham I cpu_addr);
3655d3d063aSKishon Vijay Abraham I return -EINVAL;
3665d3d063aSKishon Vijay Abraham I }
3675d3d063aSKishon Vijay Abraham I
3685d3d063aSKishon Vijay Abraham I winsize = bar_max_size[bar];
3695d3d063aSKishon Vijay Abraham I ret = cdns_pcie_host_bar_ib_config(rc, bar, cpu_addr, winsize,
3705d3d063aSKishon Vijay Abraham I flags);
3715d3d063aSKishon Vijay Abraham I if (ret) {
3725d3d063aSKishon Vijay Abraham I dev_err(dev, "IB BAR: %d config failed\n", bar);
3735d3d063aSKishon Vijay Abraham I return ret;
3745d3d063aSKishon Vijay Abraham I }
3755d3d063aSKishon Vijay Abraham I
3765d3d063aSKishon Vijay Abraham I size -= winsize;
3775d3d063aSKishon Vijay Abraham I cpu_addr += winsize;
3785d3d063aSKishon Vijay Abraham I }
3795d3d063aSKishon Vijay Abraham I
3805d3d063aSKishon Vijay Abraham I return 0;
3815d3d063aSKishon Vijay Abraham I }
3825d3d063aSKishon Vijay Abraham I
cdns_pcie_host_dma_ranges_cmp(void * priv,const struct list_head * a,const struct list_head * b)3835d3d063aSKishon Vijay Abraham I static int cdns_pcie_host_dma_ranges_cmp(void *priv, const struct list_head *a,
3845d3d063aSKishon Vijay Abraham I const struct list_head *b)
3855d3d063aSKishon Vijay Abraham I {
3865d3d063aSKishon Vijay Abraham I struct resource_entry *entry1, *entry2;
3875d3d063aSKishon Vijay Abraham I
3885d3d063aSKishon Vijay Abraham I entry1 = container_of(a, struct resource_entry, node);
3895d3d063aSKishon Vijay Abraham I entry2 = container_of(b, struct resource_entry, node);
3905d3d063aSKishon Vijay Abraham I
3915d3d063aSKishon Vijay Abraham I return resource_size(entry2->res) - resource_size(entry1->res);
3925d3d063aSKishon Vijay Abraham I }
3935d3d063aSKishon Vijay Abraham I
cdns_pcie_host_map_dma_ranges(struct cdns_pcie_rc * rc)3945d3d063aSKishon Vijay Abraham I static int cdns_pcie_host_map_dma_ranges(struct cdns_pcie_rc *rc)
3951002573eSKrzysztof Wilczyński {
3965d3d063aSKishon Vijay Abraham I struct cdns_pcie *pcie = &rc->pcie;
3975d3d063aSKishon Vijay Abraham I struct device *dev = pcie->dev;
3985d3d063aSKishon Vijay Abraham I struct device_node *np = dev->of_node;
3991002573eSKrzysztof Wilczyński struct pci_host_bridge *bridge;
4005d3d063aSKishon Vijay Abraham I struct resource_entry *entry;
4015d3d063aSKishon Vijay Abraham I u32 no_bar_nbits = 32;
4025d3d063aSKishon Vijay Abraham I int err;
4035d3d063aSKishon Vijay Abraham I
404de80f95cSTom Joseph bridge = pci_host_bridge_from_priv(rc);
405de80f95cSTom Joseph if (!bridge)
406de80f95cSTom Joseph return -ENOMEM;
40706ff98fcSRob Herring
408de80f95cSTom Joseph if (list_empty(&bridge->dma_ranges)) {
40906ff98fcSRob Herring of_property_read_u32(np, "cdns,no-bar-match-nbits",
410d07701a1SKishon Vijay Abraham I &no_bar_nbits);
411de80f95cSTom Joseph err = cdns_pcie_host_bar_ib_config(rc, RP_NO_BAR, 0x0,
4129aae3af8SQinglang Miao (u64)1 << no_bar_nbits, 0);
413ec64e279SRob Herring if (err)
414ec64e279SRob Herring dev_err(dev, "IB BAR: %d config failed\n", RP_NO_BAR);
415ec64e279SRob Herring return err;
416ec64e279SRob Herring }
417de80f95cSTom Joseph
418de80f95cSTom Joseph list_sort(NULL, &bridge->dma_ranges, cdns_pcie_host_dma_ranges_cmp);
419de80f95cSTom Joseph
420de80f95cSTom Joseph resource_list_for_each_entry(entry, &bridge->dma_ranges) {
421de80f95cSTom Joseph err = cdns_pcie_host_bar_config(rc, entry);
422de80f95cSTom Joseph if (err) {
423de80f95cSTom Joseph dev_err(dev, "Fail to configure IB using dma-ranges\n");
424ec64e279SRob Herring return err;
425de80f95cSTom Joseph }
426de80f95cSTom Joseph }
427de80f95cSTom Joseph
428d07701a1SKishon Vijay Abraham I return 0;
429d07701a1SKishon Vijay Abraham I }
430d07701a1SKishon Vijay Abraham I
cdns_pcie_host_init_address_translation(struct cdns_pcie_rc * rc)431de80f95cSTom Joseph static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc)
432de80f95cSTom Joseph {
433de80f95cSTom Joseph struct cdns_pcie *pcie = &rc->pcie;
434de80f95cSTom Joseph struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rc);
435de80f95cSTom Joseph struct resource *cfg_res = rc->cfg_res;
436de80f95cSTom Joseph struct resource_entry *entry;
437de80f95cSTom Joseph u64 cpu_addr = cfg_res->start;
43806ff98fcSRob Herring u32 addr0, addr1, desc1;
43906ff98fcSRob Herring int r, busnr = 0;
44006ff98fcSRob Herring
441de80f95cSTom Joseph entry = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
44206ff98fcSRob Herring if (entry)
443ec64e279SRob Herring busnr = entry->res->start;
444ec64e279SRob Herring
44506ff98fcSRob Herring /*
44606ff98fcSRob Herring * Reserve region 0 for PCI configure space accesses:
44706ff98fcSRob Herring * OB_REGION_PCI_ADDR0 and OB_REGION_DESC0 are updated dynamically by
448de80f95cSTom Joseph * cdns_pci_map_bus(), other region registers are set here once for all.
449ec64e279SRob Herring */
450ec64e279SRob Herring addr1 = 0; /* Should be programmed to zero. */
45106ff98fcSRob Herring desc1 = CDNS_PCIE_AT_OB_REGION_DESC1_BUS(busnr);
45206ff98fcSRob Herring cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(0), addr1);
45306ff98fcSRob Herring cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(0), desc1);
454de80f95cSTom Joseph
455de80f95cSTom Joseph if (pcie->ops->cpu_addr_fixup)
456de80f95cSTom Joseph cpu_addr = pcie->ops->cpu_addr_fixup(pcie, cpu_addr);
457de80f95cSTom Joseph
4589aae3af8SQinglang Miao addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(12) |
459de80f95cSTom Joseph (lower_32_bits(cpu_addr) & GENMASK(31, 8));
460de80f95cSTom Joseph addr1 = upper_32_bits(cpu_addr);
461de80f95cSTom Joseph cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(0), addr0);
462de80f95cSTom Joseph cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(0), addr1);
463de80f95cSTom Joseph
464de80f95cSTom Joseph r = 1;
465de80f95cSTom Joseph resource_list_for_each_entry(entry, &bridge->windows) {
466de80f95cSTom Joseph struct resource *res = entry->res;
467de80f95cSTom Joseph u64 pci_addr = res->start - entry->offset;
468de80f95cSTom Joseph
46924344226SRob Herring if (resource_type(res) == IORESOURCE_IO)
47024344226SRob Herring cdns_pcie_set_outbound_region(pcie, busnr, 0, r,
471de80f95cSTom Joseph true,
472de80f95cSTom Joseph pci_pio_to_address(res->start),
473de80f95cSTom Joseph pci_addr,
474de80f95cSTom Joseph resource_size(res));
475de80f95cSTom Joseph else
476de80f95cSTom Joseph cdns_pcie_set_outbound_region(pcie, busnr, 0, r,
477de80f95cSTom Joseph false,
478de80f95cSTom Joseph res->start,
4795d3d063aSKishon Vijay Abraham I pci_addr,
480de80f95cSTom Joseph resource_size(res));
481de80f95cSTom Joseph
482de80f95cSTom Joseph r++;
483de80f95cSTom Joseph }
484de80f95cSTom Joseph
485de80f95cSTom Joseph return cdns_pcie_host_map_dma_ranges(rc);
486de80f95cSTom Joseph }
487de80f95cSTom Joseph
cdns_pcie_host_init(struct cdns_pcie_rc * rc)488de80f95cSTom Joseph int cdns_pcie_host_init(struct cdns_pcie_rc *rc)
489de80f95cSTom Joseph {
490de80f95cSTom Joseph int err;
491de80f95cSTom Joseph
4927fb39bf2SKishon Vijay Abraham I err = cdns_pcie_host_init_root_port(rc);
493de80f95cSTom Joseph if (err)
494de80f95cSTom Joseph return err;
4957fb39bf2SKishon Vijay Abraham I
496de80f95cSTom Joseph return cdns_pcie_host_init_address_translation(rc);
497e2dcd20bSDejin Zheng }
498de80f95cSTom Joseph
cdns_pcie_host_link_setup(struct cdns_pcie_rc * rc)499de80f95cSTom Joseph int cdns_pcie_host_link_setup(struct cdns_pcie_rc *rc)
500de80f95cSTom Joseph {
501de80f95cSTom Joseph struct cdns_pcie *pcie = &rc->pcie;
502de80f95cSTom Joseph struct device *dev = rc->pcie.dev;
503de80f95cSTom Joseph int ret;
504de80f95cSTom Joseph
505da30e8bcSDejin Zheng if (rc->quirk_detect_quiet_flag)
506de80f95cSTom Joseph cdns_pcie_detect_quiet_min_delay_set(&rc->pcie);
507de80f95cSTom Joseph
508de80f95cSTom Joseph cdns_pcie_host_enable_ptm_response(pcie);
50909c24094SNadeem Athani
51009c24094SNadeem Athani ret = cdns_pcie_start_link(pcie);
51109c24094SNadeem Athani if (ret) {
512*a1f67bc1SChristian Gmeiner dev_err(dev, "Failed to start link\n");
513*a1f67bc1SChristian Gmeiner return ret;
51440d957e6SKishon Vijay Abraham I }
51540d957e6SKishon Vijay Abraham I
51640d957e6SKishon Vijay Abraham I ret = cdns_pcie_host_start_link(rc);
51740d957e6SKishon Vijay Abraham I if (ret)
51840d957e6SKishon Vijay Abraham I dev_dbg(dev, "PCIe link never came up\n");
51940d957e6SKishon Vijay Abraham I
5204740b969SNadeem Athani return 0;
52140d957e6SKishon Vijay Abraham I }
52240d957e6SKishon Vijay Abraham I
cdns_pcie_host_setup(struct cdns_pcie_rc * rc)52340d957e6SKishon Vijay Abraham I int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
5245d3d063aSKishon Vijay Abraham I {
5255d3d063aSKishon Vijay Abraham I struct device *dev = rc->pcie.dev;
5265d3d063aSKishon Vijay Abraham I struct platform_device *pdev = to_platform_device(dev);
52724344226SRob Herring struct device_node *np = dev->of_node;
528de80f95cSTom Joseph struct pci_host_bridge *bridge;
52919abcd79SKishon Vijay Abraham I enum cdns_pcie_rp_bar bar;
530de80f95cSTom Joseph struct cdns_pcie *pcie;
531c4c10c01SKishon Vijay Abraham I struct resource *res;
532de80f95cSTom Joseph int ret;
533de80f95cSTom Joseph
534de80f95cSTom Joseph bridge = pci_host_bridge_from_priv(rc);
535de80f95cSTom Joseph if (!bridge)
53624344226SRob Herring return -ENOMEM;
537de80f95cSTom Joseph
538de80f95cSTom Joseph pcie = &rc->pcie;
539de80f95cSTom Joseph pcie->is_rc = true;
540de80f95cSTom Joseph
541de80f95cSTom Joseph rc->vendor_id = 0xffff;
542de80f95cSTom Joseph of_property_read_u32(np, "vendor-id", &rc->vendor_id);
543de80f95cSTom Joseph
544de80f95cSTom Joseph rc->device_id = 0xffff;
545 of_property_read_u32(np, "device-id", &rc->device_id);
546
547 pcie->reg_base = devm_platform_ioremap_resource_byname(pdev, "reg");
548 if (IS_ERR(pcie->reg_base)) {
549 dev_err(dev, "missing \"reg\"\n");
550 return PTR_ERR(pcie->reg_base);
551 }
552
553 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
554 rc->cfg_base = devm_pci_remap_cfg_resource(dev, res);
555 if (IS_ERR(rc->cfg_base))
556 return PTR_ERR(rc->cfg_base);
557 rc->cfg_res = res;
558
559 ret = cdns_pcie_host_link_setup(rc);
560 if (ret)
561 return ret;
562
563 for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++)
564 rc->avail_ib_bar[bar] = true;
565
566 ret = cdns_pcie_host_init(rc);
567 if (ret)
568 return ret;
569
570 if (!bridge->ops)
571 bridge->ops = &cdns_pcie_host_ops;
572
573 ret = pci_host_probe(bridge);
574 if (ret < 0)
575 goto err_init;
576
577 return 0;
578
579 err_init:
580 pm_runtime_put_sync(dev);
581
582 return ret;
583 }
584