OSDN Git Service

serial: sh-sci: HSCIF support
authorUlrich Hecht <ulrich.hecht@gmail.com>
Fri, 31 May 2013 15:57:01 +0000 (17:57 +0200)
committerSimon Horman <horms+renesas@verge.net.au>
Mon, 17 Jun 2013 09:09:53 +0000 (18:09 +0900)
Adds support for "High Speed Serial Communications Interface with FIFO",
essentially a SCIF with 128-byte FIFOs and more accurate baud rate
generator.

Signed-off-by: Ulrich Hecht <ulrich.hecht@gmail.com>
Acked-by: Paul Mundt <lethal@linux-sh.org>
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Simon Horman <horms+renesas@verge.net.au>
drivers/tty/serial/sh-sci.c
include/linux/serial_sci.h
include/uapi/linux/serial_core.h

index 1564186..931d6c3 100644 (file)
@@ -146,6 +146,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
                [SCRFDR]        = sci_reg_invalid,
                [SCSPTR]        = sci_reg_invalid,
                [SCLSR]         = sci_reg_invalid,
+               [HSSRR]         = sci_reg_invalid,
        },
 
        /*
@@ -165,6 +166,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
                [SCRFDR]        = sci_reg_invalid,
                [SCSPTR]        = sci_reg_invalid,
                [SCLSR]         = sci_reg_invalid,
+               [HSSRR]         = sci_reg_invalid,
        },
 
        /*
@@ -183,6 +185,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
                [SCRFDR]        = sci_reg_invalid,
                [SCSPTR]        = sci_reg_invalid,
                [SCLSR]         = sci_reg_invalid,
+               [HSSRR]         = sci_reg_invalid,
        },
 
        /*
@@ -201,6 +204,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
                [SCRFDR]        = { 0x3c, 16 },
                [SCSPTR]        = sci_reg_invalid,
                [SCLSR]         = sci_reg_invalid,
+               [HSSRR]         = sci_reg_invalid,
        },
 
        /*
@@ -220,6 +224,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
                [SCRFDR]        = sci_reg_invalid,
                [SCSPTR]        = { 0x20, 16 },
                [SCLSR]         = { 0x24, 16 },
+               [HSSRR]         = sci_reg_invalid,
        },
 
        /*
@@ -238,6 +243,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
                [SCRFDR]        = sci_reg_invalid,
                [SCSPTR]        = sci_reg_invalid,
                [SCLSR]         = sci_reg_invalid,
+               [HSSRR]         = sci_reg_invalid,
        },
 
        /*
@@ -256,6 +262,26 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
                [SCRFDR]        = sci_reg_invalid,
                [SCSPTR]        = { 0x20, 16 },
                [SCLSR]         = { 0x24, 16 },
+               [HSSRR]         = sci_reg_invalid,
+       },
+
+       /*
+        * Common HSCIF definitions.
+        */
+       [SCIx_HSCIF_REGTYPE] = {
+               [SCSMR]         = { 0x00, 16 },
+               [SCBRR]         = { 0x04,  8 },
+               [SCSCR]         = { 0x08, 16 },
+               [SCxTDR]        = { 0x0c,  8 },
+               [SCxSR]         = { 0x10, 16 },
+               [SCxRDR]        = { 0x14,  8 },
+               [SCFCR]         = { 0x18, 16 },
+               [SCFDR]         = { 0x1c, 16 },
+               [SCTFDR]        = sci_reg_invalid,
+               [SCRFDR]        = sci_reg_invalid,
+               [SCSPTR]        = { 0x20, 16 },
+               [SCLSR]         = { 0x24, 16 },
+               [HSSRR]         = { 0x40, 16 },
        },
 
        /*
@@ -275,6 +301,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
                [SCRFDR]        = sci_reg_invalid,
                [SCSPTR]        = sci_reg_invalid,
                [SCLSR]         = { 0x24, 16 },
+               [HSSRR]         = sci_reg_invalid,
        },
 
        /*
@@ -294,6 +321,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
                [SCRFDR]        = { 0x20, 16 },
                [SCSPTR]        = { 0x24, 16 },
                [SCLSR]         = { 0x28, 16 },
+               [HSSRR]         = sci_reg_invalid,
        },
 
        /*
@@ -313,6 +341,7 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
                [SCRFDR]        = sci_reg_invalid,
                [SCSPTR]        = sci_reg_invalid,
                [SCLSR]         = sci_reg_invalid,
+               [HSSRR]         = sci_reg_invalid,
        },
 };
 
@@ -374,6 +403,9 @@ static int sci_probe_regmap(struct plat_sci_port *cfg)
                 */
                cfg->regtype = SCIx_SH4_SCIF_REGTYPE;
                break;
