mirror of
https://github.com/torvalds/linux.git
synced 2026-05-22 14:12:07 +02:00
Merge branch 'pci/endpoint'
- Destroy the EPC device in devm_pci_epc_destroy(), which previously didn't call devres_release() (Zijun Hu) - Simplify pci_epc_get() with class_find_device_by_name() (Zijun Hu) - Finish virtual EP removal in pci_epf_remove_vepf(), which previously caused a subsequent pci_epf_add_vepf() to fail with -EBUSY (Zijun Hu) - Write BAR_MASK before iATU registers in pci_epc_set_bar() so we don't depend on the BAR_MASK reset value being larger than the requested BAR size (Niklas Cassel) - Prevent changing BAR size/flags in pci_epc_set_bar() to prevent reads from bypassing the iATU if we reduced the BAR size (Niklas Cassel) - Verify address alignment when programming iATU so we don't attempt to write bits that are read-only because of the BAR size, which could lead to directing accesses to the wrong address (Niklas Cassel) - Implement artpec6 pci_epc_features so we can rely on all drivers supporting it so we can use it in EPC core code (Niklas Cassel) - Check for BARs of fixed size to prevent endpoint drivers from trying to change their size (Niklas Cassel) - Verify that requested BAR size is a power of two when endpoint driver sets the BAR (Niklas Cassel) * pci/endpoint: PCI: endpoint: Verify that requested BAR size is a power of two PCI: endpoint: Add size check for fixed size BARs in pci_epc_set_bar() PCI: artpec6: Implement dw_pcie_ep operation get_features PCI: dwc: ep: Add 'address' alignment to 'size' check in dw_pcie_prog_ep_inbound_atu() PCI: dwc: ep: Prevent changing BAR size/flags in pci_epc_set_bar() PCI: dwc: ep: Write BAR_MASK before iATU registers in pci_epc_set_bar() PCI: endpoint: Finish virtual EP removal in pci_epf_remove_vepf() PCI: endpoint: Simplify pci_epc_get() PCI: endpoint: Destroy the EPC device in devm_pci_epc_destroy() PCI: endpoint: Replace magic number '6' by PCI_STD_NUM_BARS
This commit is contained in:
commit
74855f6697
|
|
@ -369,9 +369,22 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_epc_features artpec6_pcie_epc_features = {
|
||||
.linkup_notifier = false,
|
||||
.msi_capable = true,
|
||||
.msix_capable = false,
|
||||
};
|
||||
|
||||
static const struct pci_epc_features *
|
||||
artpec6_pcie_get_features(struct dw_pcie_ep *ep)
|
||||
{
|
||||
return &artpec6_pcie_epc_features;
|
||||
}
|
||||
|
||||
static const struct dw_pcie_ep_ops pcie_ep_ops = {
|
||||
.init = artpec6_pcie_ep_init,
|
||||
.raise_irq = artpec6_pcie_raise_irq,
|
||||
.get_features = artpec6_pcie_get_features,
|
||||
};
|
||||
|
||||
static int artpec6_pcie_probe(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -128,7 +128,8 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
|||
}
|
||||
|
||||
static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
|
||||
dma_addr_t cpu_addr, enum pci_barno bar)
|
||||
dma_addr_t cpu_addr, enum pci_barno bar,
|
||||
size_t size)
|
||||
{
|
||||
int ret;
|
||||
u32 free_win;
|
||||
|
|
@ -145,7 +146,7 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
|
|||
}
|
||||
|
||||
ret = dw_pcie_prog_ep_inbound_atu(pci, func_no, free_win, type,
|
||||
cpu_addr, bar);
|
||||
cpu_addr, bar, size);
|
||||
if (ret < 0) {
|
||||
dev_err(pci->dev, "Failed to program IB window\n");
|
||||
return ret;
|
||||
|
|
@ -222,20 +223,31 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
|||
if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (bar & 1))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Certain EPF drivers dynamically change the physical address of a BAR
|
||||
* (i.e. they call set_bar() twice, without ever calling clear_bar(), as
|
||||
* calling clear_bar() would clear the BAR's PCI address assigned by the
|
||||
* host).
|
||||
*/
|
||||
if (ep->epf_bar[bar]) {
|
||||
/*
|
||||
* We can only dynamically change a BAR if the new BAR size and
|
||||
* BAR flags do not differ from the existing configuration.
|
||||
*/
|
||||
if (ep->epf_bar[bar]->barno != bar ||
|
||||
ep->epf_bar[bar]->size != size ||
|
||||
ep->epf_bar[bar]->flags != flags)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* When dynamically changing a BAR, skip writing the BAR reg, as
|
||||
* that would clear the BAR's PCI address assigned by the host.
|
||||
*/
|
||||
goto config_atu;
|
||||
}
|
||||
|
||||
reg = PCI_BASE_ADDRESS_0 + (4 * bar);
|
||||
|
||||
if (!(flags & PCI_BASE_ADDRESS_SPACE))
|
||||
type = PCIE_ATU_TYPE_MEM;
|
||||
else
|
||||
type = PCIE_ATU_TYPE_IO;
|
||||
|
||||
ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ep->epf_bar[bar])
|
||||
return 0;
|
||||
|
||||
dw_pcie_dbi_ro_wr_en(pci);
|
||||
|
||||
dw_pcie_ep_writel_dbi2(ep, func_no, reg, lower_32_bits(size - 1));
|
||||
|
|
@ -246,9 +258,21 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
|||
dw_pcie_ep_writel_dbi(ep, func_no, reg + 4, 0);
|
||||
}
|
||||
|
||||
ep->epf_bar[bar] = epf_bar;
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
|
||||
config_atu:
|
||||
if (!(flags & PCI_BASE_ADDRESS_SPACE))
|
||||
type = PCIE_ATU_TYPE_MEM;
|
||||
else
|
||||
type = PCIE_ATU_TYPE_IO;
|
||||
|
||||
ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar,
|
||||
size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ep->epf_bar[bar] = epf_bar;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -597,11 +597,12 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
|
|||
}
|
||||
|
||||
int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
|
||||
int type, u64 cpu_addr, u8 bar)
|
||||
int type, u64 cpu_addr, u8 bar, size_t size)
|
||||
{
|
||||
u32 retries, val;
|
||||
|
||||
if (!IS_ALIGNED(cpu_addr, pci->region_align))
|
||||
if (!IS_ALIGNED(cpu_addr, pci->region_align) ||
|
||||
!IS_ALIGNED(cpu_addr, size))
|
||||
return -EINVAL;
|
||||
|
||||
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
|
||||
|
|
|
|||
|
|
@ -491,7 +491,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
|
|||
int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
|
||||
u64 cpu_addr, u64 pci_addr, u64 size);
|
||||
int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
|
||||
int type, u64 cpu_addr, u8 bar);
|
||||
int type, u64 cpu_addr, u8 bar, size_t size);
|
||||
void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
|
||||
void dw_pcie_setup(struct dw_pcie *pci);
|
||||
void dw_pcie_iatu_detect(struct dw_pcie *pci);
|
||||
|
|
|
|||
|
|
@ -60,26 +60,17 @@ struct pci_epc *pci_epc_get(const char *epc_name)
|
|||
int ret = -EINVAL;
|
||||
struct pci_epc *epc;
|
||||
struct device *dev;
|
||||
struct class_dev_iter iter;
|
||||
|
||||
class_dev_iter_init(&iter, &pci_epc_class, NULL, NULL);
|
||||
while ((dev = class_dev_iter_next(&iter))) {
|
||||
if (strcmp(epc_name, dev_name(dev)))
|
||||
continue;
|
||||
dev = class_find_device_by_name(&pci_epc_class, epc_name);
|
||||
if (!dev)
|
||||
goto err;
|
||||
|
||||
epc = to_pci_epc(dev);
|
||||
if (!try_module_get(epc->ops->owner)) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
class_dev_iter_exit(&iter);
|
||||
get_device(&epc->dev);
|
||||
epc = to_pci_epc(dev);
|
||||
if (try_module_get(epc->ops->owner))
|
||||
return epc;
|
||||
}
|
||||
|
||||
err:
|
||||
class_dev_iter_exit(&iter);
|
||||
put_device(dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_epc_get);
|
||||
|
|
@ -609,10 +600,20 @@ EXPORT_SYMBOL_GPL(pci_epc_clear_bar);
|
|||
int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
||||
struct pci_epf_bar *epf_bar)
|
||||
{
|
||||
int ret;
|
||||
const struct pci_epc_features *epc_features;
|
||||
enum pci_barno bar = epf_bar->barno;
|
||||
int flags = epf_bar->flags;
|
||||
int ret;
|
||||
|
||||
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
|
||||
epc_features = pci_epc_get_features(epc, func_no, vfunc_no);
|
||||
if (!epc_features)
|
||||
return -EINVAL;
|
||||
|
||||
if (epc_features->bar[bar].type == BAR_FIXED &&
|
||||
(epc_features->bar[bar].fixed_size != epf_bar->size))
|
||||
return -EINVAL;
|
||||
|
||||
if (!is_power_of_2(epf_bar->size))
|
||||
return -EINVAL;
|
||||
|
||||
if ((epf_bar->barno == BAR_5 && flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
|
||||
|
|
@ -942,7 +943,7 @@ void devm_pci_epc_destroy(struct device *dev, struct pci_epc *epc)
|
|||
{
|
||||
int r;
|
||||
|
||||
r = devres_destroy(dev, devm_pci_epc_release, devm_pci_epc_match,
|
||||
r = devres_release(dev, devm_pci_epc_release, devm_pci_epc_match,
|
||||
epc);
|
||||
dev_WARN_ONCE(dev, r, "couldn't find PCI EPC resource\n");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ void pci_epf_remove_vepf(struct pci_epf *epf_pf, struct pci_epf *epf_vf)
|
|||
|
||||
mutex_lock(&epf_pf->lock);
|
||||
clear_bit(epf_vf->vfunc_no, &epf_pf->vfunction_num_map);
|
||||
epf_vf->epf_pf = NULL;
|
||||
list_del(&epf_vf->list);
|
||||
mutex_unlock(&epf_pf->lock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ struct pci_epf {
|
|||
struct device dev;
|
||||
const char *name;
|
||||
struct pci_epf_header *header;
|
||||
struct pci_epf_bar bar[6];
|
||||
struct pci_epf_bar bar[PCI_STD_NUM_BARS];
|
||||
u8 msi_interrupts;
|
||||
u16 msix_interrupts;
|
||||
u8 func_no;
|
||||
|
|
@ -174,7 +174,7 @@ struct pci_epf {
|
|||
/* Below members are to attach secondary EPC to an endpoint function */
|
||||
struct pci_epc *sec_epc;
|
||||
struct list_head sec_epc_list;
|
||||
struct pci_epf_bar sec_epc_bar[6];
|
||||
struct pci_epf_bar sec_epc_bar[PCI_STD_NUM_BARS];
|
||||
u8 sec_epc_func_no;
|
||||
struct config_group *group;
|
||||
unsigned int is_bound;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user