diff options
author | Théo Lebrun <theo.lebrun@bootlin.com> | 2025-02-05 18:36:51 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2025-03-14 09:18:02 +0100 |
commit | 9925aa4b025e46cffc9fc98c5fb28a4a303a739b (patch) | |
tree | 59023b7be62cbeb7d119e472823b61396e0c8fca /drivers/usb/cdns3/cdns3-ti.c | |
parent | 24346dc29174632237ad3f6f920fbe0765061cf9 (diff) | |
download | linux-9925aa4b025e46cffc9fc98c5fb28a4a303a739b.tar.gz linux-9925aa4b025e46cffc9fc98c5fb28a4a303a739b.tar.bz2 linux-9925aa4b025e46cffc9fc98c5fb28a4a303a739b.zip |
usb: cdns3-ti: run HW init at resume() if HW was reset
At runtime_resume(), read the W1 (Wrapper Register 1) register to detect
if an hardware reset occurred. If it did, run the hardware init sequence.
This callback will be called at system-wide resume. Previously, if a
reset occurred during suspend, we would crash. The wrapper config had
not been written, leading to invalid register accesses inside cdns3.
Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>
Link: https://lore.kernel.org/r/20250205-s2r-cdns-v7-6-13658a271c3c@bootlin.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/cdns3/cdns3-ti.c')
-rw-r--r-- | drivers/usb/cdns3/cdns3-ti.c | 25 |
1 files changed, 25 insertions, 0 deletions
diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c index d573a259f1ae..302ebf6d8e53 100644 --- a/drivers/usb/cdns3/cdns3-ti.c +++ b/drivers/usb/cdns3/cdns3-ti.c @@ -186,6 +186,12 @@ static int cdns_ti_probe(struct platform_device *pdev) data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider"); data->usb2_only = device_property_read_bool(dev, "ti,usb2-only"); + /* + * The call below to pm_runtime_get_sync() MIGHT reset hardware, if it + * detects it as uninitialised. We want to enforce a reset at probe, + * and so do it manually here. This means the first runtime_resume() + * will be a no-op. + */ cdns_ti_reset_and_init_hw(data); pm_runtime_enable(dev); @@ -230,6 +236,24 @@ static void cdns_ti_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); } +static int cdns_ti_runtime_resume(struct device *dev) +{ + const u32 mask = USBSS_W1_PWRUP_RST | USBSS_W1_MODESTRAP_SEL; + struct cdns_ti *data = dev_get_drvdata(dev); + u32 w1; + + w1 = cdns_ti_readl(data, USBSS_W1); + if ((w1 & mask) != mask) + cdns_ti_reset_and_init_hw(data); + + return 0; +} + +static const struct dev_pm_ops cdns_ti_pm_ops = { + RUNTIME_PM_OPS(NULL, cdns_ti_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + static const struct of_device_id cdns_ti_of_match[] = { { .compatible = "ti,j721e-usb", }, { .compatible = "ti,am64-usb", }, @@ -243,6 +267,7 @@ static struct platform_driver cdns_ti_driver = { .driver = { .name = "cdns3-ti", .of_match_table = cdns_ti_of_match, + .pm = pm_ptr(&cdns_ti_pm_ops), }, }; |