Skip to content

Commit 614a19b

Browse files
Doug Bergergregkh
authored andcommitted
serial: 8250_bcm7271: use default_mux_rate if possible
There is a scenario when resuming from some power saving states with no_console_suspend where console output can be generated before the 8250_bcm7271 driver gets the opportunity to restore the baud_mux_clk frequency. Since the baud_mux_clk is at its default frequency at this time the output can be garbled until the driver gets the opportunity to resume. Since this is only an issue with console use of the serial port during that window and the console isn't likely to use baud rates that require alternate baud_mux_clk frequencies, allow the driver to select the default_mux_rate if it is accurate enough. Fixes: 41a4694 ("serial: 8250: Add new 8250-core based Broadcom STB driver") Cc: stable@vger.kernel.org Signed-off-by: Doug Berger <opendmb@gmail.com> Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com> Tested-by: Florian Fainelli <florian.fainelli@broadcom.com> Link: https://lore.kernel.org/r/20240424222559.1844045-1-opendmb@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 4244f83 commit 614a19b

1 file changed

Lines changed: 60 additions & 41 deletions

File tree

drivers/tty/serial/8250/8250_bcm7271.c

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -675,18 +675,46 @@ static void init_real_clk_rates(struct device *dev, struct brcmuart_priv *priv)
675675
clk_set_rate(priv->baud_mux_clk, priv->default_mux_rate);
676676
}
677677

678+
static u32 find_quot(struct device *dev, u32 freq, u32 baud, u32 *percent)
679+
{
680+
u32 quot;
681+
u32 rate;
682+
u64 hires_rate;
683+
u64 hires_baud;
684+
u64 hires_err;
685+
686+
rate = freq / 16;
687+
quot = DIV_ROUND_CLOSEST(rate, baud);
688+
if (!quot)
689+
return 0;
690+
691+
/* increase resolution to get xx.xx percent */
692+
hires_rate = div_u64((u64)rate * 10000, (u64)quot);
693+
hires_baud = (u64)baud * 10000;
694+
695+
/* get the delta */
696+
if (hires_rate > hires_baud)
697+
hires_err = (hires_rate - hires_baud);
698+
else
699+
hires_err = (hires_baud - hires_rate);
700+
701+
*percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud);
702+
703+
dev_dbg(dev, "Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n",
704+
baud, freq, *percent / 100, *percent % 100);
705+
706+
return quot;
707+
}
708+
678709
static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
679710
u32 baud)
680711
{
681712
u32 percent;
682713
u32 best_percent = UINT_MAX;
683714
u32 quot;
715+
u32 freq;
684716
u32 best_quot = 1;
685-
u32 rate;
686-
int best_index = -1;
687-
u64 hires_rate;
688-
u64 hires_baud;
689-
u64 hires_err;
717+
u32 best_freq = 0;
690718
int rc;
691719
int i;
692720
int real_baud;
@@ -695,44 +723,35 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
695723
if (priv->baud_mux_clk == NULL)
696724
return;
697725

698-
/* Find the closest match for specified baud */
699-
for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) {
700-
if (priv->real_rates[i] == 0)
701-
continue;
702-
rate = priv->real_rates[i] / 16;
703-
quot = DIV_ROUND_CLOSEST(rate, baud);
704-
if (!quot)
705-
continue;
706-
707-
/* increase resolution to get xx.xx percent */
708-
hires_rate = (u64)rate * 10000;
709-
hires_baud = (u64)baud * 10000;
710-
711-
hires_err = div_u64(hires_rate, (u64)quot);
712-
713-
/* get the delta */
714-
if (hires_err > hires_baud)
715-
hires_err = (hires_err - hires_baud);
716-
else
717-
hires_err = (hires_baud - hires_err);
718-
719-
percent = (unsigned long)DIV_ROUND_CLOSEST_ULL(hires_err, baud);
720-
dev_dbg(up->dev,
721-
"Baud rate: %u, MUX Clk: %u, Error: %u.%u%%\n",
722-
baud, priv->real_rates[i], percent / 100,
723-
percent % 100);
724-
if (percent < best_percent) {
725-
best_percent = percent;
726-
best_index = i;
727-
best_quot = quot;
726+
/* Try default_mux_rate first */
727+
quot = find_quot(up->dev, priv->default_mux_rate, baud, &percent);
728+
if (quot) {
729+
best_percent = percent;
730+
best_freq = priv->default_mux_rate;
731+
best_quot = quot;
732+
}
733+
/* If more than 1% error, find the closest match for specified baud */
734+
if (best_percent > 100) {
735+
for (i = 0; i < ARRAY_SIZE(priv->real_rates); i++) {
736+
freq = priv->real_rates[i];
737+
if (freq == 0 || freq == priv->default_mux_rate)
738+
continue;
739+
quot = find_quot(up->dev, freq, baud, &percent);
740+
if (!quot)
741+
continue;
742+
743+
if (percent < best_percent) {
744+
best_percent = percent;
745+
best_freq = freq;
746+
best_quot = quot;
747+
}
728748
}
729749
}
730-
if (best_index == -1) {
750+
if (!best_freq) {
731751
dev_err(up->dev, "Error, %d BAUD rate is too fast.\n", baud);
732752
return;
733753
}
734-
rate = priv->real_rates[best_index];
735-
rc = clk_set_rate(priv->baud_mux_clk, rate);
754+
rc = clk_set_rate(priv->baud_mux_clk, best_freq);
736755
if (rc)
737756
dev_err(up->dev, "Error selecting BAUD MUX clock\n");
738757

@@ -741,8 +760,8 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
741760
dev_err(up->dev, "Error, baud: %d has %u.%u%% error\n",
742761
baud, percent / 100, percent % 100);
743762

744-
real_baud = rate / 16 / best_quot;
745-
dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", rate);
763+
real_baud = best_freq / 16 / best_quot;
764+
dev_dbg(up->dev, "Selecting BAUD MUX rate: %u\n", best_freq);
746765
dev_dbg(up->dev, "Requested baud: %u, Actual baud: %u\n",
747766
baud, real_baud);
748767

@@ -751,7 +770,7 @@ static void set_clock_mux(struct uart_port *up, struct brcmuart_priv *priv,
751770
i += (i / 2);
752771
priv->char_wait = ns_to_ktime(i);
753772

754-
up->uartclk = rate;
773+
up->uartclk = best_freq;
755774
}
756775

757776
static void brcmstb_set_termios(struct uart_port *up,

0 commit comments

Comments
 (0)