serial: lpuart: add i.MX7ULP support
authorPeng Fan <peng.fan@nxp.com>
Wed, 22 Feb 2017 08:21:52 +0000 (16:21 +0800)
committerStefano Babic <sbabic@denx.de>
Fri, 17 Mar 2017 08:27:08 +0000 (09:27 +0100)
Add i.MX7ULP support.
The buadrate calculation on i.MX7ULP is different,so add a new setbrg
function for i.MX7ULP.
Add a enum lpuart_devtype for runtime check for different platforms.

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Cc: Stefano Babic <sbabic@denx.de>
Cc: Bhuvanchandra DV <bhuvanchandra.dv@toradex.com>
Cc: York Sun <york.sun@nxp.com>
Cc: Shaohui Xie <Shaohui.Xie@nxp.com>
Cc: Alison Wang <b18965@freescale.com>
drivers/serial/serial_lpuart.c
include/fsl_lpuart.h [new file with mode: 0644]

index 3f030a622f2f7ed9440084dae5be8718ea700e68..95e002ea4b733b723c2961753b5e260508bde0c9 100644 (file)
@@ -52,8 +52,15 @@ DECLARE_GLOBAL_DATA_PTR;
 #define LPUART_FLAG_REGMAP_32BIT_REG   BIT(0)
 #define LPUART_FLAG_REGMAP_ENDIAN_BIG  BIT(1)
 
+enum lpuart_devtype {
+       DEV_VF610 = 1,
+       DEV_LS1021A,
+       DEV_MX7ULP
+};
+
 struct lpuart_serial_platdata {
        void *reg;
+       enum lpuart_devtype devtype;
        ulong flags;
 };
 
@@ -172,6 +179,65 @@ static int _lpuart_serial_init(struct lpuart_serial_platdata *plat)
        return 0;
 }
 
