Hello!

On Wednesday 29 March 2023 18:01:41 Minda Chen wrote:
> From: Mason Huo <mason....@starfivetech.com>
> 
> Add pcie driver for StarFive JH7110, the driver depends on
> starfive gpio, pinctrl, clk and reset driver to do init.
> 
> Several devices are tested:
> a) M.2 NVMe SSD
> b) Realtek 8169 Ethernet adapter.
> 
> Signed-off-by: Mason Huo <mason....@starfivetech.com>
> Signed-off-by: Minda Chen <minda.c...@starfivetech.com>
> ---
>  drivers/pci/Kconfig                |  11 +
>  drivers/pci/Makefile               |   1 +
>  drivers/pci/pcie_starfive_jh7110.c | 463 +++++++++++++++++++++++++++++
>  3 files changed, 475 insertions(+)
>  create mode 100644 drivers/pci/pcie_starfive_jh7110.c
> 
> diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
> index ef328d2652..e7b0ff5bc3 100644
> --- a/drivers/pci/Kconfig
> +++ b/drivers/pci/Kconfig
> @@ -374,4 +374,15 @@ config PCIE_UNIPHIER
>         Say Y here if you want to enable PCIe controller support on
>         UniPhier SoCs.
>  
> +config PCIE_STARFIVE_JH7110
> +     bool "Enable Starfive JH7110 PCIe driver"
> +     depends on STARFIVE_JH7110
> +     depends on PINCTRL_STARFIVE_JH7110
> +     depends on CLK_JH7110
> +     depends on RESET_JH7110

There is no direct hard dependency on on these 4 symbols (or at least I
do not see any in include header files). So to allow compile time
checks, change it to just soft dependency by switching from "depends" to
"imply" keyword.

> +     default y
> +     help
> +       Say Y here if you want to enable PCIe controller support on
> +       StarFive JH7110 SoC.
> +
>  endif
...
> diff --git a/drivers/pci/pcie_starfive_jh7110.c 
> b/drivers/pci/pcie_starfive_jh7110.c
> new file mode 100644
> index 0000000000..1005ed9919
> --- /dev/null
> +++ b/drivers/pci/pcie_starfive_jh7110.c
...
> +static int starfive_pcie_off_conf(pci_dev_t bdf, uint offset)
> +{
> +     unsigned int bus = PCI_BUS(bdf);
> +     unsigned int dev = PCI_DEV(bdf);
> +     unsigned int func = PCI_FUNC(bdf);
> +
> +     return PCIE_ECAM_OFFSET(bus, dev, func, offset);

This function is used just on one place, it is pretty straightforward
and nothing starfive_pcie-specific. Just directly inline it into
starfive_pcie_conf_address() function?

  int where = PCIE_ECAM_OFFSET(PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), 
offset);

