bk://bk.arm.linux.org.uk/linux-2.6-serial rmk@flint.arm.linux.org.uk|ChangeSet|20040410110044|30229 rmk # This is a BitKeeper generated diff -Nru style patch. # # ChangeSet # 2004/04/10 12:00:44+01:00 rmk@flint.arm.linux.org.uk # [SERIAL] Add extra suspend/resume functionality to serial_cs. # # This calls into the 8250 driver so that the serial port settings can # be saved and restored over a suspend/resume cycle. Previous kernels # have assumed that the port will be re-opened after such an event, # which may not be the case. # # drivers/serial/serial_cs.c # 2004/04/10 11:58:38+01:00 rmk@flint.arm.linux.org.uk +40 -4 # Add extra suspend/resume functionality to serial_cs. # # ChangeSet # 2004/04/10 10:28:36+01:00 bjorn.helgaas@com.rmk.(none) # [SERIAL] HCDP IRQ fixup # # Some pre-production firmware has incorrect GSI values in the # HCDP, which tells us where the serial console port is, so we # have to do the auto-IRQ thing after all. # # drivers/serial/8250_hcdp.c # 2004/04/10 10:26:14+01:00 bjorn.helgaas@com.rmk.(none) +2 -0 # [PATCH] HCDP IRQ fixup # # ChangeSet # 2004/04/09 23:01:39+01:00 rmk@flint.arm.linux.org.uk # [SERIAL] Remove UPF_HUP_NOTIFY; this is no longer used. # # include/linux/serial_core.h # 2004/04/09 22:59:15+01:00 rmk@flint.arm.linux.org.uk +0 -3 # Remove UPF_HUP_NOTIFY; this is no longer used. # # ChangeSet # 2004/04/09 22:57:30+01:00 rmk@flint.arm.linux.org.uk # [SERIAL] Pass sa11x0 struct device through to tty_register_device. # # drivers/serial/sa1100.c # 2004/04/09 22:55:47+01:00 rmk@flint.arm.linux.org.uk +1 -0 # Set uart_port dev member to point at the struct device for SA11x0 # ports. # # ChangeSet # 2004/04/09 22:52:50+01:00 rmk@flint.arm.linux.org.uk # [SERIAL] Pass device pointer through to tty_register_device. # # This allows drivers to pass their struct device through to # tty_register_device, which in turn allows sysfs to show which # device and driver owns the UART. # # include/linux/serial_core.h # 2004/04/09 22:51:02+01:00 rmk@flint.arm.linux.org.uk +2 -0 # Add struct device member to uart_port. # # drivers/serial/serial_core.c # 2004/04/09 22:51:01+01:00 rmk@flint.arm.linux.org.uk +1 -1 # Pass struct device through to tty_register_device. # # ChangeSet # 2004/04/09 22:33:45+01:00 rmk@flint.arm.linux.org.uk # [SERIAL] Don't try to free resources we didn't request. # # drivers/serial/8250.c # 2004/04/09 22:32:03+01:00 rmk@flint.arm.linux.org.uk +2 -0 # If UPF_RESOURCES wasn't set, we didn't request any resources. # Therefore, we shouldn't try to free them. # # ChangeSet # 2004/04/09 22:29:22+01:00 rmk@flint.arm.linux.org.uk # [SERIAL] Correct minor debugging format string error. # # drivers/serial/8250.c # 2004/04/09 22:27:34+01:00 rmk@flint.arm.linux.org.uk +1 -1 # Correct debugging format. # # ChangeSet # 2004/04/09 22:23:30+01:00 rmk@flint.arm.linux.org.uk # [SERIAL] Remove some dead declarations. # # drivers/serial/8250.h # 2004/04/09 22:21:45+01:00 rmk@flint.arm.linux.org.uk +0 -9 # Remove obsolete declarations; these have never been used nor # implemented. # # ChangeSet # 2004/04/09 22:16:29+01:00 rmk@flint.arm.linux.org.uk # [SERIAL] Unuse old SERIAL_IO_xxx macros. # # 8250.c should be using the replacement UPIO_xxx macros instead. # # drivers/serial/8250.c # 2004/04/09 22:14:16+01:00 rmk@flint.arm.linux.org.uk +13 -13 # Unuse SERIAL_IO_xxx macros; should be using UPIO_xxx macros instead. # # ChangeSet # 2004/04/08 19:43:18+01:00 ben-linux@org.rmk.(none) # [ARM PATCH] 1807/1: S3C2410 - onboard serial # # Patch from Ben Dooks # # Serial driver for S3C2410 on board UARTs. Re-post of 1796/1 # # Includes BAST driver to register on-board 16550s. # # include/linux/serial_core.h # 2004/04/05 17:12:25+01:00 ben-linux@org.rmk.(none) +3 -0 # [PATCH] 1807/1: S3C2410 - onboard serial # # drivers/serial/s3c2410.c # 2004/04/08 15:27:21+01:00 ben-linux@org.rmk.(none) +860 -0 # [PATCH] 1807/1: S3C2410 - onboard serial # # drivers/serial/Makefile # 2004/04/05 17:20:46+01:00 ben-linux@org.rmk.(none) +2 -0 # [PATCH] 1807/1: S3C2410 - onboard serial # # drivers/serial/s3c2410.c # 2004/04/08 15:27:21+01:00 ben-linux@org.rmk.(none) +0 -0 # BitKeeper file /usr/src/bk/linux-2.6-serial/drivers/serial/s3c2410.c # # drivers/serial/Kconfig # 2004/04/05 17:18:57+01:00 ben-linux@org.rmk.(none) +31 -0 # [PATCH] 1807/1: S3C2410 - onboard serial # # drivers/serial/bast_sio.c # 2004/04/05 22:19:09+01:00 ben-linux@org.rmk.(none) +63 -0 # [PATCH] 1807/1: S3C2410 - onboard serial # # drivers/serial/bast_sio.c # 2004/04/05 22:19:09+01:00 ben-linux@org.rmk.(none) +0 -0 # BitKeeper file /usr/src/bk/linux-2.6-serial/drivers/serial/bast_sio.c # diff -Nru a/drivers/serial/8250.c b/drivers/serial/8250.c --- a/drivers/serial/8250.c Sat Apr 10 15:38:54 2004 +++ b/drivers/serial/8250.c Sat Apr 10 15:38:54 2004 @@ -177,11 +177,11 @@ 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 +195,12 @@ 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 +574,7 @@ 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); /* @@ -1613,7 +1613,7 @@ 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 +1621,8 @@ } 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 +1639,7 @@ 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 +1649,8 @@ } 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 +1667,8 @@ 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 +1677,7 @@ size <<= up->port.regshift; switch (up->port.iotype) { - case SERIAL_IO_MEM: + case UPIO_MEM: if (up->port.mapbase) { /* * Unmap the area. @@ -1691,8 +1693,8 @@ } break; - case SERIAL_IO_HUB6: - case SERIAL_IO_PORT: + case UPIO_HUB6: + case UPIO_PORT: start = up->port.iobase; if (size) diff -Nru a/drivers/serial/8250.h b/drivers/serial/8250.h --- a/drivers/serial/8250.h Sat Apr 10 15:38:54 2004 +++ b/drivers/serial/8250.h Sat Apr 10 15:38:54 2004 @@ -17,15 +17,6 @@ #include -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); diff -Nru a/drivers/serial/8250_hcdp.c b/drivers/serial/8250_hcdp.c --- a/drivers/serial/8250_hcdp.c Sat Apr 10 15:38:54 2004 +++ b/drivers/serial/8250_hcdp.c Sat Apr 10 15:38:54 2004 @@ -186,6 +186,8 @@ 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, diff -Nru a/drivers/serial/Kconfig b/drivers/serial/Kconfig --- a/drivers/serial/Kconfig Sat Apr 10 15:38:54 2004 +++ b/drivers/serial/Kconfig Sat Apr 10 15:38:54 2004 @@ -224,6 +224,37 @@ 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 diff -Nru a/drivers/serial/Makefile b/drivers/serial/Makefile --- a/drivers/serial/Makefile Sat Apr 10 15:38:54 2004 +++ b/drivers/serial/Makefile Sat Apr 10 15:38:54 2004 @@ -20,6 +20,7 @@ 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_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 diff -Nru a/drivers/serial/bast_sio.c b/drivers/serial/bast_sio.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/serial/bast_sio.c Sat Apr 10 15:38:54 2004 @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +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"); + + diff -Nru a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/serial/s3c2410.c Sat Apr 10 15:38:54 2004 @@ -0,0 +1,860 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#if 0 +#include +#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("\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 "); +MODULE_DESCRIPTION("Samsung S3C2410X (S3C2410) Serial driver"); diff -Nru a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c --- a/drivers/serial/sa1100.c Sat Apr 10 15:38:54 2004 +++ b/drivers/serial/sa1100.c Sat Apr 10 15:38:54 2004 @@ -894,6 +894,7 @@ 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; diff -Nru a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c --- a/drivers/serial/serial_core.c Sat Apr 10 15:38:54 2004 +++ b/drivers/serial/serial_core.c Sat Apr 10 15:38:54 2004 @@ -2227,7 +2227,7 @@ * 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); diff -Nru a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c --- a/drivers/serial/serial_cs.c Sat Apr 10 15:38:54 2004 +++ b/drivers/serial/serial_cs.c Sat Apr 10 15:38:54 2004 @@ -54,6 +54,8 @@ #include #include +#include "8250.h" + #ifdef PCMCIA_DEBUG static int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); @@ -158,6 +160,38 @@ } } +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 @@ 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); diff -Nru a/include/linux/serial_core.h b/include/linux/serial_core.h --- a/include/linux/serial_core.h Sat Apr 10 15:38:54 2004 +++ b/include/linux/serial_core.h Sat Apr 10 15:38:54 2004 @@ -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 @@ -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 @@ 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 @@ 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]; };