serial: sccnxp: Allow to use non-standard baud rates
authorAlexander Shiyan <shc_work@mail.ru>
Thu, 20 Dec 2018 06:19:30 +0000 (09:19 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 20 Dec 2018 15:26:59 +0000 (16:26 +0100)
This patch adds support for the use of non-standard baud rates.
For these purposes, we use the built-in timer/counter.

Signed-off-by: Alexander Shiyan <shc_work@mail.ru>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/sccnxp.c

index c0a68eee4479203a92168fa227ee1c43d3b32d1c..68a24a14f6b72ce878c7dd6cc4922ea142ed6a12 100644 (file)
@@ -48,7 +48,6 @@
 #      define MR2_STOP1                (7 << 0)
 #      define MR2_STOP2                (0xf << 0)
 #define SCCNXP_SR_REG                  (0x01)
-#define SCCNXP_CSR_REG                 SCCNXP_SR_REG
 #      define SR_RXRDY                 (1 << 0)
 #      define SR_FULL                  (1 << 1)
 #      define SR_TXRDY                 (1 << 2)
@@ -57,6 +56,8 @@
 #      define SR_PE                    (1 << 5)
 #      define SR_FE                    (1 << 6)
 #      define SR_BRK                   (1 << 7)
+#define SCCNXP_CSR_REG                 (SCCNXP_SR_REG)
+#      define CSR_TIMER_MODE           (0x0d)
 #define SCCNXP_CR_REG                  (0x02)
 #      define CR_RX_ENABLE             (1 << 0)
 #      define CR_RX_DISABLE            (1 << 1)
 #      define IMR_RXRDY                (1 << 1)
 #      define ISR_TXRDY(x)             (1 << ((x * 4) + 0))
 #      define ISR_RXRDY(x)             (1 << ((x * 4) + 1))
+#define SCCNXP_CTPU_REG                        (0x06)
+#define SCCNXP_CTPL_REG                        (0x07)
 #define SCCNXP_IPR_REG                 (0x0d)
 #define SCCNXP_OPCR_REG                        SCCNXP_IPR_REG
 #define SCCNXP_SOP_REG                 (0x0e)
+#define SCCNXP_START_COUNTER_REG       SCCNXP_SOP_REG
 #define SCCNXP_ROP_REG                 (0x0f)
 
 /* Route helpers */
@@ -255,7 +259,7 @@ static int sccnxp_update_best_err(int a, int b, int *besterr)
 {
        int err = abs(a - b);
 
-       if ((*besterr < 0) || (*besterr > err)) {
+       if (*besterr > err) {
                *besterr = err;
                return 0;
        }
@@ -303,10 +307,22 @@ static const struct {
 static int sccnxp_set_baud(struct uart_port *port, int baud)
 {
        struct sccnxp_port *s = dev_get_drvdata(port->dev);
-       int div_std, tmp_baud, bestbaud = baud, besterr = -1;
+       int div_std, tmp_baud, bestbaud = INT_MAX, besterr = INT_MAX;
        struct sccnxp_chip *chip = s->chip;
        u8 i, acr = 0, csr = 0, mr0 = 0;
 
+       /* Find divisor to load to the timer preset registers */
+       div_std = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * baud);
+       if ((div_std >= 2) && (div_std <= 0xffff)) {
+               bestbaud = DIV_ROUND_CLOSEST(port->uartclk, 2 * 16 * div_std);
+               sccnxp_update_best_err(baud, bestbaud, &besterr);
+               csr = CSR_TIMER_MODE;
+               sccnxp_port_write(port, SCCNXP_CTPU_REG, div_std >> 8);
+               sccnxp_port_write(port, SCCNXP_CTPL_REG, div_std);
+               /* Issue start timer/counter command */
+               sccnxp_port_read(port, SCCNXP_START_COUNTER_REG);
+       }
+
        /* Find best baud from table */
        for (i = 0; baud_std[i].baud && besterr; i++) {
                if (baud_std[i].mr0 && !(chip->flags & SCCNXP_HAVE_MR0))