Hi Geert Uytterhoeven, Thanks for the patch. > -----Original Message----- > From: Geert Uytterhoeven <geert+renesas@xxxxxxxxx> > Sent: 22 August 2025 10:51 > Subject: [PATCH/RFC 6/6] can: rcar_canfd: Add suspend/resume support > > On R-Car Gen3 using PSCI, s2ram powers down the SoC. After resume, the CAN-FD interface no longer > works. Trying to bring it up again fails: > > # ip link set can0 up > RTNETLINK answers: Connection timed out > > # dmesg > ... > channel 0 communication state failed > > Fix this by populating the (currently empty) suspend and resume callbacks, to stop/start the individual > CAN-FD channels, and (de)initialize the CAN-FD controller. > > Signed-off-by: Geert Uytterhoeven <geert+renesas@xxxxxxxxx> Tested-by: Reviewed-by: Biju Das <biju.das.jz@xxxxxxxxxxxxxx> With adaption to RZ/G3E for ram_clk [1] > --- > While this fixes CAN-FD after resume from s2ram on R-Car E3 (Ebisu-4D), it does introduce a regression > on R-Car V4H (White Hawk): after resume from s2idle (White Hawk does not support s2ram), CAN frames > sent by other devices are no longer received, and the other side sometimes reports a "bus-off". > > However, the underlying issue is pre-existing, and can be reproduced without this patch: the CAN-FD > controller fails in the same way after driver unbind/rebind. So something must be missing in the > (de)initialization sequence for the R-Car Gen4 CAN-FD register layout. > Note that it keeps on working after ifdown/ifup, which does not reinitialize the full controller. > --- > drivers/net/can/rcar/rcar_canfd.c | 53 +++++++++++++++++++++++++++++++ > 1 file changed, 53 insertions(+) > > diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c > index eedce83b91414c57..6b0c563e894f74b3 100644 > --- a/drivers/net/can/rcar/rcar_canfd.c > +++ b/drivers/net/can/rcar/rcar_canfd.c > @@ -2236,11 +2236,64 @@ static void rcar_canfd_remove(struct platform_device *pdev) > > static int rcar_canfd_suspend(struct device *dev) { > + struct rcar_canfd_global *gpriv = dev_get_drvdata(dev); > + int err; > + u32 ch; > + > + for_each_set_bit(ch, &gpriv->channels_mask, gpriv->info->max_channels) { > + struct rcar_canfd_channel *priv = gpriv->ch[ch]; > + struct net_device *ndev = priv->ndev; > + > + if (!netif_running(ndev)) > + continue; > + > + netif_device_detach(ndev); > + > + err = rcar_canfd_close(ndev); > + if (err) { > + netdev_err(ndev, "rcar_canfd_close() failed %pe\n", > + ERR_PTR(err)); > + return err; > + } > + > + priv->can.state = CAN_STATE_SLEEPING; > + } > + > + /* TODO Skip if wake-up (which is not yet supported) is enabled */ > + rcar_canfd_global_deinit(gpriv, false); > + > return 0; > } > > static int rcar_canfd_resume(struct device *dev) { > + struct rcar_canfd_global *gpriv = dev_get_drvdata(dev); > + int err; > + u32 ch; > + > + err = rcar_canfd_global_init(gpriv); > + if (err) { > + dev_err(dev, "rcar_canfd_open() failed %pe\n", ERR_PTR(err)); Typo: rcar_canfd_global_init > + return err; > + } > + [1] biju@biju:~/linux-work/linux$ git diff diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c index 57ac90e57f11..cb358a4e49d0 100644 --- a/drivers/net/can/rcar/rcar_canfd.c +++ b/drivers/net/can/rcar/rcar_canfd.c @@ -468,6 +468,7 @@ struct rcar_canfd_global { struct platform_device *pdev; /* Respective platform device */ struct clk *clkp; /* Peripheral clock */ struct clk *can_clk; /* fCAN clock */ + struct clk *clk_ram; /* Clock RAM */ unsigned long channels_mask; /* Enabled channels mask */ bool extclk; /* CANFD or Ext clock */ bool fdmode; /* CAN FD or Classical CAN only mode */ @@ -1983,10 +1984,18 @@ static int rcar_canfd_global_init(struct rcar_canfd_global *gpriv) goto fail_reset2; } + /* Enable RAM clock */ + err = clk_prepare_enable(gpriv->clk_ram); + if (err) { + dev_err(dev, + "failed to enable ram clock, error %d\n", err); + goto fail_clk; + } + err = rcar_canfd_reset_controller(gpriv); if (err) { dev_err(dev, "reset controller failed: %pe\n", ERR_PTR(err)); - goto fail_clk; + goto fail_ram_clk; } /* Controller in Global reset & Channel reset mode */ @@ -2026,6 +2035,8 @@ static int rcar_canfd_global_init(struct rcar_canfd_global *gpriv) rcar_canfd_disable_global_interrupts(gpriv); fail_clk: clk_disable_unprepare(gpriv->clkp); +fail_ram_clk: + clk_disable_unprepare(gpriv->clk_ram); fail_reset2: reset_control_assert(gpriv->rstc2); fail_reset1: @@ -2045,6 +2056,7 @@ static void rcar_canfd_global_deinit(struct rcar_canfd_global *gpriv, bool full) } clk_disable_unprepare(gpriv->clkp); + clk_disable_unprepare(gpriv->clk_ram); reset_control_assert(gpriv->rstc2); reset_control_assert(gpriv->rstc1); } @@ -2153,10 +2165,10 @@ static int rcar_canfd_probe(struct platform_device *pdev) gpriv->extclk = gpriv->info->external_clk; } - clk_ram = devm_clk_get_optional_enabled(dev, "ram_clk"); + gpriv->clk_ram = devm_clk_get_optional(dev, "ram_clk"); if (IS_ERR(clk_ram)) return dev_err_probe(dev, PTR_ERR(clk_ram), - "cannot get enabled ram clock\n"); + "cannot get ram clock\n");