aboutsummaryrefslogtreecommitdiff
path: root/drivers/tty
diff options
context:
space:
mode:
authorGeert Uytterhoeven2015-11-13 17:04:56 +0100
committerGeert Uytterhoeven2015-12-17 11:18:29 +0100
commitde01e6cd0b100bac088b1d59a7040ebe2af64f1c (patch)
treeaeaefb1b24c68ad3f41a7769bcc29e08279e39e4 /drivers/tty
parent95a2703e36530c09a9416321ec21c062f3e91d01 (diff)
serial: sh-sci: Avoid overflow in sci_baud_calc_hscif()
If bps >= 1048576, the multiplication of the predivider and "bps" will overflow, and both br and err will contain bogus values. Skip the current and all higher clock select predividers when overflow is detected. Simplify the calculations using intermediates while we're at it. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/sh-sci.c25
1 files changed, 19 insertions, 6 deletions
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 77e0a582da44..c490c51d6032 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1870,7 +1870,7 @@ static unsigned int sci_scbrr_calc(struct sci_port *s, unsigned int bps,
static void sci_baud_calc_hscif(unsigned int bps, unsigned long freq, int *brr,
unsigned int *srr, unsigned int *cks)
{
- unsigned int sr, br, c;
+ unsigned int sr, br, prediv, scrate, c;
int err, recv_margin;
int min_err = 1000; /* 100% */
int recv_max_margin = 0;
@@ -1880,12 +1880,25 @@ static void sci_baud_calc_hscif(unsigned int bps, unsigned long freq, int *brr,
for (sr = 8; sr <= 32; sr++) {
for (c = 0; c <= 3; c++) {
/* integerized formulas from HSCIF documentation */
- br = DIV_ROUND_CLOSEST(freq, (sr *
- (1 << (2 * c + 1)) * bps));
+ prediv = sr * (1 << (2 * c + 1));
+
+ /*
+ * We need to calculate:
+ *
+ * br = freq / (prediv * bps) clamped to [1..256]
+ * err = (freq / (br * prediv * bps / 1000)) - 1000
+ *
+ * Watch out for overflow when calculating the desired
+ * sampling clock rate!
+ */
+ if (bps > UINT_MAX / prediv)
+ break;
+
+ scrate = prediv * bps;
+ br = DIV_ROUND_CLOSEST(freq, scrate);
br = clamp(br, 1U, 256U);
- err = DIV_ROUND_CLOSEST(freq, (br * bps * sr *
- (1 << (2 * c + 1)) / 1000)) -
- 1000;
+ err = DIV_ROUND_CLOSEST(freq, (br * scrate) / 1000) -
+ 1000;
/* Calc recv margin
* M: Receive margin (%)
* N: Ratio of bit rate to clock (N = sampling rate)