Merge branch 'pci/dwc'
- Always set DesignWare "TLP Digest" bit so generic code can enable ECRC via the AER Capability (Vidya Sagar) - Drop support for config space in DT 'ranges' (Rob Herring) - Increase width of outbound iATU size to u64 (Shradha Todi) - Add upper limit address for outbound iATU (Shradha Todi) - Allow dwc-based drivers that don't override any default ops (Jisheng Zhang) - Drop unnecessary dw_pcie_ops from the al driver (Jisheng Zhang) * pci/dwc: PCI: al: Remove useless dw_pcie_ops PCI: dwc: Don't assume the ops in dw_pcie always exist PCI: dwc: Add upper limit address for outbound iATU PCI: dwc: Change size to u64 for EP outbound iATU PCI: dwc: Drop support for config space in 'ranges' PCI: dwc: Work around ECRC configuration issue
This commit is contained in:
Коммит
29b10c606f
|
@ -314,9 +314,6 @@ static const struct dw_pcie_host_ops al_pcie_host_ops = {
|
|||
.host_init = al_pcie_host_init,
|
||||
};
|
||||
|
||||
static const struct dw_pcie_ops dw_pcie_ops = {
|
||||
};
|
||||
|
||||
static int al_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -334,7 +331,6 @@ static int al_pcie_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
pci->dev = dev;
|
||||
pci->ops = &dw_pcie_ops;
|
||||
pci->pp.ops = &al_pcie_host_ops;
|
||||
|
||||
al_pcie->pci = pci;
|
||||
|
|
|
@ -434,10 +434,8 @@ static void dw_pcie_ep_stop(struct pci_epc *epc)
|
|||
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
|
||||
if (!pci->ops->stop_link)
|
||||
return;
|
||||
|
||||
pci->ops->stop_link(pci);
|
||||
if (pci->ops && pci->ops->stop_link)
|
||||
pci->ops->stop_link(pci);
|
||||
}
|
||||
|
||||
static int dw_pcie_ep_start(struct pci_epc *epc)
|
||||
|
@ -445,7 +443,7 @@ static int dw_pcie_ep_start(struct pci_epc *epc)
|
|||
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
|
||||
if (!pci->ops->start_link)
|
||||
if (!pci->ops || !pci->ops->start_link)
|
||||
return -EINVAL;
|
||||
|
||||
return pci->ops->start_link(pci);
|
||||
|
|
|
@ -305,8 +305,13 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
if (cfg_res) {
|
||||
pp->cfg0_size = resource_size(cfg_res);
|
||||
pp->cfg0_base = cfg_res->start;
|
||||
} else if (!pp->va_cfg0_base) {
|
||||
|
||||
pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, cfg_res);
|
||||
if (IS_ERR(pp->va_cfg0_base))
|
||||
return PTR_ERR(pp->va_cfg0_base);
|
||||
} else {
|
||||
dev_err(dev, "Missing *config* reg space\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!pci->dbi_base) {
|
||||
|
@ -322,38 +327,12 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
|
||||
pp->bridge = bridge;
|
||||
|
||||
/* Get the I/O and memory ranges from DT */
|
||||
resource_list_for_each_entry(win, &bridge->windows) {
|
||||
switch (resource_type(win->res)) {
|
||||
case IORESOURCE_IO:
|
||||
pp->io_size = resource_size(win->res);
|
||||
pp->io_bus_addr = win->res->start - win->offset;
|
||||
pp->io_base = pci_pio_to_address(win->res->start);
|
||||
break;
|
||||
case 0:
|
||||
dev_err(dev, "Missing *config* reg space\n");
|
||||
pp->cfg0_size = resource_size(win->res);
|
||||
pp->cfg0_base = win->res->start;
|
||||
if (!pci->dbi_base) {
|
||||
pci->dbi_base = devm_pci_remap_cfgspace(dev,
|
||||
pp->cfg0_base,
|
||||
pp->cfg0_size);
|
||||
if (!pci->dbi_base) {
|
||||
dev_err(dev, "Error with ioremap\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pp->va_cfg0_base) {
|
||||
pp->va_cfg0_base = devm_pci_remap_cfgspace(dev,
|
||||
pp->cfg0_base, pp->cfg0_size);
|
||||
if (!pp->va_cfg0_base) {
|
||||
dev_err(dev, "Error with ioremap in function\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Get the I/O range from DT */
|
||||
win = resource_list_first_type(&bridge->windows, IORESOURCE_IO);
|
||||
if (win) {
|
||||
pp->io_size = resource_size(win->res);
|
||||
pp->io_bus_addr = win->res->start - win->offset;
|
||||
pp->io_base = pci_pio_to_address(win->res->start);
|
||||
}
|
||||
|
||||
if (pci->link_gen < 1)
|
||||
|
@ -425,7 +404,7 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
dw_pcie_setup_rc(pp);
|
||||
dw_pcie_msi_init(pp);
|
||||
|
||||
if (!dw_pcie_link_up(pci) && pci->ops->start_link) {
|
||||
if (!dw_pcie_link_up(pci) && pci->ops && pci->ops->start_link) {
|
||||
ret = pci->ops->start_link(pci);
|
||||
if (ret)
|
||||
goto err_free_msi;
|
||||
|
|
|
@ -141,7 +141,7 @@ u32 dw_pcie_read_dbi(struct dw_pcie *pci, u32 reg, size_t size)
|
|||
int ret;
|
||||
u32 val;
|
||||
|
||||
if (pci->ops->read_dbi)
|
||||
if (pci->ops && pci->ops->read_dbi)
|
||||
return pci->ops->read_dbi(pci, pci->dbi_base, reg, size);
|
||||
|
||||
ret = dw_pcie_read(pci->dbi_base + reg, size, &val);
|
||||
|
@ -156,7 +156,7 @@ void dw_pcie_write_dbi(struct dw_pcie *pci, u32 reg, size_t size, u32 val)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (pci->ops->write_dbi) {
|
||||
if (pci->ops && pci->ops->write_dbi) {
|
||||
pci->ops->write_dbi(pci, pci->dbi_base, reg, size, val);
|
||||
return;
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (pci->ops->write_dbi2) {
|
||||
if (pci->ops && pci->ops->write_dbi2) {
|
||||
pci->ops->write_dbi2(pci, pci->dbi_base2, reg, size, val);
|
||||
return;
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ static u32 dw_pcie_readl_atu(struct dw_pcie *pci, u32 reg)
|
|||
int ret;
|
||||
u32 val;
|
||||
|
||||
if (pci->ops->read_dbi)
|
||||
if (pci->ops && pci->ops->read_dbi)
|
||||
return pci->ops->read_dbi(pci, pci->atu_base, reg, 4);
|
||||
|
||||
ret = dw_pcie_read(pci->atu_base + reg, 4, &val);
|
||||
|
@ -200,7 +200,7 @@ static void dw_pcie_writel_atu(struct dw_pcie *pci, u32 reg, u32 val)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (pci->ops->write_dbi) {
|
||||
if (pci->ops && pci->ops->write_dbi) {
|
||||
pci->ops->write_dbi(pci, pci->atu_base, reg, 4, val);
|
||||
return;
|
||||
}
|
||||
|
@ -225,6 +225,47 @@ static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg,
|
|||
dw_pcie_writel_atu(pci, offset + reg, val);
|
||||
}
|
||||
|
||||
static inline u32 dw_pcie_enable_ecrc(u32 val)
|
||||
{
|
||||
/*
|
||||
* DesignWare core version 4.90A has a design issue where the 'TD'
|
||||
* bit in the Control register-1 of the ATU outbound region acts
|
||||
* like an override for the ECRC setting, i.e., the presence of TLP
|
||||
* Digest (ECRC) in the outgoing TLPs is solely determined by this
|
||||
* bit. This is contrary to the PCIe spec which says that the
|
||||
* enablement of the ECRC is solely determined by the AER
|
||||
* registers.
|
||||
*
|
||||
* Because of this, even when the ECRC is enabled through AER
|
||||
* registers, the transactions going through ATU won't have TLP
|
||||
* Digest as there is no way the PCI core AER code could program
|
||||
* the TD bit which is specific to the DesignWare core.
|
||||
*
|
||||
* The best way to handle this scenario is to program the TD bit
|
||||
* always. It affects only the traffic from root port to downstream
|
||||
* devices.
|
||||
*
|
||||
* At this point,
|
||||
* When ECRC is enabled in AER registers, everything works normally
|
||||
* When ECRC is NOT enabled in AER registers, then,
|
||||
* on Root Port:- TLP Digest (DWord size) gets appended to each packet
|
||||
* even through it is not required. Since downstream
|
||||
* TLPs are mostly for configuration accesses and BAR
|
||||
* accesses, they are not in critical path and won't
|
||||
* have much negative effect on the performance.
|
||||
* on End Point:- TLP Digest is received for some/all the packets coming
|
||||
* from the root port. TLP Digest is ignored because,
|
||||
* as per the PCIe Spec r5.0 v1.0 section 2.2.3
|
||||
* "TLP Digest Rules", when an endpoint receives TLP
|
||||
* Digest when its ECRC check functionality is disabled
|
||||
* in AER registers, received TLP Digest is just ignored.
|
||||
* Since there is no issue or error reported either side, best way to
|
||||
* handle the scenario is to program TD bit by default.
|
||||
*/
|
||||
|
||||
return val | PCIE_ATU_TD;
|
||||
}
|
||||
|
||||
static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
|
||||
int index, int type,
|
||||
u64 cpu_addr, u64 pci_addr,
|
||||
|
@ -248,6 +289,8 @@ static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
|
|||
val = type | PCIE_ATU_FUNC_NUM(func_no);
|
||||
val = upper_32_bits(size - 1) ?
|
||||
val | PCIE_ATU_INCREASE_REGION_SIZE : val;
|
||||
if (pci->version == 0x490A)
|
||||
val = dw_pcie_enable_ecrc(val);
|
||||
dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, val);
|
||||
dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
|
||||
PCIE_ATU_ENABLE);
|
||||
|
@ -273,7 +316,7 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
|
|||
{
|
||||
u32 retries, val;
|
||||
|
||||
if (pci->ops->cpu_addr_fixup)
|
||||
if (pci->ops && pci->ops->cpu_addr_fixup)
|
||||
cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr);
|
||||
|
||||
if (pci->iatu_unroll_enabled) {
|
||||
|
@ -290,12 +333,19 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
|
|||
upper_32_bits(cpu_addr));
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
|
||||
lower_32_bits(cpu_addr + size - 1));
|
||||
if (pci->version >= 0x460A)
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_LIMIT,
|
||||
upper_32_bits(cpu_addr + size - 1));
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
|
||||
lower_32_bits(pci_addr));
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET,
|
||||
upper_32_bits(pci_addr));
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, type |
|
||||
PCIE_ATU_FUNC_NUM(func_no));
|
||||
val = type | PCIE_ATU_FUNC_NUM(func_no);
|
||||
val = ((upper_32_bits(size - 1)) && (pci->version >= 0x460A)) ?
|
||||
val | PCIE_ATU_INCREASE_REGION_SIZE : val;
|
||||
if (pci->version == 0x490A)
|
||||
val = dw_pcie_enable_ecrc(val);
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, val);
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
|
||||
|
||||
/*
|
||||
|
@ -321,7 +371,7 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type,
|
|||
|
||||
void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
|
||||
int type, u64 cpu_addr, u64 pci_addr,
|
||||
u32 size)
|
||||
u64 size)
|
||||
{
|
||||
__dw_pcie_prog_outbound_atu(pci, func_no, index, type,
|
||||
cpu_addr, pci_addr, size);
|
||||
|
@ -481,7 +531,7 @@ int dw_pcie_link_up(struct dw_pcie *pci)
|
|||
{
|
||||
u32 val;
|
||||
|
||||
if (pci->ops->link_up)
|
||||
if (pci->ops && pci->ops->link_up)
|
||||
return pci->ops->link_up(pci);
|
||||
|
||||
val = readl(pci->dbi_base + PCIE_PORT_DEBUG1);
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
#define PCIE_ATU_TYPE_IO 0x2
|
||||
#define PCIE_ATU_TYPE_CFG0 0x4
|
||||
#define PCIE_ATU_TYPE_CFG1 0x5
|
||||
#define PCIE_ATU_TD BIT(8)
|
||||
#define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20)
|
||||
#define PCIE_ATU_CR2 0x908
|
||||
#define PCIE_ATU_ENABLE BIT(31)
|
||||
|
@ -99,6 +100,7 @@
|
|||
#define PCIE_ATU_DEV(x) FIELD_PREP(GENMASK(23, 19), x)
|
||||
#define PCIE_ATU_FUNC(x) FIELD_PREP(GENMASK(18, 16), x)
|
||||
#define PCIE_ATU_UPPER_TARGET 0x91C
|
||||
#define PCIE_ATU_UPPER_LIMIT 0x924
|
||||
|
||||
#define PCIE_MISC_CONTROL_1_OFF 0x8BC
|
||||
#define PCIE_DBI_RO_WR_EN BIT(0)
|
||||
|
@ -297,7 +299,7 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
|
|||
u64 size);
|
||||
void dw_pcie_prog_ep_outbound_atu(struct dw_pcie *pci, u8 func_no, int index,
|
||||
int type, u64 cpu_addr, u64 pci_addr,
|
||||
u32 size);
|
||||
u64 size);
|
||||
int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
|
||||
int bar, u64 cpu_addr,
|
||||
enum dw_pcie_as_type as_type);
|
||||
|
|
Загрузка…
Ссылка в новой задаче