Skip to content

Commit 93e41f3

Browse files
jim2101024bjorn-helgaas
authored andcommitted
PCI: brcmstb: Add control of subdevice voltage regulators
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 mechanism 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 achieve link up after the voltage regulators are turned on. 2. If, in the case of an unsuccessful link up, to redirect any PCIe accesses to subdevices, e.g. the scan for DEV/ID. This redirection is needed because the Broadcom PCIe HW will issue a CPU abort if such an access is made when the link is down. [bhelgaas: fold in https://lore.kernel.org/r/[email protected]] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jim Quinlan <[email protected]> Signed-off-by: Lorenzo Pieralisi <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]>
1 parent 67211aa commit 93e41f3

File tree

1 file changed

+78
-5
lines changed

1 file changed

+78
-5
lines changed

drivers/pci/controller/pcie-brcmstb.c

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,8 @@ static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie,
195195
static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val);
196196
static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val);
197197
static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val);
198+
static int brcm_pcie_linkup(struct brcm_pcie *pcie);
199+
static int brcm_pcie_add_bus(struct pci_bus *bus);
198200

199201
enum {
200202
RGR1_SW_INIT_1,
@@ -306,6 +308,8 @@ struct brcm_pcie {
306308
u32 hw_rev;
307309
void (*perst_set)(struct brcm_pcie *pcie, u32 val);
308310
void (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
311+
bool refusal_mode;
312+
struct subdev_regulators *sr;
309313
};
310314

311315
/*
@@ -469,6 +473,34 @@ static int pci_subdev_regulators_add_bus(struct pci_bus *bus)
469473
return 0;
470474
}
471475

476+
static int brcm_pcie_add_bus(struct pci_bus *bus)
477+
{
478+
struct device *dev = &bus->dev;
479+
struct brcm_pcie *pcie = (struct brcm_pcie *) bus->sysdata;
480+
int ret;
481+
482+
if (!dev->of_node || !bus->parent || !pci_is_root_bus(bus->parent))
483+
return 0;
484+
485+
ret = pci_subdev_regulators_add_bus(bus);
486+
if (ret)
487+
return ret;
488+
489+
/* Grab the regulators for suspend/resume */
490+
pcie->sr = bus->dev.driver_data;
491+
492+
/*
493+
* If we have failed linkup there is no point to return an error as
494+
* currently it will cause a WARNING() from pci_alloc_child_bus().
495+
* We return 0 and turn on the "refusal_mode" so that any further
496+
* accesses to the pci_dev just get 0xffffffff
497+
*/
498+
if (brcm_pcie_linkup(pcie) != 0)
499+
pcie->refusal_mode = true;
500+
501+
return 0;
502+
}
503+
472504
static void pci_subdev_regulators_remove_bus(struct pci_bus *bus)
473505
{
474506
struct device *dev = &bus->dev;
@@ -794,6 +826,18 @@ static void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn,
794826
/* Accesses to the RC go right to the RC registers if slot==0 */
795827
if (pci_is_root_bus(bus))
796828
return PCI_SLOT(devfn) ? NULL : base + where;
829+
if (pcie->refusal_mode) {
830+
/*
831+
* At this point we do not have link. There will be a CPU
832+
* abort -- a quirk with this controller --if Linux tries
833+
* to read any config-space registers besides those
834+
* targeting the host bridge. To prevent this we hijack
835+
* the address to point to a safe access that will return
836+
* 0xffffffff.
837+
*/
838+
writel(0xffffffff, base + PCIE_MISC_RC_BAR2_CONFIG_HI);
839+
return base + PCIE_MISC_RC_BAR2_CONFIG_HI + (where & 0x3);
840+
}
797841

798842
/* For devices, write to the config space index register */
799843
idx = PCIE_ECAM_OFFSET(bus->number, devfn, 0);
@@ -805,7 +849,7 @@ static struct pci_ops brcm_pcie_ops = {
805849
.map_bus = brcm_pcie_map_conf,
806850
.read = pci_generic_config_read,
807851
.write = pci_generic_config_write,
808-
.add_bus = pci_subdev_regulators_add_bus,
852+
.add_bus = brcm_pcie_add_bus,
809853
.remove_bus = pci_subdev_regulators_remove_bus,
810854
};
811855

@@ -1254,6 +1298,14 @@ static int brcm_pcie_suspend(struct device *dev)
12541298
return ret;
12551299
}
12561300

1301+
if (pcie->sr) {
1302+
ret = regulator_bulk_disable(pcie->sr->num_supplies, pcie->sr->supplies);
1303+
if (ret) {
1304+
dev_err(dev, "Could not turn off regulators\n");
1305+
reset_control_reset(pcie->rescal);
1306+
return ret;
1307+
}
1308+
}
12571309
clk_disable_unprepare(pcie->clk);
12581310

12591311
return 0;
@@ -1271,9 +1323,17 @@ static int brcm_pcie_resume(struct device *dev)
12711323
if (ret)
12721324
return ret;
12731325

1326+
if (pcie->sr) {
1327+
ret = regulator_bulk_enable(pcie->sr->num_supplies, pcie->sr->supplies);
1328+
if (ret) {
1329+
dev_err(dev, "Could not turn on regulators\n");
1330+
goto err_disable_clk;
1331+
}
1332+
}
1333+
12741334
ret = reset_control_reset(pcie->rescal);
12751335
if (ret)
1276-
goto err_disable_clk;
1336+
goto err_regulator;
12771337

12781338
ret = brcm_phy_start(pcie);
12791339
if (ret)
@@ -1305,6 +1365,9 @@ static int brcm_pcie_resume(struct device *dev)
13051365

13061366
err_reset:
13071367
reset_control_rearm(pcie->rescal);
1368+
err_regulator:
1369+
if (pcie->sr)
1370+
regulator_bulk_disable(pcie->sr->num_supplies, pcie->sr->supplies);
13081371
err_disable_clk:
13091372
clk_disable_unprepare(pcie->clk);
13101373
return ret;
@@ -1434,7 +1497,17 @@ static int brcm_pcie_probe(struct platform_device *pdev)
14341497

14351498
platform_set_drvdata(pdev, pcie);
14361499

1437-
return pci_host_probe(bridge);
1500+
ret = pci_host_probe(bridge);
1501+
if (!ret && !brcm_pcie_link_up(pcie))
1502+
ret = -ENODEV;
1503+
1504+
if (ret) {
1505+
brcm_pcie_remove(pdev);
1506+
return ret;
1507+
}
1508+
1509+
return 0;
1510+
14381511
fail:
14391512
__brcm_pcie_remove(pcie);
14401513
return ret;
@@ -1443,8 +1516,8 @@ static int brcm_pcie_probe(struct platform_device *pdev)
14431516
MODULE_DEVICE_TABLE(of, brcm_pcie_match);
14441517

14451518
static const struct dev_pm_ops brcm_pcie_pm_ops = {
1446-
.suspend = brcm_pcie_suspend,
1447-
.resume = brcm_pcie_resume,
1519+
.suspend_noirq = brcm_pcie_suspend,
1520+
.resume_noirq = brcm_pcie_resume,
14481521
};
14491522

14501523
static struct platform_driver brcm_pcie_driver = {

0 commit comments

Comments
 (0)