]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
serial: sh-sci: Update the suspend/resume support
authorClaudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Fri, 7 Feb 2025 11:33:13 +0000 (13:33 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 14 Feb 2025 09:05:33 +0000 (10:05 +0100)
The Renesas RZ/G3S supports a power saving mode where power to most of the
SoC components is turned off. When returning from this power saving mode,
SoC components need to be re-configured.

The SCIFs on the Renesas RZ/G3S need to be re-configured as well when
returning from this power saving mode. The sh-sci code already configures
the SCIF clocks, power domain and registers by calling uart_resume_port()
in sci_resume(). On suspend path the SCIF UART ports are suspended
accordingly (by calling uart_suspend_port() in sci_suspend()). The only
missing setting is the reset signal. For this assert/de-assert the reset
signal on driver suspend/resume.

In case the no_console_suspend is specified by the user, the registers need
to be saved on suspend path and restore on resume path. To do this the
sci_console_save()/sci_console_restore() functions were added. There is no
need to cache/restore the status or FIFO registers. Only the control
registers. The registers that will be saved/restored on suspend/resume are
specified by the struct sci_suspend_regs data structure.

Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Link: https://lore.kernel.org/r/20250207113313.545432-1-claudiu.beznea.uj@bp.renesas.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/sh-sci.c

index b1ea48f38248eb42d98353daa289bbe67191d201..4bc637f9d6499ee154e5e2161930322ce6c1e71d 100644 (file)
@@ -104,6 +104,15 @@ struct plat_sci_reg {
        u8 offset, size;
 };
 
+struct sci_suspend_regs {
+       u16 scsmr;
+       u16 scscr;
+       u16 scfcr;
+       u16 scsptr;
+       u8 scbrr;
+       u8 semr;
+};
+
 struct sci_port_params {
        const struct plat_sci_reg regs[SCIx_NR_REGS];
        unsigned int fifosize;
@@ -134,6 +143,8 @@ struct sci_port {
        struct dma_chan                 *chan_tx;
        struct dma_chan                 *chan_rx;
 
+       struct reset_control            *rstc;
+
 #ifdef CONFIG_SERIAL_SH_SCI_DMA
        struct dma_chan                 *chan_tx_saved;
        struct dma_chan                 *chan_rx_saved;
@@ -153,6 +164,7 @@ struct sci_port {
        int                             rx_trigger;
        struct timer_list               rx_fifo_timer;
        int                             rx_fifo_timeout;
+       struct sci_suspend_regs         suspend_regs;
        u16                             hscif_tot;
 
        bool has_rtscts;
@@ -3374,6 +3386,7 @@ static struct plat_sci_port *sci_parse_dt(struct platform_device *pdev,
        }
 
        sp = &sci_ports[id];
+       sp->rstc = rstc;
        *dev_id = id;
 
        p->type = SCI_OF_TYPE(data);
@@ -3546,13 +3559,57 @@ static int sci_probe(struct platform_device *dev)
        return 0;
 }
 
+static void sci_console_save(struct sci_port *s)
+{
+       struct sci_suspend_regs *regs = &s->suspend_regs;
+       struct uart_port *port = &s->port;
+
+       if (sci_getreg(port, SCSMR)->size)
+               regs->scsmr = sci_serial_in(port, SCSMR);
+       if (sci_getreg(port, SCSCR)->size)
+               regs->scscr = sci_serial_in(port, SCSCR);
+       if (sci_getreg(port, SCFCR)->size)
+               regs->scfcr = sci_serial_in(port, SCFCR);
+       if (sci_getreg(port, SCSPTR)->size)
+               regs->scsptr = sci_serial_in(port, SCSPTR);
+       if (sci_getreg(port, SCBRR)->size)
+               regs->scbrr = sci_serial_in(port, SCBRR);
+       if (sci_getreg(port, SEMR)->size)
+               regs->semr = sci_serial_in(port, SEMR);
+}
+
+static void sci_console_restore(struct sci_port *s)
+{
+       struct sci_suspend_regs *regs = &s->suspend_regs;
+       struct uart_port *port = &s->port;
+
+       if (sci_getreg(port, SCSMR)->size)
+               sci_serial_out(port, SCSMR, regs->scsmr);
+       if (sci_getreg(port, SCSCR)->size)
+               sci_serial_out(port, SCSCR, regs->scscr);
+       if (sci_getreg(port, SCFCR)->size)
+               sci_serial_out(port, SCFCR, regs->scfcr);
+       if (sci_getreg(port, SCSPTR)->size)
+               sci_serial_out(port, SCSPTR, regs->scsptr);
+       if (sci_getreg(port, SCBRR)->size)
+               sci_serial_out(port, SCBRR, regs->scbrr);
+       if (sci_getreg(port, SEMR)->size)
+               sci_serial_out(port, SEMR, regs->semr);
+}
+
 static __maybe_unused int sci_suspend(struct device *dev)
 {
        struct sci_port *sport = dev_get_drvdata(dev);
 
-       if (sport)
+       if (sport) {
                uart_suspend_port(&sci_uart_driver, &sport->port);
 
+               if (!console_suspend_enabled && uart_console(&sport->port))
+                       sci_console_save(sport);
+               else
+                       return reset_control_assert(sport->rstc);
+       }
+
        return 0;
 }
 
@@ -3560,8 +3617,18 @@ static __maybe_unused int sci_resume(struct device *dev)
 {
        struct sci_port *sport = dev_get_drvdata(dev);
 
-       if (sport)
+       if (sport) {
+               if (!console_suspend_enabled && uart_console(&sport->port)) {
+                       sci_console_restore(sport);
+               } else {
+                       int ret = reset_control_deassert(sport->rstc);
+
+                       if (ret)
+                               return ret;
+               }
+
                uart_resume_port(&sci_uart_driver, &sport->port);
+       }
 
        return 0;
 }