+       case PORT_HSCIF:
+               cfg->regtype = SCIx_HSCIF_REGTYPE;
+               break;
        default:
                printk(KERN_ERR "Can't probe register map for given port\n");
                return -EINVAL;
@@ -1798,6 +1830,42 @@ static unsigned int sci_scbrr_calc(unsigned int algo_id, unsigned int bps,
        return ((freq + 16 * bps) / (32 * bps) - 1);
 }
 
+/* calculate sample rate, BRR, and clock select for HSCIF */
+static void sci_baud_calc_hscif(unsigned int bps, unsigned long freq,
+                               int *brr, unsigned int *srr,
+                               unsigned int *cks)
+{
+       int sr, c, br, err;
+       int min_err = 1000; /* 100% */
+
+       /* Find the combination of sample rate and clock select with the
+          smallest deviation from the desired baud rate. */
+       for (sr = 8; sr <= 32; sr++) {
+               for (c = 0; c <= 3; c++) {
+                       /* integerized formulas from HSCIF documentation */
+                       br = freq / (sr * (1 << (2 * c + 1)) * bps) - 1;
+                       if (br < 0 || br > 255)
+                               continue;
+                       err = freq / ((br + 1) * bps * sr *
+                             (1 << (2 * c + 1)) / 1000) - 1000;
+                       if (min_err > err) {
+                               min_err = err;
+                               *brr = br;
+                               *srr = sr - 1;
+                               *cks = c;
+                       }
+               }
+       }
+
+       if (min_err == 1000) {
+               WARN_ON(1);
+               /* use defaults */
+               *brr = 255;
+               *srr = 15;
+               *cks = 0;
+       }
+}
+
 static void sci_reset(struct uart_port *port)
 {
        struct plat_sci_reg *reg;
@@ -1821,6 +1889,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
        struct plat_sci_reg *reg;
        unsigned int baud, smr_val, max_baud, cks;
        int t = -1;
+       unsigned int srr;
 
        /*
         * earlyprintk comes here early on with port->uartclk set to zero.
@@ -1833,8 +1902,17 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
        max_baud = port->uartclk ? port->uartclk / 16 : 115200;
 
        baud = uart_get_baud_rate(port, termios, old, 0, max_baud);
-       if (likely(baud && port->uartclk))
-               t = sci_scbrr_calc(s->cfg->scbrr_algo_id, baud, port->uartclk);
+       if (likely(baud && port->uartclk)) {
+               if (s->cfg->scbrr_algo_id == SCBRR_ALGO_6) {
+                       sci_baud_calc_hscif(baud, port->uartclk, &t, &srr,
+                                           &cks);
+               } else {
+                       t = sci_scbrr_calc(s->cfg->scbrr_algo_id, baud,
+                                          port->uartclk);
+                       for (cks = 0; t >= 256 && cks <= 3; cks++)
+                               t >>= 2;
+               }
+       }
 
        sci_port_enable(s);
 
@@ -1853,15 +1931,15 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
 
        uart_update_timeout(port, termios->c_cflag, baud);
 
-       for (cks = 0; t >= 256 && cks <= 3; cks++)
-               t >>= 2;
-
        dev_dbg(port->dev, "%s: SMR %x, cks %x, t %x, SCSCR %x\n",
                __func__, smr_val, cks, t, s->cfg->scscr);
 
        if (t >= 0) {
                serial_port_out(port, SCSMR, (smr_val & ~3) | cks);
                serial_port_out(port, SCBRR, t);
+               reg = sci_getreg(port, HSSRR);
+               if (reg->size)
+                       serial_port_out(port, HSSRR, srr | HSCIF_SRE);
                udelay((1000000+(baud-1)) / baud); /* Wait one bit interval */
        } else
                serial_port_out(port, SCSMR, smr_val);
@@ -1947,6 +2025,8 @@ static const char *sci_type(struct uart_port *port)
                return "scifa";
        case PORT_SCIFB:
                return "scifb";
+       case PORT_HSCIF:
+               return "hscif";
        }
 
        return NULL;
@@ -1960,7 +2040,10 @@ static inline unsigned long sci_port_size(struct uart_port *port)
         * from platform resource data at such a time that ports begin to
         * behave more erratically.
         */
