From f13033d938081deed6dcb62a17ee98925c6140f3 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Tue, 12 Jan 2010 20:30:51 +0100 Subject: Add PrimeCell PL010 serial driver Added driver for ARM PrimeCell PL010 UART Signed-off-by: Matthias Kaehlcke Signed-off-by: Sascha Hauer --- drivers/serial/serial_pl010.c | 172 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 drivers/serial/serial_pl010.c (limited to 'drivers/serial/serial_pl010.c') diff --git a/drivers/serial/serial_pl010.c b/drivers/serial/serial_pl010.c new file mode 100644 index 0000000000..1a6366f63c --- /dev/null +++ b/drivers/serial/serial_pl010.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2010 Matthias Kaehlcke + * + * (C) Copyright 2000 + * Rob Taylor, Flying Pig Systems. robt@flyingpig.com. + * + * (C) Copyright 2004 + * ARM Ltd. + * Philippe Robin, + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 + */ + +/* Simple U-Boot driver for the PrimeCell PL010/PL011 UARTs */ + +#include +#include +#include +#include +#include "serial_pl010.h" + +static int pl010_setbaudrate(struct console_device *cdev, int baudrate) +{ + struct pl010_struct *pl010 = (struct pl010_struct *)cdev->dev->map_base; + unsigned int divisor; + + switch (baudrate) { + case 9600: + divisor = UART_PL010_BAUD_9600; + break; + + case 19200: + divisor = UART_PL010_BAUD_9600; + break; + + case 38400: + divisor = UART_PL010_BAUD_38400; + break; + + case 57600: + divisor = UART_PL010_BAUD_57600; + break; + + case 115200: + divisor = UART_PL010_BAUD_115200; + break; + + default: + divisor = UART_PL010_BAUD_38400; + } + + writel((divisor & 0xf00) >> 8, &pl010->linctrlmid); + writel(divisor & 0xff, &pl010->linctrllow); + + /* high register must always be written */ + writel(readl(&pl010->linctrlhigh), &pl010->linctrlhigh); + + return 0; +} + +static int pl010_init_port(struct console_device *cdev) +{ + struct pl010_struct *pl010 = (struct pl010_struct *)cdev->dev->map_base; + + /* + * First, disable everything. + */ + writel(0x00, &pl010->ctrl); + + /* + * Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled. + */ + writel(UART_PL010_LCRH_WLEN_8 | UART_PL010_LCRH_FEN, + &pl010->linctrlhigh); + + /* + * Finally, enable the UART + */ + writel(UART_PL010_CR_UARTEN, &pl010->ctrl); + + return 0; +} + +static void pl010_putc(struct console_device *cdev, char c) +{ + struct pl010_struct *pl010 = (struct pl010_struct *)cdev->dev->map_base; + + /* Wait until there is space in the FIFO */ + while (readl(&pl010->flag) & UART_PL010_FR_TXFF) + ; /* noop */ + + /* Send the character */ + writel(c, &pl010->data); +} + +static int pl010_getc(struct console_device *cdev) +{ + struct pl010_struct *pl010 = (struct pl010_struct *)cdev->dev->map_base; + unsigned int data; + + /* Wait until there is data in the FIFO */ + while (readl(&pl010->flag) & UART_PL010_FR_RXFE) + ; /* noop */ + + data = readl(&pl010->data); + + /* Check for an error flag */ + if (data & 0xFFFFFF00) { + /* Clear the error */ + writel(0xFFFFFFFF, &pl010->errclr); + return -1; + } + + return (int)data; +} + +static int pl010_tstc(struct console_device *cdev) +{ + struct pl010_struct *pl010 = (struct pl010_struct *)cdev->dev->map_base; + + return !(readl(&pl010->flag) & UART_PL010_FR_RXFE); +} + +static int pl010_probe(struct device_d *dev) +{ + struct console_device *cdev; + + cdev = malloc(sizeof(struct console_device)); + dev->type_data = cdev; + cdev->dev = dev; + cdev->f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR; + cdev->tstc = pl010_tstc; + cdev->putc = pl010_putc; + cdev->getc = pl010_getc; + cdev->setbrg = pl010_setbaudrate; + + pl010_init_port(cdev); + + console_register(cdev); + + return 0; +} + +static struct driver_d pl010_driver = { + .name = "pl010_serial", + .probe = pl010_probe, +}; + +static int pl010_init(void) +{ + register_driver(&pl010_driver); + + return 0; +} + +console_initcall(pl010_init); -- cgit v1.2.3