File Index
Files and directories
Sign in
FILE

pcie-cadence-host-common.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pci/controller/cadence/pcie-cadence-host-common.c 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.