Skip to content

Commit 3a3d4ca

Browse files
floatiouskwilczynski
authored andcommitted
PCI: dwc: ep: Allow EPF drivers to configure the size of Resizable BARs
The DWC databook specifies three different BARn_SIZING_SCHEME_N as: - Fixed Mask (0) - Programmable Mask (1) - Resizable BAR (2) Each of these sizing schemes have different instructions for how to initialize the BAR. The DWC driver currently does not support resizable BARs. Instead, in order to somewhat support resizable BARs, the DWC EP driver currently has an ugly hack that force sets a resizable BAR to 1 MB, if such a BAR is detected. Additionally, this hack only works if the DWC glue driver also has lied in their EPC features, and claimed that the resizable BAR is a 1 MB fixed size BAR. This is unintuitive (as you somehow need to know that you need to lie in your EPC features), but other than that it is overly restrictive, since a resizable BAR is capable of supporting sizes different than 1 MB. Add proper support for resizable BARs in the DWC EP driver. Note that the pci_epc_set_bar() API takes a struct pci_epf_bar which tells the EPC driver how it wants to configure the BAR. struct pci_epf_bar only has a single size struct member. This means that an EPC driver will only be able to set a single supported size. This is perfectly fine, as we do not need the complexity of allowing a host to change the size of the BAR. If someone ever wants to support resizing a resizable BAR, the pci_epc_set_bar() API can be extended in the future. With these changes, we allow an EPF driver to configure the size of Resizable BARs, rather than forcing them to a 1 MB size. This means that an EPC driver does not need to lie in EPC features, and an EPF driver will be able to set an arbitrary size (not be forced to a 1 MB size), just like BAR_PROGRAMMABLE. Signed-off-by: Niklas Cassel <[email protected]> Reviewed-by: Manivannan Sadhasivam <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Manivannan Sadhasivam <[email protected]> [kwilczynski: commit log] Signed-off-by: Krzysztof Wilczyński <[email protected]>
1 parent 30a172d commit 3a3d4ca

File tree

1 file changed

+167
-15
lines changed

1 file changed

+167
-15
lines changed

drivers/pci/controller/dwc/pcie-designware-ep.c

Lines changed: 167 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -223,16 +223,135 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
223223
ep->bar_to_atu[bar] = 0;
224224
}
225225