> +}
> +
> +static int starfive_pcie_addr_valid(pci_dev_t bdf, int first_busno)
> +{
> +     /*
> +      * Single device limitation.
> +      * For JH7110 SoC limitation, one bus can only connnect one device.
> +      * And PCIe controller contain HW issue that non-root host bridge
> +      * bus emumerate duplicate devices.
> +      * Only can access device 0 in Bus 1 host bridge.
> +      */
> +     if ((PCI_BUS(bdf) != first_busno) && (PCI_DEV(bdf) > 0))
> +             return 0;

In v2 discussion you wrote that the issue with duplicate devices is on
the bus 1, which is secondary bus - the bus behind the root port.

So check against root port bus (first_busno) is not correct, you need to
check it against bus behind the root port (secondary bus).

Current check completely filters all devices with numbers higher than 0
behind the secondary bus, which completely breaks support for PCIe
switches. Moreover behind PCIe switch can be anything with more
complicated topology.

That secondary bus number is dynamically allocated / assigned by U-Boot
core code, so you need to check PCI_SECONDARY_BUS register of the root
port.

In pci_mvebu.c is this value cached in "->sec_busno" member. You can
reuse this pattern.

You probably want this kind of check:

  if (PCI_BUS(bdf) == priv->sec_busno && PCI_DEV(bdf) > 0)
    return false;
  else
    return true;

Also comment "non-root host bridge bus" is strange. Bus of the host
bridge is always the root one. You probably want to write the bus
immediately behind the root bus / host bridge OR the secondary bus of
the host bridge.

> +     return 1;
> +}
> +
> +static int starfive_pcie_conf_address(const struct udevice *udev, pci_dev_t 
> bdf,
> +                                   uint offset, void **paddr)
> +{
> +     struct starfive_pcie *priv = dev_get_priv(udev);
> +     int where = starfive_pcie_off_conf(bdf, offset);
> +
> +     if (!starfive_pcie_addr_valid(bdf, priv->first_busno))
> +             return -EINVAL;

Maybe better error code is -ENODEV? As technically access request is
valid but there is no device on it.

> +
> +     *paddr = (void *)(priv->cfg_base + where);
> +     return 0;
> +}
> +
...
> +static int starfive_pcie_init_port(struct udevice *dev)
> +{
> +     int ret, i;
> +     unsigned int value;
> +     struct starfive_pcie *priv = dev_get_priv(dev);
> +
> +     ret = clk_enable_bulk(&priv->clks);
> +     if (ret) {
> +             dev_err(dev, "Failed to enable clks (ret=%d)\n", ret);
> +             return ret;
> +     }
> +
> +     ret = reset_deassert_bulk(&priv->rsts);
> +     if (ret) {
> +             dev_err(dev, "Failed to deassert resets (ret=%d)\n", ret);
> +             goto err_deassert_clk;
> +     }
> +
> +     dm_gpio_set_value(&priv->reset_gpio, 1);
> +     /* Disable physical functions except #0 */
> +     for (i = 1; i < PLDA_FUNC_NUM; i++) {
> +             regmap_update_bits(priv->regmap,
> +                                priv->stg_arfun,
> +                                STG_SYSCON_AXI4_SLVL_ARFUNC_MASK,
> +                                (i << PLDA_PHY_FUNC_SHIFT) <<
> +                                STG_SYSCON_AXI4_SLVL_ARFUNC_SHIFT);
> +             regmap_update_bits(priv->regmap,
> +                                priv->stg_awfun,
> +                                STG_SYSCON_AXI4_SLVL_AWFUNC_MASK,
> +                                i << PLDA_PHY_FUNC_SHIFT);
> +
> +             value = readl(priv->reg_base + PCI_MISC);
> +             value |= PLDA_FUNCTION_DIS;
> +             writel(value, priv->reg_base + PCI_MISC);
> +     }
> +
> +     /* Disable physical functions */
> +     regmap_update_bits(priv->regmap,
> +                        priv->stg_arfun,
> +                        STG_SYSCON_AXI4_SLVL_ARFUNC_MASK,
> +                        0);
> +     regmap_update_bits(priv->regmap,
> +                        priv->stg_awfun,
> +                        STG_SYSCON_AXI4_SLVL_AWFUNC_MASK,
> +                        0);
> +
> +     /* Enable root port */
> +     value = readl(priv->reg_base + GEN_SETTINGS);
> +     value |= PLDA_RP_ENABLE;
> +     writel(value, priv->reg_base + GEN_SETTINGS);
> +
> +     /* PCIe PCI Standard Configuration Identification Settings. */
> +     value = (PCI_CLASS_BRIDGE_PCI_NORMAL << IDS_CLASS_CODE_SHIFT) | 
> IDS_REVISION_ID;
> +     writel(value, priv->reg_base + PCIE_PCI_IDS);

This looks like configuration of the PCI_CLASS_REVISION read-only
register. Is there any reason why you are removing the original
"revision" information by hardcoded IDS_REVISION_ID constant?

> +
> +     /*
> +      * The LTR message forwarding of PCIe Message Reception was set by core
> +      * as default, but the forward id & addr are also need to be reset.
> +      * If we do not disable LTR message forwarding here, or set a legal
> +      * forwarding address, the kernel will get stuck after this driver 
> probe.
> +      * To workaround, disable the LTR message forwarding support on
> +      * PCIe Message Reception.
> +      */
> +     value = readl(priv->reg_base + PMSG_SUPPORT_RX);
> +     value &= ~PMSG_LTR_SUPPORT;
> +     writel(value, priv->reg_base + PMSG_SUPPORT_RX);
> +
> +     /* Prefetchable memory window 64-bit addressing support */
> +     value = readl(priv->reg_base + PCIE_WINROM);
> +     value |= PREF_MEM_WIN_64_SUPPORT;
> +     writel(value, priv->reg_base + PCIE_WINROM);
> +
> +     starfive_pcie_atr_init(priv);

This function may fail. But return value is ignored here.

> +
> +     dm_gpio_set_value(&priv->reset_gpio, 0);
> +     /* Ensure that PERST in default at least 300 ms */
> +     mdelay(300);
> +
> +     return 0;
> +
> +err_deassert_clk:
> +     clk_disable_bulk(&priv->clks);
> +     return ret;
> +}

Reply via email to