-       return 64;
+       if (port->type == PORT_HSCIF)
+               return 96;
+       else
+               return 64;
 }
 
 static int sci_remap_port(struct uart_port *port)
@@ -2085,6 +2168,9 @@ static int sci_init_single(struct platform_device *dev,
        case PORT_SCIFB:
                port->fifosize = 256;
                break;
+       case PORT_HSCIF:
+               port->fifosize = 128;
+               break;
        case PORT_SCIFA:
                port->fifosize = 64;
                break;
@@ -2325,7 +2411,7 @@ static inline int sci_probe_earlyprintk(struct platform_device *pdev)
 #endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
 
 static char banner[] __initdata =
-       KERN_INFO "SuperH SCI(F) driver initialized\n";
+       KERN_INFO "SuperH (H)SCI(F) driver initialized\n";
 
 static struct uart_driver sci_uart_driver = {
        .owner          = THIS_MODULE,
@@ -2484,4 +2570,4 @@ module_exit(sci_exit);
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:sh-sci");
 MODULE_AUTHOR("Paul Mundt");
-MODULE_DESCRIPTION("SuperH SCI(F) serial driver");
+MODULE_DESCRIPTION("SuperH (H)SCI(F) serial driver");
index eb763ad..d340497 100644 (file)
@@ -5,7 +5,7 @@
 #include <linux/sh_dma.h>
 
 /*
- * Generic header for SuperH SCI(F) (used by sh/sh64/h8300 and related parts)
+ * Generic header for SuperH (H)SCI(F) (used by sh/sh64/h8300 and related parts)
  */
 
 #define SCIx_NOT_SUPPORTED     (-1)
@@ -16,6 +16,7 @@ enum {
        SCBRR_ALGO_3,           /* (((clk * 2) + 16 * bps) / (16 * bps) - 1) */
        SCBRR_ALGO_4,           /* (((clk * 2) + 16 * bps) / (32 * bps) - 1) */
        SCBRR_ALGO_5,           /* (((clk * 1000 / 32) / bps) - 1) */
+       SCBRR_ALGO_6,           /* HSCIF variable sample rate algorithm */
 };
 
 #define SCSCR_TIE      (1 << 7)
@@ -37,7 +38,7 @@ enum {
 
 #define SCI_DEFAULT_ERROR_MASK (SCI_PER | SCI_FER)
 
-/* SCxSR SCIF */
+/* SCxSR SCIF, HSCIF */
 #define SCIF_ER    0x0080
 #define SCIF_TEND  0x0040
 #define SCIF_TDFE  0x0020
@@ -55,6 +56,9 @@ enum {
 #define SCSPTR_SPB2IO  (1 << 1)
 #define SCSPTR_SPB2DT  (1 << 0)
 
+/* HSSRR HSCIF */
+#define HSCIF_SRE      0x8000
+
 /* Offsets into the sci_port->irqs array */
 enum {
        SCIx_ERI_IRQ,
@@ -90,6 +94,7 @@ enum {
        SCIx_SH4_SCIF_NO_SCSPTR_REGTYPE,
        SCIx_SH4_SCIF_FIFODATA_REGTYPE,
        SCIx_SH7705_SCIF_REGTYPE,
+       SCIx_HSCIF_REGTYPE,
 
        SCIx_NR_REGTYPES,
 };
@@ -115,6 +120,7 @@ enum {
        SCSMR, SCBRR, SCSCR, SCxSR,
        SCFCR, SCFDR, SCxTDR, SCxRDR,
        SCLSR, SCTFDR, SCRFDR, SCSPTR,
+       HSSRR,
 
        SCIx_NR_REGS,
 };
@@ -137,7 +143,7 @@ struct plat_sci_port {
        unsigned long   mapbase;                /* resource base */
        unsigned int    irqs[SCIx_NR_IRQS];     /* ERI, RXI, TXI, BRI */
        unsigned int    gpios[SCIx_NR_FNS];     /* SCK, RXD, TXD, CTS, RTS */
-       unsigned int    type;                   /* SCI / SCIF / IRDA */
+       unsigned int    type;                   /* SCI / SCIF / IRDA / HSCIF */
        upf_t           flags;                  /* UPF_* flags */
        unsigned long   capabilities;           /* Port features/capabilities */
 
index 74c2bf7..26eee07 100644 (file)
 /* Rocketport EXPRESS/INFINITY */
 #define PORT_RP2       102
 
+/* SH-SCI */
+#define PORT_HSCIF     103
+
 #endif /* _UAPILINUX_SERIAL_CORE_H */