226+
static unsigned int dw_pcie_ep_get_rebar_offset(struct dw_pcie *pci,
227+
enum pci_barno bar)
228+
{
229+
u32 reg, bar_index;
230+
unsigned int offset, nbars;
231+
int i;
232+
233+
offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
234+
if (!offset)
235+
return offset;
236+
237+
reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL);
238+
nbars = (reg & PCI_REBAR_CTRL_NBAR_MASK) >> PCI_REBAR_CTRL_NBAR_SHIFT;
239+
240+
for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) {
241+
reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL);
242+
bar_index = reg & PCI_REBAR_CTRL_BAR_IDX;
243+
if (bar_index == bar)
244+
return offset;
245+
}
246+
247+
return 0;
248+
}
249+
250+
static int dw_pcie_ep_set_bar_resizable(struct dw_pcie_ep *ep, u8 func_no,
251+
struct pci_epf_bar *epf_bar)
252+
{
253+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
254+
enum pci_barno bar = epf_bar->barno;
255+
size_t size = epf_bar->size;
256+
int flags = epf_bar->flags;
257+
u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar);
258+
unsigned int rebar_offset;
259+
u32 rebar_cap, rebar_ctrl;
260+
int ret;
261+
262+
rebar_offset = dw_pcie_ep_get_rebar_offset(pci, bar);
263+
if (!rebar_offset)
264+
return -EINVAL;
265+
266+
ret = pci_epc_bar_size_to_rebar_cap(size, &rebar_cap);
267+
if (ret)
268+
return ret;
269+
270+
dw_pcie_dbi_ro_wr_en(pci);
271+
272+
/*
273+
* A BAR mask should not be written for a resizable BAR. The BAR mask
274+
* is automatically derived by the controller every time the "selected
275+
* size" bits are updated, see "Figure 3-26 Resizable BAR Example for
276+
* 32-bit Memory BAR0" in DWC EP databook 5.96a. We simply need to write
277+
* BIT(0) to set the BAR enable bit.
278+
*/
279+
dw_pcie_ep_writel_dbi2(ep, func_no, reg, BIT(0));
280+
dw_pcie_ep_writel_dbi(ep, func_no, reg, flags);
281+
282+
if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
283+
dw_pcie_ep_writel_dbi2(ep, func_no, reg + 4, 0);
284+
dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0);
285+
}
286+
287+
/*
288+
* Bits 31:0 in PCI_REBAR_CAP define "supported sizes" bits for sizes
289+
* 1 MB to 128 TB. Bits 31:16 in PCI_REBAR_CTRL define "supported sizes"
290+
* bits for sizes 256 TB to 8 EB. Disallow sizes 256 TB to 8 EB.
291+
*/
292+
rebar_ctrl = dw_pcie_readl_dbi(pci, rebar_offset + PCI_REBAR_CTRL);
293+
rebar_ctrl &= ~GENMASK(31, 16);
294+
dw_pcie_writel_dbi(pci, rebar_offset + PCI_REBAR_CTRL, rebar_ctrl);
295+
296+
/*
297+
* The "selected size" (bits 13:8) in PCI_REBAR_CTRL are automatically
298+
* updated when writing PCI_REBAR_CAP, see "Figure 3-26 Resizable BAR
299+
* Example for 32-bit Memory BAR0" in DWC EP databook 5.96a.
300+
*/
301+
dw_pcie_writel_dbi(pci, rebar_offset + PCI_REBAR_CAP, rebar_cap);
302+
303+
dw_pcie_dbi_ro_wr_dis(pci);
304+
305+
return 0;
306+
}
307+
308+
static int dw_pcie_ep_set_bar_programmable(struct dw_pcie_ep *ep, u8 func_no,
309+
struct pci_epf_bar *epf_bar)
310+
{
311+
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
312+
enum pci_barno bar = epf_bar->barno;
313+
size_t size = epf_bar->size;
314+
int flags = epf_bar->flags;
315+
u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar);
316+
317+
dw_pcie_dbi_ro_wr_en(pci);
318+
319+
dw_pcie_ep_writel_dbi2(ep, func_no, reg, lower_32_bits(size - 1));
320+
dw_pcie_ep_writel_dbi(ep, func_no, reg, flags);
321+
322+
if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
323+
dw_pcie_ep_writel_dbi2(ep, func_no, reg + 4, upper_32_bits(size - 1));
324+
dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0);
325+
}
326+
327+
dw_pcie_dbi_ro_wr_dis(pci);
328+
329+
return 0;
330+
}
331+
332+
static enum pci_epc_bar_type dw_pcie_ep_get_bar_type(struct dw_pcie_ep *ep,
333+
enum pci_barno bar)
334+
{
335+
const struct pci_epc_features *epc_features;
336+
337+
if (!ep->ops->get_features)
338+
return BAR_PROGRAMMABLE;
339+
340+
epc_features = ep->ops->get_features(ep);
341+
342+
return epc_features->bar[bar].type;
343+
}
344+
226345
static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
227346
struct pci_epf_bar *epf_bar)
228347
{
229348
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
230349
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
231350
enum pci_barno bar = epf_bar->barno;
232351
size_t size = epf_bar->size;
352+
enum pci_epc_bar_type bar_type;
233353
int flags = epf_bar->flags;
234354
int ret, type;
235-
u32 reg;
236355

237356
/*
238357
* DWC does not allow BAR pairs to overlap, e.g. you cannot combine BARs
@@ -264,19 +383,30 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
264383
goto config_atu;
265384
}
266385

267-
reg = PCI_BASE_ADDRESS_0 + (4 * bar);
268-
269-
dw_pcie_dbi_ro_wr_en(pci);
270-
271-
dw_pcie_ep_writel_dbi2(ep, func_no, reg, lower_32_bits(size - 1));
272-
dw_pcie_ep_writel_dbi(ep, func_no, reg, flags);
273-
274-
if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) {
275-
dw_pcie_ep_writel_dbi2(ep, func_no, reg + 4, upper_32_bits(size - 1));
276-
dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0);
386+
bar_type = dw_pcie_ep_get_bar_type(ep, bar);
387+
switch (bar_type) {
388+
case BAR_FIXED:
389+
/*
390+
* There is no need to write a BAR mask for a fixed BAR (except
391+
* to write 1 to the LSB of the BAR mask register, to enable the
392+
* BAR). Write the BAR mask regardless. (The fixed bits in the
393+
* BAR mask register will be read-only anyway.)
394+
*/
395+
fallthrough;
396+
case BAR_PROGRAMMABLE:
397+
ret = dw_pcie_ep_set_bar_programmable(ep, func_no, epf_bar);
398+
break;
399+
case BAR_RESIZABLE:
400+
ret = dw_pcie_ep_set_bar_resizable(ep, func_no, epf_bar);
401+
break;
402+
default:
403+
ret = -EINVAL;
404+
dev_err(pci->dev, "Invalid BAR type\n");
405+
break;
277406
}
278407

279-
dw_pcie_dbi_ro_wr_dis(pci);
408+
if (ret)
409+
return ret;
280410

281411
config_atu:
282412
if (!(flags & PCI_BASE_ADDRESS_SPACE))
@@ -710,9 +840,11 @@ EXPORT_SYMBOL_GPL(dw_pcie_ep_deinit);
710840

711841
static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci)
712842
{
843+
struct dw_pcie_ep *ep = &pci->ep;
713844
unsigned int offset;
714845
unsigned int nbars;
715-
u32 reg, i;
846+
enum pci_barno bar;
847+
u32 reg, i, val;
716848

717849
offset = dw_pcie_ep_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
718850

@@ -727,9 +859,29 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci)
727859
* PCIe r6.0, sec 7.8.6.2 require us to support at least one
728860
* size in the range from 1 MB to 512 GB. Advertise support
729861
* for 1 MB BAR size only.
862+
*
863+
* For a BAR that has been configured via dw_pcie_ep_set_bar(),
864+
* advertise support for only that size instead.
730865
*/
731-
for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL)
732-
dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, BIT(4));
866+
for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) {
867+
/*
868+
* While the RESBAR_CAP_REG_* fields are sticky, the
869+
* RESBAR_CTRL_REG_BAR_SIZE field is non-sticky (it is
870+
* sticky in certain versions of DWC PCIe, but not all).
871+
*
872+
* RESBAR_CTRL_REG_BAR_SIZE is updated automatically by
873+
* the controller when RESBAR_CAP_REG is written, which
874+
* is why RESBAR_CAP_REG is written here.
875+
*/
876+
val = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL);
877+
bar = val & PCI_REBAR_CTRL_BAR_IDX;
878+
if (ep->epf_bar[bar])
879+
pci_epc_bar_size_to_rebar_cap(ep->epf_bar[bar]->size, &val);
880+
else
881+
val = BIT(4);
882+
883+
dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, val);
884+
}
733885
}
734886

735887
dw_pcie_setup(pci);

0 commit comments

Comments
 (0)