FILE
pcie-cadence-host-common.c
File preview with syntax highlighting.
linux-6.19.14/drivers/pci/controller/cadence/pcie-cadence-host-common.c
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Cadence PCIe host controller library. 4 * 5 * Copyright (c) 2017 Cadence 6 * Author: Cyrille Pitchen <cyrille.pitchen@free-electrons.com> 7 */ 8#include <linux/delay.h> 9#include <linux/kernel.h> 10#include <linux/list_sort.h> 11#include <linux/of_address.h> 12#include <linux/of_pci.h> 13#include <linux/platform_device.h> 14 15#include "pcie-cadence.h" 16#include "pcie-cadence-host-common.h" 17 18#define LINK_RETRAIN_TIMEOUT HZ 19 20u64 bar_max_size[] = { 21 [RP_BAR0] = _ULL(128 * SZ_2G), 22 [RP_BAR1] = SZ_2G, 23 [RP_NO_BAR] = _BITULL(63), 24}; 25EXPORT_SYMBOL_GPL(bar_max_size); 26 27int cdns_pcie_host_training_complete(struct cdns_pcie *pcie) 28{ 29 u32 pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET; 30 unsigned long end_jiffies; 31 u16 lnk_stat; 32 33 /* Wait for link training to complete. Exit after timeout. */ 34 end_jiffies = jiffies + LINK_RETRAIN_TIMEOUT; 35 do { 36 lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA); 37 if (!(lnk_stat & PCI_EXP_LNKSTA_LT)) 38 break; 39 usleep_range(0, 1000); 40 } while (time_before(jiffies, end_jiffies)); 41 42 if (!(lnk_stat & PCI_EXP_LNKSTA_LT)) 43 return 0; 44 45 return -ETIMEDOUT; 46} 47EXPORT_SYMBOL_GPL(cdns_pcie_host_training_complete); 48 49int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie, 50 cdns_pcie_linkup_func pcie_link_up) 51{ 52 struct device *dev = pcie->dev; 53 int retries; 54 55 /* Check if the link is up or not */ 56 for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { 57 if (pcie_link_up(pcie)) { 58 dev_info(dev, "Link up\n"); 59 return 0; 60 } 61 usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); 62 } 63 64 return -ETIMEDOUT; 65} 66EXPORT_SYMBOL_GPL(cdns_pcie_host_wait_for_link); 67 68int cdns_pcie_retrain(struct cdns_pcie *pcie, 69 cdns_pcie_linkup_func pcie_link_up) 70{ 71 u32 lnk_cap_sls, pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET; 72 u16 lnk_stat, lnk_ctl; 73 int ret = 0; 74 75 /* 76 * Set retrain bit if current speed is 2.5 GB/s, 77 * but the PCIe root port support is > 2.5 GB/s. 78 */ 79 80 lnk_cap_sls = cdns_pcie_readl(pcie, (CDNS_PCIE_RP_BASE + pcie_cap_off + 81 PCI_EXP_LNKCAP)); 82 if ((lnk_cap_sls & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB) 83 return ret; 84 85 lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA); 86 if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) { 87 lnk_ctl = cdns_pcie_rp_readw(pcie, 88 pcie_cap_off + PCI_EXP_LNKCTL); 89 lnk_ctl |= PCI_EXP_LNKCTL_RL; 90 cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL, 91 lnk_ctl); 92 93 ret = cdns_pcie_host_training_complete(pcie); 94 if (ret) 95 return ret; 96 97 ret = cdns_pcie_host_wait_for_link(pcie, pcie_link_up); 98 } 99 return ret; 100} 101EXPORT_SYMBOL_GPL(cdns_pcie_retrain); 102 103int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc, 104 cdns_pcie_linkup_func pcie_link_up) 105{ 106 struct cdns_pcie *pcie = &rc->pcie; 107 int ret; 108 109 ret = cdns_pcie_host_wait_for_link(pcie, pcie_link_up); 110 111 /* 112 * Retrain link for Gen2 training defect 113 * if quirk flag is set. 114 */ 115 if (!ret && rc->quirk_retrain_flag) 116 ret = cdns_pcie_retrain(pcie, pcie_link_up); 117 118 return ret; 119} 120EXPORT_SYMBOL_GPL(cdns_pcie_host_start_link); 121 122enum cdns_pcie_rp_bar 123cdns_pcie_host_find_min_bar(struct cdns_pcie_rc *rc, u64 size) 124{ 125 enum cdns_pcie_rp_bar bar, sel_bar; 126 127 sel_bar = RP_BAR_UNDEFINED; 128 for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) { 129 if (!rc->avail_ib_bar[bar]) 130 continue; 131 132 if (size <= bar_max_size[bar]) { 133 if (sel_bar == RP_BAR_UNDEFINED) { 134 sel_bar = bar; 135 continue; 136 } 137 138 if (bar_max_size[bar] < bar_max_size[sel_bar]) 139 sel_bar = bar; 140 } 141 } 142 143 return sel_bar; 144} 145EXPORT_SYMBOL_GPL(cdns_pcie_host_find_min_bar); 146 147enum cdns_pcie_rp_bar 148cdns_pcie_host_find_max_bar(struct cdns_pcie_rc *rc, u64 size) 149{ 150 enum cdns_pcie_rp_bar bar, sel_bar; 151 152 sel_bar = RP_BAR_UNDEFINED; 153 for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) { 154 if (!rc->avail_ib_bar[bar]) 155 continue; 156 157 if (size >= bar_max_size[bar]) { 158 if (sel_bar == RP_BAR_UNDEFINED) { 159 sel_bar = bar; 160 continue; 161 } 162 163 if (bar_max_size[bar] > bar_max_size[sel_bar]) 164 sel_bar = bar; 165 } 166 } 167 168 return sel_bar; 169} 170EXPORT_SYMBOL_GPL(cdns_pcie_host_find_max_bar); 171 172int cdns_pcie_host_dma_ranges_cmp(void *priv, const struct list_head *a, 173 const struct list_head *b) 174{ 175 struct resource_entry *entry1, *entry2; 176 u64 size1, size2; 177 178 entry1 = container_of(a, struct resource_entry, node); 179 entry2 = container_of(b, struct resource_entry, node); 180 181 size1 = resource_size(entry1->res); 182 size2 = resource_size(entry2->res); 183 184 if (size1 > size2) 185 return -1; 186 187 if (size1 < size2) 188 return 1; 189 190 return 0; 191} 192EXPORT_SYMBOL_GPL(cdns_pcie_host_dma_ranges_cmp); 193 194int cdns_pcie_host_bar_config(struct cdns_pcie_rc *rc, 195 struct resource_entry *entry, 196 cdns_pcie_host_bar_ib_cfg pci_host_ib_config) 197{ 198 struct cdns_pcie *pcie = &rc->pcie; 199 struct device *dev = pcie->dev; 200 u64 cpu_addr, size, winsize; 201 enum cdns_pcie_rp_bar bar; 202 unsigned long flags; 203 int ret; 204 205 cpu_addr = entry->res->start; 206 flags = entry->res->flags; 207 size = resource_size(entry->res); 208 209 while (size > 0) { 210 /* 211 * Try to find a minimum BAR whose size is greater than 212 * or equal to the remaining resource_entry size. This will 213 * fail if the size of each of the available BARs is less than 214 * the remaining resource_entry size. 215 * 216 * If a minimum BAR is found, IB ATU will be configured and 217 * exited. 218 */ 219 bar = cdns_pcie_host_find_min_bar(rc, size); 220 if (bar != RP_BAR_UNDEFINED) { 221 ret = pci_host_ib_config(rc, bar, cpu_addr, size, flags); 222 if (ret) 223 dev_err(dev, "IB BAR: %d config failed\n", bar); 224 return ret; 225 } 226 227 /* 228 * If the control reaches here, it would mean the remaining 229 * resource_entry size cannot be fitted in a single BAR. So we 230 * find a maximum BAR whose size is less than or equal to the 231 * remaining resource_entry size and split the resource entry 232 * so that part of resource entry is fitted inside the maximum 233 * BAR. The remaining size would be fitted during the next 234 * iteration of the loop. 235 * 236 * If a maximum BAR is not found, there is no way we can fit 237 * this resource_entry, so we error out. 238 */ 239 bar = cdns_pcie_host_find_max_bar(rc, size); 240 if (bar == RP_BAR_UNDEFINED) { 241 dev_err(dev, "No free BAR to map cpu_addr %llx\n", 242 cpu_addr); 243 return -EINVAL; 244 } 245 246 winsize = bar_max_size[bar]; 247 ret = pci_host_ib_config(rc, bar, cpu_addr, winsize, flags); 248 if (ret) { 249 dev_err(dev, "IB BAR: %d config failed\n", bar); 250 return ret; 251 } 252 253 size -= winsize; 254 cpu_addr += winsize; 255 } 256 257 return 0; 258} 259 260int cdns_pcie_host_map_dma_ranges(struct cdns_pcie_rc *rc, 261 cdns_pcie_host_bar_ib_cfg pci_host_ib_config) 262{ 263 struct cdns_pcie *pcie = &rc->pcie; 264 struct device *dev = pcie->dev; 265 struct device_node *np = dev->of_node; 266 struct pci_host_bridge *bridge; 267 struct resource_entry *entry; 268 u32 no_bar_nbits = 32; 269 int err; 270 271 bridge = pci_host_bridge_from_priv(rc); 272 if (!bridge) 273 return -ENOMEM; 274 275 if (list_empty(&bridge->dma_ranges)) { 276 of_property_read_u32(np, "cdns,no-bar-match-nbits", 277 &no_bar_nbits); 278 err = pci_host_ib_config(rc, RP_NO_BAR, 0x0, (u64)1 << no_bar_nbits, 0); 279 if (err) 280 dev_err(dev, "IB BAR: %d config failed\n", RP_NO_BAR); 281 return err; 282 } 283 284 list_sort(NULL, &bridge->dma_ranges, cdns_pcie_host_dma_ranges_cmp); 285 286 resource_list_for_each_entry(entry, &bridge->dma_ranges) { 287 err = cdns_pcie_host_bar_config(rc, entry, pci_host_ib_config); 288 if (err) { 289 dev_err(dev, "Fail to configure IB using dma-ranges\n"); 290 return err; 291 } 292 } 293 294 return 0; 295} 296 297MODULE_LICENSE("GPL"); 298MODULE_DESCRIPTION("Cadence PCIe host controller driver");
~/
Read-only file preview.