summaryrefslogtreecommitdiffstats
path: root/drivers/serial/stm-serial.c
diff options
context:
space:
mode:
authorJuergen Beisert <jbe@pengutronix.de>2010-10-11 10:58:47 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2010-10-11 15:42:19 +0200
commitc141e5bdd69def655c81b62251aff4ab520e4a30 (patch)
tree316cbb4fb7387b68dbeb6a056bd8b24467d0f94b /drivers/serial/stm-serial.c
parent7f12b3aa6823ff369567020d9a3428b889fdf78e (diff)
downloadbarebox-c141e5bdd69def655c81b62251aff4ab520e4a30.tar.gz
barebox-c141e5bdd69def655c81b62251aff4ab520e4a30.tar.xz
Add the driver for the i.MX23 debug UART
The i.MX23 comes with a special UART dedicated for debugging purposes. This one is mostly used for the console the user can reach. Signed-off-by: Juergen Beisert <jbe@pengutronix.de>
Diffstat (limited to 'drivers/serial/stm-serial.c')
-rw-r--r--drivers/serial/stm-serial.c202
1 files changed, 202 insertions, 0 deletions
diff --git a/drivers/serial/stm-serial.c b/drivers/serial/stm-serial.c
new file mode 100644
index 0000000000..90563f599e
--- /dev/null
+++ b/drivers/serial/stm-serial.c
@@ -0,0 +1,202 @@
+/*
+ * (C) Copyright 2010 Juergen Beisert - Pengutronix
+ *
+ * This code was inspired by some patches made for u-boot covered by:
+ * (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>
+ * (C) Copyright 2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * Note: This is the driver for the debug UART. There is
+ * only one of these UARTs on the Freescale/SigmaTel parts
+ */
+
+#include <common.h>
+#include <init.h>
+#include <notifier.h>
+#include <gpio.h>
+#include <asm/io.h>
+#include <mach/imx-regs.h>
+#include <mach/clock.h>
+
+#define UARTDBGDR 0x00
+#define UARTDBGFR 0x18
+# define TXFF (1 << 5)
+# define RXFE (1 << 4)
+#define UARTDBGIBRD 0x24
+#define UARTDBGFBRD 0x28
+#define UARTDBGLCR_H 0x2c
+# define WLEN8 (3 << 5)
+# define WLEN7 (2 << 5)
+# define WLEN6 (1 << 5)
+# define WLEN5 (0 << 5)
+# define FEN (1 << 4)
+#define UARTDBGCR 0x30
+# define UARTEN (1 << 0)
+# define TXE (1 << 8)
+# define RXE (1 << 9)
+#define UARTDBGIMSC 0x38
+
+struct stm_serial_local {
+ struct console_device cdev;
+ int baudrate;
+ struct notifier_block notify;
+};
+
+static void stm_serial_putc(struct console_device *cdev, char c)
+{
+ struct device_d *dev = cdev->dev;
+
+ /* Wait for room in TX FIFO */
+ while (readl(dev->map_base + UARTDBGFR) & TXFF)
+ ;
+
+ writel(c, dev->map_base + UARTDBGDR);
+}
+
+static int stm_serial_tstc(struct console_device *cdev)
+{
+ struct device_d *dev = cdev->dev;
+
+ /* Check if RX FIFO is not empty */
+ return !(readl(dev->map_base + UARTDBGFR) & RXFE);
+}
+
+static int stm_serial_getc(struct console_device *cdev)
+{
+ struct device_d *dev = cdev->dev;
+
+ /* Wait while TX FIFO is empty */
+ while (readl(dev->map_base + UARTDBGFR) & RXFE)
+ ;
+
+ return readl(dev->map_base + UARTDBGDR) & 0xff;
+}
+
+static void stm_serial_flush(struct console_device *cdev)
+{
+ struct device_d *dev = cdev->dev;
+
+ /* Wait for TX FIFO empty */
+ while (readl(dev->map_base + UARTDBGFR) & TXFF)
+ ;
+}
+
+static int stm_serial_setbaudrate(struct console_device *cdev, int new_baudrate)
+{
+ struct device_d *dev = cdev->dev;
+ struct stm_serial_local *local = container_of(cdev, struct stm_serial_local, cdev);
+ uint32_t cr, lcr_h, quot;
+
+ /* Disable everything */
+ cr = readl(dev->map_base + UARTDBGCR);
+ writel(0, dev->map_base + UARTDBGCR);
+
+ /* Calculate and set baudrate */
+ quot = (imx_get_xclk() * 4000) / new_baudrate;
+ writel(quot & 0x3f, dev->map_base + UARTDBGFBRD);
+ writel(quot >> 6, dev->map_base + UARTDBGIBRD);
+
+ /* Set 8n1 mode, enable FIFOs */
+ lcr_h = WLEN8 | FEN;
+ writel(lcr_h, dev->map_base + UARTDBGLCR_H);
+
+ /* Re-enable debug UART */
+ writel(cr, dev->map_base + UARTDBGCR);
+
+ local->baudrate = new_baudrate;
+
+ return 0;
+}
+
+static int stm_clocksource_clock_change(struct notifier_block *nb, unsigned long event, void *data)
+{
+ struct stm_serial_local *local = container_of(nb, struct stm_serial_local, notify);
+
+ return stm_serial_setbaudrate(&local->cdev, local->baudrate);
+}
+
+static int stm_serial_init_port(struct console_device *cdev)
+{
+ struct device_d *dev = cdev->dev;
+ /*
+ * If the board specific file registers this console we should force
+ * the usage of the debug UART pins, to be able to let the user see
+ * the output, even if the board file forgets to configure these pins.
+ */
+ imx_gpio_mode(PWM1_DUART_TX);
+ imx_gpio_mode(PWM0_DUART_RX);
+
+ /* Disable UART */
+ writel(0, dev->map_base + UARTDBGCR);
+
+ /* Mask interrupts */
+ writel(0, dev->map_base + UARTDBGIMSC);
+
+ return 0;
+}
+
+static struct stm_serial_local stm_device = {
+ .cdev = {
+ .f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR,
+ .tstc = stm_serial_tstc,
+ .putc = stm_serial_putc,
+ .getc = stm_serial_getc,
+ .flush = stm_serial_flush,
+ .setbrg = stm_serial_setbaudrate,
+ },
+};
+
+static int stm_serial_probe(struct device_d *dev)
+{
+ stm_device.cdev.dev = dev;
+ dev->type_data = &stm_device.cdev;
+
+ stm_serial_init_port(&stm_device.cdev);
+ stm_serial_setbaudrate(&stm_device.cdev, CONFIG_BAUDRATE);
+
+ /* Enable UART */
+ writel(TXE | RXE | UARTEN, dev->map_base + UARTDBGCR);
+
+ console_register(&stm_device.cdev);
+ stm_device.notify.notifier_call = stm_clocksource_clock_change;
+ clock_register_client(&stm_device.notify);
+
+ return 0;
+}
+
+static void stm_serial_remove(struct device_d *dev)
+{
+ struct console_device *cdev = dev->type_data;
+
+ stm_serial_flush(cdev);
+}
+
+static struct driver_d stm_serial_driver = {
+ .name = "stm_serial",
+ .probe = stm_serial_probe,
+ .remove = stm_serial_remove,
+};
+
+static int stm_serial_init(void)
+{
+ register_driver(&stm_serial_driver);
+ return 0;
+}
+
+console_initcall(stm_serial_init);