Commit 5566c10d authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://bk.arm.linux.org.uk/linux-2.6-serial

into ppc970.osdl.org:/home/torvalds/v2.6/linux
parents 0d61fc5e d6a03f19
......@@ -133,6 +133,7 @@ struct uart_8250_port {
unsigned char acr;
unsigned char ier;
unsigned char lcr;
unsigned char mcr;
unsigned char mcr_mask; /* mask of user bits */
unsigned char mcr_force; /* mask of forced bits */
unsigned char lsr_break_flag;
......@@ -177,11 +178,11 @@ static _INLINE_ unsigned int serial_in(struct uart_8250_port *up, int offset)
offset <<= up->port.regshift;
switch (up->port.iotype) {
case SERIAL_IO_HUB6:
case UPIO_HUB6:
outb(up->port.hub6 - 1 + offset, up->port.iobase);
return inb(up->port.iobase + 1);
case SERIAL_IO_MEM:
case UPIO_MEM:
return readb(up->port.membase + offset);
default:
......@@ -195,12 +196,12 @@ serial_out(struct uart_8250_port *up, int offset, int value)
offset <<= up->port.regshift;
switch (up->port.iotype) {
case SERIAL_IO_HUB6:
case UPIO_HUB6:
outb(up->port.hub6 - 1 + offset, up->port.iobase);
outb(value, up->port.iobase + 1);
break;
case SERIAL_IO_MEM:
case UPIO_MEM:
writeb(value, up->port.membase + offset);
break;
......@@ -574,7 +575,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
if (!up->port.iobase && !up->port.mapbase && !up->port.membase)
return;
DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%08lx): ",
DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%p): ",
up->port.line, up->port.iobase, up->port.membase);
/*
......@@ -1176,7 +1177,7 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
if (mctrl & TIOCM_LOOP)
mcr |= UART_MCR_LOOP;
mcr = (mcr & up->mcr_mask) | up->mcr_force;
mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
serial_out(up, UART_MCR, mcr);
}
......@@ -1202,6 +1203,7 @@ static int serial8250_startup(struct uart_port *port)
int retval;
up->capabilities = uart_config[up->port.type].flags;
up->mcr = 0;
if (up->port.type == PORT_16C950) {
/* Wake up and initialize UART */
......@@ -1451,8 +1453,19 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
else
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
}
if (up->port.type == PORT_16750)
/*
* TI16C750: hardware flow control and 64 byte FIFOs. When AFE is
* enabled, RTS will be deasserted when the receive FIFO contains
* more characters than the trigger, or the MCR RTS bit is cleared.
*/
if (up->port.type == PORT_16750) {
up->mcr &= ~UART_MCR_AFE;
if (termios->c_cflag & CRTSCTS)
up->mcr |= UART_MCR_AFE;
fcr |= UART_FCR7_64BYTE;
}
/*
* Ok, we're now changing the port state. Do it with
......@@ -1514,10 +1527,17 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
} else {
serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
}
serial_outp(up, UART_DLL, quot & 0xff); /* LS of divisor */
serial_outp(up, UART_DLM, quot >> 8); /* MS of divisor */
/*
* LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
* is written without DLAB set, this mode will be disabled.
*/
if (up->port.type == PORT_16750)
serial_outp(up, UART_FCR, fcr); /* set fcr */
serial_outp(up, UART_FCR, fcr);
serial_outp(up, UART_LCR, cval); /* reset DLAB */
up->lcr = cval; /* Save LCR */
if (up->port.type != PORT_16750) {
......@@ -1527,6 +1547,7 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios,
}
serial_outp(up, UART_FCR, fcr); /* set fcr */
}
serial8250_set_mctrl(&up->port, up->port.mctrl);
spin_unlock_irqrestore(&up->port.lock, flags);
}
......@@ -1613,7 +1634,7 @@ serial8250_request_std_resource(struct uart_8250_port *up, struct resource **res
int ret = 0;
switch (up->port.iotype) {
case SERIAL_IO_MEM:
case UPIO_MEM:
if (up->port.mapbase) {
*res = request_mem_region(up->port.mapbase, size, "serial");
if (!*res)
......@@ -1621,8 +1642,8 @@ serial8250_request_std_resource(struct uart_8250_port *up, struct resource **res
}
break;
case SERIAL_IO_HUB6:
case SERIAL_IO_PORT:
case UPIO_HUB6:
case UPIO_PORT:
*res = request_region(up->port.iobase, size, "serial");
if (!*res)
ret = -EBUSY;
......@@ -1639,7 +1660,7 @@ serial8250_request_rsa_resource(struct uart_8250_port *up, struct resource **res
int ret = 0;
switch (up->port.iotype) {
case SERIAL_IO_MEM:
case UPIO_MEM:
if (up->port.mapbase) {
start = up->port.mapbase;
start += UART_RSA_BASE << up->port.regshift;
......@@ -1649,8 +1670,8 @@ serial8250_request_rsa_resource(struct uart_8250_port *up, struct resource **res
}
break;
case SERIAL_IO_HUB6:
case SERIAL_IO_PORT:
case UPIO_HUB6:
case UPIO_PORT:
start = up->port.iobase;
start += UART_RSA_BASE << up->port.regshift;
*res = request_region(start, size, "serial-rsa");
......@@ -1667,6 +1688,8 @@ static void serial8250_release_port(struct uart_port *port)
struct uart_8250_port *up = (struct uart_8250_port *)port;
unsigned long start, offset = 0, size = 0;
if (!(up->port.flags & UPF_RESOURCES))
return;
if (up->port.type == PORT_RSA) {
offset = UART_RSA_BASE << up->port.regshift;
size = 8;
......@@ -1675,7 +1698,7 @@ static void serial8250_release_port(struct uart_port *port)
size <<= up->port.regshift;
switch (up->port.iotype) {
case SERIAL_IO_MEM:
case UPIO_MEM:
if (up->port.mapbase) {
/*
* Unmap the area.
......@@ -1691,8 +1714,8 @@ static void serial8250_release_port(struct uart_port *port)
}
break;
case SERIAL_IO_HUB6:
case SERIAL_IO_PORT:
case UPIO_HUB6:
case UPIO_PORT:
start = up->port.iobase;
if (size)
......
......@@ -17,15 +17,6 @@
#include <linux/config.h>
struct serial8250_probe {
struct module *owner;
int (*pci_init_one)(struct pci_dev *dev);
void (*pci_remove_one)(struct pci_dev *dev);
void (*pnp_init)(void);
};
int serial8250_register_probe(struct serial8250_probe *probe);
void serial8250_unregister_probe(struct serial8250_probe *probe);
void serial8250_get_irq_map(unsigned int *map);
void serial8250_suspend_port(int line);
void serial8250_resume_port(int line);
......
......@@ -186,6 +186,8 @@ setup_serial_hcdp(void *tablep)
port.irq = gsi;
#endif
port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_RESOURCES;
if (gsi)
port.flags |= UPF_AUTO_IRQ;
/*
* Note: the above memset() initializes port.line to 0,
......
......@@ -224,6 +224,37 @@ config SERIAL_CLPS711X_CONSOLE
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
config SERIAL_S3C2410
tristate "Samsung S3C2410 Serial port support"
depends on ARM && ARCH_S3C2410
select SERIAL_CORE
help
Support for the on-chip UARTs on the Samsung S3C2410X CPU,
providing /dev/ttySAC0, 1 and 2 (note, some machines may not
provide all of these ports, depending on how the serial port
pins are configured.
config SERIAL_S3C2410_CONSOLE
bool "Support for console on S3C2410 serial port"
depends on SERIAL_S3C2410=y
help
Allow selection of the S3C2410 on-board serial ports for use as
an virtual console.
Even if you say Y here, the currently visible virtual console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
"console=ttySACx". (Try "man bootparam" or see the documentation of
your boot loader about how to pass options to the kernel at
boot time.)
config SERIAL_BAST_SIO
bool "Support for BAST SuperIO serial ports"
depends on ARCH_BAST && SERIAL_8250=y
help
Support for registerin the SuperIO chip on BAST board with
the 8250/16550 uart code.
config SERIAL_DZ
bool "DECstation DZ serial driver"
depends on DECSTATION
......
......@@ -20,6 +20,7 @@ obj-$(CONFIG_SERIAL_AMBA) += amba.o
obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
obj-$(CONFIG_SERIAL_PXA) += pxa.o
obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o
obj-$(CONFIG_SERIAL_UART00) += uart00.o
obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o
obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o
......@@ -36,3 +37,4 @@ obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o
obj-$(CONFIG_SERIAL_AU1X00) += au1x00_uart.o
obj-$(CONFIG_SERIAL_DZ) += dz.o
obj-$(CONFIG_SERIAL_SH_SCI) += sh-sci.o
obj-$(CONFIG_SERIAL_BAST_SIO) += bast_sio.o
#include <linux/module.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/types.h>
#include <asm/io.h>
#include <asm/serial.h>
#include <asm/mach-types.h>
#include <asm/arch/map.h>
#include <asm/arch/irqs.h>
#include <asm/arch/bast-map.h>
#include <asm/arch/bast-irq.h>
static int __init serial_bast_register(unsigned long port, unsigned int irq)
{
struct serial_struct serial_req;
#if 0
printk("BAST: SuperIO serial (%08lx,%d)\n", port, irq);
#endif
serial_req.flags = UPF_AUTOPROBE | UPF_RESOURCES | UPF_SHARE_IRQ;
serial_req.baud_base = BASE_BAUD;
serial_req.irq = irq;
serial_req.io_type = UPIO_MEM;
serial_req.iomap_base = port;
serial_req.iomem_base = ioremap(port, 0x10);
serial_req.iomem_reg_shift = 0;
return register_serial(&serial_req);
}
#define SERIAL_BASE (S3C2410_CS2 + BAST_PA_SUPERIO)
static int __init serial_bast_init(void)
{
if (machine_is_bast()) {
serial_bast_register(SERIAL_BASE + 0x2f8, IRQ_PCSERIAL1);
serial_bast_register(SERIAL_BASE + 0x3f8, IRQ_PCSERIAL2);
}
return 0;
}
static void __exit serial_bast_exit(void)
{
/* todo -> remove both our ports */
}
module_init(serial_bast_init);
module_exit(serial_bast_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ben Dooks, ben@simtec.co.uk");
MODULE_DESCRIPTION("BAST Onboard Serial setup");
/*
* linux/drivers/char/s3c2410.c
*
* Driver for onboard UARTs on the Samsung S3C2410
*
* Based on drivers/char/serial.c and drivers/char/21285.c
*
* Ben Dooks, (c) 2003 Simtec Electronics
*
* Changelog:
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/arch/regs-serial.h>
#include <asm/mach-types.h>
#if 0
#include <asm/debug-ll.h>
#define dbg(x...) llprintk(x)
#else
#define dbg(x...)
#endif
#define SERIAL_S3C2410_NAME "ttySAC"
#define SERIAL_S3C2410_MAJOR 204
#define SERIAL_S3C2410_MINOR 4
/* we can support 3 uarts, but not always use them */
#define NR_PORTS (3)
static const char serial_s3c2410_name[] = "Samsung S3C2410 UART";
/* port irq numbers */
#define TX_IRQ(port) ((port)->irq + 1)
#define RX_IRQ(port) ((port)->irq)
#define tx_enabled(port) ((port)->unused[0])
#define rx_enabled(port) ((port)->unused[1])
/* flag to ignore all characters comming in */
#define RXSTAT_DUMMY_READ (0x10000000)
/* access functions */
#define portaddr(port, reg) ((void *)((port)->membase + (reg)))
#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))
#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))
#define wr_regb(port, reg, val) \
do { __raw_writeb(val, portaddr(port, reg)); } while(0)
#define wr_regl(port, reg, val) \
do { __raw_writel(val, portaddr(port, reg)); } while(0)
/* code */
static void
serial_s3c2410_stop_tx(struct uart_port *port, unsigned int tty_stop)
{
if (tx_enabled(port)) {
disable_irq(TX_IRQ(port));
tx_enabled(port) = 0;
}
}
static void
serial_s3c2410_start_tx(struct uart_port *port, unsigned int tty_start)
{
if (!tx_enabled(port)) {
enable_irq(TX_IRQ(port));
tx_enabled(port) = 1;
}
}
static void serial_s3c2410_stop_rx(struct uart_port *port)
{
if (rx_enabled(port)) {
dbg("serial_s3c2410_stop_rx: port=%p\n", port);
disable_irq(RX_IRQ(port));
rx_enabled(port) = 0;
}
}
static void serial_s3c2410_enable_ms(struct uart_port *port)
{
}
/* ? - where has parity gone?? */
#define S3C2410_UERSTAT_PARITY (0x1000)
static irqreturn_t
serial_s3c2410_rx_chars(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_port *port = dev_id;
struct tty_struct *tty = port->info->tty;
unsigned int ufcon, ch, rxs, ufstat;
int max_count = 256;
while (max_count-- > 0) {
ufcon = rd_regl(port, S3C2410_UFCON);
ufstat = rd_regl(port, S3C2410_UFSTAT);
if (S3C2410_UFCON_RXC(ufstat) == 0)
break;
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
tty->flip.work.func((void *)tty);
if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
printk(KERN_WARNING "TTY_DONT_FLIP set\n");
goto out;
}
}
ch = rd_regb(port, S3C2410_URXH);
*tty->flip.char_buf_ptr = ch;
*tty->flip.flag_buf_ptr = TTY_NORMAL;
port->icount.rx++;
rxs = rd_regb(port, S3C2410_UERSTAT) | RXSTAT_DUMMY_READ;
if (rxs & S3C2410_UERSTAT_ANY) {
if (rxs & S3C2410_UERSTAT_FRAME)
port->icount.frame++;
if (rxs & S3C2410_UERSTAT_OVERRUN)
port->icount.overrun++;
rxs &= port->read_status_mask;
if (rxs & S3C2410_UERSTAT_PARITY)
*tty->flip.flag_buf_ptr = TTY_PARITY;
else if (rxs & ( S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_OVERRUN))
*tty->flip.flag_buf_ptr = TTY_FRAME;
}
if ((rxs & port->ignore_status_mask) == 0) {
tty->flip.flag_buf_ptr++;
tty->flip.char_buf_ptr++;
tty->flip.count++;
}
if ((rxs & S3C2410_UERSTAT_OVERRUN) &&
tty->flip.count < TTY_FLIPBUF_SIZE) {
/*
* Overrun is special, since it's reported
* immediately, and doesn't affect the current
* character.
*/
*tty->flip.char_buf_ptr++ = 0;
*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
tty->flip.count++;
}
}
tty_flip_buffer_push(tty);
out:
return IRQ_HANDLED;
}
static irqreturn_t
serial_s3c2410_tx_chars(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_port *port = (struct uart_port *)dev_id;
struct circ_buf *xmit = &port->info->xmit;
int count = 256;
if (port->x_char) {
wr_regb(port, S3C2410_UTXH, port->x_char);
port->icount.tx++;
port->x_char = 0;
goto out;
}
/* if there isnt anything more to transmit, or the uart is now
* stopped, disable the uart and exit
*/
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
serial_s3c2410_stop_tx(port, 0);
goto out;
}
/* try and drain the buffer... */
while (!uart_circ_empty(xmit) && count-- > 0) {
if (rd_regl(port, S3C2410_UFSTAT) & S3C2410_UFSTAT_TXFULL)
break;
wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (uart_circ_empty(xmit))
serial_s3c2410_stop_tx(port, 0);
out:
return IRQ_HANDLED;
}
static unsigned int
serial_s3c2410_tx_empty(struct uart_port *port)
{
unsigned int ufcon = rd_regl(port, S3C2410_UFCON);
return (S3C2410_UFCON_TXC(ufcon) != 0) ? 0 : TIOCSER_TEMT;
}
/* no modem control lines */
static unsigned int
serial_s3c2410_get_mctrl(struct uart_port *port)
{
unsigned int umstat = rd_regb(port,S3C2410_UMSTAT);
if (umstat & S3C2410_UMSTAT_CTS)
return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
else
return TIOCM_CAR | TIOCM_DSR;
}
static void
serial_s3c2410_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
/* todo - possibly remove AFC and do manual CTS */
}
static void serial_s3c2410_break_ctl(struct uart_port *port, int break_state)
{
unsigned long flags;
unsigned int ucon;
spin_lock_irqsave(&port->lock, flags);
ucon = rd_regl(port, S3C2410_UCON);
if (break_state)
ucon |= S3C2410_UCON_SBREAK;
else
ucon &= ~S3C2410_UCON_SBREAK;
wr_regl(port, S3C2410_UCON, ucon);
spin_unlock_irqrestore(&port->lock, flags);
}
static int serial_s3c2410_startup(struct uart_port *port)
{
int ret;
tx_enabled(port) = 1;
rx_enabled(port) = 1;
dbg("serial_s3c2410_startup: port=%p (%p)\n",
port, port->mapbase);
ret = request_irq(RX_IRQ(port), serial_s3c2410_rx_chars, 0,
serial_s3c2410_name, port);
if (ret != 0)
return ret;
ret = request_irq(TX_IRQ(port), serial_s3c2410_tx_chars, 0,
serial_s3c2410_name, port);
if (ret) {
free_irq(RX_IRQ(port), port);
return ret;
}
/* the port reset code should have done the correct
* register setup for the port controls */
return ret;
}
static void serial_s3c2410_shutdown(struct uart_port *port)
{
free_irq(TX_IRQ(port), port);
free_irq(RX_IRQ(port), port);
}
static void
serial_s3c2410_set_termios(struct uart_port *port, struct termios *termios,
struct termios *old)
{
unsigned long flags;
unsigned int baud, quot;
unsigned int ulcon;
/*
* We don't support modem control lines.
*/
termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR);
termios->c_cflag |= CLOCAL;
/*
* We don't support BREAK character recognition.
*/
termios->c_iflag &= ~(IGNBRK | BRKINT);
/*
* Ask the core to calculate the divisor for us.
*/
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
quot = uart_get_divisor(port, baud);
switch (termios->c_cflag & CSIZE) {
case CS5:
dbg("config: 5bits/char\n");
ulcon = S3C2410_LCON_CS5;
break;
case CS6:
dbg("config: 6bits/char\n");
ulcon = S3C2410_LCON_CS6;
break;
case CS7:
dbg("config: 7bits/char\n");
ulcon = S3C2410_LCON_CS7;
break;
case CS8:
default:
dbg("config: 8bits/char\n");
ulcon = S3C2410_LCON_CS8;
break;
}
if (termios->c_cflag & CSTOPB)
ulcon |= S3C2410_LCON_STOPB;
if (termios->c_cflag & PARENB) {
if (!(termios->c_cflag & PARODD))
ulcon |= S3C2410_LCON_PODD;
else
ulcon |= S3C2410_LCON_PEVEN;
} else {
ulcon |= S3C2410_LCON_PNONE;
}
/*
if (port->fifosize)
enable_fifo()
*/
spin_lock_irqsave(&port->lock, flags);
dbg("setting ulcon to %08x\n", ulcon);
//dbg("<flushing output from serial>\n");
/* set the ulcon register */
wr_regl(port, S3C2410_ULCON, ulcon);
dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
rd_regl(port, S3C2410_ULCON),
rd_regl(port, S3C2410_UCON),
rd_regl(port, S3C2410_UFCON));
/*
* Update the per-port timeout.
*/
uart_update_timeout(port, termios->c_cflag, baud);
/*
* Which character status flags are we interested in?
*/
port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
if (termios->c_iflag & INPCK)
port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;
/*
* Which character status flags should we ignore?
*/
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;
/*
* Ignore all characters if CREAD is not set.
*/
if ((termios->c_cflag & CREAD) == 0)
port->ignore_status_mask |= RXSTAT_DUMMY_READ;
spin_unlock_irqrestore(&port->lock, flags);
}
static const char *serial_s3c2410_type(struct uart_port *port)
{
return port->type == PORT_S3C2410 ? "S3C2410" : NULL;
}
#define MAP_SIZE (0x100)
static void
serial_s3c2410_release_port(struct uart_port *port)
{
release_mem_region(port->mapbase, MAP_SIZE);
}
static int
serial_s3c2410_request_port(struct uart_port *port)
{
return request_mem_region(port->mapbase, MAP_SIZE, serial_s3c2410_name)
!= NULL ? 0 : -EBUSY;
}
static void
serial_s3c2410_config_port(struct uart_port *port, int flags)
{
if (flags & UART_CONFIG_TYPE &&
serial_s3c2410_request_port(port) == 0)
port->type = PORT_S3C2410;
}
/*
* verify the new serial_struct (for TIOCSSERIAL).
*/
static int
serial_s3c2410_verify_port(struct uart_port *port, struct serial_struct *ser)
{
int ret = 0;
if (ser->type != PORT_UNKNOWN && ser->type != PORT_S3C2410)
ret = -EINVAL;
return ret;
}
static struct uart_ops serial_s3c2410_ops = {
.tx_empty = serial_s3c2410_tx_empty,
.get_mctrl = serial_s3c2410_get_mctrl,
.set_mctrl = serial_s3c2410_set_mctrl,
.stop_tx = serial_s3c2410_stop_tx,
.start_tx = serial_s3c2410_start_tx,
.stop_rx = serial_s3c2410_stop_rx,
.enable_ms = serial_s3c2410_enable_ms,
.break_ctl = serial_s3c2410_break_ctl,
.startup = serial_s3c2410_startup,
.shutdown = serial_s3c2410_shutdown,
.set_termios = serial_s3c2410_set_termios,
.type = serial_s3c2410_type,
.release_port = serial_s3c2410_release_port,
.request_port = serial_s3c2410_request_port,
.config_port = serial_s3c2410_config_port,
.verify_port = serial_s3c2410_verify_port,
};
static struct uart_port serial_s3c2410_ports[NR_PORTS] = {
{
.membase = 0,
.mapbase = 0,
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX0,
.uartclk = 0,
.fifosize = 16,
.ops = &serial_s3c2410_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 0,
},
{
.membase = 0,
.mapbase = 0,
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX1,
.uartclk = 0,
.fifosize = 16,
.ops = &serial_s3c2410_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 1,
}
#if NR_PORTS > 2
,
{
.membase = 0,
.mapbase = 0,
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX2,
.uartclk = 0,
.fifosize = 16,
.ops = &serial_s3c2410_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 2,
}
#endif
};
static int
serial_s3c2410_resetport(struct uart_port *port,
struct s3c2410_uartcfg *cfg)
{
/* ensure registers are setup */
dbg("serial_s3c2410_resetport: port=%p (%08x), cfg=%p\n",
port, port->mapbase, cfg);
wr_regl(port, S3C2410_UCON, cfg->ucon);
wr_regl(port, S3C2410_ULCON, cfg->ulcon);
/* reset both fifos */
wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
wr_regl(port, S3C2410_UFCON, cfg->ufcon);
return 0;
}
/* serial_s3c2410_init_ports
*
* initialise the serial ports from the machine provided initialisation
* data.
*/
static int serial_s3c2410_init_ports(void)
{
struct uart_port *ptr = serial_s3c2410_ports;
struct s3c2410_uartcfg *cfg = s3c2410_uartcfgs;
static int inited = 0;
int i;
if (inited)
return 0;
inited = 1;
dbg("serial_s3c2410_init_ports: initialising ports...\n");
for (i = 0; i < NR_PORTS; i++, ptr++, cfg++) {
if (cfg->hwport > 3)
continue;
dbg("serial_s3c2410_init_ports: port %d (hw %d)...\n",
i, cfg->hwport);
if (cfg->clock != NULL)
ptr->uartclk = *cfg->clock;
switch (cfg->hwport) {
case 0:
ptr->mapbase = S3C2410_PA_UART0;
ptr->membase = (char *)S3C2410_VA_UART0;
ptr->irq = IRQ_S3CUART_RX0;
break;
case 1:
ptr->mapbase = S3C2410_PA_UART1;
ptr->membase = (char *)S3C2410_VA_UART1;
ptr->irq = IRQ_S3CUART_RX1;
break;
case 2:
ptr->mapbase = S3C2410_PA_UART2;
ptr->membase = (char *)S3C2410_VA_UART2;
ptr->irq = IRQ_S3CUART_RX2;
break;
}
if (ptr->mapbase == 0)
continue;
/* reset the fifos (and setup the uart */
serial_s3c2410_resetport(ptr, cfg);
}
return 0;
}
#ifdef CONFIG_SERIAL_S3C2410_CONSOLE
static struct uart_port *cons_uart;
static int
serial_s3c2410_console_txrdy(struct uart_port *port, unsigned int ufcon)
{
unsigned long ufstat, utrstat;
if (ufcon & S3C2410_UFCON_FIFOMODE) {
/* fifo mode - check ammount of data in fifo registers... */
ufstat = rd_regl(port, S3C2410_UFSTAT);
return S3C2410_UFCON_TXC(ufstat) < 12;
}
/* in non-fifo mode, we go and use the tx buffer empty */
utrstat = rd_regl(port, S3C2410_UTRSTAT);
return (utrstat & S3C2410_UTRSTAT_TXFE) ? 1 : 0;
}
static void
serial_s3c2410_console_write(struct console *co, const char *s,
unsigned int count)
{
int i;
unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
for (i = 0; i < count; i++) {
while (!serial_s3c2410_console_txrdy(cons_uart, ufcon))
barrier();
wr_regb(cons_uart, S3C2410_UTXH, s[i]);
if (s[i] == '\n') {
while (!serial_s3c2410_console_txrdy(cons_uart, ufcon))
barrier();
wr_regb(cons_uart, S3C2410_UTXH, '\r');
}
}
}
static void __init
serial_s3c2410_get_options(struct uart_port *port, int *baud,
int *parity, int *bits)
{
unsigned int ulcon, ucon, ubrdiv;
ulcon = rd_regl(port, S3C2410_ULCON);
ucon = rd_regl(port, S3C2410_UCON);
ubrdiv = rd_regl(port, S3C2410_UBRDIV);
dbg("serial_s3c2410_get_options: port=%p\n"
"registers: ulcon=%08x, ucon=%08x, ubdriv=%08x\n",
port, ulcon, ucon, ubrdiv);
if ((ucon & 0xf) != 0) {
/* consider the serial port configured if the tx/rx mode set */
switch (ulcon & S3C2410_LCON_CSMASK) {
case S3C2410_LCON_CS5:
*bits = 5;
break;
case S3C2410_LCON_CS6:
*bits = 6;
break;
case S3C2410_LCON_CS7:
*bits = 7;
break;
default:
case S3C2410_LCON_CS8:
*bits = 8;
break;
}
switch (ulcon & S3C2410_LCON_PMASK) {
case S3C2410_LCON_PEVEN:
*parity = 'e';
break;
case S3C2410_LCON_PODD:
*parity = 'o';
break;
default:
case S3C2410_LCON_PNONE:
/* nothing */
}
/* now calculate the baud rate */
*baud = port->uartclk / ( 16 * (ubrdiv + 1));
dbg("calculated baud %d\n", *baud);
}
}
static int __init
serial_s3c2410_console_setup(struct console *co, char *options)
{
struct uart_port *port;
int baud = 9600;
int bits = 8;
int parity = 'n';
int flow = 'n';
/* is this a valid port */
if (co->index == -1 || co->index >= NR_PORTS)
co->index = 0;
port = &serial_s3c2410_ports[co->index];
/* is the port configured? */
if (port->mapbase == 0x0) {
co->index = 0;
port = &serial_s3c2410_ports[co->index];
}
cons_uart = port;
dbg("serial_s3c2410_console_setup: port=%p (%d)\n", port, co->index);
/*
* Check whether an invalid uart number has been specified, and
* if so, search for the first available port that does have
* console support.
*/
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
else
serial_s3c2410_get_options(port, &baud, &parity, &bits);
return uart_set_options(port, co, baud, parity, bits, flow);
}
static struct uart_driver s3c2410_uart_drv;
static struct console serial_s3c2410_console =
{
.name = SERIAL_S3C2410_NAME,
.write = serial_s3c2410_console_write,
.device = uart_console_device,
.setup = serial_s3c2410_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &s3c2410_uart_drv,
};
static int __init s3c2410_console_init(void)
{
dbg("s3c2410_console_init:\n");
serial_s3c2410_init_ports();
register_console(&serial_s3c2410_console);
return 0;
}
console_initcall(s3c2410_console_init);
#define SERIAL_S3C2410_CONSOLE &serial_s3c2410_console
#else
#define SERIAL_S3C2410_CONSOLE NULL
#endif
static struct uart_driver s3c2410_uart_drv = {
.owner = THIS_MODULE,
.driver_name = SERIAL_S3C2410_NAME,
.dev_name = SERIAL_S3C2410_NAME,
.major = SERIAL_S3C2410_MAJOR,
.minor = SERIAL_S3C2410_MINOR,
.nr = 3,
.cons = SERIAL_S3C2410_CONSOLE,
};
/* device driver */
static int s3c2410_serial_probe(struct device *_dev);
static int s3c2410_serial_remove(struct device *_dev);
static struct device_driver s3c2410_serial_drv = {
.name = "s3c2410-uart",
.bus = &platform_bus_type,
.probe = s3c2410_serial_probe,
.remove = s3c2410_serial_remove,
.suspend = NULL,
.resume = NULL,
};
#define s3c2410_dev_to_port(__dev) (struct uart_port *)dev_get_drvdata(__dev)
static int s3c2410_serial_probe(struct device *_dev)
{
struct platform_device *dev = to_platform_device(_dev);
struct resource *res = dev->resource;
int i;
dbg("s3c2410_serial_probe: dev=%p, _dev=%p, res=%p\n", _dev, dev, res);
for (i = 0; i < dev->num_resources; i++, res++)
if (res->flags & IORESOURCE_MEM)
break;
if (i < dev->num_resources) {
struct uart_port *ptr = serial_s3c2410_ports;
for (i = 0; i < NR_PORTS; i++, ptr++) {
dbg("s3c2410_serial_probe: ptr=%p (%08x, %08x)\n",
ptr, ptr->mapbase, ptr->membase);
if (ptr->mapbase != res->start)
continue;
dbg("s3c2410_serial_probe: got device %p: port=%p\n",
_dev, ptr);
uart_add_one_port(&s3c2410_uart_drv, ptr);
dev_set_drvdata(_dev, ptr);
break;
}
}
return 0;
}
static int s3c2410_serial_remove(struct device *_dev)
{
struct uart_port *port = s3c2410_dev_to_port(_dev);
if (port)
uart_remove_one_port(&s3c2410_uart_drv, port);
return 0;
}
static int __init serial_s3c2410_init(void)
{
int ret;
printk(KERN_INFO "S3C2410X Serial, (c) 2003 Simtec Electronics\n");
ret = uart_register_driver(&s3c2410_uart_drv);
if (ret != 0)
return ret;
ret = driver_register(&s3c2410_serial_drv);
if (ret) {
uart_unregister_driver(&s3c2410_uart_drv);
}
return ret;
}
static void __exit serial_s3c2410_exit(void)
{
driver_unregister(&s3c2410_serial_drv);
uart_unregister_driver(&s3c2410_uart_drv);
}
module_init(serial_s3c2410_init);
module_exit(serial_s3c2410_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
MODULE_DESCRIPTION("Samsung S3C2410X (S3C2410) Serial driver");
......@@ -894,6 +894,7 @@ static int sa1100_serial_probe(struct device *_dev)
if (sa1100_ports[i].port.mapbase != res->start)
continue;
sa1100_ports[i].port.dev = _dev;
uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port);
dev_set_drvdata(_dev, &sa1100_ports[i]);
break;
......
......@@ -2227,7 +2227,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
* Register the port whether it's detected or not. This allows
* setserial to be used to alter this ports parameters.
*/
tty_register_device(drv->tty_driver, port->line, NULL);
tty_register_device(drv->tty_driver, port->line, port->dev);
out:
up(&port_sem);
......
......@@ -54,6 +54,8 @@
#include <pcmcia/ds.h>
#include <pcmcia/cisreg.h>
#include "8250.h"
#ifdef PCMCIA_DEBUG
static int pc_debug = PCMCIA_DEBUG;
MODULE_PARM(pc_debug, "i");
......@@ -158,6 +160,38 @@ static void serial_remove(dev_link_t *link)
}
}
static void serial_suspend(dev_link_t *link)
{
link->state |= DEV_SUSPEND;
if (link->state & DEV_CONFIG) {
struct serial_info *info = link->priv;
int i;
for (i = 0; i < info->ndev; i++)
serial8250_suspend_port(info->line[i]);
if (!info->slave)
pcmcia_release_configuration(link->handle);
}
}
static void serial_resume(dev_link_t *link)
{
link->state &= ~DEV_SUSPEND;
if (DEV_OK(link)) {
struct serial_info *info = link->priv;
int i;
if (!info->slave)
pcmcia_request_configuration(link->handle, &link->conf);
for (i = 0; i < info->ndev; i++)
serial8250_resume_port(info->line[i]);
}
}
/*======================================================================
serial_attach() creates an "instance" of the driver, allocating
......@@ -674,16 +708,18 @@ serial_event(event_t event, int priority, event_callback_args_t * args)
break;
case CS_EVENT_PM_SUSPEND:
link->state |= DEV_SUSPEND;
/* Fall through... */
serial_suspend(link);
break;
case CS_EVENT_RESET_PHYSICAL:
if ((link->state & DEV_CONFIG) && !info->slave)
pcmcia_release_configuration(link->handle);
break;
case CS_EVENT_PM_RESUME:
link->state &= ~DEV_SUSPEND;
/* Fall through... */
serial_resume(link);
break;
case CS_EVENT_CARD_RESET:
if (DEV_OK(link) && !info->slave)
pcmcia_request_configuration(link->handle, &link->conf);
......
......@@ -16,8 +16,6 @@
* 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
*
* $Id: serial_core.h,v 1.49 2002/07/20 18:06:32 rmk Exp $
*/
/*
......@@ -83,6 +81,9 @@
#define PORT_SCIF 53
#define PORT_IRDA 54
/* Samsung S3C2410 SoC and derivatives thereof */
#define PORT_S3C2410 55
#ifdef __KERNEL__
#include <linux/config.h>
......@@ -94,6 +95,7 @@
struct uart_port;
struct uart_info;
struct serial_struct;
struct device;
/*
* This structure describes all the operations that can be
......@@ -182,7 +184,6 @@ struct uart_port {
unsigned int flags;
#define UPF_HUP_NOTIFY (1 << 0)
#define UPF_FOURPORT (1 << 1)
#define UPF_SAK (1 << 2)
#define UPF_SPD_MASK (0x1030)
......@@ -215,6 +216,7 @@ struct uart_port {
unsigned int custom_divisor;
unsigned int line; /* port index */
unsigned long mapbase; /* for ioremap */
struct device *dev; /* parent device */
unsigned char hub6; /* this should be in the 8250 driver */
unsigned char unused[3];
};
......
......@@ -121,6 +121,7 @@
/*
* These are the definitions for the Modem Control Register
*/
#define UART_MCR_AFE 0x20 /* Enable auto-RTS/CTS (TI16C750) */
#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
#define UART_MCR_OUT2 0x08 /* Out2 complement */
#define UART_MCR_OUT1 0x04 /* Out1 complement */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment