lkml.org 
[lkml]   [2021]   [Nov]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v9 6/7] PCI: brcmstb: Add control of subdevice voltage regulators
    Date
    This Broadcom STB PCIe RC driver has one port and connects directly to one
    device, be it a switch or an endpoint. We want to be able to leverage
    the recently added mechansim that allocates and turns on/off subdevice
    regulators.

    All that needs to be done is to put the regulator DT nodes in the bridge
    below host and to set the pci_ops methods add_bus and remove_bus.

    Note that the pci_subdev_regulators_add_bus() method is wrapped for two
    reasons:

    1. To acheive linkup after the voltage regulators are turned on.

    2. If, in the case of an unsuccessful linkup, to redirect
    any PCIe accesses to subdevices, e.g. the scan for
    DEV/ID. This redirection is needed because the Broadcom
    PCIe HW wil issue a CPU abort if such an access is made when
    there is no linkup.

    Signed-off-by: Jim Quinlan <jim2101024@gmail.com>
    ---
    drivers/pci/controller/pcie-brcmstb.c | 81 +++++++++++++++++++++++++--
    1 file changed, 77 insertions(+), 4 deletions(-)

    diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c
    index 5f373227aad6..9b4df253e79a 100644
    --- a/drivers/pci/controller/pcie-brcmstb.c
    +++ b/drivers/pci/controller/pcie-brcmstb.c
    @@ -191,6 +191,7 @@ static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie,
    static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val);
    static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val);
    static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val);
    +static int brcm_pcie_add_bus(struct pci_bus *bus);

    enum {
    RGR1_SW_INIT_1,
    @@ -295,6 +296,8 @@ struct brcm_pcie {
    u32 hw_rev;
    void (*perst_set)(struct brcm_pcie *pcie, u32 val);
    void (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
    + bool refusal_mode;
    + struct subdev_regulators *sr;
    };

    /*
    @@ -711,6 +714,18 @@ static void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn,
    /* Accesses to the RC go right to the RC registers if slot==0 */
    if (pci_is_root_bus(bus))
    return PCI_SLOT(devfn) ? NULL : base + where;
    + if (pcie->refusal_mode) {
    + /*
    + * At this point we do not have link. There will be a CPU
    + * abort -- a quirk with this controller --if Linux tries
    + * to read any config-space registers besides those
    + * targeting the host bridge. To prevent this we hijack
    + * the address to point to a safe access that will return
    + * 0xffffffff.
    + */
    + writel(0xffffffff, base + PCIE_MISC_RC_BAR2_CONFIG_HI);
    + return base + PCIE_MISC_RC_BAR2_CONFIG_HI + (where & 0x3);
    + }

    /* For devices, write to the config space index register */
    idx = PCIE_ECAM_OFFSET(bus->number, devfn, 0);
    @@ -722,6 +737,8 @@ static struct pci_ops brcm_pcie_ops = {
    .map_bus = brcm_pcie_map_conf,
    .read = pci_generic_config_read,
    .write = pci_generic_config_write,
    + .add_bus = brcm_pcie_add_bus,
    + .remove_bus = pci_subdev_regulators_remove_bus,
    };

    static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val)
    @@ -1169,6 +1186,14 @@ static int brcm_pcie_suspend(struct device *dev)
    return ret;
    }

    + if (pcie->sr) {
    + ret = regulator_bulk_disable(pcie->sr->num_supplies, pcie->sr->supplies);
    + if (ret) {
    + dev_err(dev, "Could not turn off regulators\n");
    + reset_control_reset(pcie->rescal);
    + return ret;
    + }
    + }
    clk_disable_unprepare(pcie->clk);

    return 0;
    @@ -1186,9 +1211,17 @@ static int brcm_pcie_resume(struct device *dev)
    if (ret)
    return ret;

    + if (pcie->sr) {
    + ret = regulator_bulk_enable(pcie->sr->num_supplies, pcie->sr->supplies);
    + if (ret) {
    + dev_err(dev, "Could not turn on regulators\n");
    + goto err_disable_clk;
    + }
    + }
    +
    ret = reset_control_reset(pcie->rescal);
    if (ret)
    - goto err_disable_clk;
    + goto err_regulator;

    ret = brcm_phy_start(pcie);
    if (ret)
    @@ -1220,6 +1253,8 @@ static int brcm_pcie_resume(struct device *dev)

    err_reset:
    reset_control_rearm(pcie->rescal);
    +err_regulator:
    + regulator_bulk_disable(pcie->sr->num_supplies, pcie->sr->supplies);
    err_disable_clk:
    clk_disable_unprepare(pcie->clk);
    return ret;
    @@ -1258,6 +1293,34 @@ static const struct of_device_id brcm_pcie_match[] = {
    {},
    };

    +static int brcm_pcie_add_bus(struct pci_bus *bus)
    +{
    + struct device *dev = &bus->dev;
    + struct brcm_pcie *pcie = (struct brcm_pcie *) bus->sysdata;
    + int ret;
    +
    + if (!dev->of_node || !bus->parent || !pci_is_root_bus(bus->parent))
    + return 0;
    +
    + ret = pci_subdev_regulators_add_bus(bus);
    + if (ret)
    + return ret;
    +
    + /* Grab the regulators for suspend/resume */
    + pcie->sr = bus->dev.driver_data;
    +
    + /*
    + * If we have failed linkup there is no point to return an error as
    + * currently it will cause a WARNING() from pci_alloc_child_bus().
    + * We return 0 and turn on the "refusal_mode" so that any further
    + * accesses to the pci_dev just get 0xffffffff
    + */
    + if (brcm_pcie_linkup(pcie) != 0)
    + pcie->refusal_mode = true;
    +
    + return 0;
    +}
    +
    static int brcm_pcie_probe(struct platform_device *pdev)
    {
    struct device_node *np = pdev->dev.of_node, *msi_np;
    @@ -1349,7 +1412,17 @@ static int brcm_pcie_probe(struct platform_device *pdev)

    platform_set_drvdata(pdev, pcie);

    - return pci_host_probe(bridge);
    + ret = pci_host_probe(bridge);
    + if (!ret && !brcm_pcie_link_up(pcie))
    + ret = -ENODEV;
    +
    + if (ret) {
    + brcm_pcie_remove(pdev);
    + return ret;
    + }
    +
    + return 0;
    +
    fail:
    __brcm_pcie_remove(pcie);
    return ret;
    @@ -1358,8 +1431,8 @@ static int brcm_pcie_probe(struct platform_device *pdev)
    MODULE_DEVICE_TABLE(of, brcm_pcie_match);

    static const struct dev_pm_ops brcm_pcie_pm_ops = {
    - .suspend = brcm_pcie_suspend,
    - .resume = brcm_pcie_resume,
    + .suspend_noirq = brcm_pcie_suspend,
    + .resume_noirq = brcm_pcie_resume,
    };

    static struct platform_driver brcm_pcie_driver = {
    --
    2.17.1
    \
     
     \ /
      Last update: 2021-11-19 23:09    [W:3.369 / U:0.012 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site