RE: [PATCH v3 5/6] PCI: cadence: Add callback functions for RP and EP controller

Manikandan Karunakaran Pillai
2025-04-14T03:53:04Z

>> +void __iomem *cdns_pci_hpa_map_bus(struct pci_bus *bus, unsigned int
>devfn,
>> + int where)
>> +{
>> + struct pci_host_bridge *bridge = pci_find_host_bridge(bus);
>> + struct cdns_pcie_rc *rc = pci_host_bridge_priv(bridge);
>> + struct cdns_pcie *pcie = &rc->pcie;
>> + unsigned int busn = bus->number;
>> + u32 addr0, desc0, desc1, ctrl0;
>> + u32 regval;
>> +
>> + if (pci_is_root_bus(bus)) {
>> + /*
>> + * Only the root port (devfn == 0) is connected to this bus.
>> + * All other PCI devices are behind some bridge hence on
>another
>> + * bus.
>> + */
>> + if (devfn)
>> + return NULL;
>> +
>> + return pcie->reg_base + (where & 0xfff);
>> + }
>> +
>> + /*
>> + * Clear AXI link-down status
>> + */
>
>That is an odd thing to do in map_bus. Also, it is completely racy
>because...
>
>> + regval = cdns_pcie_hpa_readl(pcie, REG_BANK_AXI_SLAVE,
>CDNS_PCIE_HPA_AT_LINKDOWN);
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
>CDNS_PCIE_HPA_AT_LINKDOWN,
>> + (regval & GENMASK(0, 0)));
>> +
>
>What if the link goes down again here.
>
>> + desc1 = 0;
>> + ctrl0 = 0;
>> +
>> + /*
>> + * Update Output registers for AXI region 0.
>> + */
>> + addr0 = CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_NBITS(12) |
>> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_DEVFN(devfn) |
>> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0_BUS(busn);
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
>> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR0(0),
>addr0);
>> +
>> + desc1 = cdns_pcie_hpa_readl(pcie, REG_BANK_AXI_SLAVE,
>> +
>CDNS_PCIE_HPA_AT_OB_REGION_DESC1(0));
>> + desc1 &= ~CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN_MASK;
>> + desc1 |= CDNS_PCIE_HPA_AT_OB_REGION_DESC1_DEVFN(0);
>> + ctrl0 = CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_BUS |
>> + CDNS_PCIE_HPA_AT_OB_REGION_CTRL0_SUPPLY_DEV_FN;
>> +
>> + if (busn == bridge->busnr + 1)
>> + desc0 |=
>CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE0;
>> + else
>> + desc0 |=
>CDNS_PCIE_HPA_AT_OB_REGION_DESC0_TYPE_CONF_TYPE1;
>> +
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
>> + CDNS_PCIE_HPA_AT_OB_REGION_DESC0(0), desc0);
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
>> + CDNS_PCIE_HPA_AT_OB_REGION_DESC1(0), desc1);
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
>> + CDNS_PCIE_HPA_AT_OB_REGION_CTRL0(0), ctrl0);
>
>This is also racy with the read and write functions. Don't worry, lots
>of other broken h/w like this...
>
>Surely this new h/w supports ECAM style config accesses? If so, use
>and support that mode instead.
>

As an IP related driver, the ECAM address in the SoC is not available. For SoC, the
Vendor can override this function in their code with the ECAM address.