+static void _lpuart32_serial_setbrg_7ulp(struct lpuart_serial_platdata *plat,
+                                        int baudrate)
+{
+       struct lpuart_fsl_reg32 *base = plat->reg;
+       u32 sbr, osr, baud_diff, tmp_osr, tmp_sbr, tmp_diff, tmp;
+       u32 clk = get_lpuart_clk();
+
+       baud_diff = baudrate;
+       osr = 0;
+       sbr = 0;
+
+       for (tmp_osr = 4; tmp_osr <= 32; tmp_osr++) {
+               tmp_sbr = (clk / (baudrate * tmp_osr));
+
+               if (tmp_sbr == 0)
+                       tmp_sbr = 1;
+
+               /*calculate difference in actual buad w/ current values */
+               tmp_diff = (clk / (tmp_osr * tmp_sbr));
+               tmp_diff = tmp_diff - baudrate;
+
+               /* select best values between sbr and sbr+1 */
+               if (tmp_diff > (baudrate - (clk / (tmp_osr * (tmp_sbr + 1))))) {
+                       tmp_diff = baudrate - (clk / (tmp_osr * (tmp_sbr + 1)));
+                       tmp_sbr++;
+               }
+
+               if (tmp_diff <= baud_diff) {
+                       baud_diff = tmp_diff;
+                       osr = tmp_osr;
+                       sbr = tmp_sbr;
+               }
+       }
+
+       /*
+        * TODO: handle buadrate outside acceptable rate
+        * if (baudDiff > ((config->baudRate_Bps / 100) * 3))
+        * {
+        *   Unacceptable baud rate difference of more than 3%
+        *   return kStatus_LPUART_BaudrateNotSupport;
+        * }
+        */
+       tmp = in_le32(&base->baud);
+
+       if ((osr > 3) && (osr < 8))
+               tmp |= LPUART_BAUD_BOTHEDGE_MASK;
+
+       tmp &= ~LPUART_BAUD_OSR_MASK;
+       tmp |= LPUART_BAUD_OSR(osr-1);
+
+       tmp &= ~LPUART_BAUD_SBR_MASK;
+       tmp |= LPUART_BAUD_SBR(sbr);
+
+       /* explicitly disable 10 bit mode & set 1 stop bit */
+       tmp &= ~(LPUART_BAUD_M10_MASK | LPUART_BAUD_SBNS_MASK);
+
+       out_le32(&base->baud, tmp);
+}
+
 static void _lpuart32_serial_setbrg(struct lpuart_serial_platdata *plat,
                                    int baudrate)
 {
@@ -188,7 +254,7 @@ static void _lpuart32_serial_setbrg(struct lpuart_serial_platdata *plat,
 static int _lpuart32_serial_getc(struct lpuart_serial_platdata *plat)
 {
        struct lpuart_fsl_reg32 *base = plat->reg;
-       u32 stat;
+       u32 stat, val;
 
        lpuart_read32(plat->flags, &base->stat, &stat);
        while ((stat & STAT_RDRF) == 0) {
@@ -197,10 +263,15 @@ static int _lpuart32_serial_getc(struct lpuart_serial_platdata *plat)
                lpuart_read32(plat->flags, &base->stat, &stat);
        }
 
-       /* Reuse stat */
-       lpuart_read32(plat->flags, &base->data, &stat);
+       lpuart_read32(plat->flags, &base->data, &val);
 
-       return stat & 0x3ff;
+       if (plat->devtype & DEV_MX7ULP) {
+               lpuart_read32(plat->flags, &base->stat, &stat);
+               if (stat & STAT_OR)
+                       lpuart_write32(plat->flags, &base->stat, STAT_OR);
+       }
+
+       return val & 0x3ff;
 }
 
 static void _lpuart32_serial_putc(struct lpuart_serial_platdata *plat,
@@ -209,6 +280,11 @@ static void _lpuart32_serial_putc(struct lpuart_serial_platdata *plat,
        struct lpuart_fsl_reg32 *base = plat->reg;
        u32 stat;
 
+       if (plat->devtype & DEV_MX7ULP) {
+               if (c == '\n')
+                       serial_putc('\r');
+       }
+
        while (true) {
                lpuart_read32(plat->flags, &base->stat, &stat);
 
@@ -254,8 +330,12 @@ static int _lpuart32_serial_init(struct lpuart_serial_platdata *plat)
 
        lpuart_write32(plat->flags, &base->match, 0);
 
-       /* provide data bits, parity, stop bit, etc */
-       _lpuart32_serial_setbrg(plat, gd->baudrate);
+       if (plat->devtype & DEV_MX7ULP) {
+               _lpuart32_serial_setbrg_7ulp(plat, gd->baudrate);
+       } else {
+               /* provide data bits, parity, stop bit, etc */
+               _lpuart32_serial_setbrg(plat, gd->baudrate);
+       }
 
        lpuart_write32(plat->flags, &base->ctrl, CTRL_RE | CTRL_TE);
 
@@ -266,10 +346,14 @@ static int lpuart_serial_setbrg(struct udevice *dev, int baudrate)
 {
        struct lpuart_serial_platdata *plat = dev->platdata;
 
-       if (is_lpuart32(dev))
-               _lpuart32_serial_setbrg(plat, baudrate);
-       else
+       if (is_lpuart32(dev)) {
+               if (plat->devtype & DEV_MX7ULP)
+                       _lpuart32_serial_setbrg_7ulp(plat, baudrate);
+               else
+                       _lpuart32_serial_setbrg(plat, baudrate);
+       } else {
                _lpuart_serial_setbrg(plat, baudrate);
+       }
 
        return 0;
 }
@@ -331,6 +415,8 @@ static int lpuart_serial_probe(struct udevice *dev)
 static int lpuart_serial_ofdata_to_platdata(struct udevice *dev)
 {
        struct lpuart_serial_platdata *plat = dev->platdata;
+       const void *blob = gd->fdt_blob;
+       int node = dev->of_offset;
        fdt_addr_t addr;
 
        addr = dev_get_addr(dev);
@@ -340,6 +426,13 @@ static int lpuart_serial_ofdata_to_platdata(struct udevice *dev)
        plat->reg = (void *)addr;
        plat->flags = dev_get_driver_data(dev);
 
+       if (!fdt_node_check_compatible(blob, node, "fsl,ls1021a-lpuart"))
+               plat->devtype = DEV_LS1021A;
+       else if (!fdt_node_check_compatible(blob, node, "fsl,imx7ulp-lpuart"))
+               plat->devtype = DEV_MX7ULP;
+       else if (!fdt_node_check_compatible(blob, node, "fsl,vf610-lpuart"))
+               plat->devtype = DEV_VF610;
+
        return 0;
 }
 
@@ -353,6 +446,8 @@ static const struct dm_serial_ops lpuart_serial_ops = {
 static const struct udevice_id lpuart_serial_ids[] = {
        { .compatible = "fsl,ls1021a-lpuart", .data =
                LPUART_FLAG_REGMAP_32BIT_REG | LPUART_FLAG_REGMAP_ENDIAN_BIG },
+       { .compatible = "fsl,imx7ulp-lpuart",
+               .data = LPUART_FLAG_REGMAP_32BIT_REG },
        { .compatible = "fsl,vf610-lpuart"},
        { }
 };
diff --git a/include/fsl_lpuart.h b/include/fsl_lpuart.h
new file mode 100644 (file)
index 0000000..4643ee7
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2016 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ *
+ */
+
+#ifdef CONFIG_ARCH_MX7ULP
+struct lpuart_fsl_reg32 {
+       u32 verid;
+       u32 param;
+       u32 global;
+       u32 pincfg;
+       u32 baud;
+       u32 stat;
+       u32 ctrl;
+       u32 data;
+       u32 match;
+       u32 modir;
+       u32 fifo;
+       u32 water;
+};
+#else
+struct lpuart_fsl_reg32 {
+       u32 baud;
+       u32 stat;
+       u32 ctrl;
+       u32 data;
+       u32 match;
+       u32 modir;
+       u32 fifo;
+       u32 water;
+};
+#endif
+
+struct lpuart_fsl {
+       u8 ubdh;
+       u8 ubdl;
+       u8 uc1;
+       u8 uc2;
+       u8 us1;
+       u8 us2;
+       u8 uc3;
+       u8 ud;
+       u8 uma1;
+       u8 uma2;
+       u8 uc4;
+       u8 uc5;
+       u8 ued;
+       u8 umodem;
+       u8 uir;
+       u8 reserved;
+       u8 upfifo;
+       u8 ucfifo;
+       u8 usfifo;
+       u8 utwfifo;
+       u8 utcfifo;
+       u8 urwfifo;
+       u8 urcfifo;
+       u8 rsvd[28];
+};
+
+/* Used on i.MX7ULP */
+#define LPUART_BAUD_BOTHEDGE_MASK      (0x20000)
+#define LPUART_BAUD_OSR_MASK           (0x1F000000)
+#define LPUART_BAUD_OSR_SHIFT          (24)
+#define LPUART_BAUD_OSR(x)             ((((uint32_t)(x)) << 24) & 0x1F000000)
+#define LPUART_BAUD_SBR_MASK           (0x1FFF)
+#define LPUART_BAUD_SBR_SHIFT          (0U)
+#define LPUART_BAUD_SBR(x)             (((uint32_t)(x)) & 0x1FFF)
+#define LPUART_BAUD_M10_MASK           (0x20000000U)
+#define LPUART_BAUD_SBNS_MASK          (0x2000U)