]> git.sur5r.net Git - u-boot/blobdiff - drivers/serial/serial_lpuart.c
Merge branch 'master' of git://git.denx.de/u-boot-sunxi
[u-boot] / drivers / serial / serial_lpuart.c
index 3f030a622f2f7ed9440084dae5be8718ea700e68..1212b7267634c6be6d5d948c412fe2e7d3888199 100644 (file)
@@ -1,7 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright 2013 Freescale Semiconductor, Inc.
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
@@ -52,8 +51,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 +178,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 +253,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 +262,13 @@ 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);
+
+       lpuart_read32(plat->flags, &base->stat, &stat);
+       if (stat & STAT_OR)
+               lpuart_write32(plat->flags, &base->stat, STAT_OR);
 
-       return stat & 0x3ff;
+       return val & 0x3ff;
 }
 
 static void _lpuart32_serial_putc(struct lpuart_serial_platdata *plat,
@@ -209,6 +277,9 @@ static void _lpuart32_serial_putc(struct lpuart_serial_platdata *plat,
        struct lpuart_fsl_reg32 *base = plat->reg;
        u32 stat;
 
+       if (c == '\n')
+               serial_putc('\r');
+
        while (true) {
                lpuart_read32(plat->flags, &base->stat, &stat);
 
@@ -254,8 +325,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 +341,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,15 +410,24 @@ 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(dev);
        fdt_addr_t addr;
 
-       addr = dev_get_addr(dev);
+       addr = devfdt_get_addr(dev);
        if (addr == FDT_ADDR_T_NONE)
                return -EINVAL;
 
        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 +441,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"},
        { }
 };