>> +
>> + return rc->cfg_base + (where & 0xfff);
>> +}
>> +
>> static struct pci_ops cdns_pcie_host_ops = {
>> .map_bus = cdns_pci_map_bus,
>> .read = pci_generic_config_read,
>> .write = pci_generic_config_write,
>> };
>>
>> +static struct pci_ops cdns_pcie_hpa_host_ops = {
>> + .map_bus = cdns_pci_hpa_map_bus,
>> + .read = pci_generic_config_read,
>> + .write = pci_generic_config_write,
>> +};
>> +
>> static int cdns_pcie_host_training_complete(struct cdns_pcie *pcie)
>> {
>> u32 pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET;
>> @@ -154,8 +220,14 @@ static void
>cdns_pcie_host_enable_ptm_response(struct cdns_pcie *pcie)
>> {
>> u32 val;
>>
>> - val = cdns_pcie_readl(pcie, CDNS_PCIE_LM_PTM_CTRL);
>> - cdns_pcie_writel(pcie, CDNS_PCIE_LM_PTM_CTRL, val |
>CDNS_PCIE_LM_TPM_CTRL_PTMRSEN);
>> + if (!pcie->is_hpa) {
>> + val = cdns_pcie_readl(pcie, CDNS_PCIE_LM_PTM_CTRL);
>> + cdns_pcie_writel(pcie, CDNS_PCIE_LM_PTM_CTRL, val |
>CDNS_PCIE_LM_TPM_CTRL_PTMRSEN);
>> + } else {
>> + val = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_REG,
>CDNS_PCIE_HPA_LM_PTM_CTRL);
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_REG,
>CDNS_PCIE_HPA_LM_PTM_CTRL,
>> + val |
>CDNS_PCIE_HPA_LM_TPM_CTRL_PTMRSEN);
>> + }
>> }
>>
>> static int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc)
>> @@ -340,8 +412,8 @@ static int cdns_pcie_host_bar_config(struct
>cdns_pcie_rc *rc,
>> */
>> bar = cdns_pcie_host_find_min_bar(rc, size);
>> if (bar != RP_BAR_UNDEFINED) {
>> - ret = cdns_pcie_host_bar_ib_config(rc, bar, cpu_addr,
>> - size, flags);
>> + ret = pcie->ops->host_bar_ib_config(rc, bar, cpu_addr,
>> + size, flags);
>> if (ret)
>> dev_err(dev, "IB BAR: %d config failed\n", bar);
>> return ret;
>> @@ -366,8 +438,7 @@ static int cdns_pcie_host_bar_config(struct
>cdns_pcie_rc *rc,
>> }
>>
>> winsize = bar_max_size[bar];
>> - ret = cdns_pcie_host_bar_ib_config(rc, bar, cpu_addr, winsize,
>> - flags);
>> + ret = pcie->ops->host_bar_ib_config(rc, bar, cpu_addr, winsize,
>flags);
>> if (ret) {
>> dev_err(dev, "IB BAR: %d config failed\n", bar);
>> return ret;
>> @@ -408,8 +479,8 @@ static int cdns_pcie_host_map_dma_ranges(struct
>cdns_pcie_rc *rc)
>> if (list_empty(&bridge->dma_ranges)) {
>> of_property_read_u32(np, "cdns,no-bar-match-nbits",
>> &no_bar_nbits);
>> - err = cdns_pcie_host_bar_ib_config(rc, RP_NO_BAR, 0x0,
>> - (u64)1 << no_bar_nbits, 0);
>> + err = pcie->ops->host_bar_ib_config(rc, RP_NO_BAR, 0x0,
>> + (u64)1 << no_bar_nbits, 0);
>> if (err)
>> dev_err(dev, "IB BAR: %d config failed\n",
>RP_NO_BAR);
>> return err;
>> @@ -467,17 +538,159 @@ int
>cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc)
>> u64 pci_addr = res->start - entry->offset;
>>
>> if (resource_type(res) == IORESOURCE_IO)
>> - cdns_pcie_set_outbound_region(pcie, busnr, 0, r,
>> - true,
>> - pci_pio_to_address(res-
>>start),
>> - pci_addr,
>> - resource_size(res));
>> + pcie->ops->set_outbound_region(pcie, busnr, 0, r,
>> + true,
>> + pci_pio_to_address(res-
>>start),
>> + pci_addr,
>> + resource_size(res));
>> + else
>> + pcie->ops->set_outbound_region(pcie, busnr, 0, r,
>> + false,
>> + res->start,
>> + pci_addr,
>> + resource_size(res));
>> +
>> + r++;
>> + }
>> +
>> + return cdns_pcie_host_map_dma_ranges(rc);
>> +}
>> +
>> +int cdns_pcie_hpa_host_init_root_port(struct cdns_pcie_rc *rc)
>> +{
>> + struct cdns_pcie *pcie = &rc->pcie;
>> + u32 value, ctrl;
>> +
>> + /*
>> + * Set the root complex BAR configuration register:
>> + * - disable both BAR0 and BAR1.
>> + * - enable Prefetchable Memory Base and Limit registers in type 1
>> + * config space (64 bits).
>> + * - enable IO Base and Limit registers in type 1 config
>> + * space (32 bits).
>> + */
>> +
>> + ctrl = CDNS_PCIE_HPA_LM_BAR_CFG_CTRL_DISABLED;
>> + value = CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR0_CTRL(ctrl) |
>> + CDNS_PCIE_HPA_LM_RC_BAR_CFG_BAR1_CTRL(ctrl) |
>> + CDNS_PCIE_HPA_LM_RC_BAR_CFG_PREFETCH_MEM_ENABLE
>|
>> + CDNS_PCIE_HPA_LM_RC_BAR_CFG_PREFETCH_MEM_64BITS |
>> + CDNS_PCIE_HPA_LM_RC_BAR_CFG_IO_ENABLE |
>> + CDNS_PCIE_HPA_LM_RC_BAR_CFG_IO_32BITS;
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_CFG_CTRL_REG,
>> + CDNS_PCIE_HPA_LM_RC_BAR_CFG, value);
>> +
>> + if (rc->vendor_id != 0xffff)
>> + cdns_pcie_rp_writew(pcie, PCI_VENDOR_ID, rc->vendor_id);
>> +
>> + if (rc->device_id != 0xffff)
>> + cdns_pcie_rp_writew(pcie, PCI_DEVICE_ID, rc->device_id);
>> +
>> + cdns_pcie_rp_writeb(pcie, PCI_CLASS_REVISION, 0);
>> + cdns_pcie_rp_writeb(pcie, PCI_CLASS_PROG, 0);
>> + cdns_pcie_rp_writew(pcie, PCI_CLASS_DEVICE,
>PCI_CLASS_BRIDGE_PCI);
>> +
>> + return 0;
>> +}
>> +
>> +int cdns_pcie_hpa_host_bar_ib_config(struct cdns_pcie_rc *rc,
>> + enum cdns_pcie_rp_bar bar,
>> + u64 cpu_addr, u64 size,
>> + unsigned long flags)
>> +{
>> + struct cdns_pcie *pcie = &rc->pcie;
>> + u32 addr0, addr1, aperture, value;
>> +
>> + if (!rc->avail_ib_bar[bar])
>> + return -EBUSY;
>> +
>> + rc->avail_ib_bar[bar] = false;
>> +
>> + aperture = ilog2(size);
>> + addr0 = CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR0_NBITS(aperture) |
>> + (lower_32_bits(cpu_addr) & GENMASK(31, 8));
>> + addr1 = upper_32_bits(cpu_addr);
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_MASTER,
>> + CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR0(bar),
>addr0);
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_MASTER,
>> + CDNS_PCIE_HPA_AT_IB_RP_BAR_ADDR1(bar),
>addr1);
>> +
>> + if (bar == RP_NO_BAR)
>> + return 0;
>> +
>> + value = cdns_pcie_hpa_readl(pcie, REG_BANK_IP_CFG_CTRL_REG,
>CDNS_PCIE_HPA_LM_RC_BAR_CFG);
>> + value &= ~(HPA_LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar) |
>> + HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar) |
>> + HPA_LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar) |
>> + HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar) |
>> + HPA_LM_RC_BAR_CFG_APERTURE(bar,
>bar_aperture_mask[bar] + 2));
>> + if (size + cpu_addr >= SZ_4G) {
>> + if (!(flags & IORESOURCE_PREFETCH))
>> + value |=
>HPA_LM_RC_BAR_CFG_CTRL_MEM_64BITS(bar);
>> + value |=
>HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_64BITS(bar);
>> + } else {
>> + if (!(flags & IORESOURCE_PREFETCH))
>> + value |=
>HPA_LM_RC_BAR_CFG_CTRL_MEM_32BITS(bar);
>> + value |=
>HPA_LM_RC_BAR_CFG_CTRL_PREF_MEM_32BITS(bar);
>> + }
>> +
>> + value |= HPA_LM_RC_BAR_CFG_APERTURE(bar, aperture);
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_IP_CFG_CTRL_REG,
>CDNS_PCIE_HPA_LM_RC_BAR_CFG, value);
>> +
>> + return 0;
>> +}
>> +
>> +int cdns_pcie_hpa_host_init_address_translation(struct cdns_pcie_rc *rc)
>> +{
>> + struct cdns_pcie *pcie = &rc->pcie;
>> + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(rc);
>> + struct resource *cfg_res = rc->cfg_res;
>> + struct resource_entry *entry;
>> + u64 cpu_addr = cfg_res->start;
>> + u32 addr0, addr1, desc1;
>> + int r, busnr = 0;
>> +
>> + entry = resource_list_first_type(&bridge->windows,
>IORESOURCE_BUS);
>> + if (entry)
>> + busnr = entry->res->start;
>> +
>> + /*
>> + * Reserve region 0 for PCI configure space accesses:
>> + * OB_REGION_PCI_ADDR0 and OB_REGION_DESC0 are updated
>dynamically by
>> + * cdns_pci_map_bus(), other region registers are set here once for all.
>> + */
>> + addr1 = 0;
>> + desc1 = CDNS_PCIE_HPA_AT_OB_REGION_DESC1_BUS(busnr);
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
>> + CDNS_PCIE_HPA_AT_OB_REGION_PCI_ADDR1(0),
>addr1);
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
>> + CDNS_PCIE_HPA_AT_OB_REGION_DESC1(0), desc1);
>> +
>> + addr0 = CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0_NBITS(12) |
>> + (lower_32_bits(cpu_addr) & GENMASK(31, 8));
>> + addr1 = upper_32_bits(cpu_addr);
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
>> + CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR0(0),
>addr0);
>> + cdns_pcie_hpa_writel(pcie, REG_BANK_AXI_SLAVE,
>> + CDNS_PCIE_HPA_AT_OB_REGION_CPU_ADDR1(0),
>addr1);
>> +
>> + r = 1;
>> + resource_list_for_each_entry(entry, &bridge->windows) {
>> + struct resource *res = entry->res;
>> + u64 pci_addr = res->start - entry->offset;
>> +
>> + if (resource_type(res) == IORESOURCE_IO)
>> + pcie->ops->set_outbound_region(pcie, busnr, 0, r,
>> + true,
>> + pci_pio_to_address(res-
>>start),
>> + pci_addr,
>> + resource_size(res));
>> else
>> - cdns_pcie_set_outbound_region(pcie, busnr, 0, r,
>> - false,
>> - res->start,
>> - pci_addr,
>> - resource_size(res));
>> + pcie->ops->set_outbound_region(pcie, busnr, 0, r,
>> + false,
>> + res->start,
>> + pci_addr,
>> + resource_size(res));
>>
>> r++;
>> }
>> @@ -489,11 +702,11 @@ int cdns_pcie_host_init(struct cdns_pcie_rc *rc)
>> {
>> int err;
>>
>> - err = cdns_pcie_host_init_root_port(rc);
>> + err = rc->pcie.ops->host_init_root_port(rc);
>> if (err)
>> return err;
>>
>> - return cdns_pcie_host_init_address_translation(rc);
>> + return rc->pcie.ops->host_init_address_translation(rc);
>> }
>>
>> int cdns_pcie_host_link_setup(struct cdns_pcie_rc *rc)
>> @@ -503,7 +716,7 @@ int cdns_pcie_host_link_setup(struct cdns_pcie_rc
>*rc)
>> int ret;
>>
>> if (rc->quirk_detect_quiet_flag)
>> - cdns_pcie_detect_quiet_min_delay_set(&rc->pcie);
>> + pcie->ops->detect_quiet_min_delay_set(&rc->pcie);
>>
>> cdns_pcie_host_enable_ptm_response(pcie);
>>
>> @@ -566,8 +779,12 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
>> if (ret)
>> return ret;
>>
>> - if (!bridge->ops)
>> - bridge->ops = &cdns_pcie_host_ops;
>> + if (!bridge->ops) {
>> + if (pcie->is_hpa)
>> + bridge->ops = &cdns_pcie_hpa_host_ops;
>> + else
>> + bridge->ops = &cdns_pcie_host_ops;
>> + }
>>
>> ret = pci_host_probe(bridge);
>> if (ret < 0)
>> diff --git a/drivers/pci/controller/cadence/pcie-cadence-plat.c
>b/drivers/pci/controller/cadence/pcie-cadence-plat.c
>> index b24176d4df1f..8d5fbaef0a3c 100644
>> --- a/drivers/pci/controller/cadence/pcie-cadence-plat.c
>> +++ b/drivers/pci/controller/cadence/pcie-cadence-plat.c
>> @@ -43,7 +43,30 @@ static u64 cdns_plat_cpu_addr_fixup(struct cdns_pcie
>*pcie, u64 cpu_addr)
>> }
>>
>> static const struct cdns_pcie_ops cdns_plat_ops = {
>> + .link_up = cdns_pcie_linkup,
>> .cpu_addr_fixup = cdns_plat_cpu_addr_fixup,
>> + .host_init_root_port = cdns_pcie_host_init_root_port,
>> + .host_bar_ib_config = cdns_pcie_host_bar_ib_config,
>> + .host_init_address_translation =
>cdns_pcie_host_init_address_translation,
>> + .detect_quiet_min_delay_set =
>cdns_pcie_detect_quiet_min_delay_set,
>> + .set_outbound_region = cdns_pcie_set_outbound_region,
>> + .set_outbound_region_for_normal_msg =
>> +
>cdns_pcie_set_outbound_region_for_normal_msg,
>> + .reset_outbound_region = cdns_pcie_reset_outbound_region,
>> +};
>> +
>> +static const struct cdns_pcie_ops cdns_hpa_plat_ops = {
>> + .start_link = cdns_pcie_hpa_startlink,
>> + .stop_link = cdns_pcie_hpa_stop_link,
>> + .link_up = cdns_pcie_hpa_linkup,
>> + .host_init_root_port = cdns_pcie_hpa_host_init_root_port,
>> + .host_bar_ib_config = cdns_pcie_hpa_host_bar_ib_config,
>> + .host_init_address_translation =
>cdns_pcie_hpa_host_init_address_translation,
>> + .detect_quiet_min_delay_set =
>cdns_pcie_hpa_detect_quiet_min_delay_set,
>> + .set_outbound_region = cdns_pcie_hpa_set_outbound_region,
>> + .set_outbound_region_for_normal_msg =
>> +
>cdns_pcie_hpa_set_outbound_region_for_normal_msg,
>> + .reset_outbound_region = cdns_pcie_hpa_reset_outbound_region,
>
>What exactly is shared between these 2 implementations. Link handling,
>config space accesses, address translation, and host init are all
>different. What's left to share? MSIs (if not passed thru) and
>interrupts? I think it's questionable that this be the same driver.
>
The address of both these have changed as the controller architecture has
changed. In the event these driver have to be same driver, there will lot of
sprinkled "if(is_hpa)" and that was already rejected in earlier version of code.
Hence it was done similar to other drivers by architecture specific "ops".
The "if(is_hpa)" is now very limited where a specific ops functions does not make
any sense.

>A bunch of driver specific 'ops' is not the right direction despite
>other drivers (DWC) having that. If there are common parts, then make
>them library functions multiple drivers can call.
>
>Rob