|
@@ -0,0 +1,211 @@
|
|
|
+/*
|
|
|
+ * Support for Serial I/O using STMicroelectronics' on-chip ASC.
|
|
|
+ *
|
|
|
+ * Copyright (c) 2017
|
|
|
+ * Patrice Chotard <patrice.chotard@st.com>
|
|
|
+ *
|
|
|
+ * SPDX-License-Identifier: GPL-2.0
|
|
|
+ */
|
|
|
+
|
|
|
+#include <common.h>
|
|
|
+#include <dm.h>
|
|
|
+#include <serial.h>
|
|
|
+#include <asm/io.h>
|
|
|
+
|
|
|
+DECLARE_GLOBAL_DATA_PTR;
|
|
|
+
|
|
|
+#define BAUDMODE 0x00001000
|
|
|
+#define RXENABLE 0x00000100
|
|
|
+#define RUN 0x00000080
|
|
|
+#define MODE 0x00000001
|
|
|
+#define MODE_8BIT 0x0001
|
|
|
+#define STOP_1BIT 0x0008
|
|
|
+#define PARITYODD 0x0020
|
|
|
+
|
|
|
+#define STA_TF BIT(9)
|
|
|
+#define STA_RBF BIT(0)
|
|
|
+
|
|
|
+struct sti_asc_uart {
|
|
|
+ u32 baudrate;
|
|
|
+ u32 txbuf;
|
|
|
+ u32 rxbuf;
|
|
|
+ u32 control;
|
|
|
+ u32 inten;
|
|
|
+ u32 status;
|
|
|
+ u32 guardtime;
|
|
|
+ u32 timeout;
|
|
|
+ u32 txreset;
|
|
|
+ u32 rxreset;
|
|
|
+};
|
|
|
+
|
|
|
+struct sti_asc_serial {
|
|
|
+ /* address of registers in physical memory */
|
|
|
+ struct sti_asc_uart *regs;
|
|
|
+};
|
|
|
+
|
|
|
+/* Values for the BAUDRATE Register */
|
|
|
+#define PCLK (200ul * 1000000ul)
|
|
|
+#define BAUDRATE_VAL_M0(bps) (PCLK / (16 * (bps)))
|
|
|
+#define BAUDRATE_VAL_M1(bps) ((bps * (1 << 14)) + (1<<13)) / (PCLK/(1 << 6))
|
|
|
+
|
|
|
+/*
|
|
|
+ * MODE 0
|
|
|
+ * ICCLK
|
|
|
+ * ASCBaudRate = ----------------
|
|
|
+ * baudrate * 16
|
|
|
+ *
|
|
|
+ * MODE 1
|
|
|
+ * baudrate * 16 * 2^16
|
|
|
+ * ASCBaudRate = ------------------------
|
|
|
+ * ICCLK
|
|
|
+ *
|
|
|
+ * NOTE:
|
|
|
+ * Mode 1 should be used for baudrates of 19200, and above, as it
|
|
|
+ * has a lower deviation error than Mode 0 for higher frequencies.
|
|
|
+ * Mode 0 should be used for all baudrates below 19200.
|
|
|
+ */
|
|
|
+
|
|
|
+static int sti_asc_pending(struct udevice *dev, bool input)
|
|
|
+{
|
|
|
+ struct sti_asc_serial *priv = dev_get_priv(dev);
|
|
|
+ struct sti_asc_uart *const uart = priv->regs;
|
|
|
+ unsigned long status;
|
|
|
+
|
|
|
+ status = readl(&uart->status);
|
|
|
+ if (input)
|
|
|
+ return status & STA_RBF;
|
|
|
+ else
|
|
|
+ return status & STA_TF;
|
|
|
+}
|
|
|
+
|
|
|
+static int _sti_asc_serial_setbrg(struct sti_asc_uart *uart, int baudrate)
|
|
|
+{
|
|
|
+ unsigned long val;
|
|
|
+ int t, mode = 1;
|
|
|
+
|
|
|
+ switch (baudrate) {
|
|
|
+ case 9600:
|
|
|
+ t = BAUDRATE_VAL_M0(9600);
|
|
|
+ mode = 0;
|
|
|
+ break;
|
|
|
+ case 19200:
|
|
|
+ t = BAUDRATE_VAL_M1(19200);
|
|
|
+ break;
|
|
|
+ case 38400:
|
|
|
+ t = BAUDRATE_VAL_M1(38400);
|
|
|
+ break;
|
|
|
+ case 57600:
|
|
|
+ t = BAUDRATE_VAL_M1(57600);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ debug("ASC: unsupported baud rate: %d, using 115200 instead.\n",
|
|
|
+ baudrate);
|
|
|
+ case 115200:
|
|
|
+ t = BAUDRATE_VAL_M1(115200);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* disable the baudrate generator */
|
|
|
+ val = readl(&uart->control);
|
|
|
+ writel(val & ~RUN, &uart->control);
|
|
|
+
|
|
|
+ /* set baud generator reload value */
|
|
|
+ writel(t, &uart->baudrate);
|
|
|
+ /* reset the RX & TX buffers */
|
|
|
+ writel(1, &uart->txreset);
|
|
|
+ writel(1, &uart->rxreset);
|
|
|
+
|
|
|
+ /* set baud generator mode */
|
|
|
+ if (mode)
|
|
|
+ val |= BAUDMODE;
|
|
|
+
|
|
|
+ /* finally, write value and enable ASC */
|
|
|
+ writel(val, &uart->control);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* called to adjust baud-rate */
|
|
|
+static int sti_asc_serial_setbrg(struct udevice *dev, int baudrate)
|
|
|
+{
|
|
|
+ struct sti_asc_serial *priv = dev_get_priv(dev);
|
|
|
+ struct sti_asc_uart *const uart = priv->regs;
|
|
|
+
|
|
|
+ return _sti_asc_serial_setbrg(uart, baudrate);
|
|
|
+}
|
|
|
+
|
|
|
+/* blocking function, that returns next char */
|
|
|
+static int sti_asc_serial_getc(struct udevice *dev)
|
|
|
+{
|
|
|
+ struct sti_asc_serial *priv = dev_get_priv(dev);
|
|
|
+ struct sti_asc_uart *const uart = priv->regs;
|
|
|
+
|
|
|
+ /* polling wait: for a char to be read */
|
|
|
+ if (!sti_asc_pending(dev, true))
|
|
|
+ return -EAGAIN;
|
|
|
+
|
|
|
+ return readl(&uart->rxbuf);
|
|
|
+}
|
|
|
+
|
|
|
+/* write write out a single char */
|
|
|
+static int sti_asc_serial_putc(struct udevice *dev, const char c)
|
|
|
+{
|
|
|
+ struct sti_asc_serial *priv = dev_get_priv(dev);
|
|
|
+ struct sti_asc_uart *const uart = priv->regs;
|
|
|
+
|
|
|
+ /* wait till safe to write next char */
|
|
|
+ if (sti_asc_pending(dev, false))
|
|
|
+ return -EAGAIN;
|
|
|
+
|
|
|
+ /* finally, write next char */
|
|
|
+ writel(c, &uart->txbuf);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* initialize the ASC */
|
|
|
+static int sti_asc_serial_probe(struct udevice *dev)
|
|
|
+{
|
|
|
+ struct sti_asc_serial *priv = dev_get_priv(dev);
|
|
|
+ unsigned long val;
|
|
|
+ fdt_addr_t base;
|
|
|
+
|
|
|
+ base = dev_get_addr(dev);
|
|
|
+ if (base == FDT_ADDR_T_NONE)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ priv->regs = (struct sti_asc_uart *)base;
|
|
|
+ sti_asc_serial_setbrg(dev, gd->baudrate);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * build up the value to be written to CONTROL
|
|
|
+ * set character length, bit stop number, odd parity
|
|
|
+ */
|
|
|
+ val = RXENABLE | RUN | MODE_8BIT | STOP_1BIT | PARITYODD;
|
|
|
+ writel(val, &priv->regs->control);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct dm_serial_ops sti_asc_serial_ops = {
|
|
|
+ .putc = sti_asc_serial_putc,
|
|
|
+ .pending = sti_asc_pending,
|
|
|
+ .getc = sti_asc_serial_getc,
|
|
|
+ .setbrg = sti_asc_serial_setbrg,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct udevice_id sti_serial_of_match[] = {
|
|
|
+ { .compatible = "st,asc" },
|
|
|
+ { }
|
|
|
+};
|
|
|
+
|
|
|
+U_BOOT_DRIVER(serial_sti_asc) = {
|
|
|
+ .name = "serial_sti_asc",
|
|
|
+ .id = UCLASS_SERIAL,
|
|
|
+ .of_match = sti_serial_of_match,
|
|
|
+ .ops = &sti_asc_serial_ops,
|
|
|
+ .probe = sti_asc_serial_probe,
|
|
|
+ .priv_auto_alloc_size = sizeof(struct sti_asc_serial),
|
|
|
+ .flags = DM_FLAG_PRE_RELOC,
|
|
|
+};
|
|
|
+
|