diff options
Diffstat (limited to 'drivers/serial/serial_pxa.c')
-rw-r--r-- | drivers/serial/serial_pxa.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/drivers/serial/serial_pxa.c b/drivers/serial/serial_pxa.c new file mode 100644 index 0000000000..d295235ee3 --- /dev/null +++ b/drivers/serial/serial_pxa.c @@ -0,0 +1,201 @@ +/* + * (c) 2009 Sascha Hauer <s.hauer@pengutronix.de> + * 2010 by Marc Kleine-Budde <kernel@pengutronix.de> + * + * 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. + * + */ + +#include <common.h> +#include <driver.h> +#include <init.h> +#include <malloc.h> + +#include <mach/clock.h> +#include <asm/io.h> + +#define RBR 0x00 /* Receive Buffer Register (read only) */ +#define THR 0x00 /* Transmit Holding Register (write only) */ +#define IER 0x04 /* Interrupt Enable Register (read/write) */ +#define IIR 0x08 /* Interrupt ID Register (read only) */ +#define FCR 0x08 /* FIFO Control Register (write only) */ +#define LCR 0x0c /* Line Control Register (read/write) */ +#define MCR 0x10 /* Modem Control Register (read/write) */ +#define LSR 0x14 /* Line Status Register (read only) */ +#define MSR 0x18 /* Modem Status Register (read only) */ +#define SPR 0x1c /* Scratch Pad Register (read/write) */ +#define ISR 0x20 /* Infrared Selection Register (read/write) */ +#define DLL 0x00 /* Divisor Latch Low Register (DLAB = 1) (read/write) */ +#define DLH 0x04 /* Divisor Latch High Register (DLAB = 1) (read/write) */ + +#define IER_DMAE (1 << 7) /* DMA Requests Enable */ +#define IER_UUE (1 << 6) /* UART Unit Enable */ +#define IER_NRZE (1 << 5) /* NRZ coding Enable */ +#define IER_RTIOE (1 << 4) /* Receiver Time Out Interrupt Enable */ +#define IER_MIE (1 << 3) /* Modem Interrupt Enable */ +#define IER_RLSE (1 << 2) /* Receiver Line Status Interrupt Enable */ +#define IER_TIE (1 << 1) /* Transmit Data request Interrupt Enable */ +#define IER_RAVIE (1 << 0) /* Receiver Data Available Interrupt Enable */ + +#define IIR_FIFOES1 (1 << 7) /* FIFO Mode Enable Status */ +#define IIR_FIFOES0 (1 << 6) /* FIFO Mode Enable Status */ +#define IIR_TOD (1 << 3) /* Time Out Detected */ +#define IIR_IID2 (1 << 2) /* Interrupt Source Encoded */ +#define IIR_IID1 (1 << 1) /* Interrupt Source Encoded */ +#define IIR_IP (1 << 0) /* Interrupt Pending (active low) */ + +#define FCR_ITL2 (1 << 7) /* Interrupt Trigger Level */ +#define FCR_ITL1 (1 << 6) /* Interrupt Trigger Level */ +#define FCR_RESETTF (1 << 2) /* Reset Transmitter FIFO */ +#define FCR_RESETRF (1 << 1) /* Reset Receiver FIFO */ +#define FCR_TRFIFOE (1 << 0) /* Transmit and Receive FIFO Enable */ +#define FCR_ITL_1 (0) +#define FCR_ITL_8 (FCR_ITL1) +#define FCR_ITL_16 (FCR_ITL2) +#define FCR_ITL_32 (FCR_ITL2 | F CR_ITL1) + +#define LCR_DLAB (1 << 7) /* Divisor Latch Access Bit */ +#define LCR_SB (1 << 6) /* Set Break */ +#define LCR_STKYP (1 << 5) /* Sticky Parity */ +#define LCR_EPS (1 << 4) /* Even Parity Select */ +#define LCR_PEN (1 << 3) /* Parity Enable */ +#define LCR_STB (1 << 2) /* Stop Bit */ +#define LCR_WLS1 (1 << 1) /* Word Length Select */ +#define LCR_WLS0 (1 << 0) /* Word Length Select */ +#define LCR_WLEN8 (LCR_WLS1 | LCR_WLS0) + /* Wordlength: 8 bits */ + +#define LSR_FIFOE (1 << 7) /* FIFO Error Status */ +#define LSR_TEMT (1 << 6) /* Transmitter Empty */ +#define LSR_TDRQ (1 << 5) /* Transmit Data Request */ +#define LSR_BI (1 << 4) /* Break Interrupt */ +#define LSR_FE (1 << 3) /* Framing Error */ +#define LSR_PE (1 << 2) /* Parity Error */ +#define LSR_OE (1 << 1) /* Overrun Error */ +#define LSR_DR (1 << 0) /* Data Ready */ + +#define MCR_LOOP (1 << 4) /* */ +#define MCR_OUT2 (1 << 3) /* force MSR_DCD in loopback mode */ +#define MCR_OUT1 (1 << 2) /* force MSR_RI in loopback mode */ +#define MCR_RTS (1 << 1) /* Request to Send */ +#define MCR_DTR (1 << 0) /* Data Terminal Ready */ + +#define MSR_DCD (1 << 7) /* Data Carrier Detect */ +#define MSR_RI (1 << 6) /* Ring Indicator */ +#define MSR_DSR (1 << 5) /* Data Set Ready */ +#define MSR_CTS (1 << 4) /* Clear To Send */ +#define MSR_DDCD (1 << 3) /* Delta Data Carrier Detect */ +#define MSR_TERI (1 << 2) /* Trailing Edge Ring Indicator */ +#define MSR_DDSR (1 << 1) /* Delta Data Set Ready */ +#define MSR_DCTS (1 << 0) /* Delta Clear To Send */ + +struct pxa_serial_priv { + void __iomem *regs; + struct console_device cdev; +}; + +static void __iomem *to_regs(struct console_device *cdev) +{ + struct pxa_serial_priv *priv = + container_of(cdev, struct pxa_serial_priv, cdev); + return priv->regs; +} + +static void pxa_serial_putc(struct console_device *cdev, char c) +{ + while (!(readl(to_regs(cdev) + LSR) & LSR_TEMT)); + + writel(c, to_regs(cdev) + THR); +} + +static int pxa_serial_tstc(struct console_device *cdev) +{ + return readl(to_regs(cdev) + LSR) & LSR_DR; +} + +static int pxa_serial_getc(struct console_device *cdev) +{ + while (!(readl(to_regs(cdev) + LSR) & LSR_DR)); + + return readl(to_regs(cdev) + RBR) & 0xff; +} + +static void pxa_serial_flush(struct console_device *cdev) +{ +} + +static int pxa_serial_setbaudrate(struct console_device *cdev, int baudrate) +{ + unsigned char cval = LCR_WLEN8; /* 8N1 */ + unsigned int quot; + + /* enable uart */ + writel(IER_UUE, to_regs(cdev) + IER); + + /* write divisor */ + quot = (pxa_get_uartclk() + (8 * baudrate)) / (16 * baudrate); + + writel(cval | LCR_DLAB, to_regs(cdev) + LCR); /* set DLAB */ + writel(quot & 0xff, to_regs(cdev) + DLL); + /* + * work around Errata #75 according to Intel(R) PXA27x + * Processor Family Specification Update (Nov 2005) + */ + readl(to_regs(cdev) + DLL); + writel(quot >> 8, to_regs(cdev) + DLH); + writel(cval, to_regs(cdev) + LCR); /* reset DLAB */ + + /* enable fifos */ + writel(FCR_TRFIFOE, to_regs(cdev) + FCR); + + return 0; +} + +static int pxa_serial_probe(struct device_d *dev) +{ + struct console_device *cdev; + struct pxa_serial_priv *priv; + + priv = xzalloc(sizeof(*priv)); + cdev = &priv->cdev; + priv->regs = dev_request_mem_region(dev, 0); + + dev->type_data = cdev; + cdev->dev = dev; + cdev->f_caps = CONSOLE_STDIN | CONSOLE_STDOUT | CONSOLE_STDERR; + cdev->tstc = pxa_serial_tstc; + cdev->putc = pxa_serial_putc; + cdev->getc = pxa_serial_getc; + cdev->flush = pxa_serial_flush; + cdev->setbrg = pxa_serial_setbaudrate; + + console_register(cdev); + + return 0; +} + +static void pxa_serial_remove(struct device_d *dev) +{ + free(dev->type_data); +} + +static struct driver_d pxa_serial_driver = { + .name = "pxa_serial", + .probe = pxa_serial_probe, + .remove = pxa_serial_remove, +}; + +static int pxa_serial_init(void) +{ + return register_driver(&pxa_serial_driver); +} + +console_initcall(pxa_serial_init); |