diff options
author | Paul Mundt <lethal@linux-sh.org> | 2011-01-13 15:06:28 +0900 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2011-01-13 15:06:28 +0900 |
commit | f43dc23d5ea91fca257be02138a255f02d98e806 (patch) | |
tree | b29722f6e965316e90ac97abf79923ced250dc21 /drivers/serial | |
parent | f8e53553f452dcbf67cb89c8cba63a1cd6eb4cc0 (diff) | |
parent | 4162cf64973df51fc885825bc9ca4d055891c49f (diff) | |
download | op-kernel-dev-f43dc23d5ea91fca257be02138a255f02d98e806.zip op-kernel-dev-f43dc23d5ea91fca257be02138a255f02d98e806.tar.gz |
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 into common/serial-rework
Conflicts:
arch/sh/kernel/cpu/sh2/setup-sh7619.c
arch/sh/kernel/cpu/sh2a/setup-mxg.c
arch/sh/kernel/cpu/sh2a/setup-sh7201.c
arch/sh/kernel/cpu/sh2a/setup-sh7203.c
arch/sh/kernel/cpu/sh2a/setup-sh7206.c
arch/sh/kernel/cpu/sh3/setup-sh7705.c
arch/sh/kernel/cpu/sh3/setup-sh770x.c
arch/sh/kernel/cpu/sh3/setup-sh7710.c
arch/sh/kernel/cpu/sh3/setup-sh7720.c
arch/sh/kernel/cpu/sh4/setup-sh4-202.c
arch/sh/kernel/cpu/sh4/setup-sh7750.c
arch/sh/kernel/cpu/sh4/setup-sh7760.c
arch/sh/kernel/cpu/sh4a/setup-sh7343.c
arch/sh/kernel/cpu/sh4a/setup-sh7366.c
arch/sh/kernel/cpu/sh4a/setup-sh7722.c
arch/sh/kernel/cpu/sh4a/setup-sh7723.c
arch/sh/kernel/cpu/sh4a/setup-sh7724.c
arch/sh/kernel/cpu/sh4a/setup-sh7763.c
arch/sh/kernel/cpu/sh4a/setup-sh7770.c
arch/sh/kernel/cpu/sh4a/setup-sh7780.c
arch/sh/kernel/cpu/sh4a/setup-sh7785.c
arch/sh/kernel/cpu/sh4a/setup-sh7786.c
arch/sh/kernel/cpu/sh4a/setup-shx3.c
arch/sh/kernel/cpu/sh5/setup-sh5.c
drivers/serial/sh-sci.c
drivers/serial/sh-sci.h
include/linux/serial_sci.h
Diffstat (limited to 'drivers/serial')
98 files changed, 18737 insertions, 3194 deletions
diff --git a/drivers/serial/21285.c b/drivers/serial/21285.c index cb6d85d..d89aa38 100644 --- a/drivers/serial/21285.c +++ b/drivers/serial/21285.c @@ -58,7 +58,7 @@ static const char serial21285_name[] = "Footbridge UART"; static void serial21285_stop_tx(struct uart_port *port) { if (tx_enabled(port)) { - disable_irq(IRQ_CONTX); + disable_irq_nosync(IRQ_CONTX); tx_enabled(port) = 0; } } @@ -74,7 +74,7 @@ static void serial21285_start_tx(struct uart_port *port) static void serial21285_stop_rx(struct uart_port *port) { if (rx_enabled(port)) { - disable_irq(IRQ_CONRX); + disable_irq_nosync(IRQ_CONRX); rx_enabled(port) = 0; } } @@ -86,7 +86,7 @@ static void serial21285_enable_ms(struct uart_port *port) static irqreturn_t serial21285_rx_chars(int irq, void *dev_id) { struct uart_port *port = dev_id; - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; unsigned int status, ch, flag, rxs, max_count = 256; status = *CSR_UARTFLG; @@ -124,7 +124,7 @@ static irqreturn_t serial21285_rx_chars(int irq, void *dev_id) static irqreturn_t serial21285_tx_chars(int irq, void *dev_id) { struct uart_port *port = dev_id; - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; int count = 256; if (port->x_char) { @@ -216,7 +216,7 @@ serial21285_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { unsigned long flags; - unsigned int baud, quot, h_lcr; + unsigned int baud, quot, h_lcr, b; /* * We don't support modem control lines. @@ -234,12 +234,8 @@ serial21285_set_termios(struct uart_port *port, struct ktermios *termios, */ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); quot = uart_get_divisor(port, baud); - - if (port->info && port->info->port.tty) { - struct tty_struct *tty = port->info->port.tty; - unsigned int b = port->uartclk / (16 * quot); - tty_encode_baud_rate(tty, b, b); - } + b = port->uartclk / (16 * quot); + tty_termios_encode_baud_rate(termios, b, b); switch (termios->c_cflag & CSIZE) { case CS5: diff --git a/drivers/serial/68328serial.c b/drivers/serial/68328serial.c index d935b2d..be0ebce 100644 --- a/drivers/serial/68328serial.c +++ b/drivers/serial/68328serial.c @@ -35,6 +35,7 @@ #include <linux/pm.h> #include <linux/bitops.h> #include <linux/delay.h> +#include <linux/gfp.h> #include <asm/io.h> #include <asm/irq.h> @@ -77,10 +78,6 @@ struct m68k_serial *m68k_consinfo = 0; #define M68K_CLOCK (16667000) /* FIXME: 16MHz is likely wrong */ -#ifdef CONFIG_CONSOLE -extern wait_queue_head_t keypress_wait; -#endif - struct tty_driver *serial_driver; /* number of characters left in xmit buffer before we ask for more */ @@ -101,19 +98,13 @@ static void change_speed(struct m68k_serial *info); * Setup for console. Argument comes from the boot command line. */ -#if defined(CONFIG_M68EZ328ADS) || defined(CONFIG_ALMA_ANS) || defined(CONFIG_DRAGONIXVZ) -#define CONSOLE_BAUD_RATE 115200 -#define DEFAULT_CBAUD B115200 -#else - /* (es) */ - /* note: this is messy, but it works, again, perhaps defined somewhere else?*/ - #ifdef CONFIG_M68VZ328 - #define CONSOLE_BAUD_RATE 19200 - #define DEFAULT_CBAUD B19200 - #endif - /* (/es) */ +/* note: this is messy, but it works, again, perhaps defined somewhere else?*/ +#ifdef CONFIG_M68VZ328 +#define CONSOLE_BAUD_RATE 19200 +#define DEFAULT_CBAUD B19200 #endif + #ifndef CONSOLE_BAUD_RATE #define CONSOLE_BAUD_RATE 9600 #define DEFAULT_CBAUD B9600 @@ -153,8 +144,6 @@ static int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 0 }; -#define BAUD_TABLE_SIZE (sizeof(baud_table)/sizeof(baud_table[0])) - /* Sets or clears DTR/RTS on the requested line */ static inline void m68k_rtsdtr(struct m68k_serial *ss, int set) { @@ -301,10 +290,6 @@ static void receive_chars(struct m68k_serial *info, unsigned short rx) return; #endif /* CONFIG_MAGIC_SYSRQ */ } - /* It is a 'keyboard interrupt' ;-) */ -#ifdef CONFIG_CONSOLE - wake_up(&keypress_wait); -#endif } if(!tty) @@ -884,7 +869,9 @@ static int get_serial_info(struct m68k_serial * info, tmp.close_delay = info->close_delay; tmp.closing_wait = info->closing_wait; tmp.custom_divisor = info->custom_divisor; - copy_to_user(retinfo,&tmp,sizeof(*retinfo)); + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; } @@ -897,7 +884,8 @@ static int set_serial_info(struct m68k_serial * info, if (!new_info) return -EFAULT; - copy_from_user(&new_serial,new_info,sizeof(new_serial)); + if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) + return -EFAULT; old_info = *info; if (!capable(CAP_SYS_ADMIN)) { @@ -958,8 +946,7 @@ static int get_lsr_info(struct m68k_serial * info, unsigned int *value) status = 0; #endif local_irq_restore(flags); - put_user(status,value); - return 0; + return put_user(status, value); } /* @@ -1014,27 +1001,18 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file, send_break(info, arg ? arg*(100) : 250); return 0; case TIOCGSERIAL: - if (access_ok(VERIFY_WRITE, (void *) arg, - sizeof(struct serial_struct))) - return get_serial_info(info, - (struct serial_struct *) arg); - return -EFAULT; + return get_serial_info(info, + (struct serial_struct *) arg); case TIOCSSERIAL: return set_serial_info(info, (struct serial_struct *) arg); case TIOCSERGETLSR: /* Get line status register */ - if (access_ok(VERIFY_WRITE, (void *) arg, - sizeof(unsigned int))) - return get_lsr_info(info, (unsigned int *) arg); - return -EFAULT; + return get_lsr_info(info, (unsigned int *) arg); case TIOCSERGSTRUCT: - if (!access_ok(VERIFY_WRITE, (void *) arg, - sizeof(struct m68k_serial))) + if (copy_to_user((struct m68k_serial *) arg, + info, sizeof(struct m68k_serial))) return -EFAULT; - copy_to_user((struct m68k_serial *) arg, - info, sizeof(struct m68k_serial)); return 0; - default: return -ENOIOCTLCMD; } @@ -1244,7 +1222,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, retval = -ERESTARTSYS; break; } + tty_unlock(); schedule(); + tty_lock(); } current->state = TASK_RUNNING; remove_wait_queue(&info->open_wait, &wait); @@ -1406,10 +1386,10 @@ static void m68328_set_baud(void) USTCNT = ustcnt & ~USTCNT_TXEN; again: - for (i = 0; i < sizeof(baud_table) / sizeof(baud_table[0]); i++) + for (i = 0; i < ARRAY_SIZE(baud_table); i++) if (baud_table[i] == m68328_console_baud) break; - if (i >= sizeof(baud_table) / sizeof(baud_table[0])) { + if (i >= ARRAY_SIZE(baud_table)) { m68328_console_baud = 9600; goto again; } @@ -1435,10 +1415,10 @@ int m68328_console_setup(struct console *cp, char *arg) if (arg) n = simple_strtoul(arg,NULL,0); - for (i = 0; i < BAUD_TABLE_SIZE; i++) + for (i = 0; i < ARRAY_SIZE(baud_table); i++) if (baud_table[i] == n) break; - if (i < BAUD_TABLE_SIZE) { + if (i < ARRAY_SIZE(baud_table)) { m68328_console_baud = n; m68328_console_cbaud = 0; if (i > 15) { diff --git a/drivers/serial/68328serial.h b/drivers/serial/68328serial.h index 58aa215..664ceb0 100644 --- a/drivers/serial/68328serial.h +++ b/drivers/serial/68328serial.h @@ -181,13 +181,8 @@ struct m68k_serial { /* * Define the number of ports supported and their irqs. */ -#ifndef CONFIG_68328_SERIAL_UART2 #define NR_PORTS 1 #define UART_IRQ_DEFNS {UART_IRQ_NUM} -#else -#define NR_PORTS 2 -#define UART_IRQ_DEFNS {UART1_IRQ_NUM, UART2_IRQ_NUM} -#endif #endif /* __KERNEL__ */ #endif /* !(_MC683XX_SERIAL_H) */ diff --git a/drivers/serial/68360serial.c b/drivers/serial/68360serial.c index 24661cd..88b1335 100644 --- a/drivers/serial/68360serial.c +++ b/drivers/serial/68360serial.c @@ -1381,6 +1381,30 @@ static void send_break(ser_info_t *info, unsigned int duration) } +/* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ +static int rs_360_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) +{ + ser_info_t *info = (ser_info_t *)tty->driver_data; + struct async_icount cnow; + + local_irq_disable(); + cnow = info->state->icount; + local_irq_enable(); + + icount->cts = cnow.cts; + icount->dsr = cnow.dsr; + icount->rng = cnow.rng; + icount->dcd = cnow.dcd; + + return 0; +} + static int rs_360_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { @@ -1394,7 +1418,7 @@ static int rs_360_ioctl(struct tty_struct *tty, struct file * file, if (serial_paranoia_check(info, tty->name, "rs_ioctl")) return -ENODEV; - if ((cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (cmd != TIOCMIWAIT) { if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; } @@ -1477,31 +1501,6 @@ static int rs_360_ioctl(struct tty_struct *tty, struct file * file, return 0; #endif - /* - * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) - * Return: write counters to the user passed counter struct - * NB: both 1->0 and 0->1 transitions are counted except for - * RI where only 0->1 is counted. - */ - case TIOCGICOUNT: - local_irq_disable(); - cnow = info->state->icount; - local_irq_enable(); - p_cuser = (struct serial_icounter_struct *) arg; -/* error = put_user(cnow.cts, &p_cuser->cts); */ -/* if (error) return error; */ -/* error = put_user(cnow.dsr, &p_cuser->dsr); */ -/* if (error) return error; */ -/* error = put_user(cnow.rng, &p_cuser->rng); */ -/* if (error) return error; */ -/* error = put_user(cnow.dcd, &p_cuser->dcd); */ -/* if (error) return error; */ - - put_user(cnow.cts, &p_cuser->cts); - put_user(cnow.dsr, &p_cuser->dsr); - put_user(cnow.rng, &p_cuser->rng); - put_user(cnow.dcd, &p_cuser->dcd); - return 0; default: return -ENOIOCTLCMD; @@ -1705,7 +1704,6 @@ static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout) printk("jiff=%lu...", jiffies); #endif - lock_kernel(); /* We go through the loop at least once because we can't tell * exactly when the last character exits the shifter. There can * be at least two characters waiting to be sent after the buffers @@ -1734,7 +1732,6 @@ static void rs_360_wait_until_sent(struct tty_struct *tty, int timeout) bdp--; } while (bdp->status & BD_SC_READY); current->state = TASK_RUNNING; - unlock_kernel(); #ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); #endif @@ -1862,7 +1859,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp, printk("block_til_ready blocking: ttys%d, count = %d\n", info->line, state->count); #endif + tty_unlock(); schedule(); + tty_lock(); } current->state = TASK_RUNNING; remove_wait_queue(&info->open_wait, &wait); @@ -2649,7 +2648,7 @@ static int __init rs_360_init(void) sup->tfcr = SMC_EB; /* Set this to 1 for now, so we get single - * character interrupts. Using idle charater + * character interrupts. Using idle character * time requires some additional tuning. */ sup->mrblr = 1; @@ -2728,7 +2727,7 @@ static int __init rs_360_init(void) up->tfcr = SMC_EB; /* Set this to 1 for now, so we get single - * character interrupts. Using idle charater + * character interrupts. Using idle character * time requires some additional tuning. */ up->mrblr = 1; @@ -2886,7 +2885,7 @@ int serial_console_setup( struct console *co, char *options) sup->tfcr = SMC_EB; /* Set this to 1 for now, so we get single - * character interrupts. Using idle charater + * character interrupts. Using idle character * time requires some additional tuning. */ sup->mrblr = 1; diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index fb867a9..b25e6e4 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -31,6 +31,7 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/tty.h> +#include <linux/ratelimit.h> #include <linux/tty_flip.h> #include <linux/serial_reg.h> #include <linux/serial_core.h> @@ -38,6 +39,7 @@ #include <linux/serial_8250.h> #include <linux/nmi.h> #include <linux/mutex.h> +#include <linux/slab.h> #include <asm/io.h> #include <asm/irq.h> @@ -64,6 +66,8 @@ static int serial_index(struct uart_port *port) return (serial8250_reg.minor - 64) + port->line; } +static unsigned int skip_txen_test; /* force skip of txen test at init time */ + /* * Debugging. */ @@ -81,6 +85,9 @@ static int serial_index(struct uart_port *port) #define PASS_LIMIT 256 +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + + /* * We default to IRQ0 for the "no irq" hack. Some * machine types want others as well - they're free @@ -148,12 +155,6 @@ struct uart_8250_port { unsigned char lsr_saved_flags; #define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA unsigned char msr_saved_flags; - - /* - * We provide a per-port pm hook. - */ - void (*pm)(struct uart_port *port, - unsigned int state, unsigned int old); }; struct irq_info { @@ -235,7 +236,7 @@ static const struct serial8250_config uart_config[] = { .fifo_size = 128, .tx_loadsz = 128, .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, - .flags = UART_CAP_FIFO, + .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP, }, [PORT_16654] = { .name = "ST16654", @@ -294,9 +295,16 @@ static const struct serial8250_config uart_config[] = { .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00, .flags = UART_CAP_FIFO | UART_CAP_AFE, }, + [PORT_U6_16550A] = { + .name = "U6_16550A", + .fifo_size = 64, + .tx_loadsz = 64, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_AFE, + }, }; -#if defined (CONFIG_SERIAL_8250_AU1X00) +#if defined(CONFIG_MIPS_ALCHEMY) /* Au1x00 UART hardware has a weird register layout */ static const u8 au_io_in_map[] = { @@ -416,7 +424,6 @@ static unsigned int mem32_serial_in(struct uart_port *p, int offset) return readl(p->membase + offset); } -#ifdef CONFIG_SERIAL_8250_AU1X00 static unsigned int au_serial_in(struct uart_port *p, int offset) { offset = map_8250_in_reg(p, offset) << p->regshift; @@ -428,7 +435,6 @@ static void au_serial_out(struct uart_port *p, int offset, int value) offset = map_8250_out_reg(p, offset) << p->regshift; __raw_writel(value, p->membase + offset); } -#endif static unsigned int tsi_serial_in(struct uart_port *p, int offset) { @@ -448,21 +454,40 @@ static void tsi_serial_out(struct uart_port *p, int offset, int value) writeb(value, p->membase + offset); } +/* Save the LCR value so it can be re-written when a Busy Detect IRQ occurs. */ +static inline void dwapb_save_out_value(struct uart_port *p, int offset, + int value) +{ + struct uart_8250_port *up = + container_of(p, struct uart_8250_port, port); + + if (offset == UART_LCR) + up->lcr = value; +} + +/* Read the IER to ensure any interrupt is cleared before returning from ISR. */ +static inline void dwapb_check_clear_ier(struct uart_port *p, int offset) +{ + if (offset == UART_TX || offset == UART_IER) + p->serial_in(p, UART_IER); +} + static void dwapb_serial_out(struct uart_port *p, int offset, int value) { int save_offset = offset; offset = map_8250_out_reg(p, offset) << p->regshift; - /* Save the LCR value so it can be re-written when a - * Busy Detect interrupt occurs. */ - if (save_offset == UART_LCR) { - struct uart_8250_port *up = (struct uart_8250_port *)p; - up->lcr = value; - } + dwapb_save_out_value(p, save_offset, value); writeb(value, p->membase + offset); - /* Read the IER to ensure any interrupt is cleared before - * returning from ISR. */ - if (save_offset == UART_TX || save_offset == UART_IER) - value = p->serial_in(p, UART_IER); + dwapb_check_clear_ier(p, save_offset); +} + +static void dwapb32_serial_out(struct uart_port *p, int offset, int value) +{ + int save_offset = offset; + offset = map_8250_out_reg(p, offset) << p->regshift; + dwapb_save_out_value(p, save_offset, value); + writel(value, p->membase + offset); + dwapb_check_clear_ier(p, save_offset); } static unsigned int io_serial_in(struct uart_port *p, int offset) @@ -479,7 +504,8 @@ static void io_serial_out(struct uart_port *p, int offset, int value) static void set_io_from_upio(struct uart_port *p) { - struct uart_8250_port *up = (struct uart_8250_port *)p; + struct uart_8250_port *up = + container_of(p, struct uart_8250_port, port); switch (p->iotype) { case UPIO_HUB6: p->serial_in = hub6_serial_in; @@ -497,12 +523,11 @@ static void set_io_from_upio(struct uart_port *p) p->serial_out = mem32_serial_out; break; -#ifdef CONFIG_SERIAL_8250_AU1X00 case UPIO_AU: p->serial_in = au_serial_in; p->serial_out = au_serial_out; break; -#endif + case UPIO_TSI: p->serial_in = tsi_serial_in; p->serial_out = tsi_serial_out; @@ -513,6 +538,11 @@ static void set_io_from_upio(struct uart_port *p) p->serial_out = dwapb_serial_out; break; + case UPIO_DWAPB32: + p->serial_in = mem32_serial_in; + p->serial_out = dwapb32_serial_out; + break; + default: p->serial_in = io_serial_in; p->serial_out = io_serial_out; @@ -529,10 +559,9 @@ serial_out_sync(struct uart_8250_port *up, int offset, int value) switch (p->iotype) { case UPIO_MEM: case UPIO_MEM32: -#ifdef CONFIG_SERIAL_8250_AU1X00 case UPIO_AU: -#endif case UPIO_DWAPB: + case UPIO_DWAPB32: p->serial_out(p, offset, value); p->serial_in(p, UART_LCR); /* safe, no side-effects */ break; @@ -567,7 +596,7 @@ static inline void _serial_dl_write(struct uart_8250_port *up, int value) serial_outp(up, UART_DLM, value >> 8 & 0xff); } -#if defined(CONFIG_SERIAL_8250_AU1X00) +#if defined(CONFIG_MIPS_ALCHEMY) /* Au1x00 haven't got a standard divisor latch */ static int serial_dl_read(struct uart_8250_port *up) { @@ -650,13 +679,13 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) { if (p->capabilities & UART_CAP_SLEEP) { if (p->capabilities & UART_CAP_EFR) { - serial_outp(p, UART_LCR, 0xBF); + serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_B); serial_outp(p, UART_EFR, UART_EFR_ECB); serial_outp(p, UART_LCR, 0); } serial_outp(p, UART_IER, sleep ? UART_IERX_SLEEP : 0); if (p->capabilities & UART_CAP_EFR) { - serial_outp(p, UART_LCR, 0xBF); + serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_B); serial_outp(p, UART_EFR, 0); serial_outp(p, UART_LCR, 0); } @@ -749,7 +778,7 @@ static int size_fifo(struct uart_8250_port *up) serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); serial_outp(up, UART_MCR, UART_MCR_LOOP); - serial_outp(up, UART_LCR, UART_LCR_DLAB); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); old_dl = serial_dl_read(up); serial_dl_write(up, 0x0001); serial_outp(up, UART_LCR, 0x03); @@ -761,7 +790,7 @@ static int size_fifo(struct uart_8250_port *up) serial_inp(up, UART_RX); serial_outp(up, UART_FCR, old_fcr); serial_outp(up, UART_MCR, old_mcr); - serial_outp(up, UART_LCR, UART_LCR_DLAB); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); serial_dl_write(up, old_dl); serial_outp(up, UART_LCR, old_lcr); @@ -779,7 +808,7 @@ static unsigned int autoconfig_read_divisor_id(struct uart_8250_port *p) unsigned int id; old_lcr = serial_inp(p, UART_LCR); - serial_outp(p, UART_LCR, UART_LCR_DLAB); + serial_outp(p, UART_LCR, UART_LCR_CONF_MODE_A); old_dll = serial_inp(p, UART_DLL); old_dlm = serial_inp(p, UART_DLM); @@ -833,7 +862,7 @@ static void autoconfig_has_efr(struct uart_8250_port *up) * recommended for new designs). */ up->acr = 0; - serial_out(up, UART_LCR, 0xBF); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_out(up, UART_EFR, UART_EFR_ECB); serial_out(up, UART_LCR, 0x00); id1 = serial_icr_read(up, UART_ID1); @@ -916,7 +945,7 @@ static int broken_efr(struct uart_8250_port *up) /* * Exar ST16C2550 "A2" devices incorrectly detect as * having an EFR, and report an ID of 0x0201. See - * http://www.exar.com/info.php?pdf=dan180_oct2004.pdf + * http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-11/4812.html */ if (autoconfig_read_divisor_id(up) == 0x0201 && size_fifo(up) == 16) return 1; @@ -942,7 +971,7 @@ static void autoconfig_16550a(struct uart_8250_port *up) * Check for presence of the EFR when DLAB is set. * Only ST16C650V1 UARTs pass this test. */ - serial_outp(up, UART_LCR, UART_LCR_DLAB); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); if (serial_in(up, UART_EFR) == 0) { serial_outp(up, UART_EFR, 0xA8); if (serial_in(up, UART_EFR) != 0) { @@ -960,7 +989,7 @@ static void autoconfig_16550a(struct uart_8250_port *up) * Maybe it requires 0xbf to be written to the LCR. * (other ST16C650V2 UARTs, TI16C752A, etc) */ - serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); if (serial_in(up, UART_EFR) == 0 && !broken_efr(up)) { DEBUG_AUTOCONF("EFRv2 "); autoconfig_has_efr(up); @@ -1021,7 +1050,7 @@ static void autoconfig_16550a(struct uart_8250_port *up) serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); status1 = serial_in(up, UART_IIR) >> 5; serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); - serial_outp(up, UART_LCR, UART_LCR_DLAB); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_A); serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); status2 = serial_in(up, UART_IIR) >> 5; serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); @@ -1069,6 +1098,15 @@ static void autoconfig_16550a(struct uart_8250_port *up) DEBUG_AUTOCONF("Couldn't force IER_UUE to 0 "); } serial_outp(up, UART_IER, iersave); + + /* + * We distinguish between 16550A and U6 16550A by counting + * how many bytes are in the FIFO. + */ + if (up->port.type == PORT_16550A && size_fifo(up) == 64) { + up->port.type = PORT_U6_16550A; + up->capabilities |= UART_CAP_AFE; + } } /* @@ -1087,7 +1125,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%p): ", + DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04lx, 0x%p): ", serial_index(&up->port), up->port.iobase, up->port.membase); /* @@ -1171,7 +1209,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) * We also initialise the EFR (if any) to zero for later. The * EFR occupies the same register location as the FCR and IIR. */ - serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_outp(up, UART_EFR, 0); serial_outp(up, UART_LCR, 0); @@ -1212,12 +1250,6 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) } #endif -#ifdef CONFIG_SERIAL_8250_AU1X00 - /* if access method is AU, it is a 16550 with a quirk */ - if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) - up->bugs |= UART_BUG_NOMSR; -#endif - serial_outp(up, UART_LCR, save_lcr); if (up->capabilities != uart_config[up->port.type].flags) { @@ -1313,7 +1345,8 @@ static inline void __stop_tx(struct uart_8250_port *p) static void serial8250_stop_tx(struct uart_port *port) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); __stop_tx(up); @@ -1330,21 +1363,20 @@ static void transmit_chars(struct uart_8250_port *up); static void serial8250_start_tx(struct uart_port *port) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); if (!(up->ier & UART_IER_THRI)) { up->ier |= UART_IER_THRI; serial_out(up, UART_IER, up->ier); if (up->bugs & UART_BUG_TXEN) { - unsigned char lsr, iir; + unsigned char lsr; lsr = serial_in(up, UART_LSR); up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; - iir = serial_in(up, UART_IIR) & 0x0f; if ((up->port.type == PORT_RM9000) ? - (lsr & UART_LSR_THRE && - (iir == UART_IIR_NO_INT || iir == UART_IIR_THRI)) : - (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT)) + (lsr & UART_LSR_THRE) : + (lsr & UART_LSR_TEMT)) transmit_chars(up); } } @@ -1360,7 +1392,8 @@ static void serial8250_start_tx(struct uart_port *port) static void serial8250_stop_rx(struct uart_port *port) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); up->ier &= ~UART_IER_RLSI; up->port.read_status_mask &= ~UART_LSR_DR; @@ -1369,7 +1402,8 @@ static void serial8250_stop_rx(struct uart_port *port) static void serial8250_enable_ms(struct uart_port *port) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); /* no MSR capabilities */ if (up->bugs & UART_BUG_NOMSR) @@ -1382,7 +1416,7 @@ static void serial8250_enable_ms(struct uart_port *port) static void receive_chars(struct uart_8250_port *up, unsigned int *status) { - struct tty_struct *tty = up->port.info->port.tty; + struct tty_struct *tty = up->port.state->port.tty; unsigned char ch, lsr = *status; int max_count = 256; char flag; @@ -1457,7 +1491,7 @@ ignore_char: static void transmit_chars(struct uart_8250_port *up) { - struct circ_buf *xmit = &up->port.info->xmit; + struct circ_buf *xmit = &up->port.state->xmit; int count; if (up->port.x_char) { @@ -1500,7 +1534,7 @@ static unsigned int check_modem_status(struct uart_8250_port *up) status |= up->msr_saved_flags; up->msr_saved_flags = 0; if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI && - up->port.info != NULL) { + up->port.state != NULL) { if (status & UART_MSR_TERI) up->port.icount.rng++; if (status & UART_MSR_DDSR) @@ -1510,7 +1544,7 @@ static unsigned int check_modem_status(struct uart_8250_port *up) if (status & UART_MSR_DCTS) uart_handle_cts_change(&up->port, status & UART_MSR_CTS); - wake_up_interruptible(&up->port.info->delta_msr_wait); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); } return status; @@ -1577,7 +1611,8 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) handled = 1; end = NULL; - } else if (up->port.iotype == UPIO_DWAPB && + } else if ((up->port.iotype == UPIO_DWAPB || + up->port.iotype == UPIO_DWAPB32) && (iir & UART_IIR_BUSY) == UART_IIR_BUSY) { /* The DesignWare APB UART has an Busy Detect (0x07) * interrupt meaning an LCR write attempt occured while the @@ -1597,8 +1632,8 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id) if (l == i->head && pass_counter++ > PASS_LIMIT) { /* If we hit this, we're dead. */ - printk(KERN_ERR "serial8250: too much work for " - "irq%d\n", irq); + printk_ratelimited(KERN_ERR + "serial8250: too much work for irq%d\n", irq); break; } } while (l != end); @@ -1677,7 +1712,7 @@ static int serial_link_irq_chain(struct uart_8250_port *up) INIT_LIST_HEAD(&up->list); i->head = &up->list; spin_unlock_irq(&i->lock); - + irq_flags |= up->port.irqflags; ret = request_irq(up->port.irq, serial8250_interrupt, irq_flags, "serial", i); if (ret < 0) @@ -1713,12 +1748,6 @@ static void serial_unlink_irq_chain(struct uart_8250_port *up) mutex_unlock(&hash_mutex); } -/* Base timer interval for polling */ -static inline int poll_timeout(int timeout) -{ - return timeout > 6 ? (timeout / 2 - 2) : 1; -} - /* * This function is used to handle ports that do not have an * interrupt. This doesn't work very well for 16450's, but gives @@ -1733,7 +1762,7 @@ static void serial8250_timeout(unsigned long data) iir = serial_in(up, UART_IIR); if (!(iir & UART_IIR_NO_INT)) serial8250_handle_port(up); - mod_timer(&up->timer, jiffies + poll_timeout(up->port.timeout)); + mod_timer(&up->timer, jiffies + uart_poll_timeout(&up->port)); } static void serial8250_backup_timeout(unsigned long data) @@ -1764,7 +1793,7 @@ static void serial8250_backup_timeout(unsigned long data) up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; spin_unlock_irqrestore(&up->port.lock, flags); if ((iir & UART_IIR_NO_INT) && (up->ier & UART_IER_THRI) && - (!uart_circ_empty(&up->port.info->xmit) || up->port.x_char) && + (!uart_circ_empty(&up->port.state->xmit) || up->port.x_char) && (lsr & UART_LSR_THRE)) { iir &= ~(UART_IIR_ID | UART_IIR_NO_INT); iir |= UART_IIR_THRI; @@ -1778,12 +1807,13 @@ static void serial8250_backup_timeout(unsigned long data) /* Standard timer interval plus 0.2s to keep the port running */ mod_timer(&up->timer, - jiffies + poll_timeout(up->port.timeout) + HZ / 5); + jiffies + uart_poll_timeout(&up->port) + HZ / 5); } static unsigned int serial8250_tx_empty(struct uart_port *port) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); unsigned long flags; unsigned int lsr; @@ -1792,12 +1822,13 @@ static unsigned int serial8250_tx_empty(struct uart_port *port) up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; spin_unlock_irqrestore(&up->port.lock, flags); - return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0; } static unsigned int serial8250_get_mctrl(struct uart_port *port) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); unsigned int status; unsigned int ret; @@ -1817,7 +1848,8 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port) static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); unsigned char mcr = 0; if (mctrl & TIOCM_RTS) @@ -1838,7 +1870,8 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl) static void serial8250_break_ctl(struct uart_port *port, int break_state) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); @@ -1850,8 +1883,6 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) spin_unlock_irqrestore(&up->port.lock, flags); } -#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) - /* * Wait for transmitter & holding register to empty */ @@ -1860,15 +1891,17 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) unsigned int status, tmout = 10000; /* Wait up to 10ms for the character(s) to be sent. */ - do { + for (;;) { status = serial_in(up, UART_LSR); up->lsr_saved_flags |= status & LSR_SAVE_FLAGS; + if ((status & bits) == bits) + break; if (--tmout == 0) break; udelay(1); - } while ((status & bits) != bits); + } /* Wait up to 1s for flow control if necessary */ if (up->port.flags & UPF_CONS_FLOW) { @@ -1892,11 +1925,12 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) static int serial8250_get_poll_char(struct uart_port *port) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); unsigned char lsr = serial_inp(up, UART_LSR); - while (!(lsr & UART_LSR_DR)) - lsr = serial_inp(up, UART_LSR); + if (!(lsr & UART_LSR_DR)) + return NO_POLL_CHAR; return serial_inp(up, UART_RX); } @@ -1906,7 +1940,8 @@ static void serial8250_put_poll_char(struct uart_port *port, unsigned char c) { unsigned int ier; - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); /* * First save the IER then disable the interrupts @@ -1940,11 +1975,14 @@ static void serial8250_put_poll_char(struct uart_port *port, static int serial8250_startup(struct uart_port *port) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); unsigned long flags; unsigned char lsr, iir; int retval; + up->port.fifosize = uart_config[up->port.type].fifo_size; + up->tx_loadsz = uart_config[up->port.type].tx_loadsz; up->capabilities = uart_config[up->port.type].flags; up->mcr = 0; @@ -1954,7 +1992,7 @@ static int serial8250_startup(struct uart_port *port) if (up->port.type == PORT_16C950) { /* Wake up and initialize UART */ up->acr = 0; - serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_outp(up, UART_EFR, UART_EFR_ECB); serial_outp(up, UART_IER, 0); serial_outp(up, UART_LCR, 0); @@ -2004,7 +2042,7 @@ static int serial8250_startup(struct uart_port *port) if (up->port.type == PORT_16850) { unsigned char fctr; - serial_outp(up, UART_LCR, 0xbf); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); fctr = serial_inp(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX); serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_RX); @@ -2026,7 +2064,7 @@ static int serial8250_startup(struct uart_port *port) * allow register changes to become visible. */ spin_lock_irqsave(&up->port.lock, flags); - if (up->port.flags & UPF_SHARE_IRQ) + if (up->port.irqflags & IRQF_SHARED) disable_irq_nosync(up->port.irq); wait_for_xmitr(up, UART_LSR_THRE); @@ -2039,7 +2077,7 @@ static int serial8250_startup(struct uart_port *port) iir = serial_in(up, UART_IIR); serial_out(up, UART_IER, 0); - if (up->port.flags & UPF_SHARE_IRQ) + if (up->port.irqflags & IRQF_SHARED) enable_irq(up->port.irq); spin_unlock_irqrestore(&up->port.lock, flags); @@ -2062,7 +2100,7 @@ static int serial8250_startup(struct uart_port *port) up->timer.function = serial8250_backup_timeout; up->timer.data = (unsigned long)up; mod_timer(&up->timer, jiffies + - poll_timeout(up->port.timeout) + HZ / 5); + uart_poll_timeout(port) + HZ / 5); } /* @@ -2072,7 +2110,7 @@ static int serial8250_startup(struct uart_port *port) */ if (!is_real_interrupt(up->port.irq)) { up->timer.data = (unsigned long)up; - mod_timer(&up->timer, jiffies + poll_timeout(up->port.timeout)); + mod_timer(&up->timer, jiffies + uart_poll_timeout(port)); } else { retval = serial_link_irq_chain(up); if (retval) @@ -2108,7 +2146,7 @@ static int serial8250_startup(struct uart_port *port) is variable. So, let's just don't test if we receive TX irq. This way, we'll never enable UART_BUG_TXEN. */ - if (up->port.flags & UPF_NO_TXEN_TEST) + if (skip_txen_test || up->port.flags & UPF_NO_TXEN_TEST) goto dont_test_tx_en; /* @@ -2168,7 +2206,8 @@ dont_test_tx_en: static void serial8250_shutdown(struct uart_port *port) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); unsigned long flags; /* @@ -2233,11 +2272,12 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int return quot; } -static void -serial8250_set_termios(struct uart_port *port, struct ktermios *termios, - struct ktermios *old) +void +serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); unsigned char cval, fcr = 0; unsigned long flags; unsigned int baud, quot; @@ -2272,7 +2312,9 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios, /* * Ask the core to calculate the divisor for us. */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + port->uartclk / 16); quot = serial8250_get_divisor(port, baud); /* @@ -2363,7 +2405,7 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios, if (termios->c_cflag & CRTSCTS) efr |= UART_EFR_CTS; - serial_outp(up, UART_LCR, 0xBF); + serial_outp(up, UART_LCR, UART_LCR_CONF_MODE_B); serial_outp(up, UART_EFR, efr); } @@ -2409,23 +2451,53 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios, if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); } +EXPORT_SYMBOL(serial8250_do_set_termios); static void -serial8250_pm(struct uart_port *port, unsigned int state, - unsigned int oldstate) +serial8250_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) { - struct uart_8250_port *p = (struct uart_8250_port *)port; + if (port->set_termios) + port->set_termios(port, termios, old); + else + serial8250_do_set_termios(port, termios, old); +} + +static void +serial8250_set_ldisc(struct uart_port *port, int new) +{ + if (new == N_PPS) { + port->flags |= UPF_HARDPPS_CD; + serial8250_enable_ms(port); + } else + port->flags &= ~UPF_HARDPPS_CD; +} + + +void serial8250_do_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_8250_port *p = + container_of(port, struct uart_8250_port, port); serial8250_set_sleep(p, state != 0); +} +EXPORT_SYMBOL(serial8250_do_pm); - if (p->pm) - p->pm(port, state, oldstate); +static void +serial8250_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + if (port->pm) + port->pm(port, state, oldstate); + else + serial8250_do_pm(port, state, oldstate); } static unsigned int serial8250_port_size(struct uart_8250_port *pt) { if (pt->port.iotype == UPIO_AU) - return 0x100000; + return 0x1000; #ifdef CONFIG_ARCH_OMAP if (is_omap_port(pt)) return 0x16 << pt->port.regshift; @@ -2447,6 +2519,7 @@ static int serial8250_request_std_resource(struct uart_8250_port *up) case UPIO_MEM32: case UPIO_MEM: case UPIO_DWAPB: + case UPIO_DWAPB32: if (!up->port.mapbase) break; @@ -2484,6 +2557,7 @@ static void serial8250_release_std_resource(struct uart_8250_port *up) case UPIO_MEM32: case UPIO_MEM: case UPIO_DWAPB: + case UPIO_DWAPB32: if (!up->port.mapbase) break; @@ -2537,7 +2611,8 @@ static void serial8250_release_rsa_resource(struct uart_8250_port *up) static void serial8250_release_port(struct uart_port *port) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); serial8250_release_std_resource(up); if (up->port.type == PORT_RSA) @@ -2546,7 +2621,8 @@ static void serial8250_release_port(struct uart_port *port) static int serial8250_request_port(struct uart_port *port) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); int ret = 0; ret = serial8250_request_std_resource(up); @@ -2561,7 +2637,8 @@ static int serial8250_request_port(struct uart_port *port) static void serial8250_config_port(struct uart_port *port, int flags) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); int probeflags = PROBE_ANY; int ret; @@ -2582,6 +2659,11 @@ static void serial8250_config_port(struct uart_port *port, int flags) if (flags & UART_CONFIG_TYPE) autoconfig(up, probeflags); + + /* if access method is AU, it is a 16550 with a quirk */ + if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU) + up->bugs |= UART_BUG_NOMSR; + if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) autoconfig_irq(up); @@ -2624,6 +2706,7 @@ static struct uart_ops serial8250_pops = { .startup = serial8250_startup, .shutdown = serial8250_shutdown, .set_termios = serial8250_set_termios, + .set_ldisc = serial8250_set_ldisc, .pm = serial8250_pm, .type = serial8250_type, .release_port = serial8250_release_port, @@ -2638,11 +2721,21 @@ static struct uart_ops serial8250_pops = { static struct uart_8250_port serial8250_ports[UART_NR]; +static void (*serial8250_isa_config)(int port, struct uart_port *up, + unsigned short *capabilities); + +void serial8250_set_isa_configurator( + void (*v)(int port, struct uart_port *up, unsigned short *capabilities)) +{ + serial8250_isa_config = v; +} +EXPORT_SYMBOL(serial8250_set_isa_configurator); + static void __init serial8250_isa_init_ports(void) { struct uart_8250_port *up; static int first = 1; - int i; + int i, irqflag = 0; if (!first) return; @@ -2666,11 +2759,15 @@ static void __init serial8250_isa_init_ports(void) up->port.ops = &serial8250_pops; } + if (share_irqs) + irqflag = IRQF_SHARED; + for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port) && i < nr_uarts; i++, up++) { up->port.iobase = old_serial_port[i].port; up->port.irq = irq_canonicalize(old_serial_port[i].irq); + up->port.irqflags = old_serial_port[i].irqflags; up->port.uartclk = old_serial_port[i].baud_base * 16; up->port.flags = old_serial_port[i].flags; up->port.hub6 = old_serial_port[i].hub6; @@ -2678,11 +2775,22 @@ static void __init serial8250_isa_init_ports(void) up->port.iotype = old_serial_port[i].io_type; up->port.regshift = old_serial_port[i].iomem_reg_shift; set_io_from_upio(&up->port); - if (share_irqs) - up->port.flags |= UPF_SHARE_IRQ; + up->port.irqflags |= irqflag; + if (serial8250_isa_config != NULL) + serial8250_isa_config(i, &up->port, &up->capabilities); + } } +static void +serial8250_init_fixed_type_port(struct uart_8250_port *up, unsigned int type) +{ + up->port.type = type; + up->port.fifosize = uart_config[type].fifo_size; + up->capabilities = uart_config[type].flags; + up->tx_loadsz = uart_config[type].tx_loadsz; +} + static void __init serial8250_register_ports(struct uart_driver *drv, struct device *dev) { @@ -2699,6 +2807,10 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev) struct uart_8250_port *up = &serial8250_ports[i]; up->port.dev = dev; + + if (up->port.flags & UPF_FIXED_TYPE) + serial8250_init_fixed_type_port(up, up->port.type); + uart_add_one_port(drv, &up->port); } } @@ -2707,7 +2819,8 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev) static void serial8250_console_putchar(struct uart_port *port, int ch) { - struct uart_8250_port *up = (struct uart_8250_port *)port; + struct uart_8250_port *up = + container_of(port, struct uart_8250_port, port); wait_for_xmitr(up, UART_LSR_THRE); serial_out(up, UART_TX, ch); @@ -2808,7 +2921,7 @@ static struct console serial8250_console = { .device = uart_console_device, .setup = serial8250_console_setup, .early_setup = serial8250_console_early_setup, - .flags = CON_PRINTBUFFER, + .flags = CON_PRINTBUFFER | CON_ANYTIME, .index = -1, .data = &serial8250_reg, }; @@ -2869,6 +2982,7 @@ int __init early_serial_setup(struct uart_port *port) p->iobase = port->iobase; p->membase = port->membase; p->irq = port->irq; + p->irqflags = port->irqflags; p->uartclk = port->uartclk; p->fifosize = port->fifosize; p->regshift = port->regshift; @@ -2934,14 +3048,18 @@ static int __devinit serial8250_probe(struct platform_device *dev) { struct plat_serial8250_port *p = dev->dev.platform_data; struct uart_port port; - int ret, i; + int ret, i, irqflag = 0; memset(&port, 0, sizeof(struct uart_port)); + if (share_irqs) + irqflag = IRQF_SHARED; + for (i = 0; p && p->flags != 0; p++, i++) { port.iobase = p->iobase; port.membase = p->membase; port.irq = p->irq; + port.irqflags = p->irqflags; port.uartclk = p->uartclk; port.regshift = p->regshift; port.iotype = p->iotype; @@ -2952,9 +3070,10 @@ static int __devinit serial8250_probe(struct platform_device *dev) port.type = p->type; port.serial_in = p->serial_in; port.serial_out = p->serial_out; + port.set_termios = p->set_termios; + port.pm = p->pm; port.dev = &dev->dev; - if (share_irqs) - port.flags |= UPF_SHARE_IRQ; + port.irqflags |= irqflag; ret = serial8250_register_port(&port); if (ret < 0) { dev_err(&dev->dev, "unable to register port at index %d " @@ -3096,6 +3215,7 @@ int serial8250_register_port(struct uart_port *port) uart->port.iobase = port->iobase; uart->port.membase = port->membase; uart->port.irq = port->irq; + uart->port.irqflags = port->irqflags; uart->port.uartclk = port->uartclk; uart->port.fifosize = port->fifosize; uart->port.regshift = port->regshift; @@ -3106,12 +3226,8 @@ int serial8250_register_port(struct uart_port *port) if (port->dev) uart->port.dev = port->dev; - if (port->flags & UPF_FIXED_TYPE) { - uart->port.type = port->type; - uart->port.fifosize = uart_config[port->type].fifo_size; - uart->capabilities = uart_config[port->type].flags; - uart->tx_loadsz = uart_config[port->type].tx_loadsz; - } + if (port->flags & UPF_FIXED_TYPE) + serial8250_init_fixed_type_port(uart, port->type); set_io_from_upio(&uart->port); /* Possibly override default I/O functions. */ @@ -3119,6 +3235,15 @@ int serial8250_register_port(struct uart_port *port) uart->port.serial_in = port->serial_in; if (port->serial_out) uart->port.serial_out = port->serial_out; + /* Possibly override set_termios call */ + if (port->set_termios) + uart->port.set_termios = port->set_termios; + if (port->pm) + uart->port.pm = port->pm; + + if (serial8250_isa_config != NULL) + serial8250_isa_config(0, &uart->port, + &uart->capabilities); ret = uart_add_one_port(&serial8250_reg, &uart->port); if (ret == 0) @@ -3242,6 +3367,9 @@ MODULE_PARM_DESC(share_irqs, "Share IRQs with other non-8250/16x50 devices" module_param(nr_uarts, uint, 0644); MODULE_PARM_DESC(nr_uarts, "Maximum number of UARTs supported. (1-" __MODULE_STRING(CONFIG_SERIAL_8250_NR_UARTS) ")"); +module_param(skip_txen_test, uint, 0644); +MODULE_PARM_DESC(skip_txen_test, "Skip checking for the TXEN bug at init time"); + #ifdef CONFIG_SERIAL_8250_RSA module_param_array(probe_rsa, ulong, &probe_rsa_count, 0444); MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); diff --git a/drivers/serial/8250.h b/drivers/serial/8250.h index 5202603..6e19ea3 100644 --- a/drivers/serial/8250.h +++ b/drivers/serial/8250.h @@ -25,6 +25,7 @@ struct old_serial_port { unsigned char io_type; unsigned char *iomem_base; unsigned short iomem_reg_shift; + unsigned long irqflags; }; /* diff --git a/drivers/serial/8250_early.c b/drivers/serial/8250_early.c index f279745..eaafb98 100644 --- a/drivers/serial/8250_early.c +++ b/drivers/serial/8250_early.c @@ -19,9 +19,11 @@ * The user can specify the device directly, e.g., * earlycon=uart8250,io,0x3f8,9600n8 * earlycon=uart8250,mmio,0xff5e0000,115200n8 + * earlycon=uart8250,mmio32,0xff5e0000,115200n8 * or * console=uart8250,io,0x3f8,9600n8 * console=uart8250,mmio,0xff5e0000,115200n8 + * console=uart8250,mmio32,0xff5e0000,115200n8 */ #include <linux/tty.h> @@ -48,18 +50,31 @@ static struct early_serial8250_device early_device; static unsigned int __init serial_in(struct uart_port *port, int offset) { - if (port->iotype == UPIO_MEM) + switch (port->iotype) { + case UPIO_MEM: return readb(port->membase + offset); - else + case UPIO_MEM32: + return readl(port->membase + (offset << 2)); + case UPIO_PORT: return inb(port->iobase + offset); + default: + return 0; + } } static void __init serial_out(struct uart_port *port, int offset, int value) { - if (port->iotype == UPIO_MEM) + switch (port->iotype) { + case UPIO_MEM: writeb(value, port->membase + offset); - else + break; + case UPIO_MEM32: + writel(value, port->membase + (offset << 2)); + break; + case UPIO_PORT: outb(value, port->iobase + offset); + break; + } } #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) @@ -137,15 +152,21 @@ static int __init parse_options(struct early_serial8250_device *device, char *options) { struct uart_port *port = &device->port; - int mmio, length; + int mmio, mmio32, length; if (!options) return -ENODEV; port->uartclk = BASE_BAUD * 16; - if (!strncmp(options, "mmio,", 5)) { - port->iotype = UPIO_MEM; - port->mapbase = simple_strtoul(options + 5, &options, 0); + + mmio = !strncmp(options, "mmio,", 5); + mmio32 = !strncmp(options, "mmio32,", 7); + if (mmio || mmio32) { + port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32); + port->mapbase = simple_strtoul(options + (mmio ? 5 : 7), + &options, 0); + if (mmio32) + port->regshift = 2; #ifdef CONFIG_FIX_EARLYCON_MEM set_fixmap_nocache(FIX_EARLYCON_MEM_BASE, port->mapbase & PAGE_MASK); @@ -157,11 +178,10 @@ static int __init parse_options(struct early_serial8250_device *device, if (!port->membase) { printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n", __func__, - (unsigned long long)port->mapbase); + (unsigned long long) port->mapbase); return -ENOMEM; } #endif - mmio = 1; } else if (!strncmp(options, "io,", 3)) { port->iotype = UPIO_PORT; port->iobase = simple_strtoul(options + 3, &options, 0); @@ -181,11 +201,18 @@ static int __init parse_options(struct early_serial8250_device *device, device->baud); } - printk(KERN_INFO "Early serial console at %s 0x%llx (options '%s')\n", - mmio ? "MMIO" : "I/O port", - mmio ? (unsigned long long) port->mapbase - : (unsigned long long) port->iobase, - device->options); + if (mmio || mmio32) + printk(KERN_INFO + "Early serial console at MMIO%s 0x%llx (options '%s')\n", + mmio32 ? "32" : "", + (unsigned long long)port->mapbase, + device->options); + else + printk(KERN_INFO + "Early serial console at I/O port 0x%lx (options '%s')\n", + port->iobase, + device->options); + return 0; } diff --git a/drivers/serial/8250_gsc.c b/drivers/serial/8250_gsc.c index 33149d9..d8c0ffb 100644 --- a/drivers/serial/8250_gsc.c +++ b/drivers/serial/8250_gsc.c @@ -16,7 +16,6 @@ #include <linux/module.h> #include <linux/serial_core.h> #include <linux/signal.h> -#include <linux/slab.h> #include <linux/types.h> #include <asm/hardware.h> diff --git a/drivers/serial/8250_hp300.c b/drivers/serial/8250_hp300.c index 0e1410f..c13438c9 100644 --- a/drivers/serial/8250_hp300.c +++ b/drivers/serial/8250_hp300.c @@ -15,6 +15,7 @@ #include <linux/delay.h> #include <linux/dio.h> #include <linux/console.h> +#include <linux/slab.h> #include <asm/io.h> #include "8250.h" diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index e371a9c..8b8930f 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -60,11 +60,12 @@ struct serial_private { static void moan_device(const char *str, struct pci_dev *dev) { - printk(KERN_WARNING "%s: %s\n" - KERN_WARNING "Please send the output of lspci -vv, this\n" - KERN_WARNING "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" - KERN_WARNING "manufacturer and name of serial board or\n" - KERN_WARNING "modem board to rmk+serial@arm.linux.org.uk.\n", + printk(KERN_WARNING + "%s: %s\n" + "Please send the output of lspci -vv, this\n" + "message (0x%04x,0x%04x,0x%04x,0x%04x), the\n" + "manufacturer and name of serial board or\n" + "modem board to rmk+serial@arm.linux.org.uk.\n", pci_name(dev), str, dev->vendor, dev->device, dev->subsystem_vendor, dev->subsystem_device); } @@ -398,8 +399,7 @@ static int sbs_init(struct pci_dev *dev) { u8 __iomem *p; - p = ioremap_nocache(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); + p = pci_ioremap_bar(dev, 0); if (p == NULL) return -ENOMEM; @@ -423,8 +423,7 @@ static void __devexit sbs_exit(struct pci_dev *dev) { u8 __iomem *p; - p = ioremap_nocache(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); + p = pci_ioremap_bar(dev, 0); /* FIXME: What if resource_len < OCT_REG_CR_OFF */ if (p != NULL) writeb(0, p + OCT_REG_CR_OFF); @@ -761,6 +760,9 @@ static int pci_netmos_init(struct pci_dev *dev) /* subdevice 0x00PS means <P> parallel, <S> serial */ unsigned int num_serial = dev->subsystem_device & 0xf; + if ((dev->device == PCI_DEVICE_ID_NETMOS_9901) || + (dev->device == PCI_DEVICE_ID_NETMOS_9865)) + return 0; if (dev->subsystem_vendor == PCI_VENDOR_ID_IBM && dev->subsystem_device == 0x0299) return 0; @@ -955,6 +957,22 @@ pci_default_setup(struct serial_private *priv, return setup_port(priv, port, bar, offset, board->reg_shift); } +static int +ce4100_serial_setup(struct serial_private *priv, + const struct pciserial_board *board, + struct uart_port *port, int idx) +{ + int ret; + + ret = setup_port(priv, port, 0, 0, board->reg_shift); + port->iotype = UPIO_MEM32; + port->type = PORT_XSCALE; + port->flags = (port->flags | UPF_FIXED_PORT | UPF_FIXED_TYPE); + port->regshift = 2; + + return ret; +} + static int skip_tx_en_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_port *port, int idx) @@ -979,7 +997,21 @@ static int skip_tx_en_setup(struct serial_private *priv, #define PCI_SUBDEVICE_ID_POCTAL232 0x0308 #define PCI_SUBDEVICE_ID_POCTAL422 0x0408 #define PCI_VENDOR_ID_ADVANTECH 0x13fe +#define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66 #define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620 +#define PCI_DEVICE_ID_TITAN_200I 0x8028 +#define PCI_DEVICE_ID_TITAN_400I 0x8048 +#define PCI_DEVICE_ID_TITAN_800I 0x8088 +#define PCI_DEVICE_ID_TITAN_800EH 0xA007 +#define PCI_DEVICE_ID_TITAN_800EHB 0xA008 +#define PCI_DEVICE_ID_TITAN_400EH 0xA009 +#define PCI_DEVICE_ID_TITAN_100E 0xA010 +#define PCI_DEVICE_ID_TITAN_200E 0xA012 +#define PCI_DEVICE_ID_TITAN_400E 0xA013 +#define PCI_DEVICE_ID_TITAN_800E 0xA014 +#define PCI_DEVICE_ID_TITAN_200EI 0xA016 +#define PCI_DEVICE_ID_TITAN_200EISI 0xA017 +#define PCI_DEVICE_ID_OXSEMI_16PCI958 0x9538 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 @@ -1057,6 +1089,13 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = skip_tx_en_setup, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CE4100_UART, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = ce4100_serial_setup, + }, /* * ITE */ @@ -1478,6 +1517,7 @@ enum pci_board_num_t { pbn_b0_bt_1_115200, pbn_b0_bt_2_115200, + pbn_b0_bt_4_115200, pbn_b0_bt_8_115200, pbn_b0_bt_1_460800, @@ -1527,6 +1567,8 @@ enum pci_board_num_t { pbn_b2_4_921600, pbn_b2_8_921600, + pbn_b2_8_1152000, + pbn_b2_bt_1_115200, pbn_b2_bt_2_115200, pbn_b2_bt_4_115200, @@ -1538,6 +1580,10 @@ enum pci_board_num_t { pbn_b3_4_115200, pbn_b3_8_115200, + pbn_b4_bt_2_921600, + pbn_b4_bt_4_921600, + pbn_b4_bt_8_921600, + /* * Board-specific versions. */ @@ -1560,11 +1606,17 @@ enum pci_board_num_t { pbn_exar_XR17C152, pbn_exar_XR17C154, pbn_exar_XR17C158, + pbn_exar_ibm_saturn, pbn_pasemi_1682M, pbn_ni8430_2, pbn_ni8430_4, pbn_ni8430_8, pbn_ni8430_16, + pbn_ADDIDATA_PCIe_1_3906250, + pbn_ADDIDATA_PCIe_2_3906250, + pbn_ADDIDATA_PCIe_4_3906250, + pbn_ADDIDATA_PCIe_8_3906250, + pbn_ce4100_1_115200, }; /* @@ -1697,6 +1749,12 @@ static struct pciserial_board pci_boards[] __devinitdata = { .base_baud = 115200, .uart_offset = 8, }, + [pbn_b0_bt_4_115200] = { + .flags = FL_BASE0|FL_BASE_BARS, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + }, [pbn_b0_bt_8_115200] = { .flags = FL_BASE0|FL_BASE_BARS, .num_ports = 8, @@ -1930,6 +1988,13 @@ static struct pciserial_board pci_boards[] __devinitdata = { .uart_offset = 8, }, + [pbn_b2_8_1152000] = { + .flags = FL_BASE2, + .num_ports = 8, + .base_baud = 1152000, + .uart_offset = 8, + }, + [pbn_b2_bt_1_115200] = { .flags = FL_BASE2|FL_BASE_BARS, .num_ports = 1, @@ -1981,6 +2046,25 @@ static struct pciserial_board pci_boards[] __devinitdata = { .uart_offset = 8, }, + [pbn_b4_bt_2_921600] = { + .flags = FL_BASE4, + .num_ports = 2, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b4_bt_4_921600] = { + .flags = FL_BASE4, + .num_ports = 4, + .base_baud = 921600, + .uart_offset = 8, + }, + [pbn_b4_bt_8_921600] = { + .flags = FL_BASE4, + .num_ports = 8, + .base_baud = 921600, + .uart_offset = 8, + }, + /* * Entries following this are board-specific. */ @@ -2145,6 +2229,13 @@ static struct pciserial_board pci_boards[] __devinitdata = { .base_baud = 921600, .uart_offset = 0x200, }, + [pbn_exar_ibm_saturn] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .uart_offset = 0x200, + }, + /* * PA Semi PWRficient PA6T-1682M on-chip UART */ @@ -2184,10 +2275,49 @@ static struct pciserial_board pci_boards[] __devinitdata = { .uart_offset = 0x10, .first_offset = 0x800, }, + /* + * ADDI-DATA GmbH PCI-Express communication cards <info@addi-data.com> + */ + [pbn_ADDIDATA_PCIe_1_3906250] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_2_3906250] = { + .flags = FL_BASE0, + .num_ports = 2, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_4_3906250] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ADDIDATA_PCIe_8_3906250] = { + .flags = FL_BASE0, + .num_ports = 8, + .base_baud = 3906250, + .uart_offset = 0x200, + .first_offset = 0x1000, + }, + [pbn_ce4100_1_115200] = { + .flags = FL_BASE0, + .num_ports = 1, + .base_baud = 921600, + .reg_shift = 2, + }, }; static const struct pci_device_id softmodem_blacklist[] = { { PCI_VDEVICE(AL, 0x5457), }, /* ALi Corporation M5457 AC'97 Modem */ + { PCI_VDEVICE(MOTOROLA, 0x3052), }, /* Motorola Si3052-based modem */ + { PCI_DEVICE(0x1543, 0x3052), }, /* Si3052-based modem, default IDs */ }; /* @@ -2339,7 +2469,7 @@ pciserial_init_ports(struct pci_dev *dev, const struct pciserial_board *board) break; #ifdef SERIAL_DEBUG_PCI - printk(KERN_DEBUG "Setup PCI port: port %x, irq %d, type %d\n", + printk(KERN_DEBUG "Setup PCI port: port %lx, irq %d, type %d\n", serial_port.iobase, serial_port.irq, serial_port.iotype); #endif @@ -2648,6 +2778,9 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_SUBVENDOR_ID_CONNECT_TECH, PCI_SUBDEVICE_ID_CONNECT_TECH_PCI_UART_8_485, 0, 0, pbn_b0_8_1843200_200 }, + { PCI_VENDOR_ID_EXAR, PCI_DEVICE_ID_EXAR_XR17C152, + PCI_VENDOR_ID_IBM, PCI_SUBDEVICE_ID_IBM_SATURN_SERIAL_ONE_PORT, + 0, 0, pbn_exar_ibm_saturn }, { PCI_VENDOR_ID_SEALEVEL, PCI_DEVICE_ID_SEALEVEL_U530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, @@ -2763,6 +2896,9 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL, 0, 0, pbn_b0_4_1152000 }, + { PCI_VENDOR_ID_OXSEMI, 0x9505, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_921600 }, /* * The below card is a little controversial since it is the @@ -2785,6 +2921,9 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI952, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_921600 }, + { PCI_VENDOR_ID_OXSEMI, PCI_DEVICE_ID_OXSEMI_16PCI958, + PCI_ANY_ID , PCI_ANY_ID, 0, 0, + pbn_b2_8_1152000 }, /* * Oxford Semiconductor Inc. Tornado PCI express device range. @@ -2988,6 +3127,42 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800L, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_8_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b4_bt_2_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b4_bt_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800I, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b4_bt_8_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400EH, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EH, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800EHB, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_100E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_1_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_400E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_4_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_800E, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_8_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EI, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_200EISI, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_oxsemi_2_4000000 }, { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S_10x_550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, @@ -3092,6 +3267,12 @@ static struct pci_device_id serial_pci_tbl[] = { { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATRO_B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_A, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_QUATTRO_B, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_b0_bt_2_115200 }, { PCI_VENDOR_ID_LAVA, PCI_DEVICE_ID_LAVA_OCTO_A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_4_460800 }, @@ -3138,6 +3319,15 @@ static struct pci_device_id serial_pci_tbl[] = { 0x1208, 0x0004, 0, 0, pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1204, 0x0004, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF2, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, + { PCI_VENDOR_ID_KORENIX, PCI_DEVICE_ID_KORENIX_JETCARDF3, + 0x1208, 0x0004, 0, 0, + pbn_b0_4_921600 }, /* * Dell Remote Access Card 4 - Tim_T_Murphy@Dell.com */ @@ -3555,10 +3745,63 @@ static struct pci_device_id serial_pci_tbl[] = { 0, pbn_b0_8_115200 }, + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7500, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_4_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7420, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_2_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7300, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_1_3906250 }, + + { PCI_VENDOR_ID_ADDIDATA, + PCI_DEVICE_ID_ADDIDATA_APCIe7800, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + pbn_ADDIDATA_PCIe_8_3906250 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, PCI_VENDOR_ID_IBM, 0x0299, 0, 0, pbn_b0_bt_2_115200 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9901, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + /* + * Best Connectivity PCI Multi I/O cards + */ + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x1000, + 0, 0, pbn_b0_1_115200 }, + + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9865, + 0xA000, 0x3004, + 0, 0, pbn_b0_bt_4_115200 }, + /* Intel CE4100 */ + { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CE4100_UART, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + pbn_ce4100_1_115200 }, + + /* * These entries match devices with class COMMUNICATION_SERIAL, * COMMUNICATION_MODEM or COMMUNICATION_MULTISERIAL diff --git a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c index d71dfe3..4822cb5 100644 --- a/drivers/serial/8250_pnp.c +++ b/drivers/serial/8250_pnp.c @@ -328,15 +328,7 @@ static const struct pnp_device_id pnp_dev_table[] = { /* U.S. Robotics 56K Voice INT PnP*/ { "USR9190", 0 }, /* Wacom tablets */ - { "WACF004", 0 }, - { "WACF005", 0 }, - { "WACF006", 0 }, - { "WACF007", 0 }, - { "WACF008", 0 }, - { "WACF009", 0 }, - { "WACF00A", 0 }, - { "WACF00B", 0 }, - { "WACF00C", 0 }, + { "WACFXXX", 0 }, /* Compaq touchscreen */ { "FPI2002", 0 }, /* Fujitsu Stylistic touchscreens */ @@ -354,6 +346,10 @@ static const struct pnp_device_id pnp_dev_table[] = { { "FUJ02E5", 0 }, /* Fujitsu P-series tablet PC device */ { "FUJ02E6", 0 }, + /* Fujitsu Wacom 2FGT Tablet PC device */ + { "FUJ02E7", 0 }, + /* Fujitsu Wacom 1FGT Tablet PC device */ + { "FUJ02E9", 0 }, /* * LG C1 EXPRESS DUAL (C1-PB11A3) touch screen (actually a FUJ02E6 in * disguise) @@ -361,9 +357,9 @@ static const struct pnp_device_id pnp_dev_table[] = { { "LTS0001", 0 }, /* Rockwell's (PORALiNK) 33600 INT PNP */ { "WCI0003", 0 }, - /* Unkown PnP modems */ + /* Unknown PnP modems */ { "PNPCXXX", UNKNOWN_DEV }, - /* More unkown PnP modems */ + /* More unknown PnP modems */ { "PNPDXXX", UNKNOWN_DEV }, { "", 0 } }; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 037c1e0..c1df767 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -258,14 +258,6 @@ config SERIAL_8250_ACORN system, say Y to this option. The driver can handle 1, 2, or 3 port cards. If unsure, say N. -config SERIAL_8250_AU1X00 - bool "Au1x00 serial port support" - depends on SERIAL_8250 != n && SOC_AU1X00 - help - If you have an Au1x00 SOC based board and want to use the serial port, - say Y to this option. The driver can handle up to 4 serial ports, - depending on the SOC. If unsure, say N. - config SERIAL_8250_RM9K bool "Support for MIPS RM9xxx integrated serial port" depends on SERIAL_8250 != n && SERIAL_RM9000 @@ -447,7 +439,7 @@ config SERIAL_CLPS711X_CONSOLE config SERIAL_SAMSUNG tristate "Samsung SoC serial support" - depends on ARM && PLAT_S3C + depends on ARM && PLAT_SAMSUNG select SERIAL_CORE help Support for the on-chip UARTs on the Samsung S3C24XX series CPUs, @@ -455,11 +447,19 @@ config SERIAL_SAMSUNG provide all of these ports, depending on how the serial port pins are configured. +config SERIAL_SAMSUNG_UARTS_4 + bool + depends on ARM && PLAT_SAMSUNG + default y if CPU_S3C2443 + help + Internal node for the common case of 4 Samsung compatible UARTs + config SERIAL_SAMSUNG_UARTS int - depends on ARM && PLAT_S3C + depends on ARM && PLAT_SAMSUNG default 2 if ARCH_S3C2400 - default 4 if ARCH_S3C64XX || CPU_S3C2443 + default 6 if ARCH_S5P6450 + default 4 if SERIAL_SAMSUNG_UARTS_4 default 3 help Select the number of available UART ports for the Samsung S3C @@ -511,12 +511,13 @@ config SERIAL_S3C2412 Serial port support for the Samsung S3C2412 and S3C2413 SoC config SERIAL_S3C2440 - tristate "Samsung S3C2440/S3C2442 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S3C2440 || CPU_S3C2442) + tristate "Samsung S3C2440/S3C2442/S3C2416 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S3C2440 || CPU_S3C2442 || CPU_S3C2416) default y if CPU_S3C2440 default y if CPU_S3C2442 + select SERIAL_SAMSUNG_UARTS_4 if CPU_S3C2416 help - Serial port support for the Samsung S3C2440 and S3C2442 SoC + Serial port support for the Samsung S3C2440, S3C2416 and S3C2442 SoC config SERIAL_S3C24A0 tristate "Samsung S3C24A0 Serial port support" @@ -526,12 +527,22 @@ config SERIAL_S3C24A0 Serial port support for the Samsung S3C24A0 SoC config SERIAL_S3C6400 - tristate "Samsung S3C6400/S3C6410 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S3C600 || CPU_S3C6410) + tristate "Samsung S3C6400/S3C6410/S5P6440/S5P6450/S5PC100 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410 || CPU_S5P6440 || CPU_S5P6450 || CPU_S5PC100) + select SERIAL_SAMSUNG_UARTS_4 + default y + help + Serial port support for the Samsung S3C6400, S3C6410, S5P6440, S5P6450 + and S5PC100 SoCs + +config SERIAL_S5PV210 + tristate "Samsung S5PV210 Serial port support" + depends on SERIAL_SAMSUNG && (CPU_S5PV210 || CPU_S5P6442 || CPU_S5PV310) + select SERIAL_SAMSUNG_UARTS_4 if (CPU_S5PV210 || CPU_S5PV310) default y help - Serial port support for the Samsung S3C6400 and S3C6410 - SoCs + Serial port support for Samsung's S5P Family of SoC's + config SERIAL_MAX3100 tristate "MAX3100 support" @@ -540,6 +551,22 @@ config SERIAL_MAX3100 help MAX3100 chip support +config SERIAL_MAX3107 + tristate "MAX3107 support" + depends on SPI + select SERIAL_CORE + help + MAX3107 chip support + +config SERIAL_MAX3107_AAVA + tristate "MAX3107 AAVA platform support" + depends on X86_MRST && SERIAL_MAX3107 && GPIOLIB + select SERIAL_CORE + help + Support for the MAX3107 chip configuration found on the AAVA + platform. Includes the extra initialisation and GPIO support + neded for this device. + config SERIAL_DZ bool "DECstation DZ serial driver" depends on MACH_DECSTATION && 32BIT @@ -681,6 +708,26 @@ config SERIAL_SA1100_CONSOLE your boot loader (lilo or loadlin) about how to pass options to the kernel at boot time.) +config SERIAL_MRST_MAX3110 + tristate "SPI UART driver for Max3110" + depends on SPI_DW_PCI + select SERIAL_CORE + select SERIAL_CORE_CONSOLE + help + This is the UART protocol driver for the MAX3110 device on + the Intel Moorestown platform. On other systems use the max3100 + driver. + +config SERIAL_MFD_HSU + tristate "Medfield High Speed UART support" + depends on PCI + select SERIAL_CORE + +config SERIAL_MFD_HSU_CONSOLE + boolean "Medfile HSU serial console support" + depends on SERIAL_MFD_HSU=y + select SERIAL_CORE_CONSOLE + config SERIAL_BFIN tristate "Blackfin serial port support" depends on BLACKFIN @@ -729,24 +776,7 @@ config BFIN_UART0_CTSRTS bool "Enable UART0 hardware flow control" depends on SERIAL_BFIN_UART0 help - Enable hardware flow control in the driver. Using GPIO emulate the CTS/RTS - signal. - -config UART0_CTS_PIN - int "UART0 CTS pin" - depends on BFIN_UART0_CTSRTS && !BF548 - default 23 - help - The default pin is GPIO_GP7. - Refer to arch/blackfin/mach-*/include/mach/gpio.h to see the GPIO map. - -config UART0_RTS_PIN - int "UART0 RTS pin" - depends on BFIN_UART0_CTSRTS && !BF548 - default 22 - help - The default pin is GPIO_GP6. - Refer to arch/blackfin/mach-*/include/mach/gpio.h to see the GPIO map. + Enable hardware flow control in the driver. config SERIAL_BFIN_UART1 bool "Enable UART1" @@ -758,22 +788,7 @@ config BFIN_UART1_CTSRTS bool "Enable UART1 hardware flow control" depends on SERIAL_BFIN_UART1 help - Enable hardware flow control in the driver. Using GPIO emulate the CTS/RTS - signal. - -config UART1_CTS_PIN - int "UART1 CTS pin" - depends on BFIN_UART1_CTSRTS && !BF548 - default -1 - help - Refer to arch/blackfin/mach-*/include/mach/gpio.h to see the GPIO map. - -config UART1_RTS_PIN - int "UART1 RTS pin" - depends on BFIN_UART1_CTSRTS && !BF548 - default -1 - help - Refer to arch/blackfin/mach-*/include/mach/gpio.h to see the GPIO map. + Enable hardware flow control in the driver. config SERIAL_BFIN_UART2 bool "Enable UART2" @@ -785,22 +800,7 @@ config BFIN_UART2_CTSRTS bool "Enable UART2 hardware flow control" depends on SERIAL_BFIN_UART2 help - Enable hardware flow control in the driver. Using GPIO emulate the CTS/RTS - signal. - -config UART2_CTS_PIN - int "UART2 CTS pin" - depends on BFIN_UART2_CTSRTS && !BF548 - default -1 - help - Refer to arch/blackfin/mach-*/include/mach/gpio.h to see the GPIO map. - -config UART2_RTS_PIN - int "UART2 RTS pin" - depends on BFIN_UART2_CTSRTS && !BF548 - default -1 - help - Refer to arch/blackfin/mach-*/include/mach/gpio.h to see the GPIO map. + Enable hardware flow control in the driver. config SERIAL_BFIN_UART3 bool "Enable UART3" @@ -812,22 +812,7 @@ config BFIN_UART3_CTSRTS bool "Enable UART3 hardware flow control" depends on SERIAL_BFIN_UART3 help - Enable hardware flow control in the driver. Using GPIO emulate the CTS/RTS - signal. - -config UART3_CTS_PIN - int "UART3 CTS pin" - depends on BFIN_UART3_CTSRTS && !BF548 - default -1 - help - Refer to arch/blackfin/mach-*/include/mach/gpio.h to see the GPIO map. - -config UART3_RTS_PIN - int "UART3 RTS pin" - depends on BFIN_UART3_CTSRTS && !BF548 - default -1 - help - Refer to arch/blackfin/mach-*/include/mach/gpio.h to see the GPIO map. + Enable hardware flow control in the driver. config SERIAL_IMX bool "IMX serial port support" @@ -855,7 +840,7 @@ config SERIAL_IMX_CONSOLE config SERIAL_UARTLITE tristate "Xilinx uartlite serial port support" - depends on PPC32 || MICROBLAZE + depends on PPC32 || MICROBLAZE || MFD_TIMBERDALE select SERIAL_CORE help Say Y here if you want to use the Xilinx uartlite serial controller. @@ -989,7 +974,7 @@ config SERIAL_IP22_ZILOG_CONSOLE config SERIAL_SH_SCI tristate "SuperH SCI(F) serial port support" - depends on SUPERH || H8300 + depends on HAVE_CLK && (SUPERH || H8300 || ARCH_SHMOBILE) select SERIAL_CORE config SERIAL_SH_SCI_NR_UARTS @@ -1002,6 +987,10 @@ config SERIAL_SH_SCI_CONSOLE depends on SERIAL_SH_SCI=y select SERIAL_CORE_CONSOLE +config SERIAL_SH_SCI_DMA + bool "DMA support" + depends on SERIAL_SH_SCI && SH_DMAE && EXPERIMENTAL + config SERIAL_PNX8XXX bool "Enable PNX8XXX SoCs' UART Support" depends on MIPS && (SOC_PNX8550 || SOC_PNX833X) @@ -1079,12 +1068,12 @@ config SERIAL_68360 default y config SERIAL_PMACZILOG - tristate "PowerMac z85c30 ESCC support" - depends on PPC_OF && PPC_PMAC + tristate "Mac or PowerMac z85c30 ESCC support" + depends on (M68K && MAC) || (PPC_OF && PPC_PMAC) select SERIAL_CORE help This driver supports the Zilog z85C30 serial ports found on - PowerMac machines. + (Power)Mac machines. Say Y or M if you want to be able to these serial ports. config SERIAL_PMACZILOG_TTYS @@ -1109,16 +1098,16 @@ config SERIAL_PMACZILOG_TTYS unable to use the 8250 module for PCMCIA or other 16C550-style UARTs. - Say N unless you need the z85c30 ports on your powermac + Say N unless you need the z85c30 ports on your (Power)Mac to appear as /dev/ttySn. config SERIAL_PMACZILOG_CONSOLE - bool "Console on PowerMac z85c30 serial port" + bool "Console on Mac or PowerMac z85c30 serial port" depends on SERIAL_PMACZILOG=y select SERIAL_CORE_CONSOLE help If you would like to be able to use the z85c30 serial port - on your PowerMac as the console, you can do so by answering + on your (Power)Mac as the console, you can do so by answering Y to this option. config SERIAL_LH7A40X @@ -1330,6 +1319,16 @@ config SERIAL_MSM_CONSOLE depends on SERIAL_MSM=y select SERIAL_CORE_CONSOLE +config SERIAL_VT8500 + bool "VIA VT8500 on-chip serial port support" + depends on ARM && ARCH_VT8500 + select SERIAL_CORE + +config SERIAL_VT8500_CONSOLE + bool "VIA VT8500 serial console support" + depends on SERIAL_VT8500=y + select SERIAL_CORE_CONSOLE + config SERIAL_NETX tristate "NetX serial port support" depends on ARM && ARCH_NETX @@ -1351,7 +1350,7 @@ config SERIAL_NETX_CONSOLE config SERIAL_OF_PLATFORM tristate "Serial port on Open Firmware platform bus" - depends on PPC_OF || MICROBLAZE + depends on OF depends on SERIAL_8250 || SERIAL_OF_PLATFORM_NWPSERIAL help If you have a PowerPC based system that has serial ports @@ -1359,6 +1358,33 @@ config SERIAL_OF_PLATFORM Currently, only 8250 compatible ports are supported, but others can easily be added. +config SERIAL_OMAP + tristate "OMAP serial port support" + depends on ARCH_OMAP2 || ARCH_OMAP3 || ARCH_OMAP4 + select SERIAL_CORE + help + If you have a machine based on an Texas Instruments OMAP CPU you + can enable its onboard serial ports by enabling this option. + + By enabling this option you take advantage of dma feature available + with the omap-serial driver. DMA support can be enabled from platform + data. + +config SERIAL_OMAP_CONSOLE + bool "Console on OMAP serial port" + depends on SERIAL_OMAP + select SERIAL_CORE_CONSOLE + help + Select this option if you would like to use omap serial port as + 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=ttyOx". (Try "man bootparam" or see the documentation of + your boot loader about how to pass options to the kernel at + boot time.) + config SERIAL_OF_PLATFORM_NWPSERIAL tristate "NWP serial port driver" depends on PPC_OF && PPC_DCR @@ -1402,8 +1428,8 @@ config SERIAL_SC26XX_CONSOLE Support for Console on SC2681/SC2692 serial ports. config SERIAL_BFIN_SPORT - tristate "Blackfin SPORT emulate UART (EXPERIMENTAL)" - depends on BLACKFIN && EXPERIMENTAL + tristate "Blackfin SPORT emulate UART" + depends on BLACKFIN select SERIAL_CORE help Enable SPORT emulate UART on Blackfin series. @@ -1411,44 +1437,162 @@ config SERIAL_BFIN_SPORT To compile this driver as a module, choose M here: the module will be called bfin_sport_uart. -choice - prompt "Baud rate for Blackfin SPORT UART" - depends on SERIAL_BFIN_SPORT - default SERIAL_SPORT_BAUD_RATE_57600 +config SERIAL_BFIN_SPORT_CONSOLE + bool "Console on Blackfin sport emulated uart" + depends on SERIAL_BFIN_SPORT=y + select SERIAL_CORE_CONSOLE + +config SERIAL_BFIN_SPORT0_UART + bool "Enable UART over SPORT0" + depends on SERIAL_BFIN_SPORT && !(BF542 || BF544) help - Choose a baud rate for the SPORT UART, other uart settings are - 8 bit, 1 stop bit, no parity, no flow control. + Enable UART over SPORT0 -config SERIAL_SPORT_BAUD_RATE_115200 - bool "115200" +config SERIAL_BFIN_SPORT0_UART_CTSRTS + bool "Enable UART over SPORT0 hardware flow control" + depends on SERIAL_BFIN_SPORT0_UART + help + Enable hardware flow control in the driver. -config SERIAL_SPORT_BAUD_RATE_57600 - bool "57600" +config SERIAL_BFIN_SPORT1_UART + bool "Enable UART over SPORT1" + depends on SERIAL_BFIN_SPORT + help + Enable UART over SPORT1 -config SERIAL_SPORT_BAUD_RATE_38400 - bool "38400" +config SERIAL_BFIN_SPORT1_UART_CTSRTS + bool "Enable UART over SPORT1 hardware flow control" + depends on SERIAL_BFIN_SPORT1_UART + help + Enable hardware flow control in the driver. -config SERIAL_SPORT_BAUD_RATE_19200 - bool "19200" +config SERIAL_BFIN_SPORT2_UART + bool "Enable UART over SPORT2" + depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) + help + Enable UART over SPORT2 -config SERIAL_SPORT_BAUD_RATE_9600 - bool "9600" -endchoice +config SERIAL_BFIN_SPORT2_UART_CTSRTS + bool "Enable UART over SPORT2 hardware flow control" + depends on SERIAL_BFIN_SPORT2_UART + help + Enable hardware flow control in the driver. -config SPORT_BAUD_RATE - int - depends on SERIAL_BFIN_SPORT - default 115200 if (SERIAL_SPORT_BAUD_RATE_115200) - default 57600 if (SERIAL_SPORT_BAUD_RATE_57600) - default 38400 if (SERIAL_SPORT_BAUD_RATE_38400) - default 19200 if (SERIAL_SPORT_BAUD_RATE_19200) - default 9600 if (SERIAL_SPORT_BAUD_RATE_9600) +config SERIAL_BFIN_SPORT3_UART + bool "Enable UART over SPORT3" + depends on SERIAL_BFIN_SPORT && (BF54x || BF538 || BF539) + help + Enable UART over SPORT3 + +config SERIAL_BFIN_SPORT3_UART_CTSRTS + bool "Enable UART over SPORT3 hardware flow control" + depends on SERIAL_BFIN_SPORT3_UART + help + Enable hardware flow control in the driver. config SERIAL_TIMBERDALE tristate "Support for timberdale UART" - depends on MFD_TIMBERDALE select SERIAL_CORE ---help--- Add support for UART controller on timberdale. +config SERIAL_BCM63XX + tristate "bcm63xx serial port support" + select SERIAL_CORE + depends on BCM63XX + help + If you have a bcm63xx CPU, you can enable its onboard + serial port by enabling this options. + + To compile this driver as a module, choose M here: the + module will be called bcm963xx_uart. + +config SERIAL_BCM63XX_CONSOLE + bool "Console on bcm63xx serial port" + depends on SERIAL_BCM63XX=y + select SERIAL_CORE_CONSOLE + help + If you have enabled the serial port on the bcm63xx CPU + you can make it the console by answering Y to this option. + +config SERIAL_GRLIB_GAISLER_APBUART + tristate "GRLIB APBUART serial support" + depends on OF + ---help--- + Add support for the GRLIB APBUART serial port. + +config SERIAL_GRLIB_GAISLER_APBUART_CONSOLE + bool "Console on GRLIB APBUART serial port" + depends on SERIAL_GRLIB_GAISLER_APBUART=y + select SERIAL_CORE_CONSOLE + help + Support for running a console on the GRLIB APBUART + +config SERIAL_ALTERA_JTAGUART + tristate "Altera JTAG UART support" + select SERIAL_CORE + help + This driver supports the Altera JTAG UART port. + +config SERIAL_ALTERA_JTAGUART_CONSOLE + bool "Altera JTAG UART console support" + depends on SERIAL_ALTERA_JTAGUART=y + select SERIAL_CORE_CONSOLE + help + Enable a Altera JTAG UART port to be the system console. + +config SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS + bool "Bypass output when no connection" + depends on SERIAL_ALTERA_JTAGUART_CONSOLE + select SERIAL_CORE_CONSOLE + help + Bypass console output and keep going even if there is no + JTAG terminal connection with the host. + +config SERIAL_ALTERA_UART + tristate "Altera UART support" + select SERIAL_CORE + help + This driver supports the Altera softcore UART port. + +config SERIAL_ALTERA_UART_MAXPORTS + int "Maximum number of Altera UART ports" + depends on SERIAL_ALTERA_UART + default 4 + help + This setting lets you define the maximum number of the Altera + UART ports. The usual default varies from board to board, and + this setting is a way of catering for that. + +config SERIAL_ALTERA_UART_BAUDRATE + int "Default baudrate for Altera UART ports" + depends on SERIAL_ALTERA_UART + default 115200 + help + This setting lets you define what the default baudrate is for the + Altera UART ports. The usual default varies from board to board, + and this setting is a way of catering for that. + +config SERIAL_ALTERA_UART_CONSOLE + bool "Altera UART console support" + depends on SERIAL_ALTERA_UART=y + select SERIAL_CORE_CONSOLE + help + Enable a Altera UART port to be the system console. + +config SERIAL_IFX6X60 + tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)" + depends on GPIOLIB && SPI && EXPERIMENTAL + help + Support for the IFX6x60 modem devices on Intel MID platforms. + +config SERIAL_PCH_UART + tristate "Intel EG20T PCH UART" + depends on PCI && DMADEVICES + select SERIAL_CORE + select PCH_DMA + help + This driver is for PCH(Platform controller Hub) UART of Intel EG20T + which is an IOH(Input/Output Hub) for x86 embedded processor. + Enabling PCH_DMA, this PCH UART works as DMA mode. endmenu diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index d5a2998..8ea92e9 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o obj-$(CONFIG_SERIAL_PXA) += pxa.o obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o obj-$(CONFIG_SERIAL_SA1100) += sa1100.o +obj-$(CONFIG_SERIAL_BCM63XX) += bcm63xx_uart.o obj-$(CONFIG_SERIAL_BFIN) += bfin_5xx.o obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o @@ -43,7 +44,10 @@ obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o +obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o obj-$(CONFIG_SERIAL_MAX3100) += max3100.o +obj-$(CONFIG_SERIAL_MAX3107) += max3107.o +obj-$(CONFIG_SERIAL_MAX3107_AAVA) += max3107-aava.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o obj-$(CONFIG_SERIAL_MUX) += mux.o obj-$(CONFIG_SERIAL_68328) += 68328serial.o @@ -76,6 +80,15 @@ obj-$(CONFIG_SERIAL_NETX) += netx-serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o +obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o obj-$(CONFIG_SERIAL_TIMBERDALE) += timbuart.o +obj-$(CONFIG_SERIAL_GRLIB_GAISLER_APBUART) += apbuart.o +obj-$(CONFIG_SERIAL_ALTERA_JTAGUART) += altera_jtaguart.o +obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o +obj-$(CONFIG_SERIAL_VT8500) += vt8500_serial.o +obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o +obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o +obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o +obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o diff --git a/drivers/serial/altera_jtaguart.c b/drivers/serial/altera_jtaguart.c new file mode 100644 index 0000000..f9b49b5 --- /dev/null +++ b/drivers/serial/altera_jtaguart.c @@ -0,0 +1,504 @@ +/* + * altera_jtaguart.c -- Altera JTAG UART driver + * + * Based on mcf.c -- Freescale ColdFire UART driver + * + * (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com> + * (C) Copyright 2008, Thomas Chou <thomas@wytron.com.tw> + * (C) Copyright 2010, Tobias Klauser <tklauser@distanz.ch> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/altera_jtaguart.h> + +#define DRV_NAME "altera_jtaguart" + +/* + * Altera JTAG UART register definitions according to the Altera JTAG UART + * datasheet: http://www.altera.com/literature/hb/nios2/n2cpu_nii51009.pdf + */ + +#define ALTERA_JTAGUART_SIZE 8 + +#define ALTERA_JTAGUART_DATA_REG 0 + +#define ALTERA_JTAGUART_DATA_DATA_MSK 0x000000FF +#define ALTERA_JTAGUART_DATA_RVALID_MSK 0x00008000 +#define ALTERA_JTAGUART_DATA_RAVAIL_MSK 0xFFFF0000 +#define ALTERA_JTAGUART_DATA_RAVAIL_OFF 16 + +#define ALTERA_JTAGUART_CONTROL_REG 4 + +#define ALTERA_JTAGUART_CONTROL_RE_MSK 0x00000001 +#define ALTERA_JTAGUART_CONTROL_WE_MSK 0x00000002 +#define ALTERA_JTAGUART_CONTROL_RI_MSK 0x00000100 +#define ALTERA_JTAGUART_CONTROL_RI_OFF 8 +#define ALTERA_JTAGUART_CONTROL_WI_MSK 0x00000200 +#define ALTERA_JTAGUART_CONTROL_AC_MSK 0x00000400 +#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK 0xFFFF0000 +#define ALTERA_JTAGUART_CONTROL_WSPACE_OFF 16 + +/* + * Local per-uart structure. + */ +struct altera_jtaguart { + struct uart_port port; + unsigned int sigs; /* Local copy of line sigs */ + unsigned long imr; /* Local IMR mirror */ +}; + +static unsigned int altera_jtaguart_tx_empty(struct uart_port *port) +{ + return (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) ? TIOCSER_TEMT : 0; +} + +static unsigned int altera_jtaguart_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +static void altera_jtaguart_set_mctrl(struct uart_port *port, unsigned int sigs) +{ +} + +static void altera_jtaguart_start_tx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr |= ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_stop_tx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_stop_rx(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + + pp->imr &= ~ALTERA_JTAGUART_CONTROL_RE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static void altera_jtaguart_break_ctl(struct uart_port *port, int break_state) +{ +} + +static void altera_jtaguart_enable_ms(struct uart_port *port) +{ +} + +static void altera_jtaguart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + /* Just copy the old termios settings back */ + if (old) + tty_termios_copy_hw(termios, old); +} + +static void altera_jtaguart_rx_chars(struct altera_jtaguart *pp) +{ + struct uart_port *port = &pp->port; + unsigned char ch, flag; + unsigned long status; + + while ((status = readl(port->membase + ALTERA_JTAGUART_DATA_REG)) & + ALTERA_JTAGUART_DATA_RVALID_MSK) { + ch = status & ALTERA_JTAGUART_DATA_DATA_MSK; + flag = TTY_NORMAL; + port->icount.rx++; + + if (uart_handle_sysrq_char(port, ch)) + continue; + uart_insert_char(port, 0, 0, ch, flag); + } + + tty_flip_buffer_push(port->state->port.tty); +} + +static void altera_jtaguart_tx_chars(struct altera_jtaguart *pp) +{ + struct uart_port *port = &pp->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned int pending, count; + + if (port->x_char) { + /* Send special char - probably flow control */ + writel(port->x_char, port->membase + ALTERA_JTAGUART_DATA_REG); + port->x_char = 0; + port->icount.tx++; + return; + } + + pending = uart_circ_chars_pending(xmit); + if (pending > 0) { + count = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) >> + ALTERA_JTAGUART_CONTROL_WSPACE_OFF; + if (count > pending) + count = pending; + if (count > 0) { + pending -= count; + while (count--) { + writel(xmit->buf[xmit->tail], + port->membase + ALTERA_JTAGUART_DATA_REG); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + } + if (pending < WAKEUP_CHARS) + uart_write_wakeup(port); + } + } + + if (pending == 0) { + pp->imr &= ~ALTERA_JTAGUART_CONTROL_WE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + } +} + +static irqreturn_t altera_jtaguart_interrupt(int irq, void *data) +{ + struct uart_port *port = data; + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned int isr; + + isr = (readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) >> + ALTERA_JTAGUART_CONTROL_RI_OFF) & pp->imr; + + spin_lock(&port->lock); + + if (isr & ALTERA_JTAGUART_CONTROL_RE_MSK) + altera_jtaguart_rx_chars(pp); + if (isr & ALTERA_JTAGUART_CONTROL_WE_MSK) + altera_jtaguart_tx_chars(pp); + + spin_unlock(&port->lock); + + return IRQ_RETVAL(isr); +} + +static void altera_jtaguart_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_ALTERA_JTAGUART; + + /* Clear mask, so no surprise interrupts. */ + writel(0, port->membase + ALTERA_JTAGUART_CONTROL_REG); +} + +static int altera_jtaguart_startup(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned long flags; + int ret; + + ret = request_irq(port->irq, altera_jtaguart_interrupt, IRQF_DISABLED, + DRV_NAME, port); + if (ret) { + pr_err(DRV_NAME ": unable to attach Altera JTAG UART %d " + "interrupt vector=%d\n", port->line, port->irq); + return ret; + } + + spin_lock_irqsave(&port->lock, flags); + + /* Enable RX interrupts now */ + pp->imr = ALTERA_JTAGUART_CONTROL_RE_MSK; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void altera_jtaguart_shutdown(struct uart_port *port) +{ + struct altera_jtaguart *pp = + container_of(port, struct altera_jtaguart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Disable all interrupts now */ + pp->imr = 0; + writel(pp->imr, port->membase + ALTERA_JTAGUART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + free_irq(port->irq, port); +} + +static const char *altera_jtaguart_type(struct uart_port *port) +{ + return (port->type == PORT_ALTERA_JTAGUART) ? "Altera JTAG UART" : NULL; +} + +static int altera_jtaguart_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +static void altera_jtaguart_release_port(struct uart_port *port) +{ + /* Nothing to release... */ +} + +static int altera_jtaguart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type != PORT_UNKNOWN && ser->type != PORT_ALTERA_JTAGUART) + return -EINVAL; + return 0; +} + +/* + * Define the basic serial functions we support. + */ +static struct uart_ops altera_jtaguart_ops = { + .tx_empty = altera_jtaguart_tx_empty, + .get_mctrl = altera_jtaguart_get_mctrl, + .set_mctrl = altera_jtaguart_set_mctrl, + .start_tx = altera_jtaguart_start_tx, + .stop_tx = altera_jtaguart_stop_tx, + .stop_rx = altera_jtaguart_stop_rx, + .enable_ms = altera_jtaguart_enable_ms, + .break_ctl = altera_jtaguart_break_ctl, + .startup = altera_jtaguart_startup, + .shutdown = altera_jtaguart_shutdown, + .set_termios = altera_jtaguart_set_termios, + .type = altera_jtaguart_type, + .request_port = altera_jtaguart_request_port, + .release_port = altera_jtaguart_release_port, + .config_port = altera_jtaguart_config_port, + .verify_port = altera_jtaguart_verify_port, +}; + +#define ALTERA_JTAGUART_MAXPORTS 1 +static struct altera_jtaguart altera_jtaguart_ports[ALTERA_JTAGUART_MAXPORTS]; + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) + +int __init early_altera_jtaguart_setup(struct altera_jtaguart_platform_uart + *platp) +{ + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_jtaguart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_JTAGUART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->flags = ASYNC_BOOT_AUTOCONF; + port->ops = &altera_jtaguart_ops; + } + + return 0; +} + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) +static void altera_jtaguart_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(altera_jtaguart_ports + co->index)->port; + unsigned long status; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + while (((status = readl(port->membase + ALTERA_JTAGUART_CONTROL_REG)) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { + if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) { + spin_unlock_irqrestore(&port->lock, flags); + return; /* no connection activity */ + } + spin_unlock_irqrestore(&port->lock, flags); + cpu_relax(); + spin_lock_irqsave(&port->lock, flags); + } + writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); + spin_unlock_irqrestore(&port->lock, flags); +} +#else +static void altera_jtaguart_console_putc(struct console *co, const char c) +{ + struct uart_port *port = &(altera_jtaguart_ports + co->index)->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + while ((readl(port->membase + ALTERA_JTAGUART_CONTROL_REG) & + ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { + spin_unlock_irqrestore(&port->lock, flags); + cpu_relax(); + spin_lock_irqsave(&port->lock, flags); + } + writel(c, port->membase + ALTERA_JTAGUART_DATA_REG); + spin_unlock_irqrestore(&port->lock, flags); +} +#endif + +static void altera_jtaguart_console_write(struct console *co, const char *s, + unsigned int count) +{ + for (; count; count--, s++) { + altera_jtaguart_console_putc(co, *s); + if (*s == '\n') + altera_jtaguart_console_putc(co, '\r'); + } +} + +static int __init altera_jtaguart_console_setup(struct console *co, + char *options) +{ + struct uart_port *port; + + if (co->index < 0 || co->index >= ALTERA_JTAGUART_MAXPORTS) + return -EINVAL; + port = &altera_jtaguart_ports[co->index].port; + if (port->membase == 0) + return -ENODEV; + return 0; +} + +static struct uart_driver altera_jtaguart_driver; + +static struct console altera_jtaguart_console = { + .name = "ttyJ", + .write = altera_jtaguart_console_write, + .device = uart_console_device, + .setup = altera_jtaguart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &altera_jtaguart_driver, +}; + +static int __init altera_jtaguart_console_init(void) +{ + register_console(&altera_jtaguart_console); + return 0; +} + +console_initcall(altera_jtaguart_console_init); + +#define ALTERA_JTAGUART_CONSOLE (&altera_jtaguart_console) + +#else + +#define ALTERA_JTAGUART_CONSOLE NULL + +#endif /* CONFIG_ALTERA_JTAGUART_CONSOLE */ + +static struct uart_driver altera_jtaguart_driver = { + .owner = THIS_MODULE, + .driver_name = "altera_jtaguart", + .dev_name = "ttyJ", + .major = ALTERA_JTAGUART_MAJOR, + .minor = ALTERA_JTAGUART_MINOR, + .nr = ALTERA_JTAGUART_MAXPORTS, + .cons = ALTERA_JTAGUART_CONSOLE, +}; + +static int __devinit altera_jtaguart_probe(struct platform_device *pdev) +{ + struct altera_jtaguart_platform_uart *platp = pdev->dev.platform_data; + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_jtaguart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_JTAGUART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_JTAGUART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->ops = &altera_jtaguart_ops; + port->flags = ASYNC_BOOT_AUTOCONF; + + uart_add_one_port(&altera_jtaguart_driver, port); + } + + return 0; +} + +static int __devexit altera_jtaguart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + int i; + + for (i = 0; i < ALTERA_JTAGUART_MAXPORTS; i++) { + port = &altera_jtaguart_ports[i].port; + if (port) + uart_remove_one_port(&altera_jtaguart_driver, port); + } + + return 0; +} + +static struct platform_driver altera_jtaguart_platform_driver = { + .probe = altera_jtaguart_probe, + .remove = __devexit_p(altera_jtaguart_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init altera_jtaguart_init(void) +{ + int rc; + + rc = uart_register_driver(&altera_jtaguart_driver); + if (rc) + return rc; + rc = platform_driver_register(&altera_jtaguart_platform_driver); + if (rc) { + uart_unregister_driver(&altera_jtaguart_driver); + return rc; + } + return 0; +} + +static void __exit altera_jtaguart_exit(void) +{ + platform_driver_unregister(&altera_jtaguart_platform_driver); + uart_unregister_driver(&altera_jtaguart_driver); +} + +module_init(altera_jtaguart_init); +module_exit(altera_jtaguart_exit); + +MODULE_DESCRIPTION("Altera JTAG UART driver"); +MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/serial/altera_uart.c b/drivers/serial/altera_uart.c new file mode 100644 index 0000000..7212162 --- /dev/null +++ b/drivers/serial/altera_uart.c @@ -0,0 +1,608 @@ +/* + * altera_uart.c -- Altera UART driver + * + * Based on mcf.c -- Freescale ColdFire UART driver + * + * (C) Copyright 2003-2007, Greg Ungerer <gerg@snapgear.com> + * (C) Copyright 2008, Thomas Chou <thomas@wytron.com.tw> + * (C) Copyright 2010, Tobias Klauser <tklauser@distanz.ch> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/altera_uart.h> + +#define DRV_NAME "altera_uart" +#define SERIAL_ALTERA_MAJOR 204 +#define SERIAL_ALTERA_MINOR 213 + +/* + * Altera UART register definitions according to the Nios UART datasheet: + * http://www.altera.com/literature/ds/ds_nios_uart.pdf + */ + +#define ALTERA_UART_SIZE 32 + +#define ALTERA_UART_RXDATA_REG 0 +#define ALTERA_UART_TXDATA_REG 4 +#define ALTERA_UART_STATUS_REG 8 +#define ALTERA_UART_CONTROL_REG 12 +#define ALTERA_UART_DIVISOR_REG 16 +#define ALTERA_UART_EOP_REG 20 + +#define ALTERA_UART_STATUS_PE_MSK 0x0001 /* parity error */ +#define ALTERA_UART_STATUS_FE_MSK 0x0002 /* framing error */ +#define ALTERA_UART_STATUS_BRK_MSK 0x0004 /* break */ +#define ALTERA_UART_STATUS_ROE_MSK 0x0008 /* RX overrun error */ +#define ALTERA_UART_STATUS_TOE_MSK 0x0010 /* TX overrun error */ +#define ALTERA_UART_STATUS_TMT_MSK 0x0020 /* TX shift register state */ +#define ALTERA_UART_STATUS_TRDY_MSK 0x0040 /* TX ready */ +#define ALTERA_UART_STATUS_RRDY_MSK 0x0080 /* RX ready */ +#define ALTERA_UART_STATUS_E_MSK 0x0100 /* exception condition */ +#define ALTERA_UART_STATUS_DCTS_MSK 0x0400 /* CTS logic-level change */ +#define ALTERA_UART_STATUS_CTS_MSK 0x0800 /* CTS logic state */ +#define ALTERA_UART_STATUS_EOP_MSK 0x1000 /* EOP written/read */ + + /* Enable interrupt on... */ +#define ALTERA_UART_CONTROL_PE_MSK 0x0001 /* ...parity error */ +#define ALTERA_UART_CONTROL_FE_MSK 0x0002 /* ...framing error */ +#define ALTERA_UART_CONTROL_BRK_MSK 0x0004 /* ...break */ +#define ALTERA_UART_CONTROL_ROE_MSK 0x0008 /* ...RX overrun */ +#define ALTERA_UART_CONTROL_TOE_MSK 0x0010 /* ...TX overrun */ +#define ALTERA_UART_CONTROL_TMT_MSK 0x0020 /* ...TX shift register empty */ +#define ALTERA_UART_CONTROL_TRDY_MSK 0x0040 /* ...TX ready */ +#define ALTERA_UART_CONTROL_RRDY_MSK 0x0080 /* ...RX ready */ +#define ALTERA_UART_CONTROL_E_MSK 0x0100 /* ...exception*/ + +#define ALTERA_UART_CONTROL_TRBK_MSK 0x0200 /* TX break */ +#define ALTERA_UART_CONTROL_DCTS_MSK 0x0400 /* Interrupt on CTS change */ +#define ALTERA_UART_CONTROL_RTS_MSK 0x0800 /* RTS signal */ +#define ALTERA_UART_CONTROL_EOP_MSK 0x1000 /* Interrupt on EOP */ + +/* + * Local per-uart structure. + */ +struct altera_uart { + struct uart_port port; + struct timer_list tmr; + unsigned int sigs; /* Local copy of line sigs */ + unsigned short imr; /* Local IMR mirror */ +}; + +static u32 altera_uart_readl(struct uart_port *port, int reg) +{ + struct altera_uart_platform_uart *platp = port->private_data; + + return readl(port->membase + (reg << platp->bus_shift)); +} + +static void altera_uart_writel(struct uart_port *port, u32 dat, int reg) +{ + struct altera_uart_platform_uart *platp = port->private_data; + + writel(dat, port->membase + (reg << platp->bus_shift)); +} + +static unsigned int altera_uart_tx_empty(struct uart_port *port) +{ + return (altera_uart_readl(port, ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TMT_MSK) ? TIOCSER_TEMT : 0; +} + +static unsigned int altera_uart_get_mctrl(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned int sigs; + + sigs = (altera_uart_readl(port, ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_CTS_MSK) ? TIOCM_CTS : 0; + sigs |= (pp->sigs & TIOCM_RTS); + + return sigs; +} + +static void altera_uart_set_mctrl(struct uart_port *port, unsigned int sigs) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + + pp->sigs = sigs; + if (sigs & TIOCM_RTS) + pp->imr |= ALTERA_UART_CONTROL_RTS_MSK; + else + pp->imr &= ~ALTERA_UART_CONTROL_RTS_MSK; + altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); +} + +static void altera_uart_start_tx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + + pp->imr |= ALTERA_UART_CONTROL_TRDY_MSK; + altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); +} + +static void altera_uart_stop_tx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + + pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; + altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); +} + +static void altera_uart_stop_rx(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + + pp->imr &= ~ALTERA_UART_CONTROL_RRDY_MSK; + altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); +} + +static void altera_uart_break_ctl(struct uart_port *port, int break_state) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (break_state == -1) + pp->imr |= ALTERA_UART_CONTROL_TRBK_MSK; + else + pp->imr &= ~ALTERA_UART_CONTROL_TRBK_MSK; + altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_enable_ms(struct uart_port *port) +{ +} + +static void altera_uart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, baudclk; + + baud = uart_get_baud_rate(port, termios, old, 0, 4000000); + baudclk = port->uartclk / baud; + + if (old) + tty_termios_copy_hw(termios, old); + tty_termios_encode_baud_rate(termios, baud, baud); + + spin_lock_irqsave(&port->lock, flags); + uart_update_timeout(port, termios->c_cflag, baud); + altera_uart_writel(port, baudclk, ALTERA_UART_DIVISOR_REG); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void altera_uart_rx_chars(struct altera_uart *pp) +{ + struct uart_port *port = &pp->port; + unsigned char ch, flag; + unsigned short status; + + while ((status = altera_uart_readl(port, ALTERA_UART_STATUS_REG)) & + ALTERA_UART_STATUS_RRDY_MSK) { + ch = altera_uart_readl(port, ALTERA_UART_RXDATA_REG); + flag = TTY_NORMAL; + port->icount.rx++; + + if (status & ALTERA_UART_STATUS_E_MSK) { + altera_uart_writel(port, status, + ALTERA_UART_STATUS_REG); + + if (status & ALTERA_UART_STATUS_BRK_MSK) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (status & ALTERA_UART_STATUS_PE_MSK) { + port->icount.parity++; + } else if (status & ALTERA_UART_STATUS_ROE_MSK) { + port->icount.overrun++; + } else if (status & ALTERA_UART_STATUS_FE_MSK) { + port->icount.frame++; + } + + status &= port->read_status_mask; + + if (status & ALTERA_UART_STATUS_BRK_MSK) + flag = TTY_BREAK; + else if (status & ALTERA_UART_STATUS_PE_MSK) + flag = TTY_PARITY; + else if (status & ALTERA_UART_STATUS_FE_MSK) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + continue; + uart_insert_char(port, status, ALTERA_UART_STATUS_ROE_MSK, ch, + flag); + } + + tty_flip_buffer_push(port->state->port.tty); +} + +static void altera_uart_tx_chars(struct altera_uart *pp) +{ + struct uart_port *port = &pp->port; + struct circ_buf *xmit = &port->state->xmit; + + if (port->x_char) { + /* Send special char - probably flow control */ + altera_uart_writel(port, port->x_char, ALTERA_UART_TXDATA_REG); + port->x_char = 0; + port->icount.tx++; + return; + } + + while (altera_uart_readl(port, ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK) { + if (xmit->head == xmit->tail) + break; + altera_uart_writel(port, xmit->buf[xmit->tail], + ALTERA_UART_TXDATA_REG); + 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 (xmit->head == xmit->tail) { + pp->imr &= ~ALTERA_UART_CONTROL_TRDY_MSK; + altera_uart_writel(port, pp->imr, ALTERA_UART_CONTROL_REG); + } +} + +static irqreturn_t altera_uart_interrupt(int irq, void *data) +{ + struct uart_port *port = data; + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned int isr; + + isr = altera_uart_readl(port, ALTERA_UART_STATUS_REG) & pp->imr; + + spin_lock(&port->lock); + if (isr & ALTERA_UART_STATUS_RRDY_MSK) + altera_uart_rx_chars(pp); + if (isr & ALTERA_UART_STATUS_TRDY_MSK) + altera_uart_tx_chars(pp); + spin_unlock(&port->lock); + + return IRQ_RETVAL(isr); +} + +static void altera_uart_timer(unsigned long data) +{ + struct uart_port *port = (void *)data; + struct altera_uart *pp = container_of(port, struct altera_uart, port); + + altera_uart_interrupt(0, port); + mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port)); +} + +static void altera_uart_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_ALTERA_UART; + + /* Clear mask, so no surprise interrupts. */ + altera_uart_writel(port, 0, ALTERA_UART_CONTROL_REG); + /* Clear status register */ + altera_uart_writel(port, 0, ALTERA_UART_STATUS_REG); +} + +static int altera_uart_startup(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + int ret; + + if (!port->irq) { + setup_timer(&pp->tmr, altera_uart_timer, (unsigned long)port); + mod_timer(&pp->tmr, jiffies + uart_poll_timeout(port)); + return 0; + } + + ret = request_irq(port->irq, altera_uart_interrupt, IRQF_DISABLED, + DRV_NAME, port); + if (ret) { + pr_err(DRV_NAME ": unable to attach Altera UART %d " + "interrupt vector=%d\n", port->line, port->irq); + return ret; + } + + spin_lock_irqsave(&port->lock, flags); + + /* Enable RX interrupts now */ + pp->imr = ALTERA_UART_CONTROL_RRDY_MSK; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + return 0; +} + +static void altera_uart_shutdown(struct uart_port *port) +{ + struct altera_uart *pp = container_of(port, struct altera_uart, port); + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* Disable all interrupts now */ + pp->imr = 0; + writel(pp->imr, port->membase + ALTERA_UART_CONTROL_REG); + + spin_unlock_irqrestore(&port->lock, flags); + + if (port->irq) + free_irq(port->irq, port); + else + del_timer_sync(&pp->tmr); +} + +static const char *altera_uart_type(struct uart_port *port) +{ + return (port->type == PORT_ALTERA_UART) ? "Altera UART" : NULL; +} + +static int altera_uart_request_port(struct uart_port *port) +{ + /* UARTs always present */ + return 0; +} + +static void altera_uart_release_port(struct uart_port *port) +{ + /* Nothing to release... */ +} + +static int altera_uart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_ALTERA_UART)) + return -EINVAL; + return 0; +} + +/* + * Define the basic serial functions we support. + */ +static struct uart_ops altera_uart_ops = { + .tx_empty = altera_uart_tx_empty, + .get_mctrl = altera_uart_get_mctrl, + .set_mctrl = altera_uart_set_mctrl, + .start_tx = altera_uart_start_tx, + .stop_tx = altera_uart_stop_tx, + .stop_rx = altera_uart_stop_rx, + .enable_ms = altera_uart_enable_ms, + .break_ctl = altera_uart_break_ctl, + .startup = altera_uart_startup, + .shutdown = altera_uart_shutdown, + .set_termios = altera_uart_set_termios, + .type = altera_uart_type, + .request_port = altera_uart_request_port, + .release_port = altera_uart_release_port, + .config_port = altera_uart_config_port, + .verify_port = altera_uart_verify_port, +}; + +static struct altera_uart altera_uart_ports[CONFIG_SERIAL_ALTERA_UART_MAXPORTS]; + +#if defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) + +int __init early_altera_uart_setup(struct altera_uart_platform_uart *platp) +{ + struct uart_port *port; + int i; + + for (i = 0; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS && platp[i].mapbase; i++) { + port = &altera_uart_ports[i].port; + + port->line = i; + port->type = PORT_ALTERA_UART; + port->mapbase = platp[i].mapbase; + port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE); + port->iotype = SERIAL_IO_MEM; + port->irq = platp[i].irq; + port->uartclk = platp[i].uartclk; + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &altera_uart_ops; + port->private_data = platp; + } + + return 0; +} + +static void altera_uart_console_putc(struct uart_port *port, const char c) +{ + while (!(altera_uart_readl(port, ALTERA_UART_STATUS_REG) & + ALTERA_UART_STATUS_TRDY_MSK)) + cpu_relax(); + + writel(c, port->membase + ALTERA_UART_TXDATA_REG); +} + +static void altera_uart_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port = &(altera_uart_ports + co->index)->port; + + for (; count; count--, s++) { + altera_uart_console_putc(port, *s); + if (*s == '\n') + altera_uart_console_putc(port, '\r'); + } +} + +static int __init altera_uart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = CONFIG_SERIAL_ALTERA_UART_BAUDRATE; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS) + return -EINVAL; + port = &altera_uart_ports[co->index].port; + if (!port->membase) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver altera_uart_driver; + +static struct console altera_uart_console = { + .name = "ttyAL", + .write = altera_uart_console_write, + .device = uart_console_device, + .setup = altera_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &altera_uart_driver, +}; + +static int __init altera_uart_console_init(void) +{ + register_console(&altera_uart_console); + return 0; +} + +console_initcall(altera_uart_console_init); + +#define ALTERA_UART_CONSOLE (&altera_uart_console) + +#else + +#define ALTERA_UART_CONSOLE NULL + +#endif /* CONFIG_ALTERA_UART_CONSOLE */ + +/* + * Define the altera_uart UART driver structure. + */ +static struct uart_driver altera_uart_driver = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = "ttyAL", + .major = SERIAL_ALTERA_MAJOR, + .minor = SERIAL_ALTERA_MINOR, + .nr = CONFIG_SERIAL_ALTERA_UART_MAXPORTS, + .cons = ALTERA_UART_CONSOLE, +}; + +static int __devinit altera_uart_probe(struct platform_device *pdev) +{ + struct altera_uart_platform_uart *platp = pdev->dev.platform_data; + struct uart_port *port; + struct resource *res_mem; + struct resource *res_irq; + int i = pdev->id; + + /* -1 emphasizes that the platform must have one port, no .N suffix */ + if (i == -1) + i = 0; + + if (i >= CONFIG_SERIAL_ALTERA_UART_MAXPORTS) + return -EINVAL; + + port = &altera_uart_ports[i].port; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res_mem) + port->mapbase = res_mem->start; + else if (platp->mapbase) + port->mapbase = platp->mapbase; + else + return -EINVAL; + + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res_irq) + port->irq = res_irq->start; + else if (platp->irq) + port->irq = platp->irq; + + port->membase = ioremap(port->mapbase, ALTERA_UART_SIZE); + if (!port->membase) + return -ENOMEM; + + port->line = i; + port->type = PORT_ALTERA_UART; + port->iotype = SERIAL_IO_MEM; + port->uartclk = platp->uartclk; + port->ops = &altera_uart_ops; + port->flags = UPF_BOOT_AUTOCONF; + port->private_data = platp; + + uart_add_one_port(&altera_uart_driver, port); + + return 0; +} + +static int __devexit altera_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port = &altera_uart_ports[pdev->id].port; + + uart_remove_one_port(&altera_uart_driver, port); + return 0; +} + +static struct platform_driver altera_uart_platform_driver = { + .probe = altera_uart_probe, + .remove = __devexit_p(altera_uart_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = NULL, + }, +}; + +static int __init altera_uart_init(void) +{ + int rc; + + rc = uart_register_driver(&altera_uart_driver); + if (rc) + return rc; + rc = platform_driver_register(&altera_uart_platform_driver); + if (rc) { + uart_unregister_driver(&altera_uart_driver); + return rc; + } + return 0; +} + +static void __exit altera_uart_exit(void) +{ + platform_driver_unregister(&altera_uart_platform_driver); + uart_unregister_driver(&altera_uart_driver); +} + +module_init(altera_uart_init); +module_exit(altera_uart_exit); + +MODULE_DESCRIPTION("Altera UART driver"); +MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_ALIAS_CHARDEV_MAJOR(SERIAL_ALTERA_MAJOR); diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c index 58a4879..2904aa0 100644 --- a/drivers/serial/amba-pl010.c +++ b/drivers/serial/amba-pl010.c @@ -47,6 +47,7 @@ #include <linux/amba/bus.h> #include <linux/amba/serial.h> #include <linux/clk.h> +#include <linux/slab.h> #include <asm/io.h> @@ -117,7 +118,7 @@ static void pl010_enable_ms(struct uart_port *port) static void pl010_rx_chars(struct uart_amba_port *uap) { - struct tty_struct *tty = uap->port.info->port.tty; + struct tty_struct *tty = uap->port.state->port.tty; unsigned int status, ch, flag, rsr, max_count = 256; status = readb(uap->port.membase + UART01x_FR); @@ -172,7 +173,7 @@ static void pl010_rx_chars(struct uart_amba_port *uap) static void pl010_tx_chars(struct uart_amba_port *uap) { - struct circ_buf *xmit = &uap->port.info->xmit; + struct circ_buf *xmit = &uap->port.state->xmit; int count; if (uap->port.x_char) { @@ -225,7 +226,7 @@ static void pl010_modem_status(struct uart_amba_port *uap) if (delta & UART01x_FR_CTS) uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS); - wake_up_interruptible(&uap->port.info->delta_msr_wait); + wake_up_interruptible(&uap->port.state->port.delta_msr_wait); } static irqreturn_t pl010_int(int irq, void *dev_id) @@ -471,6 +472,15 @@ pl010_set_termios(struct uart_port *port, struct ktermios *termios, spin_unlock_irqrestore(&uap->port.lock, flags); } +static void pl010_set_ldisc(struct uart_port *port, int new) +{ + if (new == N_PPS) { + port->flags |= UPF_HARDPPS_CD; + pl010_enable_ms(port); + } else + port->flags &= ~UPF_HARDPPS_CD; +} + static const char *pl010_type(struct uart_port *port) { return port->type == PORT_AMBA ? "AMBA" : NULL; @@ -531,6 +541,7 @@ static struct uart_ops amba_pl010_pops = { .startup = pl010_startup, .shutdown = pl010_shutdown, .set_termios = pl010_set_termios, + .set_ldisc = pl010_set_ldisc, .type = pl010_type, .release_port = pl010_release_port, .request_port = pl010_request_port, @@ -766,7 +777,7 @@ static int pl010_resume(struct amba_device *dev) return 0; } -static struct amba_id pl010_ids[] __initdata = { +static struct amba_id pl010_ids[] = { { .id = 0x00041010, .mask = 0x000fffff, diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index bf82e28..e76d7d0 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -7,6 +7,7 @@ * * Copyright 1999 ARM Limited * Copyright (C) 2000 Deep Blue Solutions Ltd. + * Copyright (C) 2010 ST-Ericsson SA * * 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 @@ -47,6 +48,10 @@ #include <linux/amba/bus.h> #include <linux/amba/serial.h> #include <linux/clk.h> +#include <linux/slab.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/scatterlist.h> #include <asm/io.h> #include <asm/sizes.h> @@ -62,47 +67,560 @@ #define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) #define UART_DUMMY_DR_RX (1 << 16) -/* - * We wrap our port structure around the generic uart_port. - */ -struct uart_amba_port { - struct uart_port port; - struct clk *clk; - unsigned int im; /* interrupt mask */ - unsigned int old_status; - unsigned int ifls; /* vendor-specific */ -}; - /* There is by now at least one vendor with differing details, so handle it */ struct vendor_data { unsigned int ifls; unsigned int fifosize; + unsigned int lcrh_tx; + unsigned int lcrh_rx; + bool oversampling; + bool dma_threshold; }; static struct vendor_data vendor_arm = { .ifls = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8, .fifosize = 16, + .lcrh_tx = UART011_LCRH, + .lcrh_rx = UART011_LCRH, + .oversampling = false, + .dma_threshold = false, }; static struct vendor_data vendor_st = { .ifls = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF, .fifosize = 64, + .lcrh_tx = ST_UART011_LCRH_TX, + .lcrh_rx = ST_UART011_LCRH_RX, + .oversampling = true, + .dma_threshold = true, }; +/* Deals with DMA transactions */ +struct pl011_dmatx_data { + struct dma_chan *chan; + struct scatterlist sg; + char *buf; + bool queued; +}; + +/* + * We wrap our port structure around the generic uart_port. + */ +struct uart_amba_port { + struct uart_port port; + struct clk *clk; + const struct vendor_data *vendor; + unsigned int dmacr; /* dma control reg */ + unsigned int im; /* interrupt mask */ + unsigned int old_status; + unsigned int fifosize; /* vendor-specific */ + unsigned int lcrh_tx; /* vendor-specific */ + unsigned int lcrh_rx; /* vendor-specific */ + bool autorts; + char type[12]; +#ifdef CONFIG_DMA_ENGINE + /* DMA stuff */ + bool using_dma; + struct pl011_dmatx_data dmatx; +#endif +}; + +/* + * All the DMA operation mode stuff goes inside this ifdef. + * This assumes that you have a generic DMA device interface, + * no custom DMA interfaces are supported. + */ +#ifdef CONFIG_DMA_ENGINE + +#define PL011_DMA_BUFFER_SIZE PAGE_SIZE + +static void pl011_dma_probe_initcall(struct uart_amba_port *uap) +{ + /* DMA is the sole user of the platform data right now */ + struct amba_pl011_data *plat = uap->port.dev->platform_data; + struct dma_slave_config tx_conf = { + .dst_addr = uap->port.mapbase + UART01x_DR, + .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE, + .direction = DMA_TO_DEVICE, + .dst_maxburst = uap->fifosize >> 1, + }; + struct dma_chan *chan; + dma_cap_mask_t mask; + + /* We need platform data */ + if (!plat || !plat->dma_filter) { + dev_info(uap->port.dev, "no DMA platform data\n"); + return; + } + + /* Try to acquire a generic DMA engine slave channel */ + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + chan = dma_request_channel(mask, plat->dma_filter, plat->dma_tx_param); + if (!chan) { + dev_err(uap->port.dev, "no TX DMA channel!\n"); + return; + } + + dmaengine_slave_config(chan, &tx_conf); + uap->dmatx.chan = chan; + + dev_info(uap->port.dev, "DMA channel TX %s\n", + dma_chan_name(uap->dmatx.chan)); +} + +#ifndef MODULE +/* + * Stack up the UARTs and let the above initcall be done at device + * initcall time, because the serial driver is called as an arch + * initcall, and at this time the DMA subsystem is not yet registered. + * At this point the driver will switch over to using DMA where desired. + */ +struct dma_uap { + struct list_head node; + struct uart_amba_port *uap; +}; + +static LIST_HEAD(pl011_dma_uarts); + +static int __init pl011_dma_initcall(void) +{ + struct list_head *node, *tmp; + + list_for_each_safe(node, tmp, &pl011_dma_uarts) { + struct dma_uap *dmau = list_entry(node, struct dma_uap, node); + pl011_dma_probe_initcall(dmau->uap); + list_del(node); + kfree(dmau); + } + return 0; +} + +device_initcall(pl011_dma_initcall); + +static void pl011_dma_probe(struct uart_amba_port *uap) +{ + struct dma_uap *dmau = kzalloc(sizeof(struct dma_uap), GFP_KERNEL); + if (dmau) { + dmau->uap = uap; + list_add_tail(&dmau->node, &pl011_dma_uarts); + } +} +#else +static void pl011_dma_probe(struct uart_amba_port *uap) +{ + pl011_dma_probe_initcall(uap); +} +#endif + +static void pl011_dma_remove(struct uart_amba_port *uap) +{ + /* TODO: remove the initcall if it has not yet executed */ + if (uap->dmatx.chan) + dma_release_channel(uap->dmatx.chan); +} + + +/* Forward declare this for the refill routine */ +static int pl011_dma_tx_refill(struct uart_amba_port *uap); + +/* + * The current DMA TX buffer has been sent. + * Try to queue up another DMA buffer. + */ +static void pl011_dma_tx_callback(void *data) +{ + struct uart_amba_port *uap = data; + struct pl011_dmatx_data *dmatx = &uap->dmatx; + unsigned long flags; + u16 dmacr; + + spin_lock_irqsave(&uap->port.lock, flags); + if (uap->dmatx.queued) + dma_unmap_sg(dmatx->chan->device->dev, &dmatx->sg, 1, + DMA_TO_DEVICE); + + dmacr = uap->dmacr; + uap->dmacr = dmacr & ~UART011_TXDMAE; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + + /* + * If TX DMA was disabled, it means that we've stopped the DMA for + * some reason (eg, XOFF received, or we want to send an X-char.) + * + * Note: we need to be careful here of a potential race between DMA + * and the rest of the driver - if the driver disables TX DMA while + * a TX buffer completing, we must update the tx queued status to + * get further refills (hence we check dmacr). + */ + if (!(dmacr & UART011_TXDMAE) || uart_tx_stopped(&uap->port) || + uart_circ_empty(&uap->port.state->xmit)) { + uap->dmatx.queued = false; + spin_unlock_irqrestore(&uap->port.lock, flags); + return; + } + + if (pl011_dma_tx_refill(uap) <= 0) { + /* + * We didn't queue a DMA buffer for some reason, but we + * have data pending to be sent. Re-enable the TX IRQ. + */ + uap->im |= UART011_TXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); + } + spin_unlock_irqrestore(&uap->port.lock, flags); +} + +/* + * Try to refill the TX DMA buffer. + * Locking: called with port lock held and IRQs disabled. + * Returns: + * 1 if we queued up a TX DMA buffer. + * 0 if we didn't want to handle this by DMA + * <0 on error + */ +static int pl011_dma_tx_refill(struct uart_amba_port *uap) +{ + struct pl011_dmatx_data *dmatx = &uap->dmatx; + struct dma_chan *chan = dmatx->chan; + struct dma_device *dma_dev = chan->device; + struct dma_async_tx_descriptor *desc; + struct circ_buf *xmit = &uap->port.state->xmit; + unsigned int count; + + /* + * Try to avoid the overhead involved in using DMA if the + * transaction fits in the first half of the FIFO, by using + * the standard interrupt handling. This ensures that we + * issue a uart_write_wakeup() at the appropriate time. + */ + count = uart_circ_chars_pending(xmit); + if (count < (uap->fifosize >> 1)) { + uap->dmatx.queued = false; + return 0; + } + + /* + * Bodge: don't send the last character by DMA, as this + * will prevent XON from notifying us to restart DMA. + */ + count -= 1; + + /* Else proceed to copy the TX chars to the DMA buffer and fire DMA */ + if (count > PL011_DMA_BUFFER_SIZE) + count = PL011_DMA_BUFFER_SIZE; + + if (xmit->tail < xmit->head) + memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], count); + else { + size_t first = UART_XMIT_SIZE - xmit->tail; + size_t second = xmit->head; + + memcpy(&dmatx->buf[0], &xmit->buf[xmit->tail], first); + if (second) + memcpy(&dmatx->buf[first], &xmit->buf[0], second); + } + + dmatx->sg.length = count; + + if (dma_map_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE) != 1) { + uap->dmatx.queued = false; + dev_dbg(uap->port.dev, "unable to map TX DMA\n"); + return -EBUSY; + } + + desc = dma_dev->device_prep_slave_sg(chan, &dmatx->sg, 1, DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dma_unmap_sg(dma_dev->dev, &dmatx->sg, 1, DMA_TO_DEVICE); + uap->dmatx.queued = false; + /* + * If DMA cannot be used right now, we complete this + * transaction via IRQ and let the TTY layer retry. + */ + dev_dbg(uap->port.dev, "TX DMA busy\n"); + return -EBUSY; + } + + /* Some data to go along to the callback */ + desc->callback = pl011_dma_tx_callback; + desc->callback_param = uap; + + /* All errors should happen at prepare time */ + dmaengine_submit(desc); + + /* Fire the DMA transaction */ + dma_dev->device_issue_pending(chan); + + uap->dmacr |= UART011_TXDMAE; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + uap->dmatx.queued = true; + + /* + * Now we know that DMA will fire, so advance the ring buffer + * with the stuff we just dispatched. + */ + xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); + uap->port.icount.tx += count; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&uap->port); + + return 1; +} + +/* + * We received a transmit interrupt without a pending X-char but with + * pending characters. + * Locking: called with port lock held and IRQs disabled. + * Returns: + * false if we want to use PIO to transmit + * true if we queued a DMA buffer + */ +static bool pl011_dma_tx_irq(struct uart_amba_port *uap) +{ + if (!uap->using_dma) + return false; + + /* + * If we already have a TX buffer queued, but received a + * TX interrupt, it will be because we've just sent an X-char. + * Ensure the TX DMA is enabled and the TX IRQ is disabled. + */ + if (uap->dmatx.queued) { + uap->dmacr |= UART011_TXDMAE; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + uap->im &= ~UART011_TXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); + return true; + } + + /* + * We don't have a TX buffer queued, so try to queue one. + * If we succesfully queued a buffer, mask the TX IRQ. + */ + if (pl011_dma_tx_refill(uap) > 0) { + uap->im &= ~UART011_TXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); + return true; + } + return false; +} + +/* + * Stop the DMA transmit (eg, due to received XOFF). + * Locking: called with port lock held and IRQs disabled. + */ +static inline void pl011_dma_tx_stop(struct uart_amba_port *uap) +{ + if (uap->dmatx.queued) { + uap->dmacr &= ~UART011_TXDMAE; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + } +} + +/* + * Try to start a DMA transmit, or in the case of an XON/OFF + * character queued for send, try to get that character out ASAP. + * Locking: called with port lock held and IRQs disabled. + * Returns: + * false if we want the TX IRQ to be enabled + * true if we have a buffer queued + */ +static inline bool pl011_dma_tx_start(struct uart_amba_port *uap) +{ + u16 dmacr; + + if (!uap->using_dma) + return false; + + if (!uap->port.x_char) { + /* no X-char, try to push chars out in DMA mode */ + bool ret = true; + + if (!uap->dmatx.queued) { + if (pl011_dma_tx_refill(uap) > 0) { + uap->im &= ~UART011_TXIM; + ret = true; + } else { + uap->im |= UART011_TXIM; + ret = false; + } + writew(uap->im, uap->port.membase + UART011_IMSC); + } else if (!(uap->dmacr & UART011_TXDMAE)) { + uap->dmacr |= UART011_TXDMAE; + writew(uap->dmacr, + uap->port.membase + UART011_DMACR); + } + return ret; + } + + /* + * We have an X-char to send. Disable DMA to prevent it loading + * the TX fifo, and then see if we can stuff it into the FIFO. + */ + dmacr = uap->dmacr; + uap->dmacr &= ~UART011_TXDMAE; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + + if (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF) { + /* + * No space in the FIFO, so enable the transmit interrupt + * so we know when there is space. Note that once we've + * loaded the character, we should just re-enable DMA. + */ + return false; + } + + writew(uap->port.x_char, uap->port.membase + UART01x_DR); + uap->port.icount.tx++; + uap->port.x_char = 0; + + /* Success - restore the DMA state */ + uap->dmacr = dmacr; + writew(dmacr, uap->port.membase + UART011_DMACR); + + return true; +} + +/* + * Flush the transmit buffer. + * Locking: called with port lock held and IRQs disabled. + */ +static void pl011_dma_flush_buffer(struct uart_port *port) +{ + struct uart_amba_port *uap = (struct uart_amba_port *)port; + + if (!uap->using_dma) + return; + + /* Avoid deadlock with the DMA engine callback */ + spin_unlock(&uap->port.lock); + dmaengine_terminate_all(uap->dmatx.chan); + spin_lock(&uap->port.lock); + if (uap->dmatx.queued) { + dma_unmap_sg(uap->dmatx.chan->device->dev, &uap->dmatx.sg, 1, + DMA_TO_DEVICE); + uap->dmatx.queued = false; + uap->dmacr &= ~UART011_TXDMAE; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + } +} + + +static void pl011_dma_startup(struct uart_amba_port *uap) +{ + if (!uap->dmatx.chan) + return; + + uap->dmatx.buf = kmalloc(PL011_DMA_BUFFER_SIZE, GFP_KERNEL); + if (!uap->dmatx.buf) { + dev_err(uap->port.dev, "no memory for DMA TX buffer\n"); + uap->port.fifosize = uap->fifosize; + return; + } + + sg_init_one(&uap->dmatx.sg, uap->dmatx.buf, PL011_DMA_BUFFER_SIZE); + + /* The DMA buffer is now the FIFO the TTY subsystem can use */ + uap->port.fifosize = PL011_DMA_BUFFER_SIZE; + uap->using_dma = true; + + /* Turn on DMA error (RX/TX will be enabled on demand) */ + uap->dmacr |= UART011_DMAONERR; + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + + /* + * ST Micro variants has some specific dma burst threshold + * compensation. Set this to 16 bytes, so burst will only + * be issued above/below 16 bytes. + */ + if (uap->vendor->dma_threshold) + writew(ST_UART011_DMAWM_RX_16 | ST_UART011_DMAWM_TX_16, + uap->port.membase + ST_UART011_DMAWM); +} + +static void pl011_dma_shutdown(struct uart_amba_port *uap) +{ + if (!uap->using_dma) + return; + + /* Disable RX and TX DMA */ + while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) + barrier(); + + spin_lock_irq(&uap->port.lock); + uap->dmacr &= ~(UART011_DMAONERR | UART011_RXDMAE | UART011_TXDMAE); + writew(uap->dmacr, uap->port.membase + UART011_DMACR); + spin_unlock_irq(&uap->port.lock); + + /* In theory, this should already be done by pl011_dma_flush_buffer */ + dmaengine_terminate_all(uap->dmatx.chan); + if (uap->dmatx.queued) { + dma_unmap_sg(uap->dmatx.chan->device->dev, &uap->dmatx.sg, 1, + DMA_TO_DEVICE); + uap->dmatx.queued = false; + } + + kfree(uap->dmatx.buf); + + uap->using_dma = false; +} + +#else +/* Blank functions if the DMA engine is not available */ +static inline void pl011_dma_probe(struct uart_amba_port *uap) +{ +} + +static inline void pl011_dma_remove(struct uart_amba_port *uap) +{ +} + +static inline void pl011_dma_startup(struct uart_amba_port *uap) +{ +} + +static inline void pl011_dma_shutdown(struct uart_amba_port *uap) +{ +} + +static inline bool pl011_dma_tx_irq(struct uart_amba_port *uap) +{ + return false; +} + +static inline void pl011_dma_tx_stop(struct uart_amba_port *uap) +{ +} + +static inline bool pl011_dma_tx_start(struct uart_amba_port *uap) +{ + return false; +} + +#define pl011_dma_flush_buffer NULL +#endif + + static void pl011_stop_tx(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; uap->im &= ~UART011_TXIM; writew(uap->im, uap->port.membase + UART011_IMSC); + pl011_dma_tx_stop(uap); } static void pl011_start_tx(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; - uap->im |= UART011_TXIM; - writew(uap->im, uap->port.membase + UART011_IMSC); + if (!pl011_dma_tx_start(uap)) { + uap->im |= UART011_TXIM; + writew(uap->im, uap->port.membase + UART011_IMSC); + } } static void pl011_stop_rx(struct uart_port *port) @@ -124,7 +642,7 @@ static void pl011_enable_ms(struct uart_port *port) static void pl011_rx_chars(struct uart_amba_port *uap) { - struct tty_struct *tty = uap->port.info->port.tty; + struct tty_struct *tty = uap->port.state->port.tty; unsigned int status, ch, flag, max_count = 256; status = readw(uap->port.membase + UART01x_FR); @@ -175,7 +693,7 @@ static void pl011_rx_chars(struct uart_amba_port *uap) static void pl011_tx_chars(struct uart_amba_port *uap) { - struct circ_buf *xmit = &uap->port.info->xmit; + struct circ_buf *xmit = &uap->port.state->xmit; int count; if (uap->port.x_char) { @@ -189,7 +707,11 @@ static void pl011_tx_chars(struct uart_amba_port *uap) return; } - count = uap->port.fifosize >> 1; + /* If we are using DMA mode, try to send some characters. */ + if (pl011_dma_tx_irq(uap)) + return; + + count = uap->fifosize >> 1; do { writew(xmit->buf[xmit->tail], uap->port.membase + UART01x_DR); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); @@ -226,16 +748,17 @@ static void pl011_modem_status(struct uart_amba_port *uap) if (delta & UART01x_FR_CTS) uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS); - wake_up_interruptible(&uap->port.info->delta_msr_wait); + wake_up_interruptible(&uap->port.state->port.delta_msr_wait); } static irqreturn_t pl011_int(int irq, void *dev_id) { struct uart_amba_port *uap = dev_id; + unsigned long flags; unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; int handled = 0; - spin_lock(&uap->port.lock); + spin_lock_irqsave(&uap->port.lock, flags); status = readw(uap->port.membase + UART011_MIS); if (status) { @@ -260,7 +783,7 @@ static irqreturn_t pl011_int(int irq, void *dev_id) handled = 1; } - spin_unlock(&uap->port.lock); + spin_unlock_irqrestore(&uap->port.lock, flags); return IRQ_RETVAL(handled); } @@ -308,6 +831,11 @@ static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl) TIOCMBIT(TIOCM_OUT1, UART011_CR_OUT1); TIOCMBIT(TIOCM_OUT2, UART011_CR_OUT2); TIOCMBIT(TIOCM_LOOP, UART011_CR_LBE); + + if (uap->autorts) { + /* We need to disable auto-RTS if we want to turn RTS off */ + TIOCMBIT(TIOCM_RTS, UART011_CR_RTSEN); + } #undef TIOCMBIT writew(cr, uap->port.membase + UART011_CR); @@ -320,12 +848,12 @@ static void pl011_break_ctl(struct uart_port *port, int break_state) unsigned int lcr_h; spin_lock_irqsave(&uap->port.lock, flags); - lcr_h = readw(uap->port.membase + UART011_LCRH); + lcr_h = readw(uap->port.membase + uap->lcrh_tx); if (break_state == -1) lcr_h |= UART01x_LCRH_BRK; else lcr_h &= ~UART01x_LCRH_BRK; - writew(lcr_h, uap->port.membase + UART011_LCRH); + writew(lcr_h, uap->port.membase + uap->lcrh_tx); spin_unlock_irqrestore(&uap->port.lock, flags); } @@ -335,9 +863,9 @@ static int pl010_get_poll_char(struct uart_port *port) struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int status; - do { - status = readw(uap->port.membase + UART01x_FR); - } while (status & UART01x_FR_RXFE); + status = readw(uap->port.membase + UART01x_FR); + if (status & UART01x_FR_RXFE) + return NO_POLL_CHAR; return readw(uap->port.membase + UART01x_DR); } @@ -377,7 +905,7 @@ static int pl011_startup(struct uart_port *port) if (retval) goto clk_dis; - writew(uap->ifls, uap->port.membase + UART011_IFLS); + writew(uap->vendor->ifls, uap->port.membase + UART011_IFLS); /* * Provoke TX FIFO interrupt into asserting. @@ -386,7 +914,17 @@ static int pl011_startup(struct uart_port *port) writew(cr, uap->port.membase + UART011_CR); writew(0, uap->port.membase + UART011_FBRD); writew(1, uap->port.membase + UART011_IBRD); - writew(0, uap->port.membase + UART011_LCRH); + writew(0, uap->port.membase + uap->lcrh_rx); + if (uap->lcrh_tx != uap->lcrh_rx) { + int i; + /* + * Wait 10 PCLKs before writing LCRH_TX register, + * to get this delay write read only register 10 times + */ + for (i = 0; i < 10; ++i) + writew(0xff, uap->port.membase + UART011_MIS); + writew(0, uap->port.membase + uap->lcrh_tx); + } writew(0, uap->port.membase + UART01x_DR); while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_BUSY) barrier(); @@ -394,11 +932,18 @@ static int pl011_startup(struct uart_port *port) cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE; writew(cr, uap->port.membase + UART011_CR); + /* Clear pending error interrupts */ + writew(UART011_OEIS | UART011_BEIS | UART011_PEIS | UART011_FEIS, + uap->port.membase + UART011_ICR); + /* * initialise the old status of the modem signals */ uap->old_status = readw(uap->port.membase + UART01x_FR) & UART01x_FR_MODEM_ANY; + /* Startup DMA */ + pl011_dma_startup(uap); + /* * Finally, enable interrupts */ @@ -415,10 +960,19 @@ static int pl011_startup(struct uart_port *port) return retval; } +static void pl011_shutdown_channel(struct uart_amba_port *uap, + unsigned int lcrh) +{ + unsigned long val; + + val = readw(uap->port.membase + lcrh); + val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN); + writew(val, uap->port.membase + lcrh); +} + static void pl011_shutdown(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; - unsigned long val; /* * disable all interrupts @@ -429,6 +983,8 @@ static void pl011_shutdown(struct uart_port *port) writew(0xffff, uap->port.membase + UART011_ICR); spin_unlock_irq(&uap->port.lock); + pl011_dma_shutdown(uap); + /* * Free the interrupt */ @@ -437,14 +993,15 @@ static void pl011_shutdown(struct uart_port *port) /* * disable the port */ + uap->autorts = false; writew(UART01x_CR_UARTEN | UART011_CR_TXE, uap->port.membase + UART011_CR); /* * disable break condition and fifos */ - val = readw(uap->port.membase + UART011_LCRH); - val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN); - writew(val, uap->port.membase + UART011_LCRH); + pl011_shutdown_channel(uap, uap->lcrh_rx); + if (uap->lcrh_rx != uap->lcrh_tx) + pl011_shutdown_channel(uap, uap->lcrh_tx); /* * Shut down the clock producer @@ -456,15 +1013,26 @@ static void pl011_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { + struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int lcr_h, old_cr; unsigned long flags; - unsigned int baud, quot; + unsigned int baud, quot, clkdiv; + + if (uap->vendor->oversampling) + clkdiv = 8; + else + clkdiv = 16; /* * Ask the core to calculate the divisor for us. */ - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = port->uartclk * 4 / baud; + baud = uart_get_baud_rate(port, termios, old, 0, + port->uartclk / clkdiv); + + if (baud > port->uartclk/16) + quot = DIV_ROUND_CLOSEST(port->uartclk * 8, baud); + else + quot = DIV_ROUND_CLOSEST(port->uartclk * 4, baud); switch (termios->c_cflag & CSIZE) { case CS5: @@ -487,7 +1055,7 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, if (!(termios->c_cflag & PARODD)) lcr_h |= UART01x_LCRH_EPS; } - if (port->fifosize > 1) + if (uap->fifosize > 1) lcr_h |= UART01x_LCRH_FEN; spin_lock_irqsave(&port->lock, flags); @@ -532,6 +1100,24 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, old_cr = readw(port->membase + UART011_CR); writew(0, port->membase + UART011_CR); + if (termios->c_cflag & CRTSCTS) { + if (old_cr & UART011_CR_RTS) + old_cr |= UART011_CR_RTSEN; + + old_cr |= UART011_CR_CTSEN; + uap->autorts = true; + } else { + old_cr &= ~(UART011_CR_CTSEN | UART011_CR_RTSEN); + uap->autorts = false; + } + + if (uap->vendor->oversampling) { + if (baud > port->uartclk / 16) + old_cr |= ST_UART011_CR_OVSFACT; + else + old_cr &= ~ST_UART011_CR_OVSFACT; + } + /* Set baud rate */ writew(quot & 0x3f, port->membase + UART011_FBRD); writew(quot >> 6, port->membase + UART011_IBRD); @@ -541,7 +1127,17 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L * ----------^----------^----------^----------^----- */ - writew(lcr_h, port->membase + UART011_LCRH); + writew(lcr_h, port->membase + uap->lcrh_rx); + if (uap->lcrh_rx != uap->lcrh_tx) { + int i; + /* + * Wait 10 PCLKs before writing LCRH_TX register, + * to get this delay write read only register 10 times + */ + for (i = 0; i < 10; ++i) + writew(0xff, uap->port.membase + UART011_MIS); + writew(lcr_h, port->membase + uap->lcrh_tx); + } writew(old_cr, port->membase + UART011_CR); spin_unlock_irqrestore(&port->lock, flags); @@ -549,7 +1145,8 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios, static const char *pl011_type(struct uart_port *port) { - return port->type == PORT_AMBA ? "AMBA/PL011" : NULL; + struct uart_amba_port *uap = (struct uart_amba_port *)port; + return uap->port.type == PORT_AMBA ? uap->type : NULL; } /* @@ -606,6 +1203,7 @@ static struct uart_ops amba_pl011_pops = { .break_ctl = pl011_break_ctl, .startup = pl011_startup, .shutdown = pl011_shutdown, + .flush_buffer = pl011_dma_flush_buffer, .set_termios = pl011_set_termios, .type = pl011_type, .release_port = pl010_release_port, @@ -668,7 +1266,7 @@ pl011_console_get_options(struct uart_amba_port *uap, int *baud, if (readw(uap->port.membase + UART011_CR) & UART01x_CR_UARTEN) { unsigned int lcr_h, ibrd, fbrd; - lcr_h = readw(uap->port.membase + UART011_LCRH); + lcr_h = readw(uap->port.membase + uap->lcrh_tx); *parity = 'n'; if (lcr_h & UART01x_LCRH_PEN) { @@ -687,6 +1285,12 @@ pl011_console_get_options(struct uart_amba_port *uap, int *baud, fbrd = readw(uap->port.membase + UART011_FBRD); *baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd); + + if (uap->vendor->oversampling) { + if (readw(uap->port.membase + UART011_CR) + & ST_UART011_CR_OVSFACT) + *baud *= 2; + } } } @@ -779,16 +1383,22 @@ static int pl011_probe(struct amba_device *dev, struct amba_id *id) goto unmap; } - uap->ifls = vendor->ifls; + uap->vendor = vendor; + uap->lcrh_rx = vendor->lcrh_rx; + uap->lcrh_tx = vendor->lcrh_tx; + uap->fifosize = vendor->fifosize; uap->port.dev = &dev->dev; uap->port.mapbase = dev->res.start; uap->port.membase = base; uap->port.iotype = UPIO_MEM; uap->port.irq = dev->irq[0]; - uap->port.fifosize = vendor->fifosize; + uap->port.fifosize = uap->fifosize; uap->port.ops = &amba_pl011_pops; uap->port.flags = UPF_BOOT_AUTOCONF; uap->port.line = i; + pl011_dma_probe(uap); + + snprintf(uap->type, sizeof(uap->type), "PL011 rev%u", amba_rev(dev)); amba_ports[i] = uap; @@ -797,6 +1407,7 @@ static int pl011_probe(struct amba_device *dev, struct amba_id *id) if (ret) { amba_set_drvdata(dev, NULL); amba_ports[i] = NULL; + pl011_dma_remove(uap); clk_put(uap->clk); unmap: iounmap(base); @@ -820,13 +1431,36 @@ static int pl011_remove(struct amba_device *dev) if (amba_ports[i] == uap) amba_ports[i] = NULL; + pl011_dma_remove(uap); iounmap(uap->port.membase); clk_put(uap->clk); kfree(uap); return 0; } -static struct amba_id pl011_ids[] __initdata = { +#ifdef CONFIG_PM +static int pl011_suspend(struct amba_device *dev, pm_message_t state) +{ + struct uart_amba_port *uap = amba_get_drvdata(dev); + + if (!uap) + return -EINVAL; + + return uart_suspend_port(&amba_reg, &uap->port); +} + +static int pl011_resume(struct amba_device *dev) +{ + struct uart_amba_port *uap = amba_get_drvdata(dev); + + if (!uap) + return -EINVAL; + + return uart_resume_port(&amba_reg, &uap->port); +} +#endif + +static struct amba_id pl011_ids[] = { { .id = 0x00041011, .mask = 0x000fffff, @@ -847,6 +1481,10 @@ static struct amba_driver pl011_driver = { .id_table = pl011_ids, .probe = pl011_probe, .remove = pl011_remove, +#ifdef CONFIG_PM + .suspend = pl011_suspend, + .resume = pl011_resume, +#endif }; static int __init pl011_init(void) diff --git a/drivers/serial/apbuart.c b/drivers/serial/apbuart.c new file mode 100644 index 0000000..095a5d5 --- /dev/null +++ b/drivers/serial/apbuart.c @@ -0,0 +1,709 @@ +/* + * Driver for GRLIB serial ports (APBUART) + * + * Based on linux/drivers/serial/amba.c + * + * Copyright (C) 2000 Deep Blue Solutions Ltd. + * Copyright (C) 2003 Konrad Eisele <eiselekd@web.de> + * Copyright (C) 2006 Daniel Hellstrom <daniel@gaisler.com>, Aeroflex Gaisler AB + * Copyright (C) 2008 Gilead Kutnick <kutnickg@zin-tech.com> + * Copyright (C) 2009 Kristoffer Glembo <kristoffer@gaisler.com>, Aeroflex Gaisler AB + */ + +#if defined(CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/module.h> +#include <linux/tty.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/serial.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/kthread.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/serial_core.h> +#include <asm/irq.h> + +#include "apbuart.h" + +#define SERIAL_APBUART_MAJOR TTY_MAJOR +#define SERIAL_APBUART_MINOR 64 +#define UART_DUMMY_RSR_RX 0x8000 /* for ignore all read */ + +static void apbuart_tx_chars(struct uart_port *port); + +static void apbuart_stop_tx(struct uart_port *port) +{ + unsigned int cr; + + cr = UART_GET_CTRL(port); + cr &= ~UART_CTRL_TI; + UART_PUT_CTRL(port, cr); +} + +static void apbuart_start_tx(struct uart_port *port) +{ + unsigned int cr; + + cr = UART_GET_CTRL(port); + cr |= UART_CTRL_TI; + UART_PUT_CTRL(port, cr); + + if (UART_GET_STATUS(port) & UART_STATUS_THE) + apbuart_tx_chars(port); +} + +static void apbuart_stop_rx(struct uart_port *port) +{ + unsigned int cr; + + cr = UART_GET_CTRL(port); + cr &= ~(UART_CTRL_RI); + UART_PUT_CTRL(port, cr); +} + +static void apbuart_enable_ms(struct uart_port *port) +{ + /* No modem status change interrupts for APBUART */ +} + +static void apbuart_rx_chars(struct uart_port *port) +{ + struct tty_struct *tty = port->state->port.tty; + unsigned int status, ch, rsr, flag; + unsigned int max_chars = port->fifosize; + + status = UART_GET_STATUS(port); + + while (UART_RX_DATA(status) && (max_chars--)) { + + ch = UART_GET_CHAR(port); + flag = TTY_NORMAL; + + port->icount.rx++; + + rsr = UART_GET_STATUS(port) | UART_DUMMY_RSR_RX; + UART_PUT_STATUS(port, 0); + if (rsr & UART_STATUS_ERR) { + + if (rsr & UART_STATUS_BR) { + rsr &= ~(UART_STATUS_FE | UART_STATUS_PE); + port->icount.brk++; + if (uart_handle_break(port)) + goto ignore_char; + } else if (rsr & UART_STATUS_PE) { + port->icount.parity++; + } else if (rsr & UART_STATUS_FE) { + port->icount.frame++; + } + if (rsr & UART_STATUS_OE) + port->icount.overrun++; + + rsr &= port->read_status_mask; + + if (rsr & UART_STATUS_PE) + flag = TTY_PARITY; + else if (rsr & UART_STATUS_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(port, ch)) + goto ignore_char; + + uart_insert_char(port, rsr, UART_STATUS_OE, ch, flag); + + + ignore_char: + status = UART_GET_STATUS(port); + } + + tty_flip_buffer_push(tty); +} + +static void apbuart_tx_chars(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + int count; + + if (port->x_char) { + UART_PUT_CHAR(port, port->x_char); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + apbuart_stop_tx(port); + return; + } + + /* amba: fill FIFO */ + count = port->fifosize >> 1; + do { + UART_PUT_CHAR(port, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + apbuart_stop_tx(port); +} + +static irqreturn_t apbuart_int(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned int status; + + spin_lock(&port->lock); + + status = UART_GET_STATUS(port); + if (status & UART_STATUS_DR) + apbuart_rx_chars(port); + if (status & UART_STATUS_THE) + apbuart_tx_chars(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static unsigned int apbuart_tx_empty(struct uart_port *port) +{ + unsigned int status = UART_GET_STATUS(port); + return status & UART_STATUS_THE ? TIOCSER_TEMT : 0; +} + +static unsigned int apbuart_get_mctrl(struct uart_port *port) +{ + /* The GRLIB APBUART handles flow control in hardware */ + return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; +} + +static void apbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* The GRLIB APBUART handles flow control in hardware */ +} + +static void apbuart_break_ctl(struct uart_port *port, int break_state) +{ + /* We don't support sending break */ +} + +static int apbuart_startup(struct uart_port *port) +{ + int retval; + unsigned int cr; + + /* Allocate the IRQ */ + retval = request_irq(port->irq, apbuart_int, 0, "apbuart", port); + if (retval) + return retval; + + /* Finally, enable interrupts */ + cr = UART_GET_CTRL(port); + UART_PUT_CTRL(port, + cr | UART_CTRL_RE | UART_CTRL_TE | + UART_CTRL_RI | UART_CTRL_TI); + + return 0; +} + +static void apbuart_shutdown(struct uart_port *port) +{ + unsigned int cr; + + /* disable all interrupts, disable the port */ + cr = UART_GET_CTRL(port); + UART_PUT_CTRL(port, + cr & ~(UART_CTRL_RE | UART_CTRL_TE | + UART_CTRL_RI | UART_CTRL_TI)); + + /* Free the interrupt */ + free_irq(port->irq, port); +} + +static void apbuart_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + unsigned int cr; + unsigned long flags; + unsigned int baud, quot; + + /* Ask the core to calculate the divisor for us. */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + if (baud == 0) + panic("invalid baudrate %i\n", port->uartclk / 16); + + /* uart_get_divisor calc a *16 uart freq, apbuart is *8 */ + quot = (uart_get_divisor(port, baud)) * 2; + cr = UART_GET_CTRL(port); + cr &= ~(UART_CTRL_PE | UART_CTRL_PS); + + if (termios->c_cflag & PARENB) { + cr |= UART_CTRL_PE; + if ((termios->c_cflag & PARODD)) + cr |= UART_CTRL_PS; + } + + /* Enable flow control. */ + if (termios->c_cflag & CRTSCTS) + cr |= UART_CTRL_FL; + + spin_lock_irqsave(&port->lock, flags); + + /* Update the per-port timeout. */ + uart_update_timeout(port, termios->c_cflag, baud); + + port->read_status_mask = UART_STATUS_OE; + if (termios->c_iflag & INPCK) + port->read_status_mask |= UART_STATUS_FE | UART_STATUS_PE; + + /* Characters to ignore */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_STATUS_FE | UART_STATUS_PE; + + /* Ignore all characters if CREAD is not set. */ + if ((termios->c_cflag & CREAD) == 0) + port->ignore_status_mask |= UART_DUMMY_RSR_RX; + + /* Set baud rate */ + quot -= 1; + UART_PUT_SCAL(port, quot); + UART_PUT_CTRL(port, cr); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *apbuart_type(struct uart_port *port) +{ + return port->type == PORT_APBUART ? "GRLIB/APBUART" : NULL; +} + +static void apbuart_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, 0x100); +} + +static int apbuart_request_port(struct uart_port *port) +{ + return request_mem_region(port->mapbase, 0x100, "grlib-apbuart") + != NULL ? 0 : -EBUSY; + return 0; +} + +/* Configure/autoconfigure the port */ +static void apbuart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_APBUART; + apbuart_request_port(port); + } +} + +/* Verify the new serial_struct (for TIOCSSERIAL) */ +static int apbuart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_APBUART) + ret = -EINVAL; + if (ser->irq < 0 || ser->irq >= NR_IRQS) + ret = -EINVAL; + if (ser->baud_base < 9600) + ret = -EINVAL; + return ret; +} + +static struct uart_ops grlib_apbuart_ops = { + .tx_empty = apbuart_tx_empty, + .set_mctrl = apbuart_set_mctrl, + .get_mctrl = apbuart_get_mctrl, + .stop_tx = apbuart_stop_tx, + .start_tx = apbuart_start_tx, + .stop_rx = apbuart_stop_rx, + .enable_ms = apbuart_enable_ms, + .break_ctl = apbuart_break_ctl, + .startup = apbuart_startup, + .shutdown = apbuart_shutdown, + .set_termios = apbuart_set_termios, + .type = apbuart_type, + .release_port = apbuart_release_port, + .request_port = apbuart_request_port, + .config_port = apbuart_config_port, + .verify_port = apbuart_verify_port, +}; + +static struct uart_port grlib_apbuart_ports[UART_NR]; +static struct device_node *grlib_apbuart_nodes[UART_NR]; + +static int apbuart_scan_fifo_size(struct uart_port *port, int portnumber) +{ + int ctrl, loop = 0; + int status; + int fifosize; + unsigned long flags; + + ctrl = UART_GET_CTRL(port); + + /* + * Enable the transceiver and wait for it to be ready to send data. + * Clear interrupts so that this process will not be externally + * interrupted in the middle (which can cause the transceiver to + * drain prematurely). + */ + + local_irq_save(flags); + + UART_PUT_CTRL(port, ctrl | UART_CTRL_TE); + + while (!UART_TX_READY(UART_GET_STATUS(port))) + loop++; + + /* + * Disable the transceiver so data isn't actually sent during the + * actual test. + */ + + UART_PUT_CTRL(port, ctrl & ~(UART_CTRL_TE)); + + fifosize = 1; + UART_PUT_CHAR(port, 0); + + /* + * So long as transmitting a character increments the tranceivier FIFO + * length the FIFO must be at least that big. These bytes will + * automatically drain off of the FIFO. + */ + + status = UART_GET_STATUS(port); + while (((status >> 20) & 0x3F) == fifosize) { + fifosize++; + UART_PUT_CHAR(port, 0); + status = UART_GET_STATUS(port); + } + + fifosize--; + + UART_PUT_CTRL(port, ctrl); + local_irq_restore(flags); + + if (fifosize == 0) + fifosize = 1; + + return fifosize; +} + +static void apbuart_flush_fifo(struct uart_port *port) +{ + int i; + + for (i = 0; i < port->fifosize; i++) + UART_GET_CHAR(port); +} + + +/* ======================================================================== */ +/* Console driver, if enabled */ +/* ======================================================================== */ + +#ifdef CONFIG_SERIAL_GRLIB_GAISLER_APBUART_CONSOLE + +static void apbuart_console_putchar(struct uart_port *port, int ch) +{ + unsigned int status; + do { + status = UART_GET_STATUS(port); + } while (!UART_TX_READY(status)); + UART_PUT_CHAR(port, ch); +} + +static void +apbuart_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_port *port = &grlib_apbuart_ports[co->index]; + unsigned int status, old_cr, new_cr; + + /* First save the CR then disable the interrupts */ + old_cr = UART_GET_CTRL(port); + new_cr = old_cr & ~(UART_CTRL_RI | UART_CTRL_TI); + UART_PUT_CTRL(port, new_cr); + + uart_console_write(port, s, count, apbuart_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the TCR + */ + do { + status = UART_GET_STATUS(port); + } while (!UART_TX_READY(status)); + UART_PUT_CTRL(port, old_cr); +} + +static void __init +apbuart_console_get_options(struct uart_port *port, int *baud, + int *parity, int *bits) +{ + if (UART_GET_CTRL(port) & (UART_CTRL_RE | UART_CTRL_TE)) { + + unsigned int quot, status; + status = UART_GET_STATUS(port); + + *parity = 'n'; + if (status & UART_CTRL_PE) { + if ((status & UART_CTRL_PS) == 0) + *parity = 'e'; + else + *parity = 'o'; + } + + *bits = 8; + quot = UART_GET_SCAL(port) / 8; + *baud = port->uartclk / (16 * (quot + 1)); + } +} + +static int __init apbuart_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 38400; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + pr_debug("apbuart_console_setup co=%p, co->index=%i, options=%s\n", + co, co->index, options); + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= grlib_apbuart_port_nr) + co->index = 0; + + port = &grlib_apbuart_ports[co->index]; + + spin_lock_init(&port->lock); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + else + apbuart_console_get_options(port, &baud, &parity, &bits); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver grlib_apbuart_driver; + +static struct console grlib_apbuart_console = { + .name = "ttyS", + .write = apbuart_console_write, + .device = uart_console_device, + .setup = apbuart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &grlib_apbuart_driver, +}; + + +static int grlib_apbuart_configure(void); + +static int __init apbuart_console_init(void) +{ + if (grlib_apbuart_configure()) + return -ENODEV; + register_console(&grlib_apbuart_console); + return 0; +} + +console_initcall(apbuart_console_init); + +#define APBUART_CONSOLE (&grlib_apbuart_console) +#else +#define APBUART_CONSOLE NULL +#endif + +static struct uart_driver grlib_apbuart_driver = { + .owner = THIS_MODULE, + .driver_name = "serial", + .dev_name = "ttyS", + .major = SERIAL_APBUART_MAJOR, + .minor = SERIAL_APBUART_MINOR, + .nr = UART_NR, + .cons = APBUART_CONSOLE, +}; + + +/* ======================================================================== */ +/* OF Platform Driver */ +/* ======================================================================== */ + +static int __devinit apbuart_probe(struct platform_device *op, + const struct of_device_id *match) +{ + int i = -1; + struct uart_port *port = NULL; + + i = 0; + for (i = 0; i < grlib_apbuart_port_nr; i++) { + if (op->dev.of_node == grlib_apbuart_nodes[i]) + break; + } + + port = &grlib_apbuart_ports[i]; + port->dev = &op->dev; + + uart_add_one_port(&grlib_apbuart_driver, (struct uart_port *) port); + + apbuart_flush_fifo((struct uart_port *) port); + + printk(KERN_INFO "grlib-apbuart at 0x%llx, irq %d\n", + (unsigned long long) port->mapbase, port->irq); + return 0; +} + +static struct of_device_id __initdata apbuart_match[] = { + { + .name = "GAISLER_APBUART", + }, + { + .name = "01_00c", + }, + {}, +}; + +static struct of_platform_driver grlib_apbuart_of_driver = { + .probe = apbuart_probe, + .driver = { + .owner = THIS_MODULE, + .name = "grlib-apbuart", + .of_match_table = apbuart_match, + }, +}; + + +static int grlib_apbuart_configure(void) +{ + struct device_node *np, *rp; + const u32 *prop; + int freq_khz, line = 0; + + /* Get bus frequency */ + rp = of_find_node_by_path("/"); + if (!rp) + return -ENODEV; + rp = of_get_next_child(rp, NULL); + if (!rp) + return -ENODEV; + prop = of_get_property(rp, "clock-frequency", NULL); + if (!prop) + return -ENODEV; + freq_khz = *prop; + + for_each_matching_node(np, apbuart_match) { + const int *irqs, *ampopts; + const struct amba_prom_registers *regs; + struct uart_port *port; + unsigned long addr; + + ampopts = of_get_property(np, "ampopts", NULL); + if (ampopts && (*ampopts == 0)) + continue; /* Ignore if used by another OS instance */ + + irqs = of_get_property(np, "interrupts", NULL); + regs = of_get_property(np, "reg", NULL); + + if (!irqs || !regs) + continue; + + grlib_apbuart_nodes[line] = np; + + addr = regs->phys_addr; + + port = &grlib_apbuart_ports[line]; + + port->mapbase = addr; + port->membase = ioremap(addr, sizeof(struct grlib_apbuart_regs_map)); + port->irq = *irqs; + port->iotype = UPIO_MEM; + port->ops = &grlib_apbuart_ops; + port->flags = UPF_BOOT_AUTOCONF; + port->line = line; + port->uartclk = freq_khz * 1000; + port->fifosize = apbuart_scan_fifo_size((struct uart_port *) port, line); + line++; + + /* We support maximum UART_NR uarts ... */ + if (line == UART_NR) + break; + } + + grlib_apbuart_driver.nr = grlib_apbuart_port_nr = line; + return line ? 0 : -ENODEV; +} + +static int __init grlib_apbuart_init(void) +{ + int ret; + + /* Find all APBUARTS in device the tree and initialize their ports */ + ret = grlib_apbuart_configure(); + if (ret) + return ret; + + printk(KERN_INFO "Serial: GRLIB APBUART driver\n"); + + ret = uart_register_driver(&grlib_apbuart_driver); + + if (ret) { + printk(KERN_ERR "%s: uart_register_driver failed (%i)\n", + __FILE__, ret); + return ret; + } + + ret = of_register_platform_driver(&grlib_apbuart_of_driver); + if (ret) { + printk(KERN_ERR + "%s: of_register_platform_driver failed (%i)\n", + __FILE__, ret); + uart_unregister_driver(&grlib_apbuart_driver); + return ret; + } + + return ret; +} + +static void __exit grlib_apbuart_exit(void) +{ + int i; + + for (i = 0; i < grlib_apbuart_port_nr; i++) + uart_remove_one_port(&grlib_apbuart_driver, + &grlib_apbuart_ports[i]); + + uart_unregister_driver(&grlib_apbuart_driver); + of_unregister_platform_driver(&grlib_apbuart_of_driver); +} + +module_init(grlib_apbuart_init); +module_exit(grlib_apbuart_exit); + +MODULE_AUTHOR("Aeroflex Gaisler AB"); +MODULE_DESCRIPTION("GRLIB APBUART serial driver"); +MODULE_VERSION("2.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/apbuart.h b/drivers/serial/apbuart.h new file mode 100644 index 0000000..5faf87c --- /dev/null +++ b/drivers/serial/apbuart.h @@ -0,0 +1,64 @@ +#ifndef __GRLIB_APBUART_H__ +#define __GRLIB_APBUART_H__ + +#include <asm/io.h> + +#define UART_NR 8 +static int grlib_apbuart_port_nr; + +struct grlib_apbuart_regs_map { + u32 data; + u32 status; + u32 ctrl; + u32 scaler; +}; + +struct amba_prom_registers { + unsigned int phys_addr; + unsigned int reg_size; +}; + +/* + * The following defines the bits in the APBUART Status Registers. + */ +#define UART_STATUS_DR 0x00000001 /* Data Ready */ +#define UART_STATUS_TSE 0x00000002 /* TX Send Register Empty */ +#define UART_STATUS_THE 0x00000004 /* TX Hold Register Empty */ +#define UART_STATUS_BR 0x00000008 /* Break Error */ +#define UART_STATUS_OE 0x00000010 /* RX Overrun Error */ +#define UART_STATUS_PE 0x00000020 /* RX Parity Error */ +#define UART_STATUS_FE 0x00000040 /* RX Framing Error */ +#define UART_STATUS_ERR 0x00000078 /* Error Mask */ + +/* + * The following defines the bits in the APBUART Ctrl Registers. + */ +#define UART_CTRL_RE 0x00000001 /* Receiver enable */ +#define UART_CTRL_TE 0x00000002 /* Transmitter enable */ +#define UART_CTRL_RI 0x00000004 /* Receiver interrupt enable */ +#define UART_CTRL_TI 0x00000008 /* Transmitter irq */ +#define UART_CTRL_PS 0x00000010 /* Parity select */ +#define UART_CTRL_PE 0x00000020 /* Parity enable */ +#define UART_CTRL_FL 0x00000040 /* Flow control enable */ +#define UART_CTRL_LB 0x00000080 /* Loopback enable */ + +#define APBBASE(port) ((struct grlib_apbuart_regs_map *)((port)->membase)) + +#define APBBASE_DATA_P(port) (&(APBBASE(port)->data)) +#define APBBASE_STATUS_P(port) (&(APBBASE(port)->status)) +#define APBBASE_CTRL_P(port) (&(APBBASE(port)->ctrl)) +#define APBBASE_SCALAR_P(port) (&(APBBASE(port)->scaler)) + +#define UART_GET_CHAR(port) (__raw_readl(APBBASE_DATA_P(port))) +#define UART_PUT_CHAR(port, v) (__raw_writel(v, APBBASE_DATA_P(port))) +#define UART_GET_STATUS(port) (__raw_readl(APBBASE_STATUS_P(port))) +#define UART_PUT_STATUS(port, v)(__raw_writel(v, APBBASE_STATUS_P(port))) +#define UART_GET_CTRL(port) (__raw_readl(APBBASE_CTRL_P(port))) +#define UART_PUT_CTRL(port, v) (__raw_writel(v, APBBASE_CTRL_P(port))) +#define UART_GET_SCAL(port) (__raw_readl(APBBASE_SCALAR_P(port))) +#define UART_PUT_SCAL(port, v) (__raw_writel(v, APBBASE_SCALAR_P(port))) + +#define UART_RX_DATA(s) (((s) & UART_STATUS_DR) != 0) +#define UART_TX_READY(s) (((s) & UART_STATUS_THE) != 0) + +#endif /* __GRLIB_APBUART_H__ */ diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index 338b15c..3892666 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -38,8 +38,10 @@ #include <linux/dma-mapping.h> #include <linux/atmel_pdc.h> #include <linux/atmel_serial.h> +#include <linux/uaccess.h> #include <asm/io.h> +#include <asm/ioctls.h> #include <asm/mach/serial_at91.h> #include <mach/board.h> @@ -59,6 +61,9 @@ #include <linux/serial_core.h> +static void atmel_start_rx(struct uart_port *port); +static void atmel_stop_rx(struct uart_port *port); + #ifdef CONFIG_SERIAL_ATMEL_TTYAT /* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we @@ -93,6 +98,7 @@ #define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR) #define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR) #define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR) +#define UART_PUT_TTGR(port, v) __raw_writel(v, (port)->membase + ATMEL_US_TTGR) /* PDC registers */ #define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR) @@ -147,6 +153,9 @@ struct atmel_uart_port { unsigned int irq_status_prev; struct circ_buf rx_ring; + + struct serial_rs485 rs485; /* rs485 settings */ + unsigned int tx_done_mask; }; static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; @@ -187,6 +196,47 @@ static bool atmel_use_dma_tx(struct uart_port *port) } #endif +/* Enable or disable the rs485 support */ +void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + unsigned int mode; + + spin_lock(&port->lock); + + /* Disable interrupts */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + + mode = UART_GET_MR(port); + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + atmel_port->rs485 = *rs485conf; + + if (rs485conf->flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND) + UART_PUT_TTGR(port, rs485conf->delay_rts_after_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + if (atmel_use_dma_tx(port)) + atmel_port->tx_done_mask = ATMEL_US_ENDTX | + ATMEL_US_TXBUFE; + else + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + } + UART_PUT_MR(port, mode); + + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + + spin_unlock(&port->lock); + +} + /* * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. */ @@ -202,6 +252,7 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) { unsigned int control = 0; unsigned int mode; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); #ifdef CONFIG_ARCH_AT91RM9200 if (cpu_is_at91rm9200()) { @@ -236,6 +287,19 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl) mode |= ATMEL_US_CHMODE_LOC_LOOP; else mode |= ATMEL_US_CHMODE_NORMAL; + + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND) + UART_PUT_TTGR(port, + atmel_port->rs485.delay_rts_after_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + } UART_PUT_MR(port, mode); } @@ -268,12 +332,17 @@ static u_int atmel_get_mctrl(struct uart_port *port) */ static void atmel_stop_tx(struct uart_port *port) { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + if (atmel_use_dma_tx(port)) { /* disable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); - UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); - } else - UART_PUT_IDR(port, ATMEL_US_TXRDY); + } + /* Disable interrupts */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_start_rx(port); } /* @@ -281,17 +350,39 @@ static void atmel_stop_tx(struct uart_port *port) */ static void atmel_start_tx(struct uart_port *port) { + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + if (atmel_use_dma_tx(port)) { if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN) /* The transmitter is already running. Yes, we really need this.*/ return; - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_stop_rx(port); + /* re-enable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); - } else - UART_PUT_IER(port, ATMEL_US_TXRDY); + } + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); +} + +/* + * start receiving - port is in process of being opened. + */ +static void atmel_start_rx(struct uart_port *port) +{ + UART_PUT_CR(port, ATMEL_US_RSTSTA); /* reset status and receiver */ + + if (atmel_use_dma_rx(port)) { + /* enable PDC controller */ + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | + port->read_status_mask); + UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); + } else { + UART_PUT_IER(port, ATMEL_US_RXRDY); + } } /* @@ -302,9 +393,11 @@ static void atmel_stop_rx(struct uart_port *port) if (atmel_use_dma_rx(port)) { /* disable PDC receive */ UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS); - UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); - } else + UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT | + port->read_status_mask); + } else { UART_PUT_IDR(port, ATMEL_US_RXRDY); + } } /* @@ -427,9 +520,10 @@ static void atmel_rx_chars(struct uart_port *port) */ static void atmel_tx_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (port->x_char && UART_GET_CSR(port) & ATMEL_US_TXRDY) { + if (port->x_char && UART_GET_CSR(port) & atmel_port->tx_done_mask) { UART_PUT_CHAR(port, port->x_char); port->icount.tx++; port->x_char = 0; @@ -437,7 +531,7 @@ static void atmel_tx_chars(struct uart_port *port) if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return; - while (UART_GET_CSR(port) & ATMEL_US_TXRDY) { + while (UART_GET_CSR(port) & atmel_port->tx_done_mask) { UART_PUT_CHAR(port, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); port->icount.tx++; @@ -449,7 +543,8 @@ static void atmel_tx_chars(struct uart_port *port) uart_write_wakeup(port); if (!uart_circ_empty(xmit)) - UART_PUT_IER(port, ATMEL_US_TXRDY); + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); } /* @@ -501,18 +596,10 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - if (atmel_use_dma_tx(port)) { - /* PDC transmit */ - if (pending & (ATMEL_US_ENDTX | ATMEL_US_TXBUFE)) { - UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); - tasklet_schedule(&atmel_port->tasklet); - } - } else { - /* Interrupt transmit */ - if (pending & ATMEL_US_TXRDY) { - UART_PUT_IDR(port, ATMEL_US_TXRDY); - tasklet_schedule(&atmel_port->tasklet); - } + if (pending & atmel_port->tx_done_mask) { + /* Either PDC or interrupt transmission */ + UART_PUT_IDR(port, atmel_port->tx_done_mask); + tasklet_schedule(&atmel_port->tasklet); } } @@ -560,7 +647,7 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id) static void atmel_tx_dma(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; int count; @@ -590,9 +677,15 @@ static void atmel_tx_dma(struct uart_port *port) UART_PUT_TPR(port, pdc->dma_addr + xmit->tail); UART_PUT_TCR(port, count); - /* re-enable PDC transmit and interrupts */ + /* re-enable PDC transmit */ UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); - UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); + /* Enable interrupts */ + UART_PUT_IER(port, atmel_port->tx_done_mask); + } else { + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + /* DMA done, stop TX, start RX for RS485 */ + atmel_start_rx(port); + } } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -663,14 +756,14 @@ static void atmel_rx_from_ring(struct uart_port *port) * uart_start(), which takes the lock. */ spin_unlock(&port->lock); - tty_flip_buffer_push(port->info->port.tty); + tty_flip_buffer_push(port->state->port.tty); spin_lock(&port->lock); } static void atmel_rx_from_dma(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; struct atmel_dma_buffer *pdc; int rx_idx = atmel_port->pdc_rx_idx; unsigned int head; @@ -776,7 +869,7 @@ static void atmel_tasklet_func(unsigned long data) if (status_change & ATMEL_US_CTS) uart_handle_cts_change(port, !(status & ATMEL_US_CTS)); - wake_up_interruptible(&port->info->delta_msr_wait); + wake_up_interruptible(&port->state->port.delta_msr_wait); atmel_port->irq_status_prev = status; } @@ -795,7 +888,7 @@ static void atmel_tasklet_func(unsigned long data) static int atmel_startup(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; int retval; /* @@ -854,7 +947,7 @@ static int atmel_startup(struct uart_port *port) } if (atmel_use_dma_tx(port)) { struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; pdc->buf = xmit->buf; pdc->dma_addr = dma_map_single(port->dev, @@ -1017,6 +1110,7 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, { unsigned long flags; unsigned int mode, imr, quot, baud; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); /* Get current mode register */ mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL @@ -1115,6 +1209,19 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, /* disable receiver and transmitter */ UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS); + /* Resetting serial mode to RS232 (0x0) */ + mode &= ~ATMEL_US_USMODE; + + if (atmel_port->rs485.flags & SER_RS485_ENABLED) { + dev_dbg(port->dev, "Setting UART to RS485\n"); + if (atmel_port->rs485.flags & SER_RS485_RTS_AFTER_SEND) + UART_PUT_TTGR(port, + atmel_port->rs485.delay_rts_after_send); + mode |= ATMEL_US_USMODE_RS485; + } else { + dev_dbg(port->dev, "Setting UART to RS232\n"); + } + /* set the parity, stop bits and data size */ UART_PUT_MR(port, mode); @@ -1213,6 +1320,53 @@ static int atmel_verify_port(struct uart_port *port, struct serial_struct *ser) return ret; } +#ifdef CONFIG_CONSOLE_POLL +static int atmel_poll_get_char(struct uart_port *port) +{ + while (!(UART_GET_CSR(port) & ATMEL_US_RXRDY)) + cpu_relax(); + + return UART_GET_CHAR(port); +} + +static void atmel_poll_put_char(struct uart_port *port, unsigned char ch) +{ + while (!(UART_GET_CSR(port) & ATMEL_US_TXRDY)) + cpu_relax(); + + UART_PUT_CHAR(port, ch); +} +#endif + +static int +atmel_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) +{ + struct serial_rs485 rs485conf; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, + sizeof(rs485conf))) + return -EFAULT; + + atmel_config_rs485(port, &rs485conf); + break; + + case TIOCGRS485: + if (copy_to_user((struct serial_rs485 *) arg, + &(to_atmel_uart_port(port)->rs485), + sizeof(rs485conf))) + return -EFAULT; + break; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + + + static struct uart_ops atmel_pops = { .tx_empty = atmel_tx_empty, .set_mctrl = atmel_set_mctrl, @@ -1232,6 +1386,11 @@ static struct uart_ops atmel_pops = { .config_port = atmel_config_port, .verify_port = atmel_verify_port, .pm = atmel_serial_pm, + .ioctl = atmel_ioctl, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = atmel_poll_get_char, + .poll_put_char = atmel_poll_put_char, +#endif }; /* @@ -1243,13 +1402,12 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, struct uart_port *port = &atmel_port->uart; struct atmel_uart_data *data = pdev->dev.platform_data; - port->iotype = UPIO_MEM; - port->flags = UPF_BOOT_AUTOCONF; - port->ops = &atmel_pops; - port->fifosize = 1; - port->line = pdev->id; - port->dev = &pdev->dev; - + port->iotype = UPIO_MEM; + port->flags = UPF_BOOT_AUTOCONF; + port->ops = &atmel_pops; + port->fifosize = 1; + port->line = pdev->id; + port->dev = &pdev->dev; port->mapbase = pdev->resource[0].start; port->irq = pdev->resource[1].start; @@ -1277,8 +1435,16 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, atmel_port->use_dma_rx = data->use_dma_rx; atmel_port->use_dma_tx = data->use_dma_tx; - if (atmel_use_dma_tx(port)) + atmel_port->rs485 = data->rs485; + /* Use TXEMPTY for interrupt when rs485 else TXRDY or ENDTX|TXBUFE */ + if (atmel_port->rs485.flags & SER_RS485_ENABLED) + atmel_port->tx_done_mask = ATMEL_US_TXEMPTY; + else if (atmel_use_dma_tx(port)) { port->fifosize = PDC_BUFFER_SIZE; + atmel_port->tx_done_mask = ATMEL_US_ENDTX | ATMEL_US_TXBUFE; + } else { + atmel_port->tx_done_mask = ATMEL_US_TXRDY; + } } /* @@ -1312,6 +1478,7 @@ static void atmel_console_putchar(struct uart_port *port, int ch) static void atmel_console_write(struct console *co, const char *s, u_int count) { struct uart_port *port = &atmel_ports[co->index].uart; + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); unsigned int status, imr; unsigned int pdc_tx; @@ -1319,7 +1486,7 @@ static void atmel_console_write(struct console *co, const char *s, u_int count) * First, save IMR and then disable interrupts */ imr = UART_GET_IMR(port); - UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY); + UART_PUT_IDR(port, ATMEL_US_RXRDY | atmel_port->tx_done_mask); /* Store PDC transmit status and disable it */ pdc_tx = UART_GET_PTSR(port) & ATMEL_PDC_TXTEN; @@ -1531,7 +1698,7 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev) void *data; int ret; - BUILD_BUG_ON(!is_power_of_2(ATMEL_SERIAL_RINGSIZE)); + BUILD_BUG_ON(ATMEL_SERIAL_RINGSIZE & (ATMEL_SERIAL_RINGSIZE - 1)); port = &atmel_ports[pdev->id]; port->backup_imr = 0; @@ -1551,6 +1718,7 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev) if (ret) goto err_add_port; +#ifdef CONFIG_SERIAL_ATMEL_CONSOLE if (atmel_is_console_port(&port->uart) && ATMEL_CONSOLE_DEVICE->flags & CON_ENABLED) { /* @@ -1559,6 +1727,7 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev) */ clk_disable(port->clk); } +#endif device_init_wakeup(&pdev->dev, 1); platform_set_drvdata(pdev, port); diff --git a/drivers/serial/bcm63xx_uart.c b/drivers/serial/bcm63xx_uart.c new file mode 100644 index 0000000..a1a0e55 --- /dev/null +++ b/drivers/serial/bcm63xx_uart.c @@ -0,0 +1,891 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Derived from many drivers using generic_serial interface. + * + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> + * + * Serial driver for BCM63xx integrated UART. + * + * Hardware flow control was _not_ tested since I only have RX/TX on + * my board. + */ + +#if defined(CONFIG_SERIAL_BCM63XX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/console.h> +#include <linux/clk.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/sysrq.h> +#include <linux/serial.h> +#include <linux/serial_core.h> + +#include <bcm63xx_clk.h> +#include <bcm63xx_irq.h> +#include <bcm63xx_regs.h> +#include <bcm63xx_io.h> + +#define BCM63XX_NR_UARTS 2 + +static struct uart_port ports[BCM63XX_NR_UARTS]; + +/* + * rx interrupt mask / stat + * + * mask: + * - rx fifo full + * - rx fifo above threshold + * - rx fifo not empty for too long + */ +#define UART_RX_INT_MASK (UART_IR_MASK(UART_IR_RXOVER) | \ + UART_IR_MASK(UART_IR_RXTHRESH) | \ + UART_IR_MASK(UART_IR_RXTIMEOUT)) + +#define UART_RX_INT_STAT (UART_IR_STAT(UART_IR_RXOVER) | \ + UART_IR_STAT(UART_IR_RXTHRESH) | \ + UART_IR_STAT(UART_IR_RXTIMEOUT)) + +/* + * tx interrupt mask / stat + * + * mask: + * - tx fifo empty + * - tx fifo below threshold + */ +#define UART_TX_INT_MASK (UART_IR_MASK(UART_IR_TXEMPTY) | \ + UART_IR_MASK(UART_IR_TXTRESH)) + +#define UART_TX_INT_STAT (UART_IR_STAT(UART_IR_TXEMPTY) | \ + UART_IR_STAT(UART_IR_TXTRESH)) + +/* + * external input interrupt + * + * mask: any edge on CTS, DCD + */ +#define UART_EXTINP_INT_MASK (UART_EXTINP_IRMASK(UART_EXTINP_IR_CTS) | \ + UART_EXTINP_IRMASK(UART_EXTINP_IR_DCD)) + +/* + * handy uart register accessor + */ +static inline unsigned int bcm_uart_readl(struct uart_port *port, + unsigned int offset) +{ + return bcm_readl(port->membase + offset); +} + +static inline void bcm_uart_writel(struct uart_port *port, + unsigned int value, unsigned int offset) +{ + bcm_writel(value, port->membase + offset); +} + +/* + * serial core request to check if uart tx fifo is empty + */ +static unsigned int bcm_uart_tx_empty(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + return (val & UART_IR_STAT(UART_IR_TXEMPTY)) ? 1 : 0; +} + +/* + * serial core request to set RTS and DTR pin state and loopback mode + */ +static void bcm_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_MCTL_REG); + val &= ~(UART_MCTL_DTR_MASK | UART_MCTL_RTS_MASK); + /* invert of written value is reflected on the pin */ + if (!(mctrl & TIOCM_DTR)) + val |= UART_MCTL_DTR_MASK; + if (!(mctrl & TIOCM_RTS)) + val |= UART_MCTL_RTS_MASK; + bcm_uart_writel(port, val, UART_MCTL_REG); + + val = bcm_uart_readl(port, UART_CTL_REG); + if (mctrl & TIOCM_LOOP) + val |= UART_CTL_LOOPBACK_MASK; + else + val &= ~UART_CTL_LOOPBACK_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); +} + +/* + * serial core request to return RI, CTS, DCD and DSR pin state + */ +static unsigned int bcm_uart_get_mctrl(struct uart_port *port) +{ + unsigned int val, mctrl; + + mctrl = 0; + val = bcm_uart_readl(port, UART_EXTINP_REG); + if (val & UART_EXTINP_RI_MASK) + mctrl |= TIOCM_RI; + if (val & UART_EXTINP_CTS_MASK) + mctrl |= TIOCM_CTS; + if (val & UART_EXTINP_DCD_MASK) + mctrl |= TIOCM_CD; + if (val & UART_EXTINP_DSR_MASK) + mctrl |= TIOCM_DSR; + return mctrl; +} + +/* + * serial core request to disable tx ASAP (used for flow control) + */ +static void bcm_uart_stop_tx(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_CTL_REG); + val &= ~(UART_CTL_TXEN_MASK); + bcm_uart_writel(port, val, UART_CTL_REG); + + val = bcm_uart_readl(port, UART_IR_REG); + val &= ~UART_TX_INT_MASK; + bcm_uart_writel(port, val, UART_IR_REG); +} + +/* + * serial core request to (re)enable tx + */ +static void bcm_uart_start_tx(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + val |= UART_TX_INT_MASK; + bcm_uart_writel(port, val, UART_IR_REG); + + val = bcm_uart_readl(port, UART_CTL_REG); + val |= UART_CTL_TXEN_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); +} + +/* + * serial core request to stop rx, called before port shutdown + */ +static void bcm_uart_stop_rx(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + val &= ~UART_RX_INT_MASK; + bcm_uart_writel(port, val, UART_IR_REG); +} + +/* + * serial core request to enable modem status interrupt reporting + */ +static void bcm_uart_enable_ms(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + val |= UART_IR_MASK(UART_IR_EXTIP); + bcm_uart_writel(port, val, UART_IR_REG); +} + +/* + * serial core request to start/stop emitting break char + */ +static void bcm_uart_break_ctl(struct uart_port *port, int ctl) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&port->lock, flags); + + val = bcm_uart_readl(port, UART_CTL_REG); + if (ctl) + val |= UART_CTL_XMITBRK_MASK; + else + val &= ~UART_CTL_XMITBRK_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* + * return port type in string format + */ +static const char *bcm_uart_type(struct uart_port *port) +{ + return (port->type == PORT_BCM63XX) ? "bcm63xx_uart" : NULL; +} + +/* + * read all chars in rx fifo and send them to core + */ +static void bcm_uart_do_rx(struct uart_port *port) +{ + struct tty_struct *tty; + unsigned int max_count; + + /* limit number of char read in interrupt, should not be + * higher than fifo size anyway since we're much faster than + * serial port */ + max_count = 32; + tty = port->state->port.tty; + do { + unsigned int iestat, c, cstat; + char flag; + + /* get overrun/fifo empty information from ier + * register */ + iestat = bcm_uart_readl(port, UART_IR_REG); + if (!(iestat & UART_IR_STAT(UART_IR_RXNOTEMPTY))) + break; + + cstat = c = bcm_uart_readl(port, UART_FIFO_REG); + port->icount.rx++; + flag = TTY_NORMAL; + c &= 0xff; + + if (unlikely((cstat & UART_FIFO_ANYERR_MASK))) { + /* do stats first */ + if (cstat & UART_FIFO_BRKDET_MASK) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } + + if (cstat & UART_FIFO_PARERR_MASK) + port->icount.parity++; + if (cstat & UART_FIFO_FRAMEERR_MASK) + port->icount.frame++; + + /* update flag wrt read_status_mask */ + cstat &= port->read_status_mask; + if (cstat & UART_FIFO_BRKDET_MASK) + flag = TTY_BREAK; + if (cstat & UART_FIFO_FRAMEERR_MASK) + flag = TTY_FRAME; + if (cstat & UART_FIFO_PARERR_MASK) + flag = TTY_PARITY; + } + + if (uart_handle_sysrq_char(port, c)) + continue; + + if (unlikely(iestat & UART_IR_STAT(UART_IR_RXOVER))) { + port->icount.overrun++; + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + + if ((cstat & port->ignore_status_mask) == 0) + tty_insert_flip_char(tty, c, flag); + + } while (--max_count); + + tty_flip_buffer_push(tty); +} + +/* + * fill tx fifo with chars to send, stop when fifo is about to be full + * or when all chars have been sent. + */ +static void bcm_uart_do_tx(struct uart_port *port) +{ + struct circ_buf *xmit; + unsigned int val, max_count; + + if (port->x_char) { + bcm_uart_writel(port, port->x_char, UART_FIFO_REG); + port->icount.tx++; + port->x_char = 0; + return; + } + + if (uart_tx_stopped(port)) { + bcm_uart_stop_tx(port); + return; + } + + xmit = &port->state->xmit; + if (uart_circ_empty(xmit)) + goto txq_empty; + + val = bcm_uart_readl(port, UART_MCTL_REG); + val = (val & UART_MCTL_TXFIFOFILL_MASK) >> UART_MCTL_TXFIFOFILL_SHIFT; + max_count = port->fifosize - val; + + while (max_count--) { + unsigned int c; + + c = xmit->buf[xmit->tail]; + bcm_uart_writel(port, c, UART_FIFO_REG); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + if (uart_circ_empty(xmit)) + break; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + goto txq_empty; + return; + +txq_empty: + /* nothing to send, disable transmit interrupt */ + val = bcm_uart_readl(port, UART_IR_REG); + val &= ~UART_TX_INT_MASK; + bcm_uart_writel(port, val, UART_IR_REG); + return; +} + +/* + * process uart interrupt + */ +static irqreturn_t bcm_uart_interrupt(int irq, void *dev_id) +{ + struct uart_port *port; + unsigned int irqstat; + + port = dev_id; + spin_lock(&port->lock); + + irqstat = bcm_uart_readl(port, UART_IR_REG); + if (irqstat & UART_RX_INT_STAT) + bcm_uart_do_rx(port); + + if (irqstat & UART_TX_INT_STAT) + bcm_uart_do_tx(port); + + if (irqstat & UART_IR_MASK(UART_IR_EXTIP)) { + unsigned int estat; + + estat = bcm_uart_readl(port, UART_EXTINP_REG); + if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_CTS)) + uart_handle_cts_change(port, + estat & UART_EXTINP_CTS_MASK); + if (estat & UART_EXTINP_IRSTAT(UART_EXTINP_IR_DCD)) + uart_handle_dcd_change(port, + estat & UART_EXTINP_DCD_MASK); + } + + spin_unlock(&port->lock); + return IRQ_HANDLED; +} + +/* + * enable rx & tx operation on uart + */ +static void bcm_uart_enable(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_CTL_REG); + val |= (UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | UART_CTL_RXEN_MASK); + bcm_uart_writel(port, val, UART_CTL_REG); +} + +/* + * disable rx & tx operation on uart + */ +static void bcm_uart_disable(struct uart_port *port) +{ + unsigned int val; + + val = bcm_uart_readl(port, UART_CTL_REG); + val &= ~(UART_CTL_BRGEN_MASK | UART_CTL_TXEN_MASK | + UART_CTL_RXEN_MASK); + bcm_uart_writel(port, val, UART_CTL_REG); +} + +/* + * clear all unread data in rx fifo and unsent data in tx fifo + */ +static void bcm_uart_flush(struct uart_port *port) +{ + unsigned int val; + + /* empty rx and tx fifo */ + val = bcm_uart_readl(port, UART_CTL_REG); + val |= UART_CTL_RSTRXFIFO_MASK | UART_CTL_RSTTXFIFO_MASK; + bcm_uart_writel(port, val, UART_CTL_REG); + + /* read any pending char to make sure all irq status are + * cleared */ + (void)bcm_uart_readl(port, UART_FIFO_REG); +} + +/* + * serial core request to initialize uart and start rx operation + */ +static int bcm_uart_startup(struct uart_port *port) +{ + unsigned int val; + int ret; + + /* mask all irq and flush port */ + bcm_uart_disable(port); + bcm_uart_writel(port, 0, UART_IR_REG); + bcm_uart_flush(port); + + /* clear any pending external input interrupt */ + (void)bcm_uart_readl(port, UART_EXTINP_REG); + + /* set rx/tx fifo thresh to fifo half size */ + val = bcm_uart_readl(port, UART_MCTL_REG); + val &= ~(UART_MCTL_RXFIFOTHRESH_MASK | UART_MCTL_TXFIFOTHRESH_MASK); + val |= (port->fifosize / 2) << UART_MCTL_RXFIFOTHRESH_SHIFT; + val |= (port->fifosize / 2) << UART_MCTL_TXFIFOTHRESH_SHIFT; + bcm_uart_writel(port, val, UART_MCTL_REG); + + /* set rx fifo timeout to 1 char time */ + val = bcm_uart_readl(port, UART_CTL_REG); + val &= ~UART_CTL_RXTMOUTCNT_MASK; + val |= 1 << UART_CTL_RXTMOUTCNT_SHIFT; + bcm_uart_writel(port, val, UART_CTL_REG); + + /* report any edge on dcd and cts */ + val = UART_EXTINP_INT_MASK; + val |= UART_EXTINP_DCD_NOSENSE_MASK; + val |= UART_EXTINP_CTS_NOSENSE_MASK; + bcm_uart_writel(port, val, UART_EXTINP_REG); + + /* register irq and enable rx interrupts */ + ret = request_irq(port->irq, bcm_uart_interrupt, 0, + bcm_uart_type(port), port); + if (ret) + return ret; + bcm_uart_writel(port, UART_RX_INT_MASK, UART_IR_REG); + bcm_uart_enable(port); + return 0; +} + +/* + * serial core request to flush & disable uart + */ +static void bcm_uart_shutdown(struct uart_port *port) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + bcm_uart_writel(port, 0, UART_IR_REG); + spin_unlock_irqrestore(&port->lock, flags); + + bcm_uart_disable(port); + bcm_uart_flush(port); + free_irq(port->irq, port); +} + +/* + * serial core request to change current uart setting + */ +static void bcm_uart_set_termios(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ + unsigned int ctl, baud, quot, ier; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + + /* disable uart while changing speed */ + bcm_uart_disable(port); + bcm_uart_flush(port); + + /* update Control register */ + ctl = bcm_uart_readl(port, UART_CTL_REG); + ctl &= ~UART_CTL_BITSPERSYM_MASK; + + switch (new->c_cflag & CSIZE) { + case CS5: + ctl |= (0 << UART_CTL_BITSPERSYM_SHIFT); + break; + case CS6: + ctl |= (1 << UART_CTL_BITSPERSYM_SHIFT); + break; + case CS7: + ctl |= (2 << UART_CTL_BITSPERSYM_SHIFT); + break; + default: + ctl |= (3 << UART_CTL_BITSPERSYM_SHIFT); + break; + } + + ctl &= ~UART_CTL_STOPBITS_MASK; + if (new->c_cflag & CSTOPB) + ctl |= UART_CTL_STOPBITS_2; + else + ctl |= UART_CTL_STOPBITS_1; + + ctl &= ~(UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK); + if (new->c_cflag & PARENB) + ctl |= (UART_CTL_RXPAREN_MASK | UART_CTL_TXPAREN_MASK); + ctl &= ~(UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK); + if (new->c_cflag & PARODD) + ctl |= (UART_CTL_RXPAREVEN_MASK | UART_CTL_TXPAREVEN_MASK); + bcm_uart_writel(port, ctl, UART_CTL_REG); + + /* update Baudword register */ + baud = uart_get_baud_rate(port, new, old, 0, port->uartclk / 16); + quot = uart_get_divisor(port, baud) - 1; + bcm_uart_writel(port, quot, UART_BAUD_REG); + + /* update Interrupt register */ + ier = bcm_uart_readl(port, UART_IR_REG); + + ier &= ~UART_IR_MASK(UART_IR_EXTIP); + if (UART_ENABLE_MS(port, new->c_cflag)) + ier |= UART_IR_MASK(UART_IR_EXTIP); + + bcm_uart_writel(port, ier, UART_IR_REG); + + /* update read/ignore mask */ + port->read_status_mask = UART_FIFO_VALID_MASK; + if (new->c_iflag & INPCK) { + port->read_status_mask |= UART_FIFO_FRAMEERR_MASK; + port->read_status_mask |= UART_FIFO_PARERR_MASK; + } + if (new->c_iflag & (BRKINT)) + port->read_status_mask |= UART_FIFO_BRKDET_MASK; + + port->ignore_status_mask = 0; + if (new->c_iflag & IGNPAR) + port->ignore_status_mask |= UART_FIFO_PARERR_MASK; + if (new->c_iflag & IGNBRK) + port->ignore_status_mask |= UART_FIFO_BRKDET_MASK; + if (!(new->c_cflag & CREAD)) + port->ignore_status_mask |= UART_FIFO_VALID_MASK; + + uart_update_timeout(port, new->c_cflag, baud); + bcm_uart_enable(port); + spin_unlock_irqrestore(&port->lock, flags); +} + +/* + * serial core request to claim uart iomem + */ +static int bcm_uart_request_port(struct uart_port *port) +{ + unsigned int size; + + size = RSET_UART_SIZE; + if (!request_mem_region(port->mapbase, size, "bcm63xx")) { + dev_err(port->dev, "Memory region busy\n"); + return -EBUSY; + } + + port->membase = ioremap(port->mapbase, size); + if (!port->membase) { + dev_err(port->dev, "Unable to map registers\n"); + release_mem_region(port->mapbase, size); + return -EBUSY; + } + return 0; +} + +/* + * serial core request to release uart iomem + */ +static void bcm_uart_release_port(struct uart_port *port) +{ + release_mem_region(port->mapbase, RSET_UART_SIZE); + iounmap(port->membase); +} + +/* + * serial core request to do any port required autoconfiguration + */ +static void bcm_uart_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + if (bcm_uart_request_port(port)) + return; + port->type = PORT_BCM63XX; + } +} + +/* + * serial core request to check that port information in serinfo are + * suitable + */ +static int bcm_uart_verify_port(struct uart_port *port, + struct serial_struct *serinfo) +{ + if (port->type != PORT_BCM63XX) + return -EINVAL; + if (port->irq != serinfo->irq) + return -EINVAL; + if (port->iotype != serinfo->io_type) + return -EINVAL; + if (port->mapbase != (unsigned long)serinfo->iomem_base) + return -EINVAL; + return 0; +} + +/* serial core callbacks */ +static struct uart_ops bcm_uart_ops = { + .tx_empty = bcm_uart_tx_empty, + .get_mctrl = bcm_uart_get_mctrl, + .set_mctrl = bcm_uart_set_mctrl, + .start_tx = bcm_uart_start_tx, + .stop_tx = bcm_uart_stop_tx, + .stop_rx = bcm_uart_stop_rx, + .enable_ms = bcm_uart_enable_ms, + .break_ctl = bcm_uart_break_ctl, + .startup = bcm_uart_startup, + .shutdown = bcm_uart_shutdown, + .set_termios = bcm_uart_set_termios, + .type = bcm_uart_type, + .release_port = bcm_uart_release_port, + .request_port = bcm_uart_request_port, + .config_port = bcm_uart_config_port, + .verify_port = bcm_uart_verify_port, +}; + + + +#ifdef CONFIG_SERIAL_BCM63XX_CONSOLE +static inline void wait_for_xmitr(struct uart_port *port) +{ + unsigned int tmout; + + /* Wait up to 10ms for the character(s) to be sent. */ + tmout = 10000; + while (--tmout) { + unsigned int val; + + val = bcm_uart_readl(port, UART_IR_REG); + if (val & UART_IR_STAT(UART_IR_TXEMPTY)) + break; + udelay(1); + } + + /* Wait up to 1s for flow control if necessary */ + if (port->flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout) { + unsigned int val; + + val = bcm_uart_readl(port, UART_EXTINP_REG); + if (val & UART_EXTINP_CTS_MASK) + break; + udelay(1); + } + } +} + +/* + * output given char + */ +static void bcm_console_putchar(struct uart_port *port, int ch) +{ + wait_for_xmitr(port); + bcm_uart_writel(port, ch, UART_FIFO_REG); +} + +/* + * console core request to output given string + */ +static void bcm_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port; + unsigned long flags; + int locked; + + port = &ports[co->index]; + + local_irq_save(flags); + if (port->sysrq) { + /* bcm_uart_interrupt() already took the lock */ + locked = 0; + } else if (oops_in_progress) { + locked = spin_trylock(&port->lock); + } else { + spin_lock(&port->lock); + locked = 1; + } + + /* call helper to deal with \r\n */ + uart_console_write(port, s, count, bcm_console_putchar); + + /* and wait for char to be transmitted */ + wait_for_xmitr(port); + + if (locked) + spin_unlock(&port->lock); + local_irq_restore(flags); +} + +/* + * console core request to setup given console, find matching uart + * port and setup it. + */ +static int bcm_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (co->index < 0 || co->index >= BCM63XX_NR_UARTS) + return -EINVAL; + port = &ports[co->index]; + if (!port->membase) + return -ENODEV; + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver bcm_uart_driver; + +static struct console bcm63xx_console = { + .name = "ttyS", + .write = bcm_console_write, + .device = uart_console_device, + .setup = bcm_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &bcm_uart_driver, +}; + +static int __init bcm63xx_console_init(void) +{ + register_console(&bcm63xx_console); + return 0; +} + +console_initcall(bcm63xx_console_init); + +#define BCM63XX_CONSOLE (&bcm63xx_console) +#else +#define BCM63XX_CONSOLE NULL +#endif /* CONFIG_SERIAL_BCM63XX_CONSOLE */ + +static struct uart_driver bcm_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "bcm63xx_uart", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = BCM63XX_NR_UARTS, + .cons = BCM63XX_CONSOLE, +}; + +/* + * platform driver probe/remove callback + */ +static int __devinit bcm_uart_probe(struct platform_device *pdev) +{ + struct resource *res_mem, *res_irq; + struct uart_port *port; + struct clk *clk; + int ret; + + if (pdev->id < 0 || pdev->id >= BCM63XX_NR_UARTS) + return -EINVAL; + + if (ports[pdev->id].membase) + return -EBUSY; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) + return -ENODEV; + + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res_irq) + return -ENODEV; + + clk = clk_get(&pdev->dev, "periph"); + if (IS_ERR(clk)) + return -ENODEV; + + port = &ports[pdev->id]; + memset(port, 0, sizeof(*port)); + port->iotype = UPIO_MEM; + port->mapbase = res_mem->start; + port->irq = res_irq->start; + port->ops = &bcm_uart_ops; + port->flags = UPF_BOOT_AUTOCONF; + port->dev = &pdev->dev; + port->fifosize = 16; + port->uartclk = clk_get_rate(clk) / 2; + port->line = pdev->id; + clk_put(clk); + + ret = uart_add_one_port(&bcm_uart_driver, port); + if (ret) { + ports[pdev->id].membase = 0; + return ret; + } + platform_set_drvdata(pdev, port); + return 0; +} + +static int __devexit bcm_uart_remove(struct platform_device *pdev) +{ + struct uart_port *port; + + port = platform_get_drvdata(pdev); + uart_remove_one_port(&bcm_uart_driver, port); + platform_set_drvdata(pdev, NULL); + /* mark port as free */ + ports[pdev->id].membase = 0; + return 0; +} + +/* + * platform driver stuff + */ +static struct platform_driver bcm_uart_platform_driver = { + .probe = bcm_uart_probe, + .remove = __devexit_p(bcm_uart_remove), + .driver = { + .owner = THIS_MODULE, + .name = "bcm63xx_uart", + }, +}; + +static int __init bcm_uart_init(void) +{ + int ret; + + ret = uart_register_driver(&bcm_uart_driver); + if (ret) + return ret; + + ret = platform_driver_register(&bcm_uart_platform_driver); + if (ret) + uart_unregister_driver(&bcm_uart_driver); + + return ret; +} + +static void __exit bcm_uart_exit(void) +{ + platform_driver_unregister(&bcm_uart_platform_driver); + uart_unregister_driver(&bcm_uart_driver); +} + +module_init(bcm_uart_init); +module_exit(bcm_uart_exit); + +MODULE_AUTHOR("Maxime Bizon <mbizon@freebox.fr>"); +MODULE_DESCRIPTION("Broadcom 63<xx integrated uart driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c index b4a7650..e381b89 100644 --- a/drivers/serial/bfin_5xx.c +++ b/drivers/serial/bfin_5xx.c @@ -1,7 +1,7 @@ /* * Blackfin On-Chip Serial Driver * - * Copyright 2006-2008 Analog Devices Inc. + * Copyright 2006-2010 Analog Devices Inc. * * Enter bugs at http://blackfin.uclinux.org/ * @@ -12,8 +12,13 @@ #define SUPPORT_SYSRQ #endif +#define DRIVER_NAME "bfin-uart" +#define pr_fmt(fmt) DRIVER_NAME ": " fmt + #include <linux/module.h> #include <linux/ioport.h> +#include <linux/gfp.h> +#include <linux/io.h> #include <linux/init.h> #include <linux/console.h> #include <linux/sysrq.h> @@ -21,21 +26,23 @@ #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/serial_core.h> - -#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ - defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) +#include <linux/gpio.h> +#include <linux/irq.h> #include <linux/kgdb.h> -#include <asm/irq_regs.h> -#endif - -#include <asm/gpio.h> -#include <mach/bfin_serial_5xx.h> - -#ifdef CONFIG_SERIAL_BFIN_DMA +#include <linux/slab.h> #include <linux/dma-mapping.h> -#include <asm/io.h> -#include <asm/irq.h> + +#include <asm/portmux.h> #include <asm/cacheflush.h> +#include <asm/dma.h> + +#define port_membase(uart) (((struct bfin_serial_port *)(uart))->port.membase) +#define get_lsr_cache(uart) (((struct bfin_serial_port *)(uart))->lsr) +#define put_lsr_cache(uart, v) (((struct bfin_serial_port *)(uart))->lsr = (v)) +#include <asm/bfin_serial.h> + +#ifdef CONFIG_SERIAL_BFIN_MODULE +# undef CONFIG_EARLY_PRINTK #endif #ifdef CONFIG_SERIAL_BFIN_MODULE @@ -43,12 +50,11 @@ #endif /* UART name and device definitions */ -#define BFIN_SERIAL_NAME "ttyBF" +#define BFIN_SERIAL_DEV_NAME "ttyBF" #define BFIN_SERIAL_MAJOR 204 #define BFIN_SERIAL_MINOR 64 -static struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; -static int nr_active_ports = ARRAY_SIZE(bfin_serial_resource); +static struct bfin_serial_port *bfin_serial_ports[BFIN_UART_NR_PORTS]; #if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) @@ -140,7 +146,7 @@ static void bfin_serial_stop_tx(struct uart_port *port) { struct bfin_serial_port *uart = (struct bfin_serial_port *)port; #ifdef CONFIG_SERIAL_BFIN_DMA - struct circ_buf *xmit = &uart->port.info->xmit; + struct circ_buf *xmit = &uart->port.state->xmit; #endif while (!(UART_GET_LSR(uart) & TEMT)) @@ -167,7 +173,7 @@ static void bfin_serial_stop_tx(struct uart_port *port) static void bfin_serial_start_tx(struct uart_port *port) { struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - struct tty_struct *tty = uart->port.info->port.tty; + struct tty_struct *tty = uart->port.state->port.tty; #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) { @@ -233,16 +239,17 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart) #if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \ defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE) - if (kgdb_connected && kgdboc_port_line == uart->port.line) + if (kgdb_connected && kgdboc_port_line == uart->port.line + && kgdboc_break_enabled) if (ch == 0x3) {/* Ctrl + C */ kgdb_breakpoint(); return; } - if (!uart->port.info || !uart->port.info->port.tty) + if (!uart->port.state || !uart->port.state->port.tty) return; #endif - tty = uart->port.info->port.tty; + tty = uart->port.state->port.tty; if (ANOMALY_05000363) { /* The BF533 (and BF561) family of processors have a nice anomaly @@ -327,7 +334,7 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart) static void bfin_serial_tx_chars(struct bfin_serial_port *uart) { - struct circ_buf *xmit = &uart->port.info->xmit; + struct circ_buf *xmit = &uart->port.state->xmit; if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { #ifdef CONFIG_BF54x @@ -353,7 +360,6 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart) UART_PUT_CHAR(uart, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); uart->port.icount.tx++; - SSYNC(); } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -394,7 +400,7 @@ static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id) #ifdef CONFIG_SERIAL_BFIN_DMA static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart) { - struct circ_buf *xmit = &uart->port.info->xmit; + struct circ_buf *xmit = &uart->port.state->xmit; uart->tx_done = 0; @@ -432,7 +438,7 @@ static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart) static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart) { - struct tty_struct *tty = uart->port.info->port.tty; + struct tty_struct *tty = uart->port.state->port.tty; int i, flg, status; status = UART_GET_LSR(uart); @@ -484,6 +490,7 @@ void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) { int x_pos, pos; + dma_disable_irq(uart->tx_dma_channel); dma_disable_irq(uart->rx_dma_channel); spin_lock_bh(&uart->port.lock); @@ -517,6 +524,7 @@ void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) } spin_unlock_bh(&uart->port.lock); + dma_enable_irq(uart->tx_dma_channel); dma_enable_irq(uart->rx_dma_channel); mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES); @@ -525,7 +533,7 @@ void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart) static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id) { struct bfin_serial_port *uart = dev_id; - struct circ_buf *xmit = &uart->port.info->xmit; + struct circ_buf *xmit = &uart->port.state->xmit; #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS if (uart->scts && !(bfin_serial_get_mctrl(&uart->port)&TIOCM_CTS)) { @@ -679,6 +687,13 @@ static int bfin_serial_startup(struct uart_port *port) # ifdef CONFIG_BF54x { + /* + * UART2 and UART3 on BF548 share interrupt PINs and DMA + * controllers with SPORT2 and SPORT3. UART rx and tx + * interrupts are generated in PIO mode only when configure + * their peripheral mapping registers properly, which means + * request corresponding DMA channels in PIO mode as well. + */ unsigned uart_dma_ch_rx, uart_dma_ch_tx; switch (uart->port.irq) { @@ -725,30 +740,19 @@ static int bfin_serial_startup(struct uart_port *port) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED, "BFIN_UART_CTS", uart)) { uart->cts_pin = -1; - pr_info("Unable to attach BlackFin UART CTS interrupt.\ - So, disable it.\n"); + pr_info("Unable to attach BlackFin UART CTS interrupt. So, disable it.\n"); } } if (uart->rts_pin >= 0) { - gpio_request(uart->rts_pin, DRIVER_NAME); gpio_direction_output(uart->rts_pin, 0); } #endif #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS - if (request_irq(uart->status_irq, + if (uart->cts_pin >= 0 && request_irq(uart->status_irq, bfin_serial_mctrl_cts_int, IRQF_DISABLED, "BFIN_UART_MODEM_STATUS", uart)) { - pr_info("Unable to attach BlackFin UART Modem \ - Status interrupt.\n"); - } - - if (uart->cts_pin >= 0) { - gpio_request(uart->cts_pin, DRIVER_NAME); - gpio_direction_output(uart->cts_pin, 1); - } - if (uart->rts_pin >= 0) { - gpio_request(uart->rts_pin, DRIVER_NAME); - gpio_direction_output(uart->rts_pin, 0); + uart->cts_pin = -1; + pr_info("Unable to attach BlackFin UART Modem Status interrupt.\n"); } /* CTS RTS PINs are negative assertive. */ @@ -793,15 +797,9 @@ static void bfin_serial_shutdown(struct uart_port *port) #ifdef CONFIG_SERIAL_BFIN_CTSRTS if (uart->cts_pin >= 0) free_irq(gpio_to_irq(uart->cts_pin), uart); - if (uart->rts_pin >= 0) - gpio_free(uart->rts_pin); #endif #ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS if (uart->cts_pin >= 0) - gpio_free(uart->cts_pin); - if (uart->rts_pin >= 0) - gpio_free(uart->rts_pin); - if (UART_GET_IER(uart) && EDSSI) free_irq(uart->status_irq, uart); #endif } @@ -850,6 +848,8 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, if (termios->c_cflag & CMSPAR) lcr |= STP; + spin_lock_irqsave(&uart->port.lock, flags); + port->read_status_mask = OE; if (termios->c_iflag & INPCK) port->read_status_mask |= (FE | PE); @@ -873,8 +873,11 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, } baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - quot = uart_get_divisor(port, baud) - ANOMALY_05000230; - spin_lock_irqsave(&uart->port.lock, flags); + quot = uart_get_divisor(port, baud); + + /* If discipline is not IRDA, apply ANOMALY_05000230 */ + if (termios->c_line != N_IRDA) + quot -= ANOMALY_05000230; UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15); @@ -956,38 +959,35 @@ bfin_serial_verify_port(struct uart_port *port, struct serial_struct *ser) * Enable the IrDA function if tty->ldisc.num is N_IRDA. * In other cases, disable IrDA function. */ -static void bfin_serial_set_ldisc(struct uart_port *port) +static void bfin_serial_set_ldisc(struct uart_port *port, int ld) { - int line = port->line; + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; unsigned short val; - if (line >= port->info->port.tty->driver->num) - return; - - switch (port->info->port.tty->termios->c_line) { + switch (ld) { case N_IRDA: - val = UART_GET_GCTL(&bfin_serial_ports[line]); + val = UART_GET_GCTL(uart); val |= (IREN | RPOLC); - UART_PUT_GCTL(&bfin_serial_ports[line], val); + UART_PUT_GCTL(uart, val); break; default: - val = UART_GET_GCTL(&bfin_serial_ports[line]); + val = UART_GET_GCTL(uart); val &= ~(IREN | RPOLC); - UART_PUT_GCTL(&bfin_serial_ports[line], val); + UART_PUT_GCTL(uart, val); } } static void bfin_serial_reset_irda(struct uart_port *port) { - int line = port->line; + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; unsigned short val; - val = UART_GET_GCTL(&bfin_serial_ports[line]); + val = UART_GET_GCTL(uart); val &= ~(IREN | RPOLC); - UART_PUT_GCTL(&bfin_serial_ports[line], val); + UART_PUT_GCTL(uart, val); SSYNC(); val |= (IREN | RPOLC); - UART_PUT_GCTL(&bfin_serial_ports[line], val); + UART_PUT_GCTL(uart, val); SSYNC(); } @@ -1069,85 +1069,6 @@ static struct uart_ops bfin_serial_pops = { #endif }; -static void __init bfin_serial_hw_init(void) -{ -#ifdef CONFIG_SERIAL_BFIN_UART0 - peripheral_request(P_UART0_TX, DRIVER_NAME); - peripheral_request(P_UART0_RX, DRIVER_NAME); -#endif - -#ifdef CONFIG_SERIAL_BFIN_UART1 - peripheral_request(P_UART1_TX, DRIVER_NAME); - peripheral_request(P_UART1_RX, DRIVER_NAME); - -# if defined(CONFIG_BFIN_UART1_CTSRTS) && defined(CONFIG_BF54x) - peripheral_request(P_UART1_RTS, DRIVER_NAME); - peripheral_request(P_UART1_CTS, DRIVER_NAME); -# endif -#endif - -#ifdef CONFIG_SERIAL_BFIN_UART2 - peripheral_request(P_UART2_TX, DRIVER_NAME); - peripheral_request(P_UART2_RX, DRIVER_NAME); -#endif - -#ifdef CONFIG_SERIAL_BFIN_UART3 - peripheral_request(P_UART3_TX, DRIVER_NAME); - peripheral_request(P_UART3_RX, DRIVER_NAME); - -# if defined(CONFIG_BFIN_UART3_CTSRTS) && defined(CONFIG_BF54x) - peripheral_request(P_UART3_RTS, DRIVER_NAME); - peripheral_request(P_UART3_CTS, DRIVER_NAME); -# endif -#endif -} - -static void __init bfin_serial_init_ports(void) -{ - static int first = 1; - int i; - - if (!first) - return; - first = 0; - - bfin_serial_hw_init(); - - for (i = 0; i < nr_active_ports; i++) { - spin_lock_init(&bfin_serial_ports[i].port.lock); - bfin_serial_ports[i].port.uartclk = get_sclk(); - bfin_serial_ports[i].port.fifosize = BFIN_UART_TX_FIFO_SIZE; - bfin_serial_ports[i].port.ops = &bfin_serial_pops; - bfin_serial_ports[i].port.line = i; - bfin_serial_ports[i].port.iotype = UPIO_MEM; - bfin_serial_ports[i].port.membase = - (void __iomem *)bfin_serial_resource[i].uart_base_addr; - bfin_serial_ports[i].port.mapbase = - bfin_serial_resource[i].uart_base_addr; - bfin_serial_ports[i].port.irq = - bfin_serial_resource[i].uart_irq; - bfin_serial_ports[i].status_irq = - bfin_serial_resource[i].uart_status_irq; - bfin_serial_ports[i].port.flags = UPF_BOOT_AUTOCONF; -#ifdef CONFIG_SERIAL_BFIN_DMA - bfin_serial_ports[i].tx_done = 1; - bfin_serial_ports[i].tx_count = 0; - bfin_serial_ports[i].tx_dma_channel = - bfin_serial_resource[i].uart_tx_dma_channel; - bfin_serial_ports[i].rx_dma_channel = - bfin_serial_resource[i].uart_rx_dma_channel; - init_timer(&(bfin_serial_ports[i].rx_dma_timer)); -#endif -#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) - bfin_serial_ports[i].cts_pin = - bfin_serial_resource[i].uart_cts_pin; - bfin_serial_ports[i].rts_pin = - bfin_serial_resource[i].uart_rts_pin; -#endif - } -} - #if defined(CONFIG_SERIAL_BFIN_CONSOLE) || defined(CONFIG_EARLY_PRINTK) /* * If the port was already initialised (eg, by a boot loader), @@ -1195,6 +1116,34 @@ bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud, static struct uart_driver bfin_serial_reg; +static void bfin_serial_console_putchar(struct uart_port *port, int ch) +{ + struct bfin_serial_port *uart = (struct bfin_serial_port *)port; + while (!(UART_GET_LSR(uart) & THRE)) + barrier(); + UART_PUT_CHAR(uart, ch); +} + +#endif /* defined (CONFIG_SERIAL_BFIN_CONSOLE) || + defined (CONFIG_EARLY_PRINTK) */ + +#ifdef CONFIG_SERIAL_BFIN_CONSOLE +#define CLASS_BFIN_CONSOLE "bfin-console" +/* + * Interrupts are disabled on entering + */ +static void +bfin_serial_console_write(struct console *co, const char *s, unsigned int count) +{ + struct bfin_serial_port *uart = bfin_serial_ports[co->index]; + unsigned long flags; + + spin_lock_irqsave(&uart->port.lock, flags); + uart_console_write(&uart->port, s, count, bfin_serial_console_putchar); + spin_unlock_irqrestore(&uart->port.lock, flags); + +} + static int __init bfin_serial_console_setup(struct console *co, char *options) { @@ -1214,9 +1163,12 @@ bfin_serial_console_setup(struct console *co, char *options) * if so, search for the first available port that does have * console support. */ - if (co->index == -1 || co->index >= nr_active_ports) - co->index = 0; - uart = &bfin_serial_ports[co->index]; + if (co->index < 0 || co->index >= BFIN_UART_NR_PORTS) + return -ENODEV; + + uart = bfin_serial_ports[co->index]; + if (!uart) + return -ENODEV; if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); @@ -1225,36 +1177,9 @@ bfin_serial_console_setup(struct console *co, char *options) return uart_set_options(&uart->port, co, baud, parity, bits, flow); } -#endif /* defined (CONFIG_SERIAL_BFIN_CONSOLE) || - defined (CONFIG_EARLY_PRINTK) */ - -#ifdef CONFIG_SERIAL_BFIN_CONSOLE -static void bfin_serial_console_putchar(struct uart_port *port, int ch) -{ - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - while (!(UART_GET_LSR(uart) & THRE)) - barrier(); - UART_PUT_CHAR(uart, ch); - SSYNC(); -} - -/* - * Interrupts are disabled on entering - */ -static void -bfin_serial_console_write(struct console *co, const char *s, unsigned int count) -{ - struct bfin_serial_port *uart = &bfin_serial_ports[co->index]; - unsigned long flags; - - spin_lock_irqsave(&uart->port.lock, flags); - uart_console_write(&uart->port, s, count, bfin_serial_console_putchar); - spin_unlock_irqrestore(&uart->port.lock, flags); - -} static struct console bfin_serial_console = { - .name = BFIN_SERIAL_NAME, + .name = BFIN_SERIAL_DEV_NAME, .write = bfin_serial_console_write, .device = uart_console_device, .setup = bfin_serial_console_setup, @@ -1262,44 +1187,30 @@ static struct console bfin_serial_console = { .index = -1, .data = &bfin_serial_reg, }; - -static int __init bfin_serial_rs_console_init(void) -{ - bfin_serial_init_ports(); - register_console(&bfin_serial_console); - - return 0; -} -console_initcall(bfin_serial_rs_console_init); - #define BFIN_SERIAL_CONSOLE &bfin_serial_console #else #define BFIN_SERIAL_CONSOLE NULL #endif /* CONFIG_SERIAL_BFIN_CONSOLE */ +#ifdef CONFIG_EARLY_PRINTK +static struct bfin_serial_port bfin_earlyprintk_port; +#define CLASS_BFIN_EARLYPRINTK "bfin-earlyprintk" -#ifdef CONFIG_EARLY_PRINTK -static __init void early_serial_putc(struct uart_port *port, int ch) +/* + * Interrupts are disabled on entering + */ +static void +bfin_earlyprintk_console_write(struct console *co, const char *s, unsigned int count) { - unsigned timeout = 0xffff; - struct bfin_serial_port *uart = (struct bfin_serial_port *)port; - - while ((!(UART_GET_LSR(uart) & THRE)) && --timeout) - cpu_relax(); - UART_PUT_CHAR(uart, ch); -} + unsigned long flags; -static __init void early_serial_write(struct console *con, const char *s, - unsigned int n) -{ - struct bfin_serial_port *uart = &bfin_serial_ports[con->index]; - unsigned int i; + if (bfin_earlyprintk_port.port.line != co->index) + return; - for (i = 0; i < n; i++, s++) { - if (*s == '\n') - early_serial_putc(&uart->port, '\r'); - early_serial_putc(&uart->port, *s); - } + spin_lock_irqsave(&bfin_earlyprintk_port.port.lock, flags); + uart_console_write(&bfin_earlyprintk_port.port, s, count, + bfin_serial_console_putchar); + spin_unlock_irqrestore(&bfin_earlyprintk_port.port.lock, flags); } /* @@ -1310,106 +1221,204 @@ static __init void early_serial_write(struct console *con, const char *s, */ static struct __initdata console bfin_early_serial_console = { .name = "early_BFuart", - .write = early_serial_write, + .write = bfin_earlyprintk_console_write, .device = uart_console_device, .flags = CON_PRINTBUFFER, .index = -1, .data = &bfin_serial_reg, }; - -struct console __init *bfin_earlyserial_init(unsigned int port, - unsigned int cflag) -{ - struct bfin_serial_port *uart; - struct ktermios t; - - if (port == -1 || port >= nr_active_ports) - port = 0; - bfin_serial_init_ports(); - bfin_early_serial_console.index = port; - uart = &bfin_serial_ports[port]; - t.c_cflag = cflag; - t.c_iflag = 0; - t.c_oflag = 0; - t.c_lflag = ICANON; - t.c_line = port; - bfin_serial_set_termios(&uart->port, &t, &t); - return &bfin_early_serial_console; -} - -#endif /* CONFIG_EARLY_PRINTK */ +#endif static struct uart_driver bfin_serial_reg = { .owner = THIS_MODULE, - .driver_name = "bfin-uart", - .dev_name = BFIN_SERIAL_NAME, + .driver_name = DRIVER_NAME, + .dev_name = BFIN_SERIAL_DEV_NAME, .major = BFIN_SERIAL_MAJOR, .minor = BFIN_SERIAL_MINOR, .nr = BFIN_UART_NR_PORTS, .cons = BFIN_SERIAL_CONSOLE, }; -static int bfin_serial_suspend(struct platform_device *dev, pm_message_t state) +static int bfin_serial_suspend(struct platform_device *pdev, pm_message_t state) { - int i; + struct bfin_serial_port *uart = platform_get_drvdata(pdev); - for (i = 0; i < nr_active_ports; i++) { - if (bfin_serial_ports[i].port.dev != &dev->dev) - continue; - uart_suspend_port(&bfin_serial_reg, &bfin_serial_ports[i].port); - } + return uart_suspend_port(&bfin_serial_reg, &uart->port); +} - return 0; +static int bfin_serial_resume(struct platform_device *pdev) +{ + struct bfin_serial_port *uart = platform_get_drvdata(pdev); + + return uart_resume_port(&bfin_serial_reg, &uart->port); } -static int bfin_serial_resume(struct platform_device *dev) +static int bfin_serial_probe(struct platform_device *pdev) { - int i; + struct resource *res; + struct bfin_serial_port *uart = NULL; + int ret = 0; - for (i = 0; i < nr_active_ports; i++) { - if (bfin_serial_ports[i].port.dev != &dev->dev) - continue; - uart_resume_port(&bfin_serial_reg, &bfin_serial_ports[i].port); + if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) { + dev_err(&pdev->dev, "Wrong bfin uart platform device id.\n"); + return -ENOENT; } - return 0; -} + if (bfin_serial_ports[pdev->id] == NULL) { -static int bfin_serial_probe(struct platform_device *dev) -{ - struct resource *res = dev->resource; - int i; + uart = kzalloc(sizeof(*uart), GFP_KERNEL); + if (!uart) { + dev_err(&pdev->dev, + "fail to malloc bfin_serial_port\n"); + return -ENOMEM; + } + bfin_serial_ports[pdev->id] = uart; - for (i = 0; i < dev->num_resources; i++, res++) - if (res->flags & IORESOURCE_MEM) - break; +#ifdef CONFIG_EARLY_PRINTK + if (!(bfin_earlyprintk_port.port.membase + && bfin_earlyprintk_port.port.line == pdev->id)) { + /* + * If the peripheral PINs of current port is allocated + * in earlyprintk probe stage, don't do it again. + */ +#endif + ret = peripheral_request_list( + (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); + if (ret) { + dev_err(&pdev->dev, + "fail to request bfin serial peripherals\n"); + goto out_error_free_mem; + } +#ifdef CONFIG_EARLY_PRINTK + } +#endif + + spin_lock_init(&uart->port.lock); + uart->port.uartclk = get_sclk(); + uart->port.fifosize = BFIN_UART_TX_FIFO_SIZE; + uart->port.ops = &bfin_serial_pops; + uart->port.line = pdev->id; + uart->port.iotype = UPIO_MEM; + uart->port.flags = UPF_BOOT_AUTOCONF; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto out_error_free_peripherals; + } + + uart->port.membase = ioremap(res->start, + res->end - res->start); + if (!uart->port.membase) { + dev_err(&pdev->dev, "Cannot map uart IO\n"); + ret = -ENXIO; + goto out_error_free_peripherals; + } + uart->port.mapbase = res->start; + + uart->port.irq = platform_get_irq(pdev, 0); + if (uart->port.irq < 0) { + dev_err(&pdev->dev, "No uart RX/TX IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } - if (i < dev->num_resources) { - for (i = 0; i < nr_active_ports; i++, res++) { - if (bfin_serial_ports[i].port.mapbase != res->start) - continue; - bfin_serial_ports[i].port.dev = &dev->dev; - uart_add_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port); + uart->status_irq = platform_get_irq(pdev, 1); + if (uart->status_irq < 0) { + dev_err(&pdev->dev, "No uart status IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; } + +#ifdef CONFIG_SERIAL_BFIN_DMA + uart->tx_done = 1; + uart->tx_count = 0; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (res == NULL) { + dev_err(&pdev->dev, "No uart TX DMA channel specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + uart->tx_dma_channel = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (res == NULL) { + dev_err(&pdev->dev, "No uart RX DMA channel specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + uart->rx_dma_channel = res->start; + + init_timer(&(uart->rx_dma_timer)); +#endif + +#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ + defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res == NULL) + uart->cts_pin = -1; + else + uart->cts_pin = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IO, 1); + if (res == NULL) + uart->rts_pin = -1; + else + uart->rts_pin = res->start; +# if defined(CONFIG_SERIAL_BFIN_CTSRTS) + if (uart->rts_pin >= 0) + gpio_request(uart->rts_pin, DRIVER_NAME); +# endif +#endif } - return 0; +#ifdef CONFIG_SERIAL_BFIN_CONSOLE + if (!is_early_platform_device(pdev)) { +#endif + uart = bfin_serial_ports[pdev->id]; + uart->port.dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, uart); + ret = uart_add_one_port(&bfin_serial_reg, &uart->port); +#ifdef CONFIG_SERIAL_BFIN_CONSOLE + } +#endif + + if (!ret) + return 0; + + if (uart) { +out_error_unmap: + iounmap(uart->port.membase); +out_error_free_peripherals: + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); +out_error_free_mem: + kfree(uart); + bfin_serial_ports[pdev->id] = NULL; + } + + return ret; } -static int bfin_serial_remove(struct platform_device *dev) +static int __devexit bfin_serial_remove(struct platform_device *pdev) { - int i; + struct bfin_serial_port *uart = platform_get_drvdata(pdev); - for (i = 0; i < nr_active_ports; i++) { - if (bfin_serial_ports[i].port.dev != &dev->dev) - continue; - uart_remove_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port); - bfin_serial_ports[i].port.dev = NULL; -#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \ - defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS) - gpio_free(bfin_serial_ports[i].cts_pin); - gpio_free(bfin_serial_ports[i].rts_pin); + dev_set_drvdata(&pdev->dev, NULL); + + if (uart) { + uart_remove_one_port(&bfin_serial_reg, &uart->port); +#ifdef CONFIG_SERIAL_BFIN_CTSRTS + if (uart->rts_pin >= 0) + gpio_free(uart->rts_pin); #endif + iounmap(uart->port.membase); + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); + kfree(uart); + bfin_serial_ports[pdev->id] = NULL; } return 0; @@ -1417,31 +1426,160 @@ static int bfin_serial_remove(struct platform_device *dev) static struct platform_driver bfin_serial_driver = { .probe = bfin_serial_probe, - .remove = bfin_serial_remove, + .remove = __devexit_p(bfin_serial_remove), .suspend = bfin_serial_suspend, .resume = bfin_serial_resume, .driver = { - .name = "bfin-uart", + .name = DRIVER_NAME, .owner = THIS_MODULE, }, }; -static int __init bfin_serial_init(void) +#if defined(CONFIG_SERIAL_BFIN_CONSOLE) +static __initdata struct early_platform_driver early_bfin_serial_driver = { + .class_str = CLASS_BFIN_CONSOLE, + .pdrv = &bfin_serial_driver, + .requested_id = EARLY_PLATFORM_ID_UNSET, +}; + +static int __init bfin_serial_rs_console_init(void) +{ + early_platform_driver_register(&early_bfin_serial_driver, DRIVER_NAME); + + early_platform_driver_probe(CLASS_BFIN_CONSOLE, BFIN_UART_NR_PORTS, 0); + + register_console(&bfin_serial_console); + + return 0; +} +console_initcall(bfin_serial_rs_console_init); +#endif + +#ifdef CONFIG_EARLY_PRINTK +/* + * Memory can't be allocated dynamically during earlyprink init stage. + * So, do individual probe for earlyprink with a static uart port variable. + */ +static int bfin_earlyprintk_probe(struct platform_device *pdev) { + struct resource *res; int ret; - pr_info("Serial: Blackfin serial driver\n"); + if (pdev->id < 0 || pdev->id >= BFIN_UART_NR_PORTS) { + dev_err(&pdev->dev, "Wrong earlyprintk platform device id.\n"); + return -ENOENT; + } + + ret = peripheral_request_list( + (unsigned short *)pdev->dev.platform_data, DRIVER_NAME); + if (ret) { + dev_err(&pdev->dev, + "fail to request bfin serial peripherals\n"); + return ret; + } - bfin_serial_init_ports(); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto out_error_free_peripherals; + } + + bfin_earlyprintk_port.port.membase = ioremap(res->start, + res->end - res->start); + if (!bfin_earlyprintk_port.port.membase) { + dev_err(&pdev->dev, "Cannot map uart IO\n"); + ret = -ENXIO; + goto out_error_free_peripherals; + } + bfin_earlyprintk_port.port.mapbase = res->start; + bfin_earlyprintk_port.port.line = pdev->id; + bfin_earlyprintk_port.port.uartclk = get_sclk(); + bfin_earlyprintk_port.port.fifosize = BFIN_UART_TX_FIFO_SIZE; + spin_lock_init(&bfin_earlyprintk_port.port.lock); + + return 0; + +out_error_free_peripherals: + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); + + return ret; +} + +static struct platform_driver bfin_earlyprintk_driver = { + .probe = bfin_earlyprintk_probe, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static __initdata struct early_platform_driver early_bfin_earlyprintk_driver = { + .class_str = CLASS_BFIN_EARLYPRINTK, + .pdrv = &bfin_earlyprintk_driver, + .requested_id = EARLY_PLATFORM_ID_UNSET, +}; + +struct console __init *bfin_earlyserial_init(unsigned int port, + unsigned int cflag) +{ + struct ktermios t; + char port_name[20]; + + if (port < 0 || port >= BFIN_UART_NR_PORTS) + return NULL; + + /* + * Only probe resource of the given port in earlyprintk boot arg. + * The expected port id should be indicated in port name string. + */ + snprintf(port_name, 20, DRIVER_NAME ".%d", port); + early_platform_driver_register(&early_bfin_earlyprintk_driver, + port_name); + early_platform_driver_probe(CLASS_BFIN_EARLYPRINTK, 1, 0); + + if (!bfin_earlyprintk_port.port.membase) + return NULL; + +#ifdef CONFIG_SERIAL_BFIN_CONSOLE + /* + * If we are using early serial, don't let the normal console rewind + * log buffer, since that causes things to be printed multiple times + */ + bfin_serial_console.flags &= ~CON_PRINTBUFFER; +#endif + + bfin_early_serial_console.index = port; + t.c_cflag = cflag; + t.c_iflag = 0; + t.c_oflag = 0; + t.c_lflag = ICANON; + t.c_line = port; + bfin_serial_set_termios(&bfin_earlyprintk_port.port, &t, &t); + + return &bfin_early_serial_console; +} +#endif /* CONFIG_EARLY_PRINTK */ + +static int __init bfin_serial_init(void) +{ + int ret; + + pr_info("Blackfin serial driver\n"); ret = uart_register_driver(&bfin_serial_reg); - if (ret == 0) { - ret = platform_driver_register(&bfin_serial_driver); - if (ret) { - pr_debug("uart register failed\n"); - uart_unregister_driver(&bfin_serial_reg); - } + if (ret) { + pr_err("failed to register %s:%d\n", + bfin_serial_reg.driver_name, ret); + } + + ret = platform_driver_register(&bfin_serial_driver); + if (ret) { + pr_err("fail to register bfin uart\n"); + uart_unregister_driver(&bfin_serial_reg); } + return ret; } @@ -1455,7 +1593,7 @@ static void __exit bfin_serial_exit(void) module_init(bfin_serial_init); module_exit(bfin_serial_exit); -MODULE_AUTHOR("Aubrey.Li <aubrey.li@analog.com>"); +MODULE_AUTHOR("Sonic Zhang, Aubrey Li"); MODULE_DESCRIPTION("Blackfin generic serial port driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS_CHARDEV_MAJOR(BFIN_SERIAL_MAJOR); diff --git a/drivers/serial/bfin_sport_uart.c b/drivers/serial/bfin_sport_uart.c index 34b4ae0..e95c524 100644 --- a/drivers/serial/bfin_sport_uart.c +++ b/drivers/serial/bfin_sport_uart.c @@ -1,134 +1,101 @@ /* - * File: linux/drivers/serial/bfin_sport_uart.c + * Blackfin On-Chip Sport Emulated UART Driver * - * Based on: drivers/serial/bfin_5xx.c by Aubrey Li. - * Author: Roy Huang <roy.huang@analog.com> + * Copyright 2006-2009 Analog Devices Inc. * - * Created: Nov 22, 2006 - * Copyright: (c) 2006-2007 Analog Devices Inc. - * Description: this driver enable SPORTs on Blackfin emulate UART. + * Enter bugs at http://blackfin.uclinux.org/ * - * 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, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Licensed under the GPL-2 or later. */ /* * This driver and the hardware supported are in term of EE-191 of ADI. - * http://www.analog.com/UploadedFiles/Application_Notes/399447663EE191.pdf + * http://www.analog.com/static/imported-files/application_notes/EE191.pdf * This application note describe how to implement a UART on a Sharc DSP, * but this driver is implemented on Blackfin Processor. + * Transmit Frame Sync is not used by this driver to transfer data out. */ -/* After reset, there is a prelude of low level pulse when transmit data first - * time. No addtional pulse in following transmit. - * According to document: - * The SPORTs are ready to start transmitting or receiving data no later than - * three serial clock cycles after they are enabled in the SPORTx_TCR1 or - * SPORTx_RCR1 register. No serial clock cycles are lost from this point on. - * The first internal frame sync will occur one frame sync delay after the - * SPORTs are ready. External frame syncs can occur as soon as the SPORT is - * ready. - */ +/* #define DEBUG */ -/* Thanks to Axel Alatalo <axel@rubico.se> for fixing sport rx bug. Sometimes - * sport receives data incorrectly. The following is Axel's words. - * As EE-191, sport rx samples 3 times of the UART baudrate and takes the - * middle smaple of every 3 samples as the data bit. For a 8-N-1 UART setting, - * 30 samples will be required for a byte. If transmitter sends a 1/3 bit short - * byte due to buadrate drift, then the 30th sample of a byte, this sample is - * also the third sample of the stop bit, will happens on the immediately - * following start bit which will be thrown away and missed. Thus since parts - * of the startbit will be missed and the receiver will begin to drift, the - * effect accumulates over time until synchronization is lost. - * If only require 2 samples of the stopbit (by sampling in total 29 samples), - * then a to short byte as in the case above will be tolerated. Then the 1/3 - * early startbit will trigger a framesync since the last read is complete - * after only 2/3 stopbit and framesync is active during the last 1/3 looking - * for a possible early startbit. */ - -//#define DEBUG +#define DRV_NAME "bfin-sport-uart" +#define DEVICE_NAME "ttySS" +#define pr_fmt(fmt) DRV_NAME ": " fmt #include <linux/module.h> #include <linux/ioport.h> +#include <linux/io.h> #include <linux/init.h> #include <linux/console.h> #include <linux/sysrq.h> +#include <linux/slab.h> #include <linux/platform_device.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/serial_core.h> +#include <asm/bfin_sport.h> #include <asm/delay.h> #include <asm/portmux.h> #include "bfin_sport_uart.h" -unsigned short bfin_uart_pin_req_sport0[] = - {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \ - P_SPORT0_DRPRI, P_SPORT0_RSCLK, P_SPORT0_DRSEC, P_SPORT0_DTSEC, 0}; - -unsigned short bfin_uart_pin_req_sport1[] = - {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \ - P_SPORT1_DRPRI, P_SPORT1_RSCLK, P_SPORT1_DRSEC, P_SPORT1_DTSEC, 0}; - -#define DRV_NAME "bfin-sport-uart" - struct sport_uart_port { struct uart_port port; - char *name; - - int tx_irq; - int rx_irq; int err_irq; + unsigned short csize; + unsigned short rxmask; + unsigned short txmask1; + unsigned short txmask2; + unsigned char stopb; +/* unsigned char parib; */ +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + int cts_pin; + int rts_pin; +#endif }; -static void sport_uart_tx_chars(struct sport_uart_port *up); +static int sport_uart_tx_chars(struct sport_uart_port *up); static void sport_stop_tx(struct uart_port *port); static inline void tx_one_byte(struct sport_uart_port *up, unsigned int value) { - pr_debug("%s value:%x\n", __func__, value); - /* Place a Start and Stop bit */ + pr_debug("%s value:%x, mask1=0x%x, mask2=0x%x\n", __func__, value, + up->txmask1, up->txmask2); + + /* Place Start and Stop bits */ __asm__ __volatile__ ( - "R2 = b#01111111100;" - "R3 = b#10000000001;" - "%0 <<= 2;" - "%0 = %0 & R2;" - "%0 = %0 | R3;" - : "=d"(value) - : "d"(value) - : "ASTAT", "R2", "R3" + "%[val] <<= 1;" + "%[val] = %[val] & %[mask1];" + "%[val] = %[val] | %[mask2];" + : [val]"+d"(value) + : [mask1]"d"(up->txmask1), [mask2]"d"(up->txmask2) + : "ASTAT" ); pr_debug("%s value:%x\n", __func__, value); SPORT_PUT_TX(up, value); } -static inline unsigned int rx_one_byte(struct sport_uart_port *up) +static inline unsigned char rx_one_byte(struct sport_uart_port *up) { - unsigned int value, extract; + unsigned int value; + unsigned char extract; u32 tmp_mask1, tmp_mask2, tmp_shift, tmp; - value = SPORT_GET_RX32(up); - pr_debug("%s value:%x\n", __func__, value); + if ((up->csize + up->stopb) > 7) + value = SPORT_GET_RX32(up); + else + value = SPORT_GET_RX(up); - /* Extract 8 bits data */ + pr_debug("%s value:%x, cs=%d, mask=0x%x\n", __func__, value, + up->csize, up->rxmask); + + /* Extract data */ __asm__ __volatile__ ( "%[extr] = 0;" - "%[mask1] = 0x1801(Z);" - "%[mask2] = 0x0300(Z);" + "%[mask1] = %[rxmask];" + "%[mask2] = 0x0200(Z);" "%[shift] = 0;" "LSETUP(.Lloop_s, .Lloop_e) LC0 = %[lc];" ".Lloop_s:" @@ -138,9 +105,9 @@ static inline unsigned int rx_one_byte(struct sport_uart_port *up) "%[mask1] = %[mask1] - %[mask2];" ".Lloop_e:" "%[shift] += 1;" - : [val]"=d"(value), [extr]"=d"(extract), [shift]"=d"(tmp_shift), [tmp]"=d"(tmp), - [mask1]"=d"(tmp_mask1), [mask2]"=d"(tmp_mask2) - : "d"(value), [lc]"a"(8) + : [extr]"=&d"(extract), [shift]"=&d"(tmp_shift), [tmp]"=&d"(tmp), + [mask1]"=&d"(tmp_mask1), [mask2]"=&d"(tmp_mask2) + : [val]"d"(value), [rxmask]"d"(up->rxmask), [lc]"a"(up->csize) : "ASTAT", "LB0", "LC0", "LT0" ); @@ -148,29 +115,33 @@ static inline unsigned int rx_one_byte(struct sport_uart_port *up) return extract; } -static int sport_uart_setup(struct sport_uart_port *up, int sclk, int baud_rate) +static int sport_uart_setup(struct sport_uart_port *up, int size, int baud_rate) { - int tclkdiv, tfsdiv, rclkdiv; + int tclkdiv, rclkdiv; + unsigned int sclk = get_sclk(); - /* Set TCR1 and TCR2 */ + /* Set TCR1 and TCR2, TFSR is not enabled for uart */ SPORT_PUT_TCR1(up, (LATFS | ITFS | TFSR | TLSBIT | ITCLK)); - SPORT_PUT_TCR2(up, 10); + SPORT_PUT_TCR2(up, size + 1); pr_debug("%s TCR1:%x, TCR2:%x\n", __func__, SPORT_GET_TCR1(up), SPORT_GET_TCR2(up)); /* Set RCR1 and RCR2 */ SPORT_PUT_RCR1(up, (RCKFE | LARFS | LRFS | RFSR | IRCLK)); - SPORT_PUT_RCR2(up, 28); + SPORT_PUT_RCR2(up, (size + 1) * 2 - 1); pr_debug("%s RCR1:%x, RCR2:%x\n", __func__, SPORT_GET_RCR1(up), SPORT_GET_RCR2(up)); - tclkdiv = sclk/(2 * baud_rate) - 1; - tfsdiv = 12; - rclkdiv = sclk/(2 * baud_rate * 3) - 1; + tclkdiv = sclk / (2 * baud_rate) - 1; + /* The actual uart baud rate of devices vary between +/-2%. The sport + * RX sample rate should be faster than the double of the worst case, + * otherwise, wrong data are received. So, set sport RX clock to be + * 3% faster. + */ + rclkdiv = sclk / (2 * baud_rate * 2 * 97 / 100) - 1; SPORT_PUT_TCLKDIV(up, tclkdiv); - SPORT_PUT_TFSDIV(up, tfsdiv); SPORT_PUT_RCLKDIV(up, rclkdiv); SSYNC(); - pr_debug("%s sclk:%d, baud_rate:%d, tclkdiv:%d, tfsdiv:%d, rclkdiv:%d\n", - __func__, sclk, baud_rate, tclkdiv, tfsdiv, rclkdiv); + pr_debug("%s sclk:%d, baud_rate:%d, tclkdiv:%d, rclkdiv:%d\n", + __func__, sclk, baud_rate, tclkdiv, rclkdiv); return 0; } @@ -178,26 +149,32 @@ static int sport_uart_setup(struct sport_uart_port *up, int sclk, int baud_rate) static irqreturn_t sport_uart_rx_irq(int irq, void *dev_id) { struct sport_uart_port *up = dev_id; - struct tty_struct *tty = up->port.info->port.tty; + struct tty_struct *tty = up->port.state->port.tty; unsigned int ch; - do { + spin_lock(&up->port.lock); + + while (SPORT_GET_STAT(up) & RXNE) { ch = rx_one_byte(up); up->port.icount.rx++; - if (uart_handle_sysrq_char(&up->port, ch)) - ; - else + if (!uart_handle_sysrq_char(&up->port, ch)) tty_insert_flip_char(tty, ch, TTY_NORMAL); - } while (SPORT_GET_STAT(up) & RXNE); + } tty_flip_buffer_push(tty); + spin_unlock(&up->port.lock); + return IRQ_HANDLED; } static irqreturn_t sport_uart_tx_irq(int irq, void *dev_id) { - sport_uart_tx_chars(dev_id); + struct sport_uart_port *up = dev_id; + + spin_lock(&up->port.lock); + sport_uart_tx_chars(up); + spin_unlock(&up->port.lock); return IRQ_HANDLED; } @@ -205,9 +182,11 @@ static irqreturn_t sport_uart_tx_irq(int irq, void *dev_id) static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) { struct sport_uart_port *up = dev_id; - struct tty_struct *tty = up->port.info->port.tty; + struct tty_struct *tty = up->port.state->port.tty; unsigned int stat = SPORT_GET_STAT(up); + spin_lock(&up->port.lock); + /* Overflow in RX FIFO */ if (stat & ROVF) { up->port.icount.overrun++; @@ -216,96 +195,153 @@ static irqreturn_t sport_uart_err_irq(int irq, void *dev_id) } /* These should not happen */ if (stat & (TOVF | TUVF | RUVF)) { - printk(KERN_ERR "SPORT Error:%s %s %s\n", - (stat & TOVF)?"TX overflow":"", - (stat & TUVF)?"TX underflow":"", - (stat & RUVF)?"RX underflow":""); + pr_err("SPORT Error:%s %s %s\n", + (stat & TOVF) ? "TX overflow" : "", + (stat & TUVF) ? "TX underflow" : "", + (stat & RUVF) ? "RX underflow" : ""); SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); } SSYNC(); + spin_unlock(&up->port.lock); return IRQ_HANDLED; } +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS +static unsigned int sport_get_mctrl(struct uart_port *port) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + if (up->cts_pin < 0) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + + /* CTS PIN is negative assertive. */ + if (SPORT_UART_GET_CTS(up)) + return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR; + else + return TIOCM_DSR | TIOCM_CAR; +} + +static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + if (up->rts_pin < 0) + return; + + /* RTS PIN is negative assertive. */ + if (mctrl & TIOCM_RTS) + SPORT_UART_ENABLE_RTS(up); + else + SPORT_UART_DISABLE_RTS(up); +} + +/* + * Handle any change of modem status signal. + */ +static irqreturn_t sport_mctrl_cts_int(int irq, void *dev_id) +{ + struct sport_uart_port *up = (struct sport_uart_port *)dev_id; + unsigned int status; + + status = sport_get_mctrl(&up->port); + uart_handle_cts_change(&up->port, status & TIOCM_CTS); + + return IRQ_HANDLED; +} +#else +static unsigned int sport_get_mctrl(struct uart_port *port) +{ + pr_debug("%s enter\n", __func__); + return TIOCM_CTS | TIOCM_CD | TIOCM_DSR; +} + +static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + pr_debug("%s enter\n", __func__); +} +#endif + /* Reqeust IRQ, Setup clock */ static int sport_startup(struct uart_port *port) { struct sport_uart_port *up = (struct sport_uart_port *)port; - char buffer[20]; - int retval; + int ret; pr_debug("%s enter\n", __func__); - memset(buffer, 20, '\0'); - snprintf(buffer, 20, "%s rx", up->name); - retval = request_irq(up->rx_irq, sport_uart_rx_irq, IRQF_SAMPLE_RANDOM, buffer, up); - if (retval) { - printk(KERN_ERR "Unable to request interrupt %s\n", buffer); - return retval; + ret = request_irq(up->port.irq, sport_uart_rx_irq, 0, + "SPORT_UART_RX", up); + if (ret) { + dev_err(port->dev, "unable to request SPORT RX interrupt\n"); + return ret; } - snprintf(buffer, 20, "%s tx", up->name); - retval = request_irq(up->tx_irq, sport_uart_tx_irq, IRQF_SAMPLE_RANDOM, buffer, up); - if (retval) { - printk(KERN_ERR "Unable to request interrupt %s\n", buffer); + ret = request_irq(up->port.irq+1, sport_uart_tx_irq, 0, + "SPORT_UART_TX", up); + if (ret) { + dev_err(port->dev, "unable to request SPORT TX interrupt\n"); goto fail1; } - snprintf(buffer, 20, "%s err", up->name); - retval = request_irq(up->err_irq, sport_uart_err_irq, IRQF_SAMPLE_RANDOM, buffer, up); - if (retval) { - printk(KERN_ERR "Unable to request interrupt %s\n", buffer); + ret = request_irq(up->err_irq, sport_uart_err_irq, 0, + "SPORT_UART_STATUS", up); + if (ret) { + dev_err(port->dev, "unable to request SPORT status interrupt\n"); goto fail2; } - if (port->line) { - if (peripheral_request_list(bfin_uart_pin_req_sport1, DRV_NAME)) - goto fail3; - } else { - if (peripheral_request_list(bfin_uart_pin_req_sport0, DRV_NAME)) - goto fail3; +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + if (up->cts_pin >= 0) { + if (request_irq(gpio_to_irq(up->cts_pin), + sport_mctrl_cts_int, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_DISABLED, "BFIN_SPORT_UART_CTS", up)) { + up->cts_pin = -1; + dev_info(port->dev, "Unable to attach BlackFin UART \ + over SPORT CTS interrupt. So, disable it.\n"); + } } - - sport_uart_setup(up, get_sclk(), port->uartclk); - - /* Enable receive interrupt */ - SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) | RSPEN)); - SSYNC(); + if (up->rts_pin >= 0) + gpio_direction_output(up->rts_pin, 0); +#endif return 0; + fail2: + free_irq(up->port.irq+1, up); + fail1: + free_irq(up->port.irq, up); - -fail3: - printk(KERN_ERR DRV_NAME - ": Requesting Peripherals failed\n"); - - free_irq(up->err_irq, up); -fail2: - free_irq(up->tx_irq, up); -fail1: - free_irq(up->rx_irq, up); - - return retval; - + return ret; } -static void sport_uart_tx_chars(struct sport_uart_port *up) +/* + * sport_uart_tx_chars + * + * ret 1 means need to enable sport. + * ret 0 means do nothing. + */ +static int sport_uart_tx_chars(struct sport_uart_port *up) { - struct circ_buf *xmit = &up->port.info->xmit; + struct circ_buf *xmit = &up->port.state->xmit; if (SPORT_GET_STAT(up) & TXF) - return; + return 0; if (up->port.x_char) { tx_one_byte(up, up->port.x_char); up->port.icount.tx++; up->port.x_char = 0; - return; + return 1; } if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { - sport_stop_tx(&up->port); - return; + /* The waiting loop to stop SPORT TX from TX interrupt is + * too long. This may block SPORT RX interrupts and cause + * RX FIFO overflow. So, do stop sport TX only after the last + * char in TX FIFO is moved into the shift register. + */ + if (SPORT_GET_STAT(up) & TXHRE) + sport_stop_tx(&up->port); + return 0; } while(!(SPORT_GET_STAT(up) & TXF) && !uart_circ_empty(xmit)) { @@ -316,6 +352,8 @@ static void sport_uart_tx_chars(struct sport_uart_port *up) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&up->port); + + return 1; } static unsigned int sport_tx_empty(struct uart_port *port) @@ -331,34 +369,23 @@ static unsigned int sport_tx_empty(struct uart_port *port) return 0; } -static unsigned int sport_get_mctrl(struct uart_port *port) -{ - pr_debug("%s enter\n", __func__); - return (TIOCM_CTS | TIOCM_CD | TIOCM_DSR); -} - -static void sport_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - pr_debug("%s enter\n", __func__); -} - static void sport_stop_tx(struct uart_port *port) { struct sport_uart_port *up = (struct sport_uart_port *)port; - unsigned int stat; pr_debug("%s enter\n", __func__); - stat = SPORT_GET_STAT(up); - while(!(stat & TXHRE)) { - udelay(1); - stat = SPORT_GET_STAT(up); - } + if (!(SPORT_GET_TCR1(up) & TSPEN)) + return; + /* Although the hold register is empty, last byte is still in shift - * register and not sent out yet. If baud rate is lower than default, - * delay should be longer. For example, if the baud rate is 9600, - * the delay must be at least 2ms by experience */ - udelay(500); + * register and not sent out yet. So, put a dummy data into TX FIFO. + * Then, sport tx stops when last byte is shift out and the dummy + * data is moved into the shift register. + */ + SPORT_PUT_TX(up, 0xffff); + while (!(SPORT_GET_STAT(up) & TXHRE)) + cpu_relax(); SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); SSYNC(); @@ -371,12 +398,14 @@ static void sport_start_tx(struct uart_port *port) struct sport_uart_port *up = (struct sport_uart_port *)port; pr_debug("%s enter\n", __func__); + /* Write data into SPORT FIFO before enable SPROT to transmit */ - sport_uart_tx_chars(up); + if (sport_uart_tx_chars(up)) { + /* Enable transmit, then an interrupt will generated */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); + SSYNC(); + } - /* Enable transmit, then an interrupt will generated */ - SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); - SSYNC(); pr_debug("%s exit\n", __func__); } @@ -404,29 +433,20 @@ static void sport_shutdown(struct uart_port *port) { struct sport_uart_port *up = (struct sport_uart_port *)port; - pr_debug("%s enter\n", __func__); + dev_dbg(port->dev, "%s enter\n", __func__); /* Disable sport */ SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); SPORT_PUT_RCR1(up, (SPORT_GET_RCR1(up) & ~RSPEN)); SSYNC(); - if (port->line) { - peripheral_free_list(bfin_uart_pin_req_sport1); - } else { - peripheral_free_list(bfin_uart_pin_req_sport0); - } - - free_irq(up->rx_irq, up); - free_irq(up->tx_irq, up); + free_irq(up->port.irq, up); + free_irq(up->port.irq+1, up); free_irq(up->err_irq, up); -} - -static void sport_set_termios(struct uart_port *port, - struct ktermios *termios, struct ktermios *old) -{ - pr_debug("%s enter, c_cflag:%08x\n", __func__, termios->c_cflag); - uart_update_timeout(port, CS8 ,port->uartclk); +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + if (up->cts_pin >= 0) + free_irq(gpio_to_irq(up->cts_pin), up); +#endif } static const char *sport_type(struct uart_port *port) @@ -434,7 +454,7 @@ static const char *sport_type(struct uart_port *port) struct sport_uart_port *up = (struct sport_uart_port *)port; pr_debug("%s enter\n", __func__); - return up->name; + return up->port.type == PORT_BFIN_SPORT ? "BFIN-SPORT-UART" : NULL; } static void sport_release_port(struct uart_port *port) @@ -462,6 +482,95 @@ static int sport_verify_port(struct uart_port *port, struct serial_struct *ser) return 0; } +static void sport_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + unsigned long flags; + int i; + + pr_debug("%s enter, c_cflag:%08x\n", __func__, termios->c_cflag); + + switch (termios->c_cflag & CSIZE) { + case CS8: + up->csize = 8; + break; + case CS7: + up->csize = 7; + break; + case CS6: + up->csize = 6; + break; + case CS5: + up->csize = 5; + break; + default: + pr_warning("requested word length not supported\n"); + } + + if (termios->c_cflag & CSTOPB) { + up->stopb = 1; + } + if (termios->c_cflag & PARENB) { + pr_warning("PAREN bits is not supported yet\n"); + /* up->parib = 1; */ + } + + spin_lock_irqsave(&up->port.lock, flags); + + port->read_status_mask = 0; + + /* + * Characters to ignore + */ + port->ignore_status_mask = 0; + + /* RX extract mask */ + up->rxmask = 0x01 | (((up->csize + up->stopb) * 2 - 1) << 0x8); + /* TX masks, 8 bit data and 1 bit stop for example: + * mask1 = b#0111111110 + * mask2 = b#1000000000 + */ + for (i = 0, up->txmask1 = 0; i < up->csize; i++) + up->txmask1 |= (1<<i); + up->txmask2 = (1<<i); + if (up->stopb) { + ++i; + up->txmask2 |= (1<<i); + } + up->txmask1 <<= 1; + up->txmask2 <<= 1; + /* uart baud rate */ + port->uartclk = uart_get_baud_rate(port, termios, old, 0, get_sclk()/16); + + /* Disable UART */ + SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); + SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) & ~RSPEN); + + sport_uart_setup(up, up->csize + up->stopb, port->uartclk); + + /* driver TX line high after config, one dummy data is + * necessary to stop sport after shift one byte + */ + SPORT_PUT_TX(up, 0xffff); + SPORT_PUT_TX(up, 0xffff); + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); + SSYNC(); + while (!(SPORT_GET_STAT(up) & TXHRE)) + cpu_relax(); + SPORT_PUT_TCR1(up, SPORT_GET_TCR1(up) & ~TSPEN); + SSYNC(); + + /* Port speed changed, update the per-port timeout. */ + uart_update_timeout(port, termios->c_cflag, port->uartclk); + + /* Enable sport rx */ + SPORT_PUT_RCR1(up, SPORT_GET_RCR1(up) | RSPEN); + SSYNC(); + + spin_unlock_irqrestore(&up->port.lock, flags); +} + struct uart_ops sport_uart_ops = { .tx_empty = sport_tx_empty, .set_mctrl = sport_set_mctrl, @@ -481,138 +590,346 @@ struct uart_ops sport_uart_ops = { .verify_port = sport_verify_port, }; -static struct sport_uart_port sport_uart_ports[] = { - { /* SPORT 0 */ - .name = "SPORT0", - .tx_irq = IRQ_SPORT0_TX, - .rx_irq = IRQ_SPORT0_RX, - .err_irq= IRQ_SPORT0_ERROR, - .port = { - .type = PORT_BFIN_SPORT, - .iotype = UPIO_MEM, - .membase = (void __iomem *)SPORT0_TCR1, - .mapbase = SPORT0_TCR1, - .irq = IRQ_SPORT0_RX, - .uartclk = CONFIG_SPORT_BAUD_RATE, - .fifosize = 8, - .ops = &sport_uart_ops, - .line = 0, - }, - }, { /* SPORT 1 */ - .name = "SPORT1", - .tx_irq = IRQ_SPORT1_TX, - .rx_irq = IRQ_SPORT1_RX, - .err_irq= IRQ_SPORT1_ERROR, - .port = { - .type = PORT_BFIN_SPORT, - .iotype = UPIO_MEM, - .membase = (void __iomem *)SPORT1_TCR1, - .mapbase = SPORT1_TCR1, - .irq = IRQ_SPORT1_RX, - .uartclk = CONFIG_SPORT_BAUD_RATE, - .fifosize = 8, - .ops = &sport_uart_ops, - .line = 1, - }, +#define BFIN_SPORT_UART_MAX_PORTS 4 + +static struct sport_uart_port *bfin_sport_uart_ports[BFIN_SPORT_UART_MAX_PORTS]; + +#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE +#define CLASS_BFIN_SPORT_CONSOLE "bfin-sport-console" + +static int __init +sport_uart_console_setup(struct console *co, char *options) +{ + struct sport_uart_port *up; + int baud = 57600; + int bits = 8; + int parity = 'n'; +# ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + int flow = 'r'; +# else + int flow = 'n'; +# endif + + /* Check whether an invalid uart number has been specified */ + if (co->index < 0 || co->index >= BFIN_SPORT_UART_MAX_PORTS) + return -ENODEV; + + up = bfin_sport_uart_ports[co->index]; + if (!up) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&up->port, co, baud, parity, bits, flow); +} + +static void sport_uart_console_putchar(struct uart_port *port, int ch) +{ + struct sport_uart_port *up = (struct sport_uart_port *)port; + + while (SPORT_GET_STAT(up) & TXF) + barrier(); + + tx_one_byte(up, ch); +} + +/* + * Interrupts are disabled on entering + */ +static void +sport_uart_console_write(struct console *co, const char *s, unsigned int count) +{ + struct sport_uart_port *up = bfin_sport_uart_ports[co->index]; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + + if (SPORT_GET_TCR1(up) & TSPEN) + uart_console_write(&up->port, s, count, sport_uart_console_putchar); + else { + /* dummy data to start sport */ + while (SPORT_GET_STAT(up) & TXF) + barrier(); + SPORT_PUT_TX(up, 0xffff); + /* Enable transmit, then an interrupt will generated */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) | TSPEN)); + SSYNC(); + + uart_console_write(&up->port, s, count, sport_uart_console_putchar); + + /* Although the hold register is empty, last byte is still in shift + * register and not sent out yet. So, put a dummy data into TX FIFO. + * Then, sport tx stops when last byte is shift out and the dummy + * data is moved into the shift register. + */ + while (SPORT_GET_STAT(up) & TXF) + barrier(); + SPORT_PUT_TX(up, 0xffff); + while (!(SPORT_GET_STAT(up) & TXHRE)) + barrier(); + + /* Stop sport tx transfer */ + SPORT_PUT_TCR1(up, (SPORT_GET_TCR1(up) & ~TSPEN)); + SSYNC(); } + + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static struct uart_driver sport_uart_reg; + +static struct console sport_uart_console = { + .name = DEVICE_NAME, + .write = sport_uart_console_write, + .device = uart_console_device, + .setup = sport_uart_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &sport_uart_reg, }; +#define SPORT_UART_CONSOLE (&sport_uart_console) +#else +#define SPORT_UART_CONSOLE NULL +#endif /* CONFIG_SERIAL_BFIN_SPORT_CONSOLE */ + + static struct uart_driver sport_uart_reg = { .owner = THIS_MODULE, - .driver_name = "SPORT-UART", - .dev_name = "ttySS", + .driver_name = DRV_NAME, + .dev_name = DEVICE_NAME, .major = 204, .minor = 84, - .nr = ARRAY_SIZE(sport_uart_ports), - .cons = NULL, + .nr = BFIN_SPORT_UART_MAX_PORTS, + .cons = SPORT_UART_CONSOLE, }; -static int sport_uart_suspend(struct platform_device *dev, pm_message_t state) +#ifdef CONFIG_PM +static int sport_uart_suspend(struct device *dev) { - struct sport_uart_port *sport = platform_get_drvdata(dev); + struct sport_uart_port *sport = dev_get_drvdata(dev); - pr_debug("%s enter\n", __func__); + dev_dbg(dev, "%s enter\n", __func__); if (sport) uart_suspend_port(&sport_uart_reg, &sport->port); return 0; } -static int sport_uart_resume(struct platform_device *dev) +static int sport_uart_resume(struct device *dev) { - struct sport_uart_port *sport = platform_get_drvdata(dev); + struct sport_uart_port *sport = dev_get_drvdata(dev); - pr_debug("%s enter\n", __func__); + dev_dbg(dev, "%s enter\n", __func__); if (sport) uart_resume_port(&sport_uart_reg, &sport->port); return 0; } -static int sport_uart_probe(struct platform_device *dev) +static struct dev_pm_ops bfin_sport_uart_dev_pm_ops = { + .suspend = sport_uart_suspend, + .resume = sport_uart_resume, +}; +#endif + +static int __devinit sport_uart_probe(struct platform_device *pdev) { - pr_debug("%s enter\n", __func__); - sport_uart_ports[dev->id].port.dev = &dev->dev; - uart_add_one_port(&sport_uart_reg, &sport_uart_ports[dev->id].port); - platform_set_drvdata(dev, &sport_uart_ports[dev->id]); + struct resource *res; + struct sport_uart_port *sport; + int ret = 0; - return 0; + dev_dbg(&pdev->dev, "%s enter\n", __func__); + + if (pdev->id < 0 || pdev->id >= BFIN_SPORT_UART_MAX_PORTS) { + dev_err(&pdev->dev, "Wrong sport uart platform device id.\n"); + return -ENOENT; + } + + if (bfin_sport_uart_ports[pdev->id] == NULL) { + bfin_sport_uart_ports[pdev->id] = + kzalloc(sizeof(struct sport_uart_port), GFP_KERNEL); + sport = bfin_sport_uart_ports[pdev->id]; + if (!sport) { + dev_err(&pdev->dev, + "Fail to malloc sport_uart_port\n"); + return -ENOMEM; + } + + ret = peripheral_request_list( + (unsigned short *)pdev->dev.platform_data, DRV_NAME); + if (ret) { + dev_err(&pdev->dev, + "Fail to request SPORT peripherals\n"); + goto out_error_free_mem; + } + + spin_lock_init(&sport->port.lock); + sport->port.fifosize = SPORT_TX_FIFO_SIZE, + sport->port.ops = &sport_uart_ops; + sport->port.line = pdev->id; + sport->port.iotype = UPIO_MEM; + sport->port.flags = UPF_BOOT_AUTOCONF; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + ret = -ENOENT; + goto out_error_free_peripherals; + } + + sport->port.membase = ioremap(res->start, resource_size(res)); + if (!sport->port.membase) { + dev_err(&pdev->dev, "Cannot map sport IO\n"); + ret = -ENXIO; + goto out_error_free_peripherals; + } + sport->port.mapbase = res->start; + + sport->port.irq = platform_get_irq(pdev, 0); + if (sport->port.irq < 0) { + dev_err(&pdev->dev, "No sport RX/TX IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } + + sport->err_irq = platform_get_irq(pdev, 1); + if (sport->err_irq < 0) { + dev_err(&pdev->dev, "No sport status IRQ specified\n"); + ret = -ENOENT; + goto out_error_unmap; + } +#ifdef CONFIG_SERIAL_BFIN_SPORT_CTSRTS + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (res == NULL) + sport->cts_pin = -1; + else + sport->cts_pin = res->start; + + res = platform_get_resource(pdev, IORESOURCE_IO, 1); + if (res == NULL) + sport->rts_pin = -1; + else + sport->rts_pin = res->start; + + if (sport->rts_pin >= 0) + gpio_request(sport->rts_pin, DRV_NAME); +#endif + } + +#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE + if (!is_early_platform_device(pdev)) { +#endif + sport = bfin_sport_uart_ports[pdev->id]; + sport->port.dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, sport); + ret = uart_add_one_port(&sport_uart_reg, &sport->port); +#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE + } +#endif + if (!ret) + return 0; + + if (sport) { +out_error_unmap: + iounmap(sport->port.membase); +out_error_free_peripherals: + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); +out_error_free_mem: + kfree(sport); + bfin_sport_uart_ports[pdev->id] = NULL; + } + + return ret; } -static int sport_uart_remove(struct platform_device *dev) +static int __devexit sport_uart_remove(struct platform_device *pdev) { - struct sport_uart_port *sport = platform_get_drvdata(dev); + struct sport_uart_port *sport = platform_get_drvdata(pdev); - pr_debug("%s enter\n", __func__); - platform_set_drvdata(dev, NULL); + dev_dbg(&pdev->dev, "%s enter\n", __func__); + dev_set_drvdata(&pdev->dev, NULL); - if (sport) + if (sport) { uart_remove_one_port(&sport_uart_reg, &sport->port); +#ifdef CONFIG_SERIAL_BFIN_CTSRTS + if (sport->rts_pin >= 0) + gpio_free(sport->rts_pin); +#endif + iounmap(sport->port.membase); + peripheral_free_list( + (unsigned short *)pdev->dev.platform_data); + kfree(sport); + bfin_sport_uart_ports[pdev->id] = NULL; + } return 0; } static struct platform_driver sport_uart_driver = { .probe = sport_uart_probe, - .remove = sport_uart_remove, - .suspend = sport_uart_suspend, - .resume = sport_uart_resume, + .remove = __devexit_p(sport_uart_remove), .driver = { .name = DRV_NAME, +#ifdef CONFIG_PM + .pm = &bfin_sport_uart_dev_pm_ops, +#endif }, }; +#ifdef CONFIG_SERIAL_BFIN_SPORT_CONSOLE +static __initdata struct early_platform_driver early_sport_uart_driver = { + .class_str = CLASS_BFIN_SPORT_CONSOLE, + .pdrv = &sport_uart_driver, + .requested_id = EARLY_PLATFORM_ID_UNSET, +}; + +static int __init sport_uart_rs_console_init(void) +{ + early_platform_driver_register(&early_sport_uart_driver, DRV_NAME); + + early_platform_driver_probe(CLASS_BFIN_SPORT_CONSOLE, + BFIN_SPORT_UART_MAX_PORTS, 0); + + register_console(&sport_uart_console); + + return 0; +} +console_initcall(sport_uart_rs_console_init); +#endif + static int __init sport_uart_init(void) { int ret; - pr_debug("%s enter\n", __func__); + pr_info("Blackfin uart over sport driver\n"); + ret = uart_register_driver(&sport_uart_reg); - if (ret != 0) { - printk(KERN_ERR "Failed to register %s:%d\n", + if (ret) { + pr_err("failed to register %s:%d\n", sport_uart_reg.driver_name, ret); return ret; } ret = platform_driver_register(&sport_uart_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register sport uart driver:%d\n", ret); + if (ret) { + pr_err("failed to register sport uart driver:%d\n", ret); uart_unregister_driver(&sport_uart_reg); } - - pr_debug("%s exit\n", __func__); return ret; } +module_init(sport_uart_init); static void __exit sport_uart_exit(void) { - pr_debug("%s enter\n", __func__); platform_driver_unregister(&sport_uart_driver); uart_unregister_driver(&sport_uart_reg); } - -module_init(sport_uart_init); module_exit(sport_uart_exit); +MODULE_AUTHOR("Sonic Zhang, Roy Huang"); +MODULE_DESCRIPTION("Blackfin serial over SPORT driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/serial/bfin_sport_uart.h b/drivers/serial/bfin_sport_uart.h index 671d41c..6d06ce1 100644 --- a/drivers/serial/bfin_sport_uart.h +++ b/drivers/serial/bfin_sport_uart.h @@ -1,29 +1,23 @@ /* - * File: linux/drivers/serial/bfin_sport_uart.h + * Blackfin On-Chip Sport Emulated UART Driver * - * Based on: include/asm-blackfin/mach-533/bfin_serial_5xx.h - * Author: Roy Huang <roy.huang>analog.com> + * Copyright 2006-2008 Analog Devices Inc. * - * Created: Nov 22, 2006 - * Copyright: (C) Analog Device Inc. - * Description: this driver enable SPORTs on Blackfin emulate UART. + * Enter bugs at http://blackfin.uclinux.org/ * - * 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, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Licensed under the GPL-2 or later. + */ + +/* + * This driver and the hardware supported are in term of EE-191 of ADI. + * http://www.analog.com/static/imported-files/application_notes/EE191.pdf + * This application note describe how to implement a UART on a Sharc DSP, + * but this driver is implemented on Blackfin Processor. + * Transmit Frame Sync is not used by this driver to transfer data out. */ +#ifndef _BFIN_SPORT_UART_H +#define _BFIN_SPORT_UART_H #define OFFSET_TCR1 0x00 /* Transmit Configuration 1 Register */ #define OFFSET_TCR2 0x04 /* Transmit Configuration 2 Register */ @@ -43,7 +37,21 @@ #define SPORT_GET_TFSDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_TFSDIV)) #define SPORT_GET_TX(sport) bfin_read16(((sport)->port.membase + OFFSET_TX)) #define SPORT_GET_RX(sport) bfin_read16(((sport)->port.membase + OFFSET_RX)) -#define SPORT_GET_RX32(sport) bfin_read32(((sport)->port.membase + OFFSET_RX)) +/* + * If another interrupt fires while doing a 32-bit read from RX FIFO, + * a fake RX underflow error will be generated. So disable interrupts + * to prevent interruption while reading the FIFO. + */ +#define SPORT_GET_RX32(sport) \ +({ \ + unsigned int __ret; \ + if (ANOMALY_05000473) \ + local_irq_disable(); \ + __ret = bfin_read32((sport)->port.membase + OFFSET_RX); \ + if (ANOMALY_05000473) \ + local_irq_enable(); \ + __ret; \ +}) #define SPORT_GET_RCR1(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR1)) #define SPORT_GET_RCR2(sport) bfin_read16(((sport)->port.membase + OFFSET_RCR2)) #define SPORT_GET_RCLKDIV(sport) bfin_read16(((sport)->port.membase + OFFSET_RCLKDIV)) @@ -61,3 +69,18 @@ #define SPORT_PUT_RCLKDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RCLKDIV), v) #define SPORT_PUT_RFSDIV(sport, v) bfin_write16(((sport)->port.membase + OFFSET_RFSDIV), v) #define SPORT_PUT_STAT(sport, v) bfin_write16(((sport)->port.membase + OFFSET_STAT), v) + +#define SPORT_TX_FIFO_SIZE 8 + +#define SPORT_UART_GET_CTS(x) gpio_get_value(x->cts_pin) +#define SPORT_UART_DISABLE_RTS(x) gpio_set_value(x->rts_pin, 1) +#define SPORT_UART_ENABLE_RTS(x) gpio_set_value(x->rts_pin, 0) + +#if defined(CONFIG_SERIAL_BFIN_SPORT0_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT1_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT2_UART_CTSRTS) \ + || defined(CONFIG_SERIAL_BFIN_SPORT3_UART_CTSRTS) +# define CONFIG_SERIAL_BFIN_SPORT_CTSRTS +#endif + +#endif /* _BFIN_SPORT_UART_H */ diff --git a/drivers/serial/clps711x.c b/drivers/serial/clps711x.c index 80e7642..b6acd19 100644 --- a/drivers/serial/clps711x.c +++ b/drivers/serial/clps711x.c @@ -93,7 +93,7 @@ static void clps711xuart_enable_ms(struct uart_port *port) static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id) { struct uart_port *port = dev_id; - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; unsigned int status, ch, flg; status = clps_readl(SYSFLG(port)); @@ -147,7 +147,7 @@ static irqreturn_t clps711xuart_int_rx(int irq, void *dev_id) static irqreturn_t clps711xuart_int_tx(int irq, void *dev_id) { struct uart_port *port = dev_id; - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; int count; if (port->x_char) { diff --git a/drivers/serial/cpm_uart/cpm_uart.h b/drivers/serial/cpm_uart/cpm_uart.h index 7274b52..b754dcf 100644 --- a/drivers/serial/cpm_uart/cpm_uart.h +++ b/drivers/serial/cpm_uart/cpm_uart.h @@ -76,18 +76,12 @@ struct uart_cpm_port { unsigned char *tx_buf; unsigned char *rx_buf; u32 flags; - void (*set_lineif)(struct uart_cpm_port *); struct clk *clk; u8 brg; uint dp_addr; void *mem_addr; dma_addr_t dma_addr; u32 mem_size; - /* helpers */ - int baud; - int bits; - /* Keep track of 'odd' SMC2 wirings */ - int is_portb; /* wait on close if needed */ int wait_closing; /* value to combine with opcode to form cpm command */ diff --git a/drivers/serial/cpm_uart/cpm_uart_core.c b/drivers/serial/cpm_uart/cpm_uart_core.c index f8df068..8692ff9 100644 --- a/drivers/serial/cpm_uart/cpm_uart_core.c +++ b/drivers/serial/cpm_uart/cpm_uart_core.c @@ -72,6 +72,8 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo); /**************************************************************/ +#define HW_BUF_SPD_THRESHOLD 9600 + /* * Check, if transmit buffers are processed */ @@ -244,7 +246,7 @@ static void cpm_uart_int_rx(struct uart_port *port) int i; unsigned char ch; u8 *cp; - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; cbd_t __iomem *bdp; u16 status; @@ -503,6 +505,11 @@ static void cpm_uart_set_termios(struct uart_port *port, pr_debug("CPM uart[%d]:set_termios\n", port->line); baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + if (baud <= HW_BUF_SPD_THRESHOLD || + (pinfo->port.state && pinfo->port.state->port.tty->low_latency)) + pinfo->rx_fifosize = 1; + else + pinfo->rx_fifosize = RX_BUF_SIZE; /* Character length programmed into the mode register is the * sum of: 1 start bit, number of data bits, 0 or 1 parity bit, @@ -594,6 +601,17 @@ static void cpm_uart_set_termios(struct uart_port *port, */ bits++; if (IS_SMC(pinfo)) { + /* + * MRBLR can be changed while an SMC/SCC is operating only + * if it is done in a single bus cycle with one 16-bit move + * (not two 8-bit bus cycles back-to-back). This occurs when + * the cp shifts control to the next RxBD, so the change does + * not take effect immediately. To guarantee the exact RxBD + * on which the change occurs, change MRBLR only while the + * SMC/SCC receiver is disabled. + */ + out_be16(&pinfo->smcup->smc_mrblr, pinfo->rx_fifosize); + /* Set the mode register. We want to keep a copy of the * enables, because we want to put them back if they were * present. @@ -604,6 +622,7 @@ static void cpm_uart_set_termios(struct uart_port *port, out_be16(&smcp->smc_smcmr, smcr_mk_clen(bits) | cval | SMCMR_SM_UART | prev_mode); } else { + out_be16(&pinfo->sccup->scc_genscc.scc_mrblr, pinfo->rx_fifosize); out_be16(&sccp->scc_psmr, (sbits << 12) | scval); } @@ -649,7 +668,7 @@ static int cpm_uart_tx_pump(struct uart_port *port) u8 *p; int count; struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port; - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; /* Handle xon/xoff */ if (port->x_char) { @@ -852,7 +871,7 @@ static void cpm_uart_init_smc(struct uart_cpm_port *pinfo) */ cpm_set_smc_fcr(up); - /* Using idle charater time requires some additional tuning. */ + /* Using idle character time requires some additional tuning. */ out_be16(&up->smc_mrblr, pinfo->rx_fifosize); out_be16(&up->smc_maxidl, pinfo->rx_fifosize); out_be16(&up->smc_brklen, 0); @@ -930,6 +949,83 @@ static void cpm_uart_config_port(struct uart_port *port, int flags) } } +#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_CPM_CONSOLE) +/* + * Write a string to the serial port + * Note that this is called with interrupts already disabled + */ +static void cpm_uart_early_write(struct uart_cpm_port *pinfo, + const char *string, u_int count) +{ + unsigned int i; + cbd_t __iomem *bdp, *bdbase; + unsigned char *cpm_outp_addr; + + /* Get the address of the host memory buffer. + */ + bdp = pinfo->tx_cur; + bdbase = pinfo->tx_bd_base; + + /* + * Now, do each character. This is not as bad as it looks + * since this is a holding FIFO and not a transmitting FIFO. + * We could add the complexity of filling the entire transmit + * buffer, but we would just wait longer between accesses...... + */ + for (i = 0; i < count; i++, string++) { + /* Wait for transmitter fifo to empty. + * Ready indicates output is ready, and xmt is doing + * that, not that it is ready for us to send. + */ + while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) + ; + + /* Send the character out. + * If the buffer address is in the CPM DPRAM, don't + * convert it. + */ + cpm_outp_addr = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), + pinfo); + *cpm_outp_addr = *string; + + out_be16(&bdp->cbd_datlen, 1); + setbits16(&bdp->cbd_sc, BD_SC_READY); + + if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) + bdp = bdbase; + else + bdp++; + + /* if a LF, also do CR... */ + if (*string == 10) { + while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) + ; + + cpm_outp_addr = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), + pinfo); + *cpm_outp_addr = 13; + + out_be16(&bdp->cbd_datlen, 1); + setbits16(&bdp->cbd_sc, BD_SC_READY); + + if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) + bdp = bdbase; + else + bdp++; + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) + ; + + pinfo->tx_cur = bdp; +} +#endif + #ifdef CONFIG_CONSOLE_POLL /* Serial polling routines for writing and reading from the uart while * in an interrupt or debug context. @@ -999,7 +1095,7 @@ static void cpm_put_poll_char(struct uart_port *port, static char ch[2]; ch[0] = (char)c; - cpm_uart_early_write(pinfo->port.line, ch, 1); + cpm_uart_early_write(pinfo, ch, 1); } #endif /* CONFIG_CONSOLE_POLL */ @@ -1130,9 +1226,6 @@ static void cpm_uart_console_write(struct console *co, const char *s, u_int count) { struct uart_cpm_port *pinfo = &cpm_uart_ports[co->index]; - unsigned int i; - cbd_t __iomem *bdp, *bdbase; - unsigned char *cp; unsigned long flags; int nolock = oops_in_progress; @@ -1142,66 +1235,7 @@ static void cpm_uart_console_write(struct console *co, const char *s, spin_lock_irqsave(&pinfo->port.lock, flags); } - /* Get the address of the host memory buffer. - */ - bdp = pinfo->tx_cur; - bdbase = pinfo->tx_bd_base; - - /* - * Now, do each character. This is not as bad as it looks - * since this is a holding FIFO and not a transmitting FIFO. - * We could add the complexity of filling the entire transmit - * buffer, but we would just wait longer between accesses...... - */ - for (i = 0; i < count; i++, s++) { - /* Wait for transmitter fifo to empty. - * Ready indicates output is ready, and xmt is doing - * that, not that it is ready for us to send. - */ - while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) - ; - - /* Send the character out. - * If the buffer address is in the CPM DPRAM, don't - * convert it. - */ - cp = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo); - *cp = *s; - - out_be16(&bdp->cbd_datlen, 1); - setbits16(&bdp->cbd_sc, BD_SC_READY); - - if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) - bdp = bdbase; - else - bdp++; - - /* if a LF, also do CR... */ - if (*s == 10) { - while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) - ; - - cp = cpm2cpu_addr(in_be32(&bdp->cbd_bufaddr), pinfo); - *cp = 13; - - out_be16(&bdp->cbd_datlen, 1); - setbits16(&bdp->cbd_sc, BD_SC_READY); - - if (in_be16(&bdp->cbd_sc) & BD_SC_WRAP) - bdp = bdbase; - else - bdp++; - } - } - - /* - * Finally, Wait for transmitter & holding register to empty - * and restore the IER - */ - while ((in_be16(&bdp->cbd_sc) & BD_SC_READY) != 0) - ; - - pinfo->tx_cur = bdp; + cpm_uart_early_write(pinfo, s, count); if (unlikely(nolock)) { local_irq_restore(flags); @@ -1325,7 +1359,7 @@ static struct uart_driver cpm_reg = { static int probe_index; -static int __devinit cpm_uart_probe(struct of_device *ofdev, +static int __devinit cpm_uart_probe(struct platform_device *ofdev, const struct of_device_id *match) { int index = probe_index++; @@ -1342,14 +1376,14 @@ static int __devinit cpm_uart_probe(struct of_device *ofdev, /* initialize the device pointer for the port */ pinfo->port.dev = &ofdev->dev; - ret = cpm_uart_init_port(ofdev->node, pinfo); + ret = cpm_uart_init_port(ofdev->dev.of_node, pinfo); if (ret) return ret; return uart_add_one_port(&cpm_reg, &pinfo->port); } -static int __devexit cpm_uart_remove(struct of_device *ofdev) +static int __devexit cpm_uart_remove(struct platform_device *ofdev) { struct uart_cpm_port *pinfo = dev_get_drvdata(&ofdev->dev); return uart_remove_one_port(&cpm_reg, &pinfo->port); @@ -1372,8 +1406,11 @@ static struct of_device_id cpm_uart_match[] = { }; static struct of_platform_driver cpm_uart_driver = { - .name = "cpm_uart", - .match_table = cpm_uart_match, + .driver = { + .name = "cpm_uart", + .owner = THIS_MODULE, + .of_match_table = cpm_uart_match, + }, .probe = cpm_uart_probe, .remove = cpm_uart_remove, }; diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm1.c b/drivers/serial/cpm_uart/cpm_uart_cpm1.c index 1b94c56..3fc1d66 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm1.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm1.c @@ -29,6 +29,7 @@ #include <linux/module.h> #include <linux/tty.h> +#include <linux/gfp.h> #include <linux/ioport.h> #include <linux/init.h> #include <linux/serial.h> diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c index 141c0a3..814ac00 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c @@ -30,6 +30,7 @@ #include <linux/module.h> #include <linux/tty.h> #include <linux/ioport.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/serial.h> #include <linux/console.h> @@ -61,7 +62,7 @@ void __iomem *cpm_uart_map_pram(struct uart_cpm_port *port, void __iomem *pram; unsigned long offset; struct resource res; - unsigned long len; + resource_size_t len; /* Don't remap parameter RAM if it has already been initialized * during console setup. @@ -74,7 +75,7 @@ void __iomem *cpm_uart_map_pram(struct uart_cpm_port *port, if (of_address_to_resource(np, 1, &res)) return NULL; - len = 1 + res.end - res.start; + len = resource_size(&res); pram = ioremap(res.start, len); if (!pram) return NULL; @@ -132,7 +133,7 @@ int cpm_uart_allocbuf(struct uart_cpm_port *pinfo, unsigned int is_con) memsz = L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize) + L1_CACHE_ALIGN(pinfo->tx_nrfifos * pinfo->tx_fifosize); if (is_con) { - mem_addr = alloc_bootmem(memsz); + mem_addr = kzalloc(memsz, GFP_NOWAIT); dma_addr = virt_to_bus(mem_addr); } else diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c index 7be52fe..bcc31f2 100644 --- a/drivers/serial/crisv10.c +++ b/drivers/serial/crisv10.c @@ -1410,11 +1410,12 @@ e100_enable_rs485(struct tty_struct *tty, struct serial_rs485 *r) CONFIG_ETRAX_RS485_LTC1387_RXEN_PORT_G_BIT, 1); #endif - info->rs485.flags = r->flags; - if (r->delay_rts_before_send >= 1000) + info->rs485 = *r; + + /* Maximum delay before RTS equal to 1000 */ + if (info->rs485.delay_rts_before_send >= 1000) info->rs485.delay_rts_before_send = 1000; - else - info->rs485.delay_rts_before_send = r->delay_rts_before_send; + /* printk("rts: on send = %i, after = %i, enabled = %i", info->rs485.rts_on_send, info->rs485.rts_after_sent, @@ -3233,9 +3234,9 @@ rs_write(struct tty_struct *tty, e100_disable_rx(info); e100_enable_rx_irq(info); #endif - - if (info->rs485.delay_rts_before_send > 0) - msleep(info->rs485.delay_rts_before_send); + if ((info->rs485.flags & SER_RS485_RTS_BEFORE_SEND) && + (info->rs485.delay_rts_before_send > 0)) + msleep(info->rs485.delay_rts_before_send); } #endif /* CONFIG_ETRAX_RS485 */ @@ -3693,6 +3694,11 @@ rs_ioctl(struct tty_struct *tty, struct file * file, rs485data.delay_rts_before_send = rs485ctrl.delay_rts_before_send; rs485data.flags = 0; + if (rs485data.delay_rts_before_send != 0) + rs485data.flags |= SER_RS485_RTS_BEFORE_SEND; + else + rs485data.flags &= ~(SER_RS485_RTS_BEFORE_SEND); + if (rs485ctrl.enabled) rs485data.flags |= SER_RS485_ENABLED; else @@ -3723,6 +3729,17 @@ rs_ioctl(struct tty_struct *tty, struct file * file, return e100_enable_rs485(tty, &rs485data); } + case TIOCGRS485: + { + struct serial_rs485 *rs485data = + &(((struct e100_serial *)tty->driver_data)->rs485); + /* This is the ioctl to get RS485 data from user-space */ + if (copy_to_user((struct serial_rs485 *) arg, + rs485data, + sizeof(struct serial_rs485))) + return -EFAULT; + break; + } case TIOCSERWRRS485: { @@ -3923,7 +3940,6 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k) */ - lock_kernel(); orig_jiffies = jiffies; while (info->xmit.head != info->xmit.tail || /* More in send queue */ (*info->ostatusadr & 0x007f) || /* more in FIFO */ @@ -3940,7 +3956,6 @@ static void rs_wait_until_sent(struct tty_struct *tty, int timeout) curr_time_usec - info->last_tx_active_usec; } set_current_state(TASK_RUNNING); - unlock_kernel(); } /* @@ -3980,7 +3995,7 @@ block_til_ready(struct tty_struct *tty, struct file * filp, */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible(info->close_wait, + wait_event_interruptible_tty(info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART if (info->flags & ASYNC_HUP_NOTIFY) @@ -4056,7 +4071,9 @@ block_til_ready(struct tty_struct *tty, struct file * filp, printk("block_til_ready blocking: ttyS%d, count = %d\n", info->line, info->count); #endif + tty_unlock(); schedule(); + tty_lock(); } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); @@ -4138,7 +4155,7 @@ rs_open(struct tty_struct *tty, struct file * filp) */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { - wait_event_interruptible(info->close_wait, + wait_event_interruptible_tty(info->close_wait, !(info->flags & ASYNC_CLOSING)); #ifdef SERIAL_DO_RESTART return ((info->flags & ASYNC_HUP_NOTIFY) ? @@ -4515,14 +4532,15 @@ static int __init rs_init(void) /* Set sane defaults */ info->rs485.flags &= ~(SER_RS485_RTS_ON_SEND); info->rs485.flags |= SER_RS485_RTS_AFTER_SEND; + info->rs485.flags &= ~(SER_RS485_RTS_BEFORE_SEND); info->rs485.delay_rts_before_send = 0; info->rs485.flags &= ~(SER_RS485_ENABLED); #endif INIT_WORK(&info->work, do_softint); if (info->enabled) { - printk(KERN_INFO "%s%d at 0x%x is a builtin UART with DMA\n", - serial_driver->name, info->line, (unsigned int)info->ioport); + printk(KERN_INFO "%s%d at %p is a builtin UART with DMA\n", + serial_driver->name, info->line, info->ioport); } } #ifdef CONFIG_ETRAX_FAST_TIMER diff --git a/drivers/serial/dz.c b/drivers/serial/dz.c index 6042b87..57421d7 100644 --- a/drivers/serial/dz.c +++ b/drivers/serial/dz.c @@ -197,7 +197,7 @@ static inline void dz_receive_chars(struct dz_mux *mux) while ((status = dz_in(dport, DZ_RBUF)) & DZ_DVAL) { dport = &mux->dport[LINE(status)]; uport = &dport->port; - tty = uport->info->port.tty; /* point to the proper dev */ + tty = uport->state->port.tty; /* point to the proper dev */ ch = UCHAR(status); /* grab the char */ flag = TTY_NORMAL; @@ -249,7 +249,7 @@ static inline void dz_receive_chars(struct dz_mux *mux) } for (i = 0; i < DZ_NB_PORT; i++) if (lines_rx[i]) - tty_flip_buffer_push(mux->dport[i].port.info->port.tty); + tty_flip_buffer_push(mux->dport[i].port.state->port.tty); } /* @@ -268,7 +268,7 @@ static inline void dz_transmit_chars(struct dz_mux *mux) status = dz_in(dport, DZ_CSR); dport = &mux->dport[LINE(status)]; - xmit = &dport->port.info->xmit; + xmit = &dport->port.state->xmit; if (dport->port.x_char) { /* XON/XOFF chars */ dz_out(dport, DZ_TDR, dport->port.x_char); diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c index 9f2891c..53a4682 100644 --- a/drivers/serial/icom.c +++ b/drivers/serial/icom.c @@ -307,7 +307,7 @@ static void stop_processor(struct icom_port *icom_port) if (port < 4) { temp = readl(stop_proc[port].global_control_reg); temp = - (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id; + (temp & ~start_proc[port].processor_id) | stop_proc[port].processor_id; writel(temp, stop_proc[port].global_control_reg); /* write flush */ @@ -336,7 +336,7 @@ static void start_processor(struct icom_port *icom_port) if (port < 4) { temp = readl(start_proc[port].global_control_reg); temp = - (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id; + (temp & ~stop_proc[port].processor_id) | start_proc[port].processor_id; writel(temp, start_proc[port].global_control_reg); /* write flush */ @@ -509,8 +509,8 @@ static void load_code(struct icom_port *icom_port) dev_err(&icom_port->adapter->pci_dev->dev,"Port not opertional\n"); } - if (new_page != NULL) - pci_free_consistent(dev, 4096, new_page, temp_pci); + if (new_page != NULL) + pci_free_consistent(dev, 4096, new_page, temp_pci); } static int startup(struct icom_port *icom_port) @@ -617,7 +617,7 @@ static void shutdown(struct icom_port *icom_port) * disable break condition */ cmdReg = readb(&icom_port->dram->CmdReg); - if ((cmdReg | CMD_SND_BREAK) == CMD_SND_BREAK) { + if (cmdReg & CMD_SND_BREAK) { writeb(cmdReg & ~CMD_SND_BREAK, &icom_port->dram->CmdReg); } } @@ -627,7 +627,7 @@ static int icom_write(struct uart_port *port) unsigned long data_count; unsigned char cmdReg; unsigned long offset; - int temp_tail = port->info->xmit.tail; + int temp_tail = port->state->xmit.tail; trace(ICOM_PORT, "WRITE", 0); @@ -638,11 +638,11 @@ static int icom_write(struct uart_port *port) } data_count = 0; - while ((port->info->xmit.head != temp_tail) && + while ((port->state->xmit.head != temp_tail) && (data_count <= XMIT_BUFF_SZ)) { ICOM_PORT->xmit_buf[data_count++] = - port->info->xmit.buf[temp_tail]; + port->state->xmit.buf[temp_tail]; temp_tail++; temp_tail &= (UART_XMIT_SIZE - 1); @@ -694,8 +694,8 @@ static inline void check_modem_status(struct icom_port *icom_port) uart_handle_cts_change(&icom_port->uart_port, delta_status & ICOM_CTS); - wake_up_interruptible(&icom_port->uart_port.info-> - delta_msr_wait); + wake_up_interruptible(&icom_port->uart_port.state-> + port.delta_msr_wait); old_status = status; } spin_unlock(&icom_port->uart_port.lock); @@ -718,10 +718,10 @@ static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) icom_port->uart_port.icount.tx += count; for (i=0; i<count && - !uart_circ_empty(&icom_port->uart_port.info->xmit); i++) { + !uart_circ_empty(&icom_port->uart_port.state->xmit); i++) { - icom_port->uart_port.info->xmit.tail++; - icom_port->uart_port.info->xmit.tail &= + icom_port->uart_port.state->xmit.tail++; + icom_port->uart_port.state->xmit.tail &= (UART_XMIT_SIZE - 1); } @@ -735,7 +735,7 @@ static void xmit_interrupt(u16 port_int_reg, struct icom_port *icom_port) static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port) { short int count, rcv_buff; - struct tty_struct *tty = icom_port->uart_port.info->port.tty; + struct tty_struct *tty = icom_port->uart_port.state->port.tty; unsigned short int status; struct uart_icount *icount; unsigned long offset; @@ -751,7 +751,6 @@ static void recv_interrupt(u16 port_int_reg, struct icom_port *icom_port) trace(icom_port, "FID_STATUS", status); count = cpu_to_le16(icom_port->statStg->rcv[rcv_buff].leLength); - count = tty_buffer_request_room(tty, count); trace(icom_port, "RCV_COUNT", count); trace(icom_port, "REAL_COUNT", count); @@ -1493,15 +1492,15 @@ static int __devinit icom_probe(struct pci_dev *dev, const struct pci_device_id *ent) { int index; - unsigned int command_reg; - int retval; - struct icom_adapter *icom_adapter; - struct icom_port *icom_port; + unsigned int command_reg; + int retval; + struct icom_adapter *icom_adapter; + struct icom_port *icom_port; - retval = pci_enable_device(dev); - if (retval) { + retval = pci_enable_device(dev); + if (retval) { dev_err(&dev->dev, "Device enable FAILED\n"); - return retval; + return retval; } if ( (retval = pci_request_regions(dev, "icom"))) { @@ -1510,23 +1509,23 @@ static int __devinit icom_probe(struct pci_dev *dev, return retval; } - pci_set_master(dev); + pci_set_master(dev); - if ( (retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg))) { + if ( (retval = pci_read_config_dword(dev, PCI_COMMAND, &command_reg))) { dev_err(&dev->dev, "PCI Config read FAILED\n"); - return retval; - } + return retval; + } pci_write_config_dword(dev, PCI_COMMAND, command_reg | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_PARITY | PCI_COMMAND_SERR); - if (ent->driver_data == ADAPTER_V1) { + if (ent->driver_data == ADAPTER_V1) { pci_write_config_dword(dev, 0x44, 0x8300830A); - } else { + } else { pci_write_config_dword(dev, 0x44, 0x42004200); pci_write_config_dword(dev, 0x48, 0x42004200); - } + } retval = icom_alloc_adapter(&icom_adapter); @@ -1536,10 +1535,10 @@ static int __devinit icom_probe(struct pci_dev *dev, goto probe_exit0; } - icom_adapter->base_addr_pci = pci_resource_start(dev, 0); - icom_adapter->pci_dev = dev; - icom_adapter->version = ent->driver_data; - icom_adapter->subsystem_id = ent->subdevice; + icom_adapter->base_addr_pci = pci_resource_start(dev, 0); + icom_adapter->pci_dev = dev; + icom_adapter->version = ent->driver_data; + icom_adapter->subsystem_id = ent->subdevice; retval = icom_init_ports(icom_adapter); @@ -1548,8 +1547,7 @@ static int __devinit icom_probe(struct pci_dev *dev, goto probe_exit1; } - icom_adapter->base_addr = ioremap(icom_adapter->base_addr_pci, - pci_resource_len(dev, 0)); + icom_adapter->base_addr = pci_ioremap_bar(dev, 0); if (!icom_adapter->base_addr) goto probe_exit1; @@ -1563,7 +1561,7 @@ static int __devinit icom_probe(struct pci_dev *dev, retval = icom_load_ports(icom_adapter); - for (index = 0; index < icom_adapter->numb_ports; index++) { + for (index = 0; index < icom_adapter->numb_ports; index++) { icom_port = &icom_adapter->port_info[index]; if (icom_port->status == ICOM_PORT_ACTIVE) { @@ -1580,7 +1578,7 @@ static int __devinit icom_probe(struct pci_dev *dev, icom_port->status = ICOM_PORT_OFF; dev_err(&dev->dev, "Device add failed\n"); } else - dev_info(&dev->dev, "Device added\n"); + dev_info(&dev->dev, "Device added\n"); } } @@ -1596,9 +1594,7 @@ probe_exit0: pci_release_regions(dev); pci_disable_device(dev); - return retval; - - + return retval; } static void __devexit icom_remove(struct pci_dev *dev) @@ -1657,4 +1653,6 @@ MODULE_DESCRIPTION("IBM iSeries Serial IOA driver"); MODULE_SUPPORTED_DEVICE ("IBM iSeries 2745, 2771, 2772, 2742, 2793 and 2805 Communications adapters"); MODULE_LICENSE("GPL"); - +MODULE_FIRMWARE("icom_call_setup.bin"); +MODULE_FIRMWARE("icom_res_dce.bin"); +MODULE_FIRMWARE("icom_asc.bin"); diff --git a/drivers/serial/ifx6x60.c b/drivers/serial/ifx6x60.c new file mode 100644 index 0000000..ab93763 --- /dev/null +++ b/drivers/serial/ifx6x60.c @@ -0,0 +1,1406 @@ +/**************************************************************************** + * + * Driver for the IFX 6x60 spi modem. + * + * Copyright (C) 2008 Option International + * Copyright (C) 2008 Filip Aben <f.aben@option.com> + * Denis Joseph Barrow <d.barow@option.com> + * Jan Dumon <j.dumon@option.com> + * + * Copyright (C) 2009, 2010 Intel Corp + * Russ Gorby <richardx.r.gorby@intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + * Driver modified by Intel from Option gtm501l_spi.c + * + * Notes + * o The driver currently assumes a single device only. If you need to + * change this then look for saved_ifx_dev and add a device lookup + * o The driver is intended to be big-endian safe but has never been + * tested that way (no suitable hardware). There are a couple of FIXME + * notes by areas that may need addressing + * o Some of the GPIO naming/setup assumptions may need revisiting if + * you need to use this driver for another platform. + * + *****************************************************************************/ +#include <linux/module.h> +#include <linux/termios.h> +#include <linux/tty.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/tty.h> +#include <linux/kfifo.h> +#include <linux/tty_flip.h> +#include <linux/timer.h> +#include <linux/serial.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/rfkill.h> +#include <linux/fs.h> +#include <linux/ip.h> +#include <linux/dmapool.h> +#include <linux/gpio.h> +#include <linux/sched.h> +#include <linux/time.h> +#include <linux/wait.h> +#include <linux/tty.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/spi/ifx_modem.h> +#include <linux/delay.h> + +#include "ifx6x60.h" + +#define IFX_SPI_MORE_MASK 0x10 +#define IFX_SPI_MORE_BIT 12 /* bit position in u16 */ +#define IFX_SPI_CTS_BIT 13 /* bit position in u16 */ +#define IFX_SPI_TTY_ID 0 +#define IFX_SPI_TIMEOUT_SEC 2 +#define IFX_SPI_HEADER_0 (-1) +#define IFX_SPI_HEADER_F (-2) + +/* forward reference */ +static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev); + +/* local variables */ +static int spi_b16 = 1; /* 8 or 16 bit word length */ +static struct tty_driver *tty_drv; +static struct ifx_spi_device *saved_ifx_dev; +static struct lock_class_key ifx_spi_key; + +/* GPIO/GPE settings */ + +/** + * mrdy_set_high - set MRDY GPIO + * @ifx: device we are controlling + * + */ +static inline void mrdy_set_high(struct ifx_spi_device *ifx) +{ + gpio_set_value(ifx->gpio.mrdy, 1); +} + +/** + * mrdy_set_low - clear MRDY GPIO + * @ifx: device we are controlling + * + */ +static inline void mrdy_set_low(struct ifx_spi_device *ifx) +{ + gpio_set_value(ifx->gpio.mrdy, 0); +} + +/** + * ifx_spi_power_state_set + * @ifx_dev: our SPI device + * @val: bits to set + * + * Set bit in power status and signal power system if status becomes non-0 + */ +static void +ifx_spi_power_state_set(struct ifx_spi_device *ifx_dev, unsigned char val) +{ + unsigned long flags; + + spin_lock_irqsave(&ifx_dev->power_lock, flags); + + /* + * if power status is already non-0, just update, else + * tell power system + */ + if (!ifx_dev->power_status) + pm_runtime_get(&ifx_dev->spi_dev->dev); + ifx_dev->power_status |= val; + + spin_unlock_irqrestore(&ifx_dev->power_lock, flags); +} + +/** + * ifx_spi_power_state_clear - clear power bit + * @ifx_dev: our SPI device + * @val: bits to clear + * + * clear bit in power status and signal power system if status becomes 0 + */ +static void +ifx_spi_power_state_clear(struct ifx_spi_device *ifx_dev, unsigned char val) +{ + unsigned long flags; + + spin_lock_irqsave(&ifx_dev->power_lock, flags); + + if (ifx_dev->power_status) { + ifx_dev->power_status &= ~val; + if (!ifx_dev->power_status) + pm_runtime_put(&ifx_dev->spi_dev->dev); + } + + spin_unlock_irqrestore(&ifx_dev->power_lock, flags); +} + +/** + * swap_buf + * @buf: our buffer + * @len : number of bytes (not words) in the buffer + * @end: end of buffer + * + * Swap the contents of a buffer into big endian format + */ +static inline void swap_buf(u16 *buf, int len, void *end) +{ + int n; + + len = ((len + 1) >> 1); + if ((void *)&buf[len] > end) { + pr_err("swap_buf: swap exceeds boundary (%p > %p)!", + &buf[len], end); + return; + } + for (n = 0; n < len; n++) { + *buf = cpu_to_be16(*buf); + buf++; + } +} + +/** + * mrdy_assert - assert MRDY line + * @ifx_dev: our SPI device + * + * Assert mrdy and set timer to wait for SRDY interrupt, if SRDY is low + * now. + * + * FIXME: Can SRDY even go high as we are running this code ? + */ +static void mrdy_assert(struct ifx_spi_device *ifx_dev) +{ + int val = gpio_get_value(ifx_dev->gpio.srdy); + if (!val) { + if (!test_and_set_bit(IFX_SPI_STATE_TIMER_PENDING, + &ifx_dev->flags)) { + ifx_dev->spi_timer.expires = + jiffies + IFX_SPI_TIMEOUT_SEC*HZ; + add_timer(&ifx_dev->spi_timer); + + } + } + ifx_spi_power_state_set(ifx_dev, IFX_SPI_POWER_DATA_PENDING); + mrdy_set_high(ifx_dev); +} + +/** + * ifx_spi_hangup - hang up an IFX device + * @ifx_dev: our SPI device + * + * Hang up the tty attached to the IFX device if one is currently + * open. If not take no action + */ +static void ifx_spi_ttyhangup(struct ifx_spi_device *ifx_dev) +{ + struct tty_port *pport = &ifx_dev->tty_port; + struct tty_struct *tty = tty_port_tty_get(pport); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } +} + +/** + * ifx_spi_timeout - SPI timeout + * @arg: our SPI device + * + * The SPI has timed out: hang up the tty. Users will then see a hangup + * and error events. + */ +static void ifx_spi_timeout(unsigned long arg) +{ + struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *)arg; + + dev_warn(&ifx_dev->spi_dev->dev, "*** SPI Timeout ***"); + ifx_spi_ttyhangup(ifx_dev); + mrdy_set_low(ifx_dev); + clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); +} + +/* char/tty operations */ + +/** + * ifx_spi_tiocmget - get modem lines + * @tty: our tty device + * @filp: file handle issuing the request + * + * Map the signal state into Linux modem flags and report the value + * in Linux terms + */ +static int ifx_spi_tiocmget(struct tty_struct *tty, struct file *filp) +{ + unsigned int value; + struct ifx_spi_device *ifx_dev = tty->driver_data; + + value = + (test_bit(IFX_SPI_RTS, &ifx_dev->signal_state) ? TIOCM_RTS : 0) | + (test_bit(IFX_SPI_DTR, &ifx_dev->signal_state) ? TIOCM_DTR : 0) | + (test_bit(IFX_SPI_CTS, &ifx_dev->signal_state) ? TIOCM_CTS : 0) | + (test_bit(IFX_SPI_DSR, &ifx_dev->signal_state) ? TIOCM_DSR : 0) | + (test_bit(IFX_SPI_DCD, &ifx_dev->signal_state) ? TIOCM_CAR : 0) | + (test_bit(IFX_SPI_RI, &ifx_dev->signal_state) ? TIOCM_RNG : 0); + return value; +} + +/** + * ifx_spi_tiocmset - set modem bits + * @tty: the tty structure + * @filp: file handle issuing the request + * @set: bits to set + * @clear: bits to clear + * + * The IFX6x60 only supports DTR and RTS. Set them accordingly + * and flag that an update to the modem is needed. + * + * FIXME: do we need to kick the tranfers when we do this ? + */ +static int ifx_spi_tiocmset(struct tty_struct *tty, struct file *filp, + unsigned int set, unsigned int clear) +{ + struct ifx_spi_device *ifx_dev = tty->driver_data; + + if (set & TIOCM_RTS) + set_bit(IFX_SPI_RTS, &ifx_dev->signal_state); + if (set & TIOCM_DTR) + set_bit(IFX_SPI_DTR, &ifx_dev->signal_state); + if (clear & TIOCM_RTS) + clear_bit(IFX_SPI_RTS, &ifx_dev->signal_state); + if (clear & TIOCM_DTR) + clear_bit(IFX_SPI_DTR, &ifx_dev->signal_state); + + set_bit(IFX_SPI_UPDATE, &ifx_dev->signal_state); + return 0; +} + +/** + * ifx_spi_open - called on tty open + * @tty: our tty device + * @filp: file handle being associated with the tty + * + * Open the tty interface. We let the tty_port layer do all the work + * for us. + * + * FIXME: Remove single device assumption and saved_ifx_dev + */ +static int ifx_spi_open(struct tty_struct *tty, struct file *filp) +{ + return tty_port_open(&saved_ifx_dev->tty_port, tty, filp); +} + +/** + * ifx_spi_close - called when our tty closes + * @tty: the tty being closed + * @filp: the file handle being closed + * + * Perform the close of the tty. We use the tty_port layer to do all + * our hard work. + */ +static void ifx_spi_close(struct tty_struct *tty, struct file *filp) +{ + struct ifx_spi_device *ifx_dev = tty->driver_data; + tty_port_close(&ifx_dev->tty_port, tty, filp); + /* FIXME: should we do an ifx_spi_reset here ? */ +} + +/** + * ifx_decode_spi_header - decode received header + * @buffer: the received data + * @length: decoded length + * @more: decoded more flag + * @received_cts: status of cts we received + * + * Note how received_cts is handled -- if header is all F it is left + * the same as it was, if header is all 0 it is set to 0 otherwise it is + * taken from the incoming header. + * + * FIXME: endianness + */ +static int ifx_spi_decode_spi_header(unsigned char *buffer, int *length, + unsigned char *more, unsigned char *received_cts) +{ + u16 h1; + u16 h2; + u16 *in_buffer = (u16 *)buffer; + + h1 = *in_buffer; + h2 = *(in_buffer+1); + + if (h1 == 0 && h2 == 0) { + *received_cts = 0; + return IFX_SPI_HEADER_0; + } else if (h1 == 0xffff && h2 == 0xffff) { + /* spi_slave_cts remains as it was */ + return IFX_SPI_HEADER_F; + } + + *length = h1 & 0xfff; /* upper bits of byte are flags */ + *more = (buffer[1] >> IFX_SPI_MORE_BIT) & 1; + *received_cts = (buffer[3] >> IFX_SPI_CTS_BIT) & 1; + return 0; +} + +/** + * ifx_setup_spi_header - set header fields + * @txbuffer: pointer to start of SPI buffer + * @tx_count: bytes + * @more: indicate if more to follow + * + * Format up an SPI header for a transfer + * + * FIXME: endianness? + */ +static void ifx_spi_setup_spi_header(unsigned char *txbuffer, int tx_count, + unsigned char more) +{ + *(u16 *)(txbuffer) = tx_count; + *(u16 *)(txbuffer+2) = IFX_SPI_PAYLOAD_SIZE; + txbuffer[1] |= (more << IFX_SPI_MORE_BIT) & IFX_SPI_MORE_MASK; +} + +/** + * ifx_spi_wakeup_serial - SPI space made + * @port_data: our SPI device + * + * We have emptied the FIFO enough that we want to get more data + * queued into it. Poke the line discipline via tty_wakeup so that + * it will feed us more bits + */ +static void ifx_spi_wakeup_serial(struct ifx_spi_device *ifx_dev) +{ + struct tty_struct *tty; + + tty = tty_port_tty_get(&ifx_dev->tty_port); + if (!tty) + return; + tty_wakeup(tty); + tty_kref_put(tty); +} + +/** + * ifx_spi_prepare_tx_buffer - prepare transmit frame + * @ifx_dev: our SPI device + * + * The transmit buffr needs a header and various other bits of + * information followed by as much data as we can pull from the FIFO + * and transfer. This function formats up a suitable buffer in the + * ifx_dev->tx_buffer + * + * FIXME: performance - should we wake the tty when the queue is half + * empty ? + */ +static int ifx_spi_prepare_tx_buffer(struct ifx_spi_device *ifx_dev) +{ + int temp_count; + int queue_length; + int tx_count; + unsigned char *tx_buffer; + + tx_buffer = ifx_dev->tx_buffer; + memset(tx_buffer, 0, IFX_SPI_TRANSFER_SIZE); + + /* make room for required SPI header */ + tx_buffer += IFX_SPI_HEADER_OVERHEAD; + tx_count = IFX_SPI_HEADER_OVERHEAD; + + /* clear to signal no more data if this turns out to be the + * last buffer sent in a sequence */ + ifx_dev->spi_more = 0; + + /* if modem cts is set, just send empty buffer */ + if (!ifx_dev->spi_slave_cts) { + /* see if there's tx data */ + queue_length = kfifo_len(&ifx_dev->tx_fifo); + if (queue_length != 0) { + /* data to mux -- see if there's room for it */ + temp_count = min(queue_length, IFX_SPI_PAYLOAD_SIZE); + temp_count = kfifo_out_locked(&ifx_dev->tx_fifo, + tx_buffer, temp_count, + &ifx_dev->fifo_lock); + + /* update buffer pointer and data count in message */ + tx_buffer += temp_count; + tx_count += temp_count; + if (temp_count == queue_length) + /* poke port to get more data */ + ifx_spi_wakeup_serial(ifx_dev); + else /* more data in port, use next SPI message */ + ifx_dev->spi_more = 1; + } + } + /* have data and info for header -- set up SPI header in buffer */ + /* spi header needs payload size, not entire buffer size */ + ifx_spi_setup_spi_header(ifx_dev->tx_buffer, + tx_count-IFX_SPI_HEADER_OVERHEAD, + ifx_dev->spi_more); + /* swap actual data in the buffer */ + swap_buf((u16 *)(ifx_dev->tx_buffer), tx_count, + &ifx_dev->tx_buffer[IFX_SPI_TRANSFER_SIZE]); + return tx_count; +} + +/** + * ifx_spi_write - line discipline write + * @tty: our tty device + * @buf: pointer to buffer to write (kernel space) + * @count: size of buffer + * + * Write the characters we have been given into the FIFO. If the device + * is not active then activate it, when the SRDY line is asserted back + * this will commence I/O + */ +static int ifx_spi_write(struct tty_struct *tty, const unsigned char *buf, + int count) +{ + struct ifx_spi_device *ifx_dev = tty->driver_data; + unsigned char *tmp_buf = (unsigned char *)buf; + int tx_count = kfifo_in_locked(&ifx_dev->tx_fifo, tmp_buf, count, + &ifx_dev->fifo_lock); + mrdy_assert(ifx_dev); + return tx_count; +} + +/** + * ifx_spi_chars_in_buffer - line discipline helper + * @tty: our tty device + * + * Report how much data we can accept before we drop bytes. As we use + * a simple FIFO this is nice and easy. + */ +static int ifx_spi_write_room(struct tty_struct *tty) +{ + struct ifx_spi_device *ifx_dev = tty->driver_data; + return IFX_SPI_FIFO_SIZE - kfifo_len(&ifx_dev->tx_fifo); +} + +/** + * ifx_spi_chars_in_buffer - line discipline helper + * @tty: our tty device + * + * Report how many characters we have buffered. In our case this is the + * number of bytes sitting in our transmit FIFO. + */ +static int ifx_spi_chars_in_buffer(struct tty_struct *tty) +{ + struct ifx_spi_device *ifx_dev = tty->driver_data; + return kfifo_len(&ifx_dev->tx_fifo); +} + +/** + * ifx_port_hangup + * @port: our tty port + * + * tty port hang up. Called when tty_hangup processing is invoked either + * by loss of carrier, or by software (eg vhangup). Serialized against + * activate/shutdown by the tty layer. + */ +static void ifx_spi_hangup(struct tty_struct *tty) +{ + struct ifx_spi_device *ifx_dev = tty->driver_data; + tty_port_hangup(&ifx_dev->tty_port); +} + +/** + * ifx_port_activate + * @port: our tty port + * + * tty port activate method - called for first open. Serialized + * with hangup and shutdown by the tty layer. + */ +static int ifx_port_activate(struct tty_port *port, struct tty_struct *tty) +{ + struct ifx_spi_device *ifx_dev = + container_of(port, struct ifx_spi_device, tty_port); + + /* clear any old data; can't do this in 'close' */ + kfifo_reset(&ifx_dev->tx_fifo); + + /* put port data into this tty */ + tty->driver_data = ifx_dev; + + /* allows flip string push from int context */ + tty->low_latency = 1; + + return 0; +} + +/** + * ifx_port_shutdown + * @port: our tty port + * + * tty port shutdown method - called for last port close. Serialized + * with hangup and activate by the tty layer. + */ +static void ifx_port_shutdown(struct tty_port *port) +{ + struct ifx_spi_device *ifx_dev = + container_of(port, struct ifx_spi_device, tty_port); + + mrdy_set_low(ifx_dev); + clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); + tasklet_kill(&ifx_dev->io_work_tasklet); +} + +static const struct tty_port_operations ifx_tty_port_ops = { + .activate = ifx_port_activate, + .shutdown = ifx_port_shutdown, +}; + +static const struct tty_operations ifx_spi_serial_ops = { + .open = ifx_spi_open, + .close = ifx_spi_close, + .write = ifx_spi_write, + .hangup = ifx_spi_hangup, + .write_room = ifx_spi_write_room, + .chars_in_buffer = ifx_spi_chars_in_buffer, + .tiocmget = ifx_spi_tiocmget, + .tiocmset = ifx_spi_tiocmset, +}; + +/** + * ifx_spi_insert_fip_string - queue received data + * @ifx_ser: our SPI device + * @chars: buffer we have received + * @size: number of chars reeived + * + * Queue bytes to the tty assuming the tty side is currently open. If + * not the discard the data. + */ +static void ifx_spi_insert_flip_string(struct ifx_spi_device *ifx_dev, + unsigned char *chars, size_t size) +{ + struct tty_struct *tty = tty_port_tty_get(&ifx_dev->tty_port); + if (!tty) + return; + tty_insert_flip_string(tty, chars, size); + tty_flip_buffer_push(tty); + tty_kref_put(tty); +} + +/** + * ifx_spi_complete - SPI transfer completed + * @ctx: our SPI device + * + * An SPI transfer has completed. Process any received data and kick off + * any further transmits we can commence. + */ +static void ifx_spi_complete(void *ctx) +{ + struct ifx_spi_device *ifx_dev = ctx; + struct tty_struct *tty; + struct tty_ldisc *ldisc = NULL; + int length; + int actual_length; + unsigned char more; + unsigned char cts; + int local_write_pending = 0; + int queue_length; + int srdy; + int decode_result; + + mrdy_set_low(ifx_dev); + + if (!ifx_dev->spi_msg.status) { + /* check header validity, get comm flags */ + swap_buf((u16 *)ifx_dev->rx_buffer, IFX_SPI_HEADER_OVERHEAD, + &ifx_dev->rx_buffer[IFX_SPI_HEADER_OVERHEAD]); + decode_result = ifx_spi_decode_spi_header(ifx_dev->rx_buffer, + &length, &more, &cts); + if (decode_result == IFX_SPI_HEADER_0) { + dev_dbg(&ifx_dev->spi_dev->dev, + "ignore input: invalid header 0"); + ifx_dev->spi_slave_cts = 0; + goto complete_exit; + } else if (decode_result == IFX_SPI_HEADER_F) { + dev_dbg(&ifx_dev->spi_dev->dev, + "ignore input: invalid header F"); + goto complete_exit; + } + + ifx_dev->spi_slave_cts = cts; + + actual_length = min((unsigned int)length, + ifx_dev->spi_msg.actual_length); + swap_buf((u16 *)(ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD), + actual_length, + &ifx_dev->rx_buffer[IFX_SPI_TRANSFER_SIZE]); + ifx_spi_insert_flip_string( + ifx_dev, + ifx_dev->rx_buffer + IFX_SPI_HEADER_OVERHEAD, + (size_t)actual_length); + } else { + dev_dbg(&ifx_dev->spi_dev->dev, "SPI transfer error %d", + ifx_dev->spi_msg.status); + } + +complete_exit: + if (ifx_dev->write_pending) { + ifx_dev->write_pending = 0; + local_write_pending = 1; + } + + clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &(ifx_dev->flags)); + + queue_length = kfifo_len(&ifx_dev->tx_fifo); + srdy = gpio_get_value(ifx_dev->gpio.srdy); + if (!srdy) + ifx_spi_power_state_clear(ifx_dev, IFX_SPI_POWER_SRDY); + + /* schedule output if there is more to do */ + if (test_and_clear_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags)) + tasklet_schedule(&ifx_dev->io_work_tasklet); + else { + if (more || ifx_dev->spi_more || queue_length > 0 || + local_write_pending) { + if (ifx_dev->spi_slave_cts) { + if (more) + mrdy_assert(ifx_dev); + } else + mrdy_assert(ifx_dev); + } else { + /* + * poke line discipline driver if any for more data + * may or may not get more data to write + * for now, say not busy + */ + ifx_spi_power_state_clear(ifx_dev, + IFX_SPI_POWER_DATA_PENDING); + tty = tty_port_tty_get(&ifx_dev->tty_port); + if (tty) { + ldisc = tty_ldisc_ref(tty); + if (ldisc) { + ldisc->ops->write_wakeup(tty); + tty_ldisc_deref(ldisc); + } + tty_kref_put(tty); + } + } + } +} + +/** + * ifx_spio_io - I/O tasklet + * @data: our SPI device + * + * Queue data for transmission if possible and then kick off the + * transfer. + */ +static void ifx_spi_io(unsigned long data) +{ + int retval; + struct ifx_spi_device *ifx_dev = (struct ifx_spi_device *) data; + + if (!test_and_set_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags)) { + if (ifx_dev->gpio.unack_srdy_int_nb > 0) + ifx_dev->gpio.unack_srdy_int_nb--; + + ifx_spi_prepare_tx_buffer(ifx_dev); + + spi_message_init(&ifx_dev->spi_msg); + INIT_LIST_HEAD(&ifx_dev->spi_msg.queue); + + ifx_dev->spi_msg.context = ifx_dev; + ifx_dev->spi_msg.complete = ifx_spi_complete; + + /* set up our spi transfer */ + /* note len is BYTES, not transfers */ + ifx_dev->spi_xfer.len = IFX_SPI_TRANSFER_SIZE; + ifx_dev->spi_xfer.cs_change = 0; + ifx_dev->spi_xfer.speed_hz = 12500000; + /* ifx_dev->spi_xfer.speed_hz = 390625; */ + ifx_dev->spi_xfer.bits_per_word = spi_b16 ? 16 : 8; + + ifx_dev->spi_xfer.tx_buf = ifx_dev->tx_buffer; + ifx_dev->spi_xfer.rx_buf = ifx_dev->rx_buffer; + + /* + * setup dma pointers + */ + if (ifx_dev->is_6160) { + ifx_dev->spi_msg.is_dma_mapped = 1; + ifx_dev->tx_dma = ifx_dev->tx_bus; + ifx_dev->rx_dma = ifx_dev->rx_bus; + ifx_dev->spi_xfer.tx_dma = ifx_dev->tx_dma; + ifx_dev->spi_xfer.rx_dma = ifx_dev->rx_dma; + } else { + ifx_dev->spi_msg.is_dma_mapped = 0; + ifx_dev->tx_dma = (dma_addr_t)0; + ifx_dev->rx_dma = (dma_addr_t)0; + ifx_dev->spi_xfer.tx_dma = (dma_addr_t)0; + ifx_dev->spi_xfer.rx_dma = (dma_addr_t)0; + } + + spi_message_add_tail(&ifx_dev->spi_xfer, &ifx_dev->spi_msg); + + /* Assert MRDY. This may have already been done by the write + * routine. + */ + mrdy_assert(ifx_dev); + + retval = spi_async(ifx_dev->spi_dev, &ifx_dev->spi_msg); + if (retval) { + clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, + &ifx_dev->flags); + tasklet_schedule(&ifx_dev->io_work_tasklet); + return; + } + } else + ifx_dev->write_pending = 1; +} + +/** + * ifx_spi_free_port - free up the tty side + * @ifx_dev: IFX device going away + * + * Unregister and free up a port when the device goes away + */ +static void ifx_spi_free_port(struct ifx_spi_device *ifx_dev) +{ + if (ifx_dev->tty_dev) + tty_unregister_device(tty_drv, ifx_dev->minor); + kfifo_free(&ifx_dev->tx_fifo); +} + +/** + * ifx_spi_create_port - create a new port + * @ifx_dev: our spi device + * + * Allocate and initialise the tty port that goes with this interface + * and add it to the tty layer so that it can be opened. + */ +static int ifx_spi_create_port(struct ifx_spi_device *ifx_dev) +{ + int ret = 0; + struct tty_port *pport = &ifx_dev->tty_port; + + spin_lock_init(&ifx_dev->fifo_lock); + lockdep_set_class_and_subclass(&ifx_dev->fifo_lock, + &ifx_spi_key, 0); + + if (kfifo_alloc(&ifx_dev->tx_fifo, IFX_SPI_FIFO_SIZE, GFP_KERNEL)) { + ret = -ENOMEM; + goto error_ret; + } + + pport->ops = &ifx_tty_port_ops; + tty_port_init(pport); + ifx_dev->minor = IFX_SPI_TTY_ID; + ifx_dev->tty_dev = tty_register_device(tty_drv, ifx_dev->minor, + &ifx_dev->spi_dev->dev); + if (IS_ERR(ifx_dev->tty_dev)) { + dev_dbg(&ifx_dev->spi_dev->dev, + "%s: registering tty device failed", __func__); + ret = PTR_ERR(ifx_dev->tty_dev); + goto error_ret; + } + return 0; + +error_ret: + ifx_spi_free_port(ifx_dev); + return ret; +} + +/** + * ifx_spi_handle_srdy - handle SRDY + * @ifx_dev: device asserting SRDY + * + * Check our device state and see what we need to kick off when SRDY + * is asserted. This usually means killing the timer and firing off the + * I/O processing. + */ +static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev) +{ + if (test_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags)) { + del_timer_sync(&ifx_dev->spi_timer); + clear_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags); + } + + ifx_spi_power_state_set(ifx_dev, IFX_SPI_POWER_SRDY); + + if (!test_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags)) + tasklet_schedule(&ifx_dev->io_work_tasklet); + else + set_bit(IFX_SPI_STATE_IO_READY, &ifx_dev->flags); +} + +/** + * ifx_spi_srdy_interrupt - SRDY asserted + * @irq: our IRQ number + * @dev: our ifx device + * + * The modem asserted SRDY. Handle the srdy event + */ +static irqreturn_t ifx_spi_srdy_interrupt(int irq, void *dev) +{ + struct ifx_spi_device *ifx_dev = dev; + ifx_dev->gpio.unack_srdy_int_nb++; + ifx_spi_handle_srdy(ifx_dev); + return IRQ_HANDLED; +} + +/** + * ifx_spi_reset_interrupt - Modem has changed reset state + * @irq: interrupt number + * @dev: our device pointer + * + * The modem has either entered or left reset state. Check the GPIO + * line to see which. + * + * FIXME: review locking on MR_INPROGRESS versus + * parallel unsolicited reset/solicited reset + */ +static irqreturn_t ifx_spi_reset_interrupt(int irq, void *dev) +{ + struct ifx_spi_device *ifx_dev = dev; + int val = gpio_get_value(ifx_dev->gpio.reset_out); + int solreset = test_bit(MR_START, &ifx_dev->mdm_reset_state); + + if (val == 0) { + /* entered reset */ + set_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state); + if (!solreset) { + /* unsolicited reset */ + ifx_spi_ttyhangup(ifx_dev); + } + } else { + /* exited reset */ + clear_bit(MR_INPROGRESS, &ifx_dev->mdm_reset_state); + if (solreset) { + set_bit(MR_COMPLETE, &ifx_dev->mdm_reset_state); + wake_up(&ifx_dev->mdm_reset_wait); + } + } + return IRQ_HANDLED; +} + +/** + * ifx_spi_free_device - free device + * @ifx_dev: device to free + * + * Free the IFX device + */ +static void ifx_spi_free_device(struct ifx_spi_device *ifx_dev) +{ + ifx_spi_free_port(ifx_dev); + dma_free_coherent(&ifx_dev->spi_dev->dev, + IFX_SPI_TRANSFER_SIZE, + ifx_dev->tx_buffer, + ifx_dev->tx_bus); + dma_free_coherent(&ifx_dev->spi_dev->dev, + IFX_SPI_TRANSFER_SIZE, + ifx_dev->rx_buffer, + ifx_dev->rx_bus); +} + +/** + * ifx_spi_reset - reset modem + * @ifx_dev: modem to reset + * + * Perform a reset on the modem + */ +static int ifx_spi_reset(struct ifx_spi_device *ifx_dev) +{ + int ret; + /* + * set up modem power, reset + * + * delays are required on some platforms for the modem + * to reset properly + */ + set_bit(MR_START, &ifx_dev->mdm_reset_state); + gpio_set_value(ifx_dev->gpio.po, 0); + gpio_set_value(ifx_dev->gpio.reset, 0); + msleep(25); + gpio_set_value(ifx_dev->gpio.reset, 1); + msleep(1); + gpio_set_value(ifx_dev->gpio.po, 1); + msleep(1); + gpio_set_value(ifx_dev->gpio.po, 0); + ret = wait_event_timeout(ifx_dev->mdm_reset_wait, + test_bit(MR_COMPLETE, + &ifx_dev->mdm_reset_state), + IFX_RESET_TIMEOUT); + if (!ret) + dev_warn(&ifx_dev->spi_dev->dev, "Modem reset timeout: (state:%lx)", + ifx_dev->mdm_reset_state); + + ifx_dev->mdm_reset_state = 0; + return ret; +} + +/** + * ifx_spi_spi_probe - probe callback + * @spi: our possible matching SPI device + * + * Probe for a 6x60 modem on SPI bus. Perform any needed device and + * GPIO setup. + * + * FIXME: + * - Support for multiple devices + * - Split out MID specific GPIO handling eventually + */ + +static int ifx_spi_spi_probe(struct spi_device *spi) +{ + int ret; + int srdy; + struct ifx_modem_platform_data *pl_data = NULL; + struct ifx_spi_device *ifx_dev; + + if (saved_ifx_dev) { + dev_dbg(&spi->dev, "ignoring subsequent detection"); + return -ENODEV; + } + + /* initialize structure to hold our device variables */ + ifx_dev = kzalloc(sizeof(struct ifx_spi_device), GFP_KERNEL); + if (!ifx_dev) { + dev_err(&spi->dev, "spi device allocation failed"); + return -ENOMEM; + } + saved_ifx_dev = ifx_dev; + ifx_dev->spi_dev = spi; + clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &ifx_dev->flags); + spin_lock_init(&ifx_dev->write_lock); + spin_lock_init(&ifx_dev->power_lock); + ifx_dev->power_status = 0; + init_timer(&ifx_dev->spi_timer); + ifx_dev->spi_timer.function = ifx_spi_timeout; + ifx_dev->spi_timer.data = (unsigned long)ifx_dev; + ifx_dev->is_6160 = pl_data->is_6160; + + /* ensure SPI protocol flags are initialized to enable transfer */ + ifx_dev->spi_more = 0; + ifx_dev->spi_slave_cts = 0; + + /*initialize transfer and dma buffers */ + ifx_dev->tx_buffer = dma_alloc_coherent(&ifx_dev->spi_dev->dev, + IFX_SPI_TRANSFER_SIZE, + &ifx_dev->tx_bus, + GFP_KERNEL); + if (!ifx_dev->tx_buffer) { + dev_err(&spi->dev, "DMA-TX buffer allocation failed"); + ret = -ENOMEM; + goto error_ret; + } + ifx_dev->rx_buffer = dma_alloc_coherent(&ifx_dev->spi_dev->dev, + IFX_SPI_TRANSFER_SIZE, + &ifx_dev->rx_bus, + GFP_KERNEL); + if (!ifx_dev->rx_buffer) { + dev_err(&spi->dev, "DMA-RX buffer allocation failed"); + ret = -ENOMEM; + goto error_ret; + } + + /* initialize waitq for modem reset */ + init_waitqueue_head(&ifx_dev->mdm_reset_wait); + + spi_set_drvdata(spi, ifx_dev); + tasklet_init(&ifx_dev->io_work_tasklet, ifx_spi_io, + (unsigned long)ifx_dev); + + set_bit(IFX_SPI_STATE_PRESENT, &ifx_dev->flags); + + /* create our tty port */ + ret = ifx_spi_create_port(ifx_dev); + if (ret != 0) { + dev_err(&spi->dev, "create default tty port failed"); + goto error_ret; + } + + pl_data = (struct ifx_modem_platform_data *)spi->dev.platform_data; + if (pl_data) { + ifx_dev->gpio.reset = pl_data->rst_pmu; + ifx_dev->gpio.po = pl_data->pwr_on; + ifx_dev->gpio.mrdy = pl_data->mrdy; + ifx_dev->gpio.srdy = pl_data->srdy; + ifx_dev->gpio.reset_out = pl_data->rst_out; + } else { + dev_err(&spi->dev, "missing platform data!"); + ret = -ENODEV; + goto error_ret; + } + + dev_info(&spi->dev, "gpios %d, %d, %d, %d, %d", + ifx_dev->gpio.reset, ifx_dev->gpio.po, ifx_dev->gpio.mrdy, + ifx_dev->gpio.srdy, ifx_dev->gpio.reset_out); + + /* Configure gpios */ + ret = gpio_request(ifx_dev->gpio.reset, "ifxModem"); + if (ret < 0) { + dev_err(&spi->dev, "Unable to allocate GPIO%d (RESET)", + ifx_dev->gpio.reset); + goto error_ret; + } + ret += gpio_direction_output(ifx_dev->gpio.reset, 0); + ret += gpio_export(ifx_dev->gpio.reset, 1); + if (ret) { + dev_err(&spi->dev, "Unable to configure GPIO%d (RESET)", + ifx_dev->gpio.reset); + ret = -EBUSY; + goto error_ret2; + } + + ret = gpio_request(ifx_dev->gpio.po, "ifxModem"); + ret += gpio_direction_output(ifx_dev->gpio.po, 0); + ret += gpio_export(ifx_dev->gpio.po, 1); + if (ret) { + dev_err(&spi->dev, "Unable to configure GPIO%d (ON)", + ifx_dev->gpio.po); + ret = -EBUSY; + goto error_ret3; + } + + ret = gpio_request(ifx_dev->gpio.mrdy, "ifxModem"); + if (ret < 0) { + dev_err(&spi->dev, "Unable to allocate GPIO%d (MRDY)", + ifx_dev->gpio.mrdy); + goto error_ret3; + } + ret += gpio_export(ifx_dev->gpio.mrdy, 1); + ret += gpio_direction_output(ifx_dev->gpio.mrdy, 0); + if (ret) { + dev_err(&spi->dev, "Unable to configure GPIO%d (MRDY)", + ifx_dev->gpio.mrdy); + ret = -EBUSY; + goto error_ret4; + } + + ret = gpio_request(ifx_dev->gpio.srdy, "ifxModem"); + if (ret < 0) { + dev_err(&spi->dev, "Unable to allocate GPIO%d (SRDY)", + ifx_dev->gpio.srdy); + ret = -EBUSY; + goto error_ret4; + } + ret += gpio_export(ifx_dev->gpio.srdy, 1); + ret += gpio_direction_input(ifx_dev->gpio.srdy); + if (ret) { + dev_err(&spi->dev, "Unable to configure GPIO%d (SRDY)", + ifx_dev->gpio.srdy); + ret = -EBUSY; + goto error_ret5; + } + + ret = gpio_request(ifx_dev->gpio.reset_out, "ifxModem"); + if (ret < 0) { + dev_err(&spi->dev, "Unable to allocate GPIO%d (RESET_OUT)", + ifx_dev->gpio.reset_out); + goto error_ret5; + } + ret += gpio_export(ifx_dev->gpio.reset_out, 1); + ret += gpio_direction_input(ifx_dev->gpio.reset_out); + if (ret) { + dev_err(&spi->dev, "Unable to configure GPIO%d (RESET_OUT)", + ifx_dev->gpio.reset_out); + ret = -EBUSY; + goto error_ret6; + } + + ret = request_irq(gpio_to_irq(ifx_dev->gpio.reset_out), + ifx_spi_reset_interrupt, + IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, DRVNAME, + (void *)ifx_dev); + if (ret) { + dev_err(&spi->dev, "Unable to get irq %x\n", + gpio_to_irq(ifx_dev->gpio.reset_out)); + goto error_ret6; + } + + ret = ifx_spi_reset(ifx_dev); + + ret = request_irq(gpio_to_irq(ifx_dev->gpio.srdy), + ifx_spi_srdy_interrupt, + IRQF_TRIGGER_RISING, DRVNAME, + (void *)ifx_dev); + if (ret) { + dev_err(&spi->dev, "Unable to get irq %x", + gpio_to_irq(ifx_dev->gpio.srdy)); + goto error_ret7; + } + + /* set pm runtime power state and register with power system */ + pm_runtime_set_active(&spi->dev); + pm_runtime_enable(&spi->dev); + + /* handle case that modem is already signaling SRDY */ + /* no outgoing tty open at this point, this just satisfies the + * modem's read and should reset communication properly + */ + srdy = gpio_get_value(ifx_dev->gpio.srdy); + + if (srdy) { + mrdy_assert(ifx_dev); + ifx_spi_handle_srdy(ifx_dev); + } else + mrdy_set_low(ifx_dev); + return 0; + +error_ret7: + free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), (void *)ifx_dev); +error_ret6: + gpio_free(ifx_dev->gpio.srdy); +error_ret5: + gpio_free(ifx_dev->gpio.mrdy); +error_ret4: + gpio_free(ifx_dev->gpio.reset); +error_ret3: + gpio_free(ifx_dev->gpio.po); +error_ret2: + gpio_free(ifx_dev->gpio.reset_out); +error_ret: + ifx_spi_free_device(ifx_dev); + saved_ifx_dev = NULL; + return ret; +} + +/** + * ifx_spi_spi_remove - SPI device was removed + * @spi: SPI device + * + * FIXME: We should be shutting the device down here not in + * the module unload path. + */ + +static int ifx_spi_spi_remove(struct spi_device *spi) +{ + struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi); + /* stop activity */ + tasklet_kill(&ifx_dev->io_work_tasklet); + /* free irq */ + free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), (void *)ifx_dev); + free_irq(gpio_to_irq(ifx_dev->gpio.srdy), (void *)ifx_dev); + + gpio_free(ifx_dev->gpio.srdy); + gpio_free(ifx_dev->gpio.mrdy); + gpio_free(ifx_dev->gpio.reset); + gpio_free(ifx_dev->gpio.po); + gpio_free(ifx_dev->gpio.reset_out); + + /* free allocations */ + ifx_spi_free_device(ifx_dev); + + saved_ifx_dev = NULL; + return 0; +} + +/** + * ifx_spi_spi_shutdown - called on SPI shutdown + * @spi: SPI device + * + * No action needs to be taken here + */ + +static void ifx_spi_spi_shutdown(struct spi_device *spi) +{ +} + +/* + * various suspends and resumes have nothing to do + * no hardware to save state for + */ + +/** + * ifx_spi_spi_suspend - suspend SPI on system suspend + * @dev: device being suspended + * + * Suspend the SPI side. No action needed on Intel MID platforms, may + * need extending for other systems. + */ +static int ifx_spi_spi_suspend(struct spi_device *spi, pm_message_t msg) +{ + return 0; +} + +/** + * ifx_spi_spi_resume - resume SPI side on system resume + * @dev: device being suspended + * + * Suspend the SPI side. No action needed on Intel MID platforms, may + * need extending for other systems. + */ +static int ifx_spi_spi_resume(struct spi_device *spi) +{ + return 0; +} + +/** + * ifx_spi_pm_suspend - suspend modem on system suspend + * @dev: device being suspended + * + * Suspend the modem. No action needed on Intel MID platforms, may + * need extending for other systems. + */ +static int ifx_spi_pm_suspend(struct device *dev) +{ + return 0; +} + +/** + * ifx_spi_pm_resume - resume modem on system resume + * @dev: device being suspended + * + * Allow the modem to resume. No action needed. + * + * FIXME: do we need to reset anything here ? + */ +static int ifx_spi_pm_resume(struct device *dev) +{ + return 0; +} + +/** + * ifx_spi_pm_runtime_resume - suspend modem + * @dev: device being suspended + * + * Allow the modem to resume. No action needed. + */ +static int ifx_spi_pm_runtime_resume(struct device *dev) +{ + return 0; +} + +/** + * ifx_spi_pm_runtime_suspend - suspend modem + * @dev: device being suspended + * + * Allow the modem to suspend and thus suspend to continue up the + * device tree. + */ +static int ifx_spi_pm_runtime_suspend(struct device *dev) +{ + return 0; +} + +/** + * ifx_spi_pm_runtime_idle - check if modem idle + * @dev: our device + * + * Check conditions and queue runtime suspend if idle. + */ +static int ifx_spi_pm_runtime_idle(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct ifx_spi_device *ifx_dev = spi_get_drvdata(spi); + + if (!ifx_dev->power_status) + pm_runtime_suspend(dev); + + return 0; +} + +static const struct dev_pm_ops ifx_spi_pm = { + .resume = ifx_spi_pm_resume, + .suspend = ifx_spi_pm_suspend, + .runtime_resume = ifx_spi_pm_runtime_resume, + .runtime_suspend = ifx_spi_pm_runtime_suspend, + .runtime_idle = ifx_spi_pm_runtime_idle +}; + +static const struct spi_device_id ifx_id_table[] = { + {"ifx6160", 0}, + {"ifx6260", 0}, + { } +}; +MODULE_DEVICE_TABLE(spi, ifx_id_table); + +/* spi operations */ +static const struct spi_driver ifx_spi_driver_6160 = { + .driver = { + .name = "ifx6160", + .bus = &spi_bus_type, + .pm = &ifx_spi_pm, + .owner = THIS_MODULE}, + .probe = ifx_spi_spi_probe, + .shutdown = ifx_spi_spi_shutdown, + .remove = __devexit_p(ifx_spi_spi_remove), + .suspend = ifx_spi_spi_suspend, + .resume = ifx_spi_spi_resume, + .id_table = ifx_id_table +}; + +/** + * ifx_spi_exit - module exit + * + * Unload the module. + */ + +static void __exit ifx_spi_exit(void) +{ + /* unregister */ + tty_unregister_driver(tty_drv); + spi_unregister_driver((void *)&ifx_spi_driver_6160); +} + +/** + * ifx_spi_init - module entry point + * + * Initialise the SPI and tty interfaces for the IFX SPI driver + * We need to initialize upper-edge spi driver after the tty + * driver because otherwise the spi probe will race + */ + +static int __init ifx_spi_init(void) +{ + int result; + + tty_drv = alloc_tty_driver(1); + if (!tty_drv) { + pr_err("%s: alloc_tty_driver failed", DRVNAME); + return -ENOMEM; + } + + tty_drv->magic = TTY_DRIVER_MAGIC; + tty_drv->owner = THIS_MODULE; + tty_drv->driver_name = DRVNAME; + tty_drv->name = TTYNAME; + tty_drv->minor_start = IFX_SPI_TTY_ID; + tty_drv->num = 1; + tty_drv->type = TTY_DRIVER_TYPE_SERIAL; + tty_drv->subtype = SERIAL_TYPE_NORMAL; + tty_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + tty_drv->init_termios = tty_std_termios; + + tty_set_operations(tty_drv, &ifx_spi_serial_ops); + + result = tty_register_driver(tty_drv); + if (result) { + pr_err("%s: tty_register_driver failed(%d)", + DRVNAME, result); + put_tty_driver(tty_drv); + return result; + } + + result = spi_register_driver((void *)&ifx_spi_driver_6160); + if (result) { + pr_err("%s: spi_register_driver failed(%d)", + DRVNAME, result); + tty_unregister_driver(tty_drv); + } + return result; +} + +module_init(ifx_spi_init); +module_exit(ifx_spi_exit); + +MODULE_AUTHOR("Intel"); +MODULE_DESCRIPTION("IFX6x60 spi driver"); +MODULE_LICENSE("GPL"); +MODULE_INFO(Version, "0.1-IFX6x60"); diff --git a/drivers/serial/ifx6x60.h b/drivers/serial/ifx6x60.h new file mode 100644 index 0000000..deb7b8d --- /dev/null +++ b/drivers/serial/ifx6x60.h @@ -0,0 +1,129 @@ +/**************************************************************************** + * + * Driver for the IFX spi modem. + * + * Copyright (C) 2009, 2010 Intel Corp + * Jim Stanley <jim.stanley@intel.com> + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA + * + * + * + *****************************************************************************/ +#ifndef _IFX6X60_H +#define _IFX6X60_H + +#define DRVNAME "ifx6x60" +#define TTYNAME "ttyIFX" + +/* #define IFX_THROTTLE_CODE */ + +#define IFX_SPI_MAX_MINORS 1 +#define IFX_SPI_TRANSFER_SIZE 2048 +#define IFX_SPI_FIFO_SIZE 4096 + +#define IFX_SPI_HEADER_OVERHEAD 4 +#define IFX_RESET_TIMEOUT msecs_to_jiffies(50) + +/* device flags bitfield definitions */ +#define IFX_SPI_STATE_PRESENT 0 +#define IFX_SPI_STATE_IO_IN_PROGRESS 1 +#define IFX_SPI_STATE_IO_READY 2 +#define IFX_SPI_STATE_TIMER_PENDING 3 + +/* flow control bitfields */ +#define IFX_SPI_DCD 0 +#define IFX_SPI_CTS 1 +#define IFX_SPI_DSR 2 +#define IFX_SPI_RI 3 +#define IFX_SPI_DTR 4 +#define IFX_SPI_RTS 5 +#define IFX_SPI_TX_FC 6 +#define IFX_SPI_RX_FC 7 +#define IFX_SPI_UPDATE 8 + +#define IFX_SPI_PAYLOAD_SIZE (IFX_SPI_TRANSFER_SIZE - \ + IFX_SPI_HEADER_OVERHEAD) + +#define IFX_SPI_IRQ_TYPE DETECT_EDGE_RISING +#define IFX_SPI_GPIO_TARGET 0 +#define IFX_SPI_GPIO0 0x105 + +#define IFX_SPI_STATUS_TIMEOUT (2000*HZ) + +/* values for bits in power status byte */ +#define IFX_SPI_POWER_DATA_PENDING 1 +#define IFX_SPI_POWER_SRDY 2 + +struct ifx_spi_device { + /* Our SPI device */ + struct spi_device *spi_dev; + + /* Port specific data */ + struct kfifo tx_fifo; + spinlock_t fifo_lock; + unsigned long signal_state; + + /* TTY Layer logic */ + struct tty_port tty_port; + struct device *tty_dev; + int minor; + + /* Low level I/O work */ + struct tasklet_struct io_work_tasklet; + unsigned long flags; + dma_addr_t rx_dma; + dma_addr_t tx_dma; + + int is_6160; /* Modem type */ + + spinlock_t write_lock; + int write_pending; + spinlock_t power_lock; + unsigned char power_status; + + unsigned char *rx_buffer; + unsigned char *tx_buffer; + dma_addr_t rx_bus; + dma_addr_t tx_bus; + unsigned char spi_more; + unsigned char spi_slave_cts; + + struct timer_list spi_timer; + + struct spi_message spi_msg; + struct spi_transfer spi_xfer; + + struct { + /* gpio lines */ + unsigned short srdy; /* slave-ready gpio */ + unsigned short mrdy; /* master-ready gpio */ + unsigned short reset; /* modem-reset gpio */ + unsigned short po; /* modem-on gpio */ + unsigned short reset_out; /* modem-in-reset gpio */ + /* state/stats */ + int unack_srdy_int_nb; + } gpio; + + /* modem reset */ + unsigned long mdm_reset_state; +#define MR_START 0 +#define MR_INPROGRESS 1 +#define MR_COMPLETE 2 + wait_queue_head_t mdm_reset_wait; +}; + +#endif /* _IFX6X60_H */ diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index 5d7b58f..dfcf4b1 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -46,6 +46,7 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/rational.h> +#include <linux/slab.h> #include <asm/io.h> #include <asm/irq.h> @@ -67,21 +68,8 @@ #define UBIR 0xa4 /* BRM Incremental Register */ #define UBMR 0xa8 /* BRM Modulator Register */ #define UBRC 0xac /* Baud Rate Count Register */ -#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2 -#define ONEMS 0xb0 /* One Millisecond register */ -#define UTS 0xb4 /* UART Test Register */ -#endif -#ifdef CONFIG_ARCH_MX1 -#define BIPR1 0xb0 /* Incremental Preset Register 1 */ -#define BIPR2 0xb4 /* Incremental Preset Register 2 */ -#define BIPR3 0xb8 /* Incremental Preset Register 3 */ -#define BIPR4 0xbc /* Incremental Preset Register 4 */ -#define BMPR1 0xc0 /* BRM Modulator Register 1 */ -#define BMPR2 0xc4 /* BRM Modulator Register 2 */ -#define BMPR3 0xc8 /* BRM Modulator Register 3 */ -#define BMPR4 0xcc /* BRM Modulator Register 4 */ -#define UTS 0xd0 /* UART Test Register */ -#endif +#define MX2_ONEMS 0xb0 /* One Millisecond register */ +#define UTS (cpu_is_mx1() ? 0xd0 : 0xb4) /* UART Test Register */ /* UART Control Register Bit Fields.*/ #define URXD_CHARRDY (1<<15) @@ -101,12 +89,7 @@ #define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */ #define UCR1_SNDBRK (1<<4) /* Send break */ #define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */ -#ifdef CONFIG_ARCH_MX1 -#define UCR1_UARTCLKEN (1<<2) /* UART clock enabled */ -#endif -#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2 -#define UCR1_UARTCLKEN (0) /* not present on mx2/mx3 */ -#endif +#define MX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, mx1 only */ #define UCR1_DOZE (1<<1) /* Doze */ #define UCR1_UARTEN (1<<0) /* UART enabled */ #define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */ @@ -132,16 +115,13 @@ #define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */ #define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */ #define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */ -#ifdef CONFIG_ARCH_MX1 -#define UCR3_REF25 (1<<3) /* Ref freq 25 MHz, only on mx1 */ -#define UCR3_REF30 (1<<2) /* Ref Freq 30 MHz, only on mx1 */ -#endif -#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3 -#define UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select, on mx2/mx3 */ -#endif +#define MX1_UCR3_REF25 (1<<3) /* Ref freq 25 MHz, only on mx1 */ +#define MX1_UCR3_REF30 (1<<2) /* Ref Freq 30 MHz, only on mx1 */ +#define MX2_UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select, on mx2/mx3 */ #define UCR3_INVT (1<<1) /* Inverted Infrared transmission */ #define UCR3_BPEN (1<<0) /* Preset registers enable */ -#define UCR4_CTSTL_32 (32<<10) /* CTS trigger level (32 chars) */ +#define UCR4_CTSTL_SHF 10 /* CTS trigger level shift */ +#define UCR4_CTSTL_MASK 0x3F /* CTS trigger is 6 bits wide */ #define UCR4_INVR (1<<9) /* Inverted infrared reception */ #define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */ #define UCR4_WKEN (1<<7) /* Wake interrupt enable */ @@ -186,12 +166,10 @@ #define UTS_SOFTRST (1<<0) /* Software reset */ /* We've been assigned a range on the "Low-density serial ports" major */ -#ifdef CONFIG_ARCH_MXC #define SERIAL_IMX_MAJOR 207 #define MINOR_START 16 #define DEV_NAME "ttymxc" #define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS -#endif /* * This determines how often we check the modem status signals @@ -248,7 +226,7 @@ static void imx_mctrl_check(struct imx_port *sport) if (changed & TIOCM_CTS) uart_handle_cts_change(&sport->port, status & TIOCM_CTS); - wake_up_interruptible(&sport->port.info->delta_msr_wait); + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); } /* @@ -260,7 +238,7 @@ static void imx_timeout(unsigned long data) struct imx_port *sport = (struct imx_port *)data; unsigned long flags; - if (sport->port.info) { + if (sport->port.state) { spin_lock_irqsave(&sport->port.lock, flags); imx_mctrl_check(sport); spin_unlock_irqrestore(&sport->port.lock, flags); @@ -347,16 +325,15 @@ static void imx_enable_ms(struct uart_port *port) static inline void imx_transmit_buffer(struct imx_port *sport) { - struct circ_buf *xmit = &sport->port.info->xmit; + struct circ_buf *xmit = &sport->port.state->xmit; - while (!(readl(sport->port.membase + UTS) & UTS_TXFULL)) { + while (!uart_circ_empty(xmit) && + !(readl(sport->port.membase + UTS) & UTS_TXFULL)) { /* send xmit->buf[xmit->tail] * out the port here */ writel(xmit->buf[xmit->tail], sport->port.membase + URTX0); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); sport->port.icount.tx++; - if (uart_circ_empty(xmit)) - break; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -412,7 +389,7 @@ static irqreturn_t imx_rtsint(int irq, void *dev_id) writel(USR1_RTSD, sport->port.membase + USR1); uart_handle_cts_change(&sport->port, !!val); - wake_up_interruptible(&sport->port.info->delta_msr_wait); + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); spin_unlock_irqrestore(&sport->port.lock, flags); return IRQ_HANDLED; @@ -421,7 +398,7 @@ static irqreturn_t imx_rtsint(int irq, void *dev_id) static irqreturn_t imx_txint(int irq, void *dev_id) { struct imx_port *sport = dev_id; - struct circ_buf *xmit = &sport->port.info->xmit; + struct circ_buf *xmit = &sport->port.state->xmit; unsigned long flags; spin_lock_irqsave(&sport->port.lock,flags); @@ -451,7 +428,7 @@ static irqreturn_t imx_rxint(int irq, void *dev_id) { struct imx_port *sport = dev_id; unsigned int rx,flg,ignored = 0; - struct tty_struct *tty = sport->port.info->port.tty; + struct tty_struct *tty = sport->port.state->port.tty; unsigned long flags, temp; spin_lock_irqsave(&sport->port.lock,flags); @@ -464,7 +441,7 @@ static irqreturn_t imx_rxint(int irq, void *dev_id) temp = readl(sport->port.membase + USR2); if (temp & USR2_BRCD) { - writel(temp | USR2_BRCD, sport->port.membase + USR2); + writel(USR2_BRCD, sport->port.membase + USR2); if (uart_handle_break(&sport->port)) continue; } @@ -614,6 +591,9 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode) return 0; } +/* half the RX buffer size */ +#define CTSTL 16 + static int imx_startup(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; @@ -630,6 +610,10 @@ static int imx_startup(struct uart_port *port) if (USE_IRDA(sport)) temp |= UCR4_IRSC; + /* set the trigger level for CTS */ + temp &= ~(UCR4_CTSTL_MASK<< UCR4_CTSTL_SHF); + temp |= CTSTL<< UCR4_CTSTL_SHF; + writel(temp & ~UCR4_DREN, sport->port.membase + UCR4); if (USE_IRDA(sport)) { @@ -706,11 +690,11 @@ static int imx_startup(struct uart_port *port) } } -#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3 - temp = readl(sport->port.membase + UCR3); - temp |= UCR3_RXDMUXSEL; - writel(temp, sport->port.membase + UCR3); -#endif + if (!cpu_is_mx1()) { + temp = readl(sport->port.membase + UCR3); + temp |= MX2_UCR3_RXDMUXSEL; + writel(temp, sport->port.membase + UCR3); + } if (USE_IRDA(sport)) { temp = readl(sport->port.membase + UCR4); @@ -924,13 +908,11 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, rational_best_approximation(16 * div * baud, sport->port.uartclk, 1 << 16, 1 << 16, &num, &denom); - if (port->info && port->info->port.tty) { - tdiv64 = sport->port.uartclk; - tdiv64 *= num; - do_div(tdiv64, denom * 16 * div); - tty_encode_baud_rate(sport->port.info->port.tty, + tdiv64 = sport->port.uartclk; + tdiv64 *= num; + do_div(tdiv64, denom * 16 * div); + tty_termios_encode_baud_rate(termios, (speed_t)tdiv64, (speed_t)tdiv64); - } num -= 1; denom -= 1; @@ -942,9 +924,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios, writel(num, sport->port.membase + UBIR); writel(denom, sport->port.membase + UBMR); -#ifdef ONEMS - writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS); -#endif + if (!cpu_is_mx1()) + writel(sport->port.uartclk / div / 1000, + sport->port.membase + MX2_ONEMS); writel(old_ucr1, sport->port.membase + UCR1); @@ -1074,17 +1056,20 @@ static void imx_console_write(struct console *co, const char *s, unsigned int count) { struct imx_port *sport = imx_ports[co->index]; - unsigned int old_ucr1, old_ucr2; + unsigned int old_ucr1, old_ucr2, ucr1; /* * First, save UCR1/2 and then disable interrupts */ - old_ucr1 = readl(sport->port.membase + UCR1); + ucr1 = old_ucr1 = readl(sport->port.membase + UCR1); old_ucr2 = readl(sport->port.membase + UCR2); - writel((old_ucr1 | UCR1_UARTCLKEN | UCR1_UARTEN) & - ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN), - sport->port.membase + UCR1); + if (cpu_is_mx1()) + ucr1 |= MX1_UCR1_UARTCLKEN; + ucr1 |= UCR1_UARTEN; + ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN); + + writel(ucr1, sport->port.membase + UCR1); writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2); @@ -1109,7 +1094,7 @@ imx_console_get_options(struct imx_port *sport, int *baud, int *parity, int *bits) { - if ( readl(sport->port.membase + UCR1) | UCR1_UARTEN ) { + if (readl(sport->port.membase + UCR1) & UCR1_UARTEN) { /* ok, the port was enabled */ unsigned int ucr2, ubir,ubmr, uartclk; unsigned int baud_raw; @@ -1300,7 +1285,7 @@ static int serial_imx_probe(struct platform_device *pdev) sport->use_irda = 1; #endif - if (pdata->init) { + if (pdata && pdata->init) { ret = pdata->init(pdev); if (ret) goto clkput; @@ -1313,7 +1298,7 @@ static int serial_imx_probe(struct platform_device *pdev) return 0; deinit: - if (pdata->exit) + if (pdata && pdata->exit) pdata->exit(pdev); clkput: clk_put(sport->clk); @@ -1342,7 +1327,7 @@ static int serial_imx_remove(struct platform_device *pdev) clk_disable(sport->clk); - if (pdata->exit) + if (pdata && pdata->exit) pdata->exit(pdev); iounmap(sport->port.membase); diff --git a/drivers/serial/ioc3_serial.c b/drivers/serial/ioc3_serial.c index ae3699d..ee43efc 100644 --- a/drivers/serial/ioc3_serial.c +++ b/drivers/serial/ioc3_serial.c @@ -20,6 +20,7 @@ #include <linux/pci.h> #include <linux/serial_core.h> #include <linux/ioc3.h> +#include <linux/slab.h> /* * Interesting things about the ioc3 @@ -897,25 +898,25 @@ static void transmit_chars(struct uart_port *the_port) char *start; struct tty_struct *tty; struct ioc3_port *port = get_ioc3_port(the_port); - struct uart_info *info; + struct uart_state *state; if (!the_port) return; if (!port) return; - info = the_port->info; - tty = info->port.tty; + state = the_port->state; + tty = state->port.tty; - if (uart_circ_empty(&info->xmit) || uart_tx_stopped(the_port)) { + if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) { /* Nothing to do or hw stopped */ set_notification(port, N_ALL_OUTPUT, 0); return; } - head = info->xmit.head; - tail = info->xmit.tail; - start = (char *)&info->xmit.buf[tail]; + head = state->xmit.head; + tail = state->xmit.tail; + start = (char *)&state->xmit.buf[tail]; /* write out all the data or until the end of the buffer */ xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail); @@ -928,14 +929,14 @@ static void transmit_chars(struct uart_port *the_port) /* advance the pointers */ tail += result; tail &= UART_XMIT_SIZE - 1; - info->xmit.tail = tail; - start = (char *)&info->xmit.buf[tail]; + state->xmit.tail = tail; + start = (char *)&state->xmit.buf[tail]; } } - if (uart_circ_chars_pending(&info->xmit) < WAKEUP_CHARS) + if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS) uart_write_wakeup(the_port); - if (uart_circ_empty(&info->xmit)) { + if (uart_circ_empty(&state->xmit)) { set_notification(port, N_OUTPUT_LOWAT, 0); } else { set_notification(port, N_OUTPUT_LOWAT, 1); @@ -953,12 +954,13 @@ ioc3_change_speed(struct uart_port *the_port, struct ktermios *new_termios, struct ktermios *old_termios) { struct ioc3_port *port = get_ioc3_port(the_port); - unsigned int cflag; + unsigned int cflag, iflag; int baud; int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8; - struct uart_info *info = the_port->info; + struct uart_state *state = the_port->state; cflag = new_termios->c_cflag; + iflag = new_termios->c_iflag; switch (cflag & CSIZE) { case CS5: @@ -997,14 +999,14 @@ ioc3_change_speed(struct uart_port *the_port, the_port->ignore_status_mask = N_ALL_INPUT; - info->port.tty->low_latency = 1; + state->port.tty->low_latency = 1; - if (I_IGNPAR(info->port.tty)) + if (iflag & IGNPAR) the_port->ignore_status_mask &= ~(N_PARITY_ERROR | N_FRAMING_ERROR); - if (I_IGNBRK(info->port.tty)) { + if (iflag & IGNBRK) { the_port->ignore_status_mask &= ~N_BREAK; - if (I_IGNPAR(info->port.tty)) + if (iflag & IGNPAR) the_port->ignore_status_mask &= ~N_OVERRUN_ERROR; } if (!(cflag & CREAD)) { @@ -1286,8 +1288,8 @@ static inline int do_read(struct uart_port *the_port, char *buf, int len) uart_handle_dcd_change (port->ip_port, 0); wake_up_interruptible - (&the_port->info-> - delta_msr_wait); + (&the_port->state-> + port.delta_msr_wait); } /* If we had any data to return, we @@ -1392,27 +1394,26 @@ static int receive_chars(struct uart_port *the_port) struct tty_struct *tty; unsigned char ch[MAX_CHARS]; int read_count = 0, read_room, flip = 0; - struct uart_info *info = the_port->info; + struct uart_state *state = the_port->state; struct ioc3_port *port = get_ioc3_port(the_port); unsigned long pflags; /* Make sure all the pointers are "good" ones */ - if (!info) + if (!state) return 0; - if (!info->port.tty) + if (!state->port.tty) return 0; if (!(port->ip_flags & INPUT_ENABLE)) return 0; spin_lock_irqsave(&the_port->lock, pflags); - tty = info->port.tty; + tty = state->port.tty; read_count = do_read(the_port, ch, MAX_CHARS); if (read_count > 0) { flip = 1; - read_room = tty_buffer_request_room(tty, read_count); - tty_insert_flip_string(tty, ch, read_room); + read_room = tty_insert_flip_string(tty, ch, read_count); the_port->icount.rx += read_count; } spin_unlock_irqrestore(&the_port->lock, pflags); @@ -1491,7 +1492,7 @@ ioc3uart_intr_one(struct ioc3_submodule *is, uart_handle_dcd_change(the_port, shadow & SHADOW_DCD); wake_up_interruptible - (&the_port->info->delta_msr_wait); + (&the_port->state->port.delta_msr_wait); } else if ((port->ip_notify & N_DDCD) && !(shadow & SHADOW_DCD)) { /* Flag delta DCD/no DCD */ @@ -1511,7 +1512,7 @@ ioc3uart_intr_one(struct ioc3_submodule *is, uart_handle_cts_change(the_port, shadow & SHADOW_CTS); wake_up_interruptible - (&the_port->info->delta_msr_wait); + (&the_port->state->port.delta_msr_wait); } } @@ -1721,14 +1722,14 @@ static void ic3_shutdown(struct uart_port *the_port) { unsigned long port_flags; struct ioc3_port *port; - struct uart_info *info; + struct uart_state *state; port = get_ioc3_port(the_port); if (!port) return; - info = the_port->info; - wake_up_interruptible(&info->delta_msr_wait); + state = the_port->state; + wake_up_interruptible(&state->port.delta_msr_wait); spin_lock_irqsave(&the_port->lock, port_flags); set_notification(port, N_ALL, 0); @@ -2016,6 +2017,7 @@ ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) struct ioc3_port *port; struct ioc3_port *ports[PORTS_PER_CARD]; int phys_port; + int cnt; DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __func__, is, idd)); @@ -2043,6 +2045,7 @@ ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) if (!port) { printk(KERN_WARNING "IOC3 serial memory not available for port\n"); + ret = -ENOMEM; goto out4; } spin_lock_init(&port->ip_lock); @@ -2145,6 +2148,9 @@ ioc3uart_probe(struct ioc3_submodule *is, struct ioc3_driver_data *idd) /* error exits that give back resources */ out4: + for (cnt = 0; cnt < phys_port; cnt++) + kfree(ports[cnt]); + kfree(card_ptr); return ret; } @@ -2162,7 +2168,7 @@ static struct ioc3_submodule ioc3uart_ops = { /** * ioc3_detect - module init called, */ -static int __devinit ioc3uart_init(void) +static int __init ioc3uart_init(void) { int ret; @@ -2179,7 +2185,7 @@ static int __devinit ioc3uart_init(void) return ret; } -static void __devexit ioc3uart_exit(void) +static void __exit ioc3uart_exit(void) { ioc3_unregister_submodule(&ioc3uart_ops); uart_unregister_driver(&ioc3_uart); diff --git a/drivers/serial/ioc4_serial.c b/drivers/serial/ioc4_serial.c index 6bab63c..fcfe826 100644 --- a/drivers/serial/ioc4_serial.c +++ b/drivers/serial/ioc4_serial.c @@ -22,6 +22,7 @@ #include <linux/pci.h> #include <linux/ioc4.h> #include <linux/serial_core.h> +#include <linux/slab.h> /* * interesting things about the ioc4 @@ -930,7 +931,7 @@ static void handle_dma_error_intr(void *arg, uint32_t other_ir) if (readl(&port->ip_mem->pci_err_addr_l.raw) & IOC4_PCI_ERR_ADDR_VLD) { printk(KERN_ERR - "PCI error address is 0x%lx, " + "PCI error address is 0x%llx, " "master is serial port %c %s\n", (((uint64_t)readl(&port->ip_mem->pci_err_addr_h) << 32) @@ -1627,25 +1628,25 @@ static void transmit_chars(struct uart_port *the_port) char *start; struct tty_struct *tty; struct ioc4_port *port = get_ioc4_port(the_port, 0); - struct uart_info *info; + struct uart_state *state; if (!the_port) return; if (!port) return; - info = the_port->info; - tty = info->port.tty; + state = the_port->state; + tty = state->port.tty; - if (uart_circ_empty(&info->xmit) || uart_tx_stopped(the_port)) { + if (uart_circ_empty(&state->xmit) || uart_tx_stopped(the_port)) { /* Nothing to do or hw stopped */ set_notification(port, N_ALL_OUTPUT, 0); return; } - head = info->xmit.head; - tail = info->xmit.tail; - start = (char *)&info->xmit.buf[tail]; + head = state->xmit.head; + tail = state->xmit.tail; + start = (char *)&state->xmit.buf[tail]; /* write out all the data or until the end of the buffer */ xmit_count = (head < tail) ? (UART_XMIT_SIZE - tail) : (head - tail); @@ -1658,14 +1659,14 @@ static void transmit_chars(struct uart_port *the_port) /* advance the pointers */ tail += result; tail &= UART_XMIT_SIZE - 1; - info->xmit.tail = tail; - start = (char *)&info->xmit.buf[tail]; + state->xmit.tail = tail; + start = (char *)&state->xmit.buf[tail]; } } - if (uart_circ_chars_pending(&info->xmit) < WAKEUP_CHARS) + if (uart_circ_chars_pending(&state->xmit) < WAKEUP_CHARS) uart_write_wakeup(the_port); - if (uart_circ_empty(&info->xmit)) { + if (uart_circ_empty(&state->xmit)) { set_notification(port, N_OUTPUT_LOWAT, 0); } else { set_notification(port, N_OUTPUT_LOWAT, 1); @@ -1684,11 +1685,12 @@ ioc4_change_speed(struct uart_port *the_port, { struct ioc4_port *port = get_ioc4_port(the_port, 0); int baud, bits; - unsigned cflag; + unsigned cflag, iflag; int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8; - struct uart_info *info = the_port->info; + struct uart_state *state = the_port->state; cflag = new_termios->c_cflag; + iflag = new_termios->c_iflag; switch (cflag & CSIZE) { case CS5: @@ -1738,14 +1740,14 @@ ioc4_change_speed(struct uart_port *the_port, the_port->ignore_status_mask = N_ALL_INPUT; - info->port.tty->low_latency = 1; + state->port.tty->low_latency = 1; - if (I_IGNPAR(info->port.tty)) + if (iflag & IGNPAR) the_port->ignore_status_mask &= ~(N_PARITY_ERROR | N_FRAMING_ERROR); - if (I_IGNBRK(info->port.tty)) { + if (iflag & IGNBRK) { the_port->ignore_status_mask &= ~N_BREAK; - if (I_IGNPAR(info->port.tty)) + if (iflag & IGNPAR) the_port->ignore_status_mask &= ~N_OVERRUN_ERROR; } if (!(cflag & CREAD)) { @@ -1784,7 +1786,7 @@ ioc4_change_speed(struct uart_port *the_port, static inline int ic4_startup_local(struct uart_port *the_port) { struct ioc4_port *port; - struct uart_info *info; + struct uart_state *state; if (!the_port) return -1; @@ -1793,7 +1795,7 @@ static inline int ic4_startup_local(struct uart_port *the_port) if (!port) return -1; - info = the_port->info; + state = the_port->state; local_open(port); @@ -1801,7 +1803,7 @@ static inline int ic4_startup_local(struct uart_port *the_port) ioc4_set_proto(port, the_port->mapbase); /* set the speed of the serial port */ - ioc4_change_speed(the_port, info->port.tty->termios, + ioc4_change_speed(the_port, state->port.tty->termios, (struct ktermios *)0); return 0; @@ -1882,7 +1884,7 @@ static void handle_intr(void *arg, uint32_t sio_ir) the_port = port->ip_port; the_port->icount.dcd = 1; wake_up_interruptible - (&the_port-> info->delta_msr_wait); + (&the_port->state->port.delta_msr_wait); } else if ((port->ip_notify & N_DDCD) && !(shadow & IOC4_SHADOW_DCD)) { /* Flag delta DCD/no DCD */ @@ -1904,7 +1906,7 @@ static void handle_intr(void *arg, uint32_t sio_ir) the_port->icount.cts = (shadow & IOC4_SHADOW_CTS) ? 1 : 0; wake_up_interruptible - (&the_port->info->delta_msr_wait); + (&the_port->state->port.delta_msr_wait); } } @@ -2236,8 +2238,8 @@ static inline int do_read(struct uart_port *the_port, unsigned char *buf, && port->ip_port) { the_port->icount.dcd = 0; wake_up_interruptible - (&the_port->info-> - delta_msr_wait); + (&the_port->state-> + port.delta_msr_wait); } /* If we had any data to return, we @@ -2341,17 +2343,17 @@ static void receive_chars(struct uart_port *the_port) unsigned char ch[IOC4_MAX_CHARS]; int read_count, request_count = IOC4_MAX_CHARS; struct uart_icount *icount; - struct uart_info *info = the_port->info; + struct uart_state *state = the_port->state; unsigned long pflags; /* Make sure all the pointers are "good" ones */ - if (!info) + if (!state) return; - if (!info->port.tty) + if (!state->port.tty) return; spin_lock_irqsave(&the_port->lock, pflags); - tty = info->port.tty; + tty = state->port.tty; request_count = tty_buffer_request_room(tty, IOC4_MAX_CHARS); @@ -2430,19 +2432,19 @@ static void ic4_shutdown(struct uart_port *the_port) { unsigned long port_flags; struct ioc4_port *port; - struct uart_info *info; + struct uart_state *state; port = get_ioc4_port(the_port, 0); if (!port) return; - info = the_port->info; + state = the_port->state; port->ip_port = NULL; - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&state->port.delta_msr_wait); - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + if (state->port.tty) + set_bit(TTY_IO_ERROR, &state->port.tty->flags); spin_lock_irqsave(&the_port->lock, port_flags); set_notification(port, N_ALL, 0); @@ -2538,7 +2540,7 @@ static int ic4_startup(struct uart_port *the_port) int retval; struct ioc4_port *port; struct ioc4_control *control; - struct uart_info *info; + struct uart_state *state; unsigned long port_flags; if (!the_port) @@ -2546,7 +2548,7 @@ static int ic4_startup(struct uart_port *the_port) port = get_ioc4_port(the_port, 1); if (!port) return -ENODEV; - info = the_port->info; + state = the_port->state; control = port->ip_control; if (!control) { @@ -2904,7 +2906,7 @@ static struct ioc4_submodule ioc4_serial_submodule = { /** * ioc4_serial_init - module init */ -int ioc4_serial_init(void) +static int __init ioc4_serial_init(void) { int ret; @@ -2913,20 +2915,30 @@ int ioc4_serial_init(void) printk(KERN_WARNING "%s: Couldn't register rs232 IOC4 serial driver\n", __func__); - return ret; + goto out; } if ((ret = uart_register_driver(&ioc4_uart_rs422)) < 0) { printk(KERN_WARNING "%s: Couldn't register rs422 IOC4 serial driver\n", __func__); - return ret; + goto out_uart_rs232; } /* register with IOC4 main module */ - return ioc4_register_submodule(&ioc4_serial_submodule); + ret = ioc4_register_submodule(&ioc4_serial_submodule); + if (ret) + goto out_uart_rs422; + return 0; + +out_uart_rs422: + uart_unregister_driver(&ioc4_uart_rs422); +out_uart_rs232: + uart_unregister_driver(&ioc4_uart_rs232); +out: + return ret; } -static void __devexit ioc4_serial_exit(void) +static void __exit ioc4_serial_exit(void) { ioc4_unregister_submodule(&ioc4_serial_submodule); uart_unregister_driver(&ioc4_uart_rs232); diff --git a/drivers/serial/ip22zilog.c b/drivers/serial/ip22zilog.c index 0d9acbd..ebff4a1 100644 --- a/drivers/serial/ip22zilog.c +++ b/drivers/serial/ip22zilog.c @@ -256,9 +256,9 @@ static struct tty_struct *ip22zilog_receive_chars(struct uart_ip22zilog_port *up unsigned int r1; tty = NULL; - if (up->port.info != NULL && - up->port.info->port.tty != NULL) - tty = up->port.info->port.tty; + if (up->port.state != NULL && + up->port.state->port.tty != NULL) + tty = up->port.state->port.tty; for (;;) { ch = readb(&channel->control); @@ -354,7 +354,7 @@ static void ip22zilog_status_handle(struct uart_ip22zilog_port *up, uart_handle_cts_change(&up->port, (status & CTS)); - wake_up_interruptible(&up->port.info->delta_msr_wait); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); } up->prev_status = status; @@ -404,9 +404,9 @@ static void ip22zilog_transmit_chars(struct uart_ip22zilog_port *up, return; } - if (up->port.info == NULL) + if (up->port.state == NULL) goto ack_tx_int; - xmit = &up->port.info->xmit; + xmit = &up->port.state->xmit; if (uart_circ_empty(xmit)) goto ack_tx_int; if (uart_tx_stopped(&up->port)) @@ -607,7 +607,7 @@ static void ip22zilog_start_tx(struct uart_port *port) port->icount.tx++; port->x_char = 0; } else { - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; writeb(xmit->buf[xmit->tail], &channel->data); ZSDELAY(); diff --git a/drivers/serial/jsm/jsm.h b/drivers/serial/jsm/jsm.h index 4e5f3bd..38a509c 100644 --- a/drivers/serial/jsm/jsm.h +++ b/drivers/serial/jsm/jsm.h @@ -138,7 +138,6 @@ struct jsm_board u32 nasync; /* Number of ports on card */ u32 irq; /* Interrupt request number */ - u64 intr_count; /* Count of interrupts */ u64 membase; /* Start of base memory of the card */ u64 membase_end; /* End of base memory of the card */ @@ -206,8 +205,6 @@ struct jsm_channel { u64 ch_close_delay; /* How long we should drop RTS/DTR for */ - u64 ch_cpstime; /* Time for CPS calculations */ - tcflag_t ch_c_iflag; /* channel iflags */ tcflag_t ch_c_cflag; /* channel cflags */ tcflag_t ch_c_oflag; /* channel oflags */ @@ -215,11 +212,6 @@ struct jsm_channel { u8 ch_stopc; /* Stop character */ u8 ch_startc; /* Start character */ - u32 ch_old_baud; /* Cache of the current baud */ - u32 ch_custom_speed;/* Custom baud, if set */ - - u32 ch_wopen; /* Waiting for open process cnt */ - u8 ch_mostat; /* FEP output modem status */ u8 ch_mistat; /* FEP input modem status */ diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c index b3604aa..18f5484 100644 --- a/drivers/serial/jsm/jsm_driver.c +++ b/drivers/serial/jsm/jsm_driver.c @@ -26,6 +26,7 @@ ***********************************************************************/ #include <linux/moduleparam.h> #include <linux/pci.h> +#include <linux/slab.h> #include "jsm.h" @@ -48,6 +49,17 @@ struct uart_driver jsm_uart_driver = { .nr = NR_PORTS, }; +static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev, + pci_channel_state_t state); +static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev); +static void jsm_io_resume(struct pci_dev *pdev); + +static struct pci_error_handlers jsm_err_handler = { + .error_detected = jsm_io_error_detected, + .slot_reset = jsm_io_slot_reset, + .resume = jsm_io_resume, +}; + int jsm_debug; module_param(jsm_debug, int, 0); MODULE_PARM_DESC(jsm_debug, "Driver debugging level"); @@ -123,7 +135,7 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device } rc = request_irq(brd->irq, brd->bd_ops->intr, - IRQF_DISABLED|IRQF_SHARED, "JSM", brd); + IRQF_SHARED, "JSM", brd); if (rc) { printk(KERN_WARNING "Failed to hook IRQ %d\n",brd->irq); goto out_iounmap; @@ -160,13 +172,17 @@ static int __devinit jsm_probe_one(struct pci_dev *pdev, const struct pci_device jsm_uart_port_init here! */ dev_err(&pdev->dev, "memory allocation for flipbuf failed\n"); rc = -ENOMEM; - goto out_free_irq; + goto out_free_uart; } pci_set_drvdata(pdev, brd); + pci_save_state(pdev); return 0; + out_free_uart: + jsm_remove_uart_port(brd); out_free_irq: + jsm_remove_uart_port(brd); free_irq(brd->irq, brd); out_iounmap: iounmap(brd->re_map_membase); @@ -222,8 +238,42 @@ static struct pci_driver jsm_driver = { .id_table = jsm_pci_tbl, .probe = jsm_probe_one, .remove = __devexit_p(jsm_remove_one), + .err_handler = &jsm_err_handler, }; +static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct jsm_board *brd = pci_get_drvdata(pdev); + + jsm_remove_uart_port(brd); + + return PCI_ERS_RESULT_NEED_RESET; +} + +static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev) +{ + int rc; + + rc = pci_enable_device(pdev); + + if (rc) + return PCI_ERS_RESULT_DISCONNECT; + + pci_set_master(pdev); + + return PCI_ERS_RESULT_RECOVERED; +} + +static void jsm_io_resume(struct pci_dev *pdev) +{ + struct jsm_board *brd = pci_get_drvdata(pdev); + + pci_restore_state(pdev); + + jsm_uart_port_init(brd); +} + static int __init jsm_init_module(void) { int rc; diff --git a/drivers/serial/jsm/jsm_neo.c b/drivers/serial/jsm/jsm_neo.c index 9dadaa11..7960d96 100644 --- a/drivers/serial/jsm/jsm_neo.c +++ b/drivers/serial/jsm/jsm_neo.c @@ -954,13 +954,8 @@ static void neo_param(struct jsm_channel *ch) ch->ch_flags |= (CH_BAUD0); ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR); neo_assert_modem_signals(ch); - ch->ch_old_baud = 0; return; - } else if (ch->ch_custom_speed) { - baud = ch->ch_custom_speed; - if (ch->ch_flags & CH_BAUD0) - ch->ch_flags &= ~(CH_BAUD0); } else { int i; unsigned int cflag; @@ -989,7 +984,7 @@ static void neo_param(struct jsm_channel *ch) { 50, B50 }, }; - cflag = C_BAUD(ch->uart_port.info->port.tty); + cflag = C_BAUD(ch->uart_port.state->port.tty); baud = 9600; for (i = 0; i < ARRAY_SIZE(baud_rates); i++) { if (baud_rates[i].cflag == cflag) { @@ -1045,7 +1040,6 @@ static void neo_param(struct jsm_channel *ch) quot = ch->ch_bd->bd_dividend / baud; if (quot != 0) { - ch->ch_old_baud = baud; writeb(UART_LCR_DLAB, &ch->ch_neo_uart->lcr); writeb((quot & 0xff), &ch->ch_neo_uart->txrx); writeb((quot >> 8), &ch->ch_neo_uart->ier); @@ -1123,8 +1117,6 @@ static irqreturn_t neo_intr(int irq, void *voidbrd) unsigned long lock_flags2; int outofloop_count = 0; - brd->intr_count++; - /* Lock out the slow poller from running on this board. */ spin_lock_irqsave(&brd->bd_intr_lock, lock_flags); diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c index 107ce2e..7a4a914 100644 --- a/drivers/serial/jsm/jsm_tty.c +++ b/drivers/serial/jsm/jsm_tty.c @@ -30,6 +30,7 @@ #include <linux/serial_reg.h> #include <linux/delay.h> /* For udelay */ #include <linux/pci.h> +#include <linux/slab.h> #include "jsm.h" @@ -147,7 +148,7 @@ static void jsm_tty_send_xchar(struct uart_port *port, char ch) struct ktermios *termios; spin_lock_irqsave(&port->lock, lock_flags); - termios = port->info->port.tty->termios; + termios = port->state->port.tty->termios; if (ch == termios->c_cc[VSTART]) channel->ch_bd->bd_ops->send_start_character(channel); @@ -245,7 +246,7 @@ static int jsm_tty_open(struct uart_port *port) channel->ch_cached_lsr = 0; channel->ch_stops_sent = 0; - termios = port->info->port.tty->termios; + termios = port->state->port.tty->termios; channel->ch_c_cflag = termios->c_cflag; channel->ch_c_iflag = termios->c_iflag; channel->ch_c_oflag = termios->c_oflag; @@ -278,7 +279,7 @@ static void jsm_tty_close(struct uart_port *port) jsm_printk(CLOSE, INFO, &channel->ch_bd->pci_dev, "start\n"); bd = channel->ch_bd; - ts = port->info->port.tty->termios; + ts = port->state->port.tty->termios; channel->ch_flags &= ~(CH_STOPI); @@ -296,8 +297,6 @@ static void jsm_tty_close(struct uart_port *port) bd->bd_ops->assert_modem_signals(channel); } - channel->ch_old_baud = 0; - /* Turn off UART interrupts for this port */ channel->ch_bd->bd_ops->uart_off(channel); @@ -432,9 +431,9 @@ int __devinit jsm_tty_init(struct jsm_board *brd) return 0; } -int __devinit jsm_uart_port_init(struct jsm_board *brd) +int jsm_uart_port_init(struct jsm_board *brd) { - int i; + int i, rc; unsigned int line; struct jsm_channel *ch; @@ -467,12 +466,15 @@ int __devinit jsm_uart_port_init(struct jsm_board *brd) printk(KERN_INFO "jsm: linemap is full, added device failed\n"); continue; } else - set_bit((int)line, linemap); + set_bit(line, linemap); brd->channels[i]->uart_port.line = line; - if (uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port)) - printk(KERN_INFO "jsm: add device failed\n"); + rc = uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port); + if (rc){ + printk(KERN_INFO "jsm: Port %d failed. Aborting...\n", i); + return rc; + } else - printk(KERN_INFO "Added device \n"); + printk(KERN_INFO "jsm: Port %d added\n", i); } jsm_printk(INIT, INFO, &brd->pci_dev, "finish\n"); @@ -503,7 +505,7 @@ int jsm_remove_uart_port(struct jsm_board *brd) ch = brd->channels[i]; - clear_bit((int)(ch->uart_port.line), linemap); + clear_bit(ch->uart_port.line, linemap); uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port); } @@ -530,7 +532,7 @@ void jsm_input(struct jsm_channel *ch) if (!ch) return; - tp = ch->uart_port.info->port.tty; + tp = ch->uart_port.state->port.tty; bd = ch->ch_bd; if(!bd) @@ -849,7 +851,7 @@ int jsm_tty_write(struct uart_port *port) u16 tail; u16 tmask; u32 remain; - int temp_tail = port->info->xmit.tail; + int temp_tail = port->state->xmit.tail; struct jsm_channel *channel = (struct jsm_channel *)port; tmask = WQUEUEMASK; @@ -865,10 +867,10 @@ int jsm_tty_write(struct uart_port *port) data_count = 0; if (bufcount >= remain) { bufcount -= remain; - while ((port->info->xmit.head != temp_tail) && + while ((port->state->xmit.head != temp_tail) && (data_count < remain)) { channel->ch_wqueue[head++] = - port->info->xmit.buf[temp_tail]; + port->state->xmit.buf[temp_tail]; temp_tail++; temp_tail &= (UART_XMIT_SIZE - 1); @@ -880,10 +882,10 @@ int jsm_tty_write(struct uart_port *port) data_count1 = 0; if (bufcount > 0) { remain = bufcount; - while ((port->info->xmit.head != temp_tail) && + while ((port->state->xmit.head != temp_tail) && (data_count1 < remain)) { channel->ch_wqueue[head++] = - port->info->xmit.buf[temp_tail]; + port->state->xmit.buf[temp_tail]; temp_tail++; temp_tail &= (UART_XMIT_SIZE - 1); @@ -892,7 +894,7 @@ int jsm_tty_write(struct uart_port *port) } } - port->info->xmit.tail = temp_tail; + port->state->xmit.tail = temp_tail; data_count += data_count1; if (data_count) { diff --git a/drivers/serial/kgdboc.c b/drivers/serial/kgdboc.c index eadc1ab..25a8bc5 100644 --- a/drivers/serial/kgdboc.c +++ b/drivers/serial/kgdboc.c @@ -14,7 +14,11 @@ #include <linux/kernel.h> #include <linux/ctype.h> #include <linux/kgdb.h> +#include <linux/kdb.h> #include <linux/tty.h> +#include <linux/console.h> +#include <linux/vt_kern.h> +#include <linux/input.h> #define MAX_CONFIG_LEN 40 @@ -29,9 +33,102 @@ static struct kparam_string kps = { .maxlen = MAX_CONFIG_LEN, }; +static int kgdboc_use_kms; /* 1 if we use kernel mode switching */ static struct tty_driver *kgdb_tty_driver; static int kgdb_tty_line; +#ifdef CONFIG_KDB_KEYBOARD +static int kgdboc_reset_connect(struct input_handler *handler, + struct input_dev *dev, + const struct input_device_id *id) +{ + input_reset_device(dev); + + /* Retrun an error - we do not want to bind, just to reset */ + return -ENODEV; +} + +static void kgdboc_reset_disconnect(struct input_handle *handle) +{ + /* We do not expect anyone to actually bind to us */ + BUG(); +} + +static const struct input_device_id kgdboc_reset_ids[] = { + { + .flags = INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit = { BIT_MASK(EV_KEY) }, + }, + { } +}; + +static struct input_handler kgdboc_reset_handler = { + .connect = kgdboc_reset_connect, + .disconnect = kgdboc_reset_disconnect, + .name = "kgdboc_reset", + .id_table = kgdboc_reset_ids, +}; + +static DEFINE_MUTEX(kgdboc_reset_mutex); + +static void kgdboc_restore_input_helper(struct work_struct *dummy) +{ + /* + * We need to take a mutex to prevent several instances of + * this work running on different CPUs so they don't try + * to register again already registered handler. + */ + mutex_lock(&kgdboc_reset_mutex); + + if (input_register_handler(&kgdboc_reset_handler) == 0) + input_unregister_handler(&kgdboc_reset_handler); + + mutex_unlock(&kgdboc_reset_mutex); +} + +static DECLARE_WORK(kgdboc_restore_input_work, kgdboc_restore_input_helper); + +static void kgdboc_restore_input(void) +{ + if (likely(system_state == SYSTEM_RUNNING)) + schedule_work(&kgdboc_restore_input_work); +} + +static int kgdboc_register_kbd(char **cptr) +{ + if (strncmp(*cptr, "kbd", 3) == 0) { + if (kdb_poll_idx < KDB_POLL_FUNC_MAX) { + kdb_poll_funcs[kdb_poll_idx] = kdb_get_kbd_char; + kdb_poll_idx++; + if (cptr[0][3] == ',') + *cptr += 4; + else + return 1; + } + } + return 0; +} + +static void kgdboc_unregister_kbd(void) +{ + int i; + + for (i = 0; i < kdb_poll_idx; i++) { + if (kdb_poll_funcs[i] == kdb_get_kbd_char) { + kdb_poll_idx--; + kdb_poll_funcs[i] = kdb_poll_funcs[kdb_poll_idx]; + kdb_poll_funcs[kdb_poll_idx] = NULL; + i--; + } + } + flush_work_sync(&kgdboc_restore_input_work); +} +#else /* ! CONFIG_KDB_KEYBOARD */ +#define kgdboc_register_kbd(x) 0 +#define kgdboc_unregister_kbd() +#define kgdboc_restore_input() +#endif /* ! CONFIG_KDB_KEYBOARD */ + static int kgdboc_option_setup(char *opt) { if (strlen(opt) > MAX_CONFIG_LEN) { @@ -45,25 +142,57 @@ static int kgdboc_option_setup(char *opt) __setup("kgdboc=", kgdboc_option_setup); +static void cleanup_kgdboc(void) +{ + kgdboc_unregister_kbd(); + if (configured == 1) + kgdb_unregister_io_module(&kgdboc_io_ops); +} + static int configure_kgdboc(void) { struct tty_driver *p; int tty_line = 0; int err; + char *cptr = config; + struct console *cons; err = kgdboc_option_setup(config); if (err || !strlen(config) || isspace(config[0])) goto noconfig; err = -ENODEV; + kgdboc_io_ops.is_console = 0; + kgdb_tty_driver = NULL; + + kgdboc_use_kms = 0; + if (strncmp(cptr, "kms,", 4) == 0) { + cptr += 4; + kgdboc_use_kms = 1; + } + + if (kgdboc_register_kbd(&cptr)) + goto do_register; - p = tty_find_polling_driver(config, &tty_line); + p = tty_find_polling_driver(cptr, &tty_line); if (!p) goto noconfig; + cons = console_drivers; + while (cons) { + int idx; + if (cons->device && cons->device(cons, &idx) == p && + idx == tty_line) { + kgdboc_io_ops.is_console = 1; + break; + } + cons = cons->next; + } + kgdb_tty_driver = p; kgdb_tty_line = tty_line; +do_register: err = kgdb_register_io_module(&kgdboc_io_ops); if (err) goto noconfig; @@ -75,6 +204,7 @@ static int configure_kgdboc(void) noconfig: config[0] = 0; configured = 0; + cleanup_kgdboc(); return err; } @@ -88,20 +218,18 @@ static int __init init_kgdboc(void) return configure_kgdboc(); } -static void cleanup_kgdboc(void) -{ - if (configured == 1) - kgdb_unregister_io_module(&kgdboc_io_ops); -} - static int kgdboc_get_char(void) { + if (!kgdb_tty_driver) + return -1; return kgdb_tty_driver->ops->poll_get_char(kgdb_tty_driver, kgdb_tty_line); } static void kgdboc_put_char(u8 chr) { + if (!kgdb_tty_driver) + return; kgdb_tty_driver->ops->poll_put_char(kgdb_tty_driver, kgdb_tty_line, chr); } @@ -140,8 +268,14 @@ static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp) return configure_kgdboc(); } +static int dbg_restore_graphics; + static void kgdboc_pre_exp_handler(void) { + if (!dbg_restore_graphics && kgdboc_use_kms) { + dbg_restore_graphics = 1; + con_debug_enter(vc_cons[fg_console].d); + } /* Increment the module count when the debugger is active */ if (!kgdb_connected) try_module_get(THIS_MODULE); @@ -152,6 +286,11 @@ static void kgdboc_post_exp_handler(void) /* decrement the module count when the debugger detaches */ if (!kgdb_connected) module_put(THIS_MODULE); + if (kgdboc_use_kms && dbg_restore_graphics) { + dbg_restore_graphics = 0; + con_debug_leave(); + } + kgdboc_restore_input(); } static struct kgdb_io kgdboc_io_ops = { @@ -162,6 +301,25 @@ static struct kgdb_io kgdboc_io_ops = { .post_exception = kgdboc_post_exp_handler, }; +#ifdef CONFIG_KGDB_SERIAL_CONSOLE +/* This is only available if kgdboc is a built in for early debugging */ +static int __init kgdboc_early_init(char *opt) +{ + /* save the first character of the config string because the + * init routine can destroy it. + */ + char save_ch; + + kgdboc_option_setup(opt); + save_ch = config[0]; + init_kgdboc(); + config[0] = save_ch; + return 0; +} + +early_param("ekgdboc", kgdboc_early_init); +#endif /* CONFIG_KGDB_SERIAL_CONSOLE */ + module_init(init_kgdboc); module_exit(cleanup_kgdboc); module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644); diff --git a/drivers/serial/m32r_sio.c b/drivers/serial/m32r_sio.c index 611c97a..bea5c21 100644 --- a/drivers/serial/m32r_sio.c +++ b/drivers/serial/m32r_sio.c @@ -286,7 +286,7 @@ static void m32r_sio_start_tx(struct uart_port *port) { #ifdef CONFIG_SERIAL_M32R_PLDSIO struct uart_sio_port *up = (struct uart_sio_port *)port; - struct circ_buf *xmit = &up->port.info->xmit; + struct circ_buf *xmit = &up->port.state->xmit; if (!(up->ier & UART_IER_THRI)) { up->ier |= UART_IER_THRI; @@ -325,7 +325,7 @@ static void m32r_sio_enable_ms(struct uart_port *port) static void receive_chars(struct uart_sio_port *up, int *status) { - struct tty_struct *tty = up->port.info->port.tty; + struct tty_struct *tty = up->port.state->port.tty; unsigned char ch; unsigned char flag; int max_count = 256; @@ -398,7 +398,7 @@ static void receive_chars(struct uart_sio_port *up, int *status) static void transmit_chars(struct uart_sio_port *up) { - struct circ_buf *xmit = &up->port.info->xmit; + struct circ_buf *xmit = &up->port.state->xmit; int count; if (up->port.x_char) { diff --git a/drivers/serial/max3100.c b/drivers/serial/max3100.c index 9fd33e5..beb1afa2 100644 --- a/drivers/serial/max3100.c +++ b/drivers/serial/max3100.c @@ -41,6 +41,7 @@ #define MAX_MAX3100 4 #include <linux/delay.h> +#include <linux/slab.h> #include <linux/device.h> #include <linux/serial_core.h> #include <linux/serial.h> @@ -184,7 +185,7 @@ static void max3100_timeout(unsigned long data) { struct max3100_port *s = (struct max3100_port *)data; - if (s->port.info) { + if (s->port.state) { max3100_dowork(s); mod_timer(&s->timer, jiffies + s->poll_time); } @@ -261,7 +262,7 @@ static void max3100_work(struct work_struct *w) int rxchars; u16 tx, rx; int conf, cconf, rts, crts; - struct circ_buf *xmit = &s->port.info->xmit; + struct circ_buf *xmit = &s->port.state->xmit; dev_dbg(&s->spi->dev, "%s\n", __func__); @@ -307,8 +308,8 @@ static void max3100_work(struct work_struct *w) } } - if (rxchars > 16 && s->port.info->port.tty != NULL) { - tty_flip_buffer_push(s->port.info->port.tty); + if (rxchars > 16 && s->port.state->port.tty != NULL) { + tty_flip_buffer_push(s->port.state->port.tty); rxchars = 0; } if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) @@ -320,8 +321,8 @@ static void max3100_work(struct work_struct *w) (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)))); - if (rxchars > 0 && s->port.info->port.tty != NULL) - tty_flip_buffer_push(s->port.info->port.tty); + if (rxchars > 0 && s->port.state->port.tty != NULL) + tty_flip_buffer_push(s->port.state->port.tty); } static irqreturn_t max3100_irq(int irqno, void *dev_id) @@ -429,17 +430,14 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios, int baud = 0; unsigned cflag; u32 param_new, param_mask, parity = 0; - struct tty_struct *tty = s->port.info->port.tty; dev_dbg(&s->spi->dev, "%s\n", __func__); - if (!tty) - return; cflag = termios->c_cflag; param_new = 0; param_mask = 0; - baud = tty_get_baud_rate(tty); + baud = tty_termios_baud_rate(termios); param_new = s->conf & MAX3100_BAUD; switch (baud) { case 300: @@ -484,7 +482,7 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios, default: baud = s->baud; } - tty_encode_baud_rate(tty, baud, baud); + tty_termios_encode_baud_rate(termios, baud, baud); s->baud = baud; param_mask |= MAX3100_BAUD; @@ -529,7 +527,7 @@ max3100_set_termios(struct uart_port *port, struct ktermios *termios, MAX3100_STATUS_OE; /* we are sending char from a workqueue so enable */ - s->port.info->port.tty->low_latency = 1; + s->port.state->port.tty->low_latency = 1; if (s->poll_time > 0) del_timer_sync(&s->timer); @@ -925,3 +923,4 @@ module_exit(max3100_exit); MODULE_DESCRIPTION("MAX3100 driver"); MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:max3100"); diff --git a/drivers/serial/max3107-aava.c b/drivers/serial/max3107-aava.c new file mode 100644 index 0000000..a1fe304 --- /dev/null +++ b/drivers/serial/max3107-aava.c @@ -0,0 +1,344 @@ +/* + * max3107.c - spi uart protocol driver for Maxim 3107 + * Based on max3100.c + * by Christian Pellegrin <chripell@evolware.org> + * and max3110.c + * by Feng Tang <feng.tang@intel.com> + * + * Copyright (C) Aavamobile 2009 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/spi/spi.h> +#include <linux/freezer.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/sfi.h> +#include <asm/mrst.h> +#include "max3107.h" + +/* GPIO direction to input function */ +static int max3107_gpio_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[1]; /* Buffer for SPI transfer */ + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return -EINVAL; + } + + /* Read current GPIO configuration register */ + buf[0] = MAX3107_GPIOCFG_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, "SPI transfer GPIO read failed\n"); + return -EIO; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + + /* Set GPIO to input */ + buf[0] &= ~(0x0001 << offset); + + /* Write new GPIO configuration register value */ + buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG); + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 2)) { + dev_err(&s->spi->dev, "SPI transfer GPIO write failed\n"); + return -EIO; + } + return 0; +} + +/* GPIO direction to output function */ +static int max3107_gpio_direction_out(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[2]; /* Buffer for SPI transfers */ + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return -EINVAL; + } + + /* Read current GPIO configuration and data registers */ + buf[0] = MAX3107_GPIOCFG_REG; + buf[1] = MAX3107_GPIODATA_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) { + dev_err(&s->spi->dev, "SPI transfer gpio failed\n"); + return -EIO; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + buf[1] &= MAX3107_SPI_RX_DATA_MASK; + + /* Set GPIO to output */ + buf[0] |= (0x0001 << offset); + /* Set value */ + if (value) + buf[1] |= (0x0001 << offset); + else + buf[1] &= ~(0x0001 << offset); + + /* Write new GPIO configuration and data register values */ + buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG); + buf[1] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG); + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 4)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO conf data w failed\n"); + return -EIO; + } + return 0; +} + +/* GPIO value query function */ +static int max3107_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[1]; /* Buffer for SPI transfer */ + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return -EINVAL; + } + + /* Read current GPIO data register */ + buf[0] = MAX3107_GPIODATA_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, "SPI transfer GPIO data r failed\n"); + return -EIO; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + + /* Return value */ + return buf[0] & (0x0001 << offset); +} + +/* GPIO value set function */ +static void max3107_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct max3107_port *s = container_of(chip, struct max3107_port, chip); + u16 buf[2]; /* Buffer for SPI transfers */ + + if (offset >= MAX3107_GPIO_COUNT) { + dev_err(&s->spi->dev, "Invalid GPIO\n"); + return; + } + + /* Read current GPIO configuration registers*/ + buf[0] = MAX3107_GPIODATA_REG; + buf[1] = MAX3107_GPIOCFG_REG; + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) { + dev_err(&s->spi->dev, + "SPI transfer for GPIO data and config read failed\n"); + return; + } + buf[0] &= MAX3107_SPI_RX_DATA_MASK; + buf[1] &= MAX3107_SPI_RX_DATA_MASK; + + if (!(buf[1] & (0x0001 << offset))) { + /* Configured as input, can't set value */ + dev_warn(&s->spi->dev, + "Trying to set value for input GPIO\n"); + return; + } + + /* Set value */ + if (value) + buf[0] |= (0x0001 << offset); + else + buf[0] &= ~(0x0001 << offset); + + /* Write new GPIO data register value */ + buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG); + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 2)) + dev_err(&s->spi->dev, "SPI transfer GPIO data w failed\n"); +} + +/* GPIO chip data */ +static struct gpio_chip max3107_gpio_chip = { + .owner = THIS_MODULE, + .direction_input = max3107_gpio_direction_in, + .direction_output = max3107_gpio_direction_out, + .get = max3107_gpio_get, + .set = max3107_gpio_set, + .can_sleep = 1, + .base = MAX3107_GPIO_BASE, + .ngpio = MAX3107_GPIO_COUNT, +}; + +/** + * max3107_aava_reset - reset on AAVA systems + * @spi: The SPI device we are probing + * + * Reset the device ready for probing. + */ + +static int max3107_aava_reset(struct spi_device *spi) +{ + /* Reset the chip */ + if (gpio_request(MAX3107_RESET_GPIO, "max3107")) { + pr_err("Requesting RESET GPIO failed\n"); + return -EIO; + } + if (gpio_direction_output(MAX3107_RESET_GPIO, 0)) { + pr_err("Setting RESET GPIO to 0 failed\n"); + gpio_free(MAX3107_RESET_GPIO); + return -EIO; + } + msleep(MAX3107_RESET_DELAY); + if (gpio_direction_output(MAX3107_RESET_GPIO, 1)) { + pr_err("Setting RESET GPIO to 1 failed\n"); + gpio_free(MAX3107_RESET_GPIO); + return -EIO; + } + gpio_free(MAX3107_RESET_GPIO); + msleep(MAX3107_WAKEUP_DELAY); + return 0; +} + +static int max3107_aava_configure(struct max3107_port *s) +{ + int retval; + + /* Initialize GPIO chip data */ + s->chip = max3107_gpio_chip; + s->chip.label = s->spi->modalias; + s->chip.dev = &s->spi->dev; + + /* Add GPIO chip */ + retval = gpiochip_add(&s->chip); + if (retval) { + dev_err(&s->spi->dev, "Adding GPIO chip failed\n"); + return retval; + } + + /* Temporary fix for EV2 boot problems, set modem reset to 0 */ + max3107_gpio_direction_out(&s->chip, 3, 0); + return 0; +} + +#if 0 +/* This will get enabled once we have the board stuff merged for this + specific case */ + +static const struct baud_table brg13_ext[] = { + { 300, MAX3107_BRG13_B300 }, + { 600, MAX3107_BRG13_B600 }, + { 1200, MAX3107_BRG13_B1200 }, + { 2400, MAX3107_BRG13_B2400 }, + { 4800, MAX3107_BRG13_B4800 }, + { 9600, MAX3107_BRG13_B9600 }, + { 19200, MAX3107_BRG13_B19200 }, + { 57600, MAX3107_BRG13_B57600 }, + { 115200, MAX3107_BRG13_B115200 }, + { 230400, MAX3107_BRG13_B230400 }, + { 460800, MAX3107_BRG13_B460800 }, + { 921600, MAX3107_BRG13_B921600 }, + { 0, 0 } +}; + +static void max3107_aava_init(struct max3107_port *s) +{ + /*override for AAVA SC specific*/ + if (mrst_platform_id() == MRST_PLATFORM_AAVA_SC) { + if (get_koski_build_id() <= KOSKI_EV2) + if (s->ext_clk) { + s->brg_cfg = MAX3107_BRG13_B9600; + s->baud_tbl = (struct baud_table *)brg13_ext; + } + } +} +#endif + +static int __devexit max3107_aava_remove(struct spi_device *spi) +{ + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + /* Remove GPIO chip */ + if (gpiochip_remove(&s->chip)) + dev_warn(&spi->dev, "Removing GPIO chip failed\n"); + + /* Then do the default remove */ + return max3107_remove(spi); +} + +/* Platform data */ +static struct max3107_plat aava_plat_data = { + .loopback = 0, + .ext_clk = 1, +/* .init = max3107_aava_init, */ + .configure = max3107_aava_configure, + .hw_suspend = max3107_hw_susp, + .polled_mode = 0, + .poll_time = 0, +}; + + +static int __devinit max3107_probe_aava(struct spi_device *spi) +{ + int err = max3107_aava_reset(spi); + if (err < 0) + return err; + return max3107_probe(spi, &aava_plat_data); +} + +/* Spi driver data */ +static struct spi_driver max3107_driver = { + .driver = { + .name = "aava-max3107", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = max3107_probe_aava, + .remove = __devexit_p(max3107_aava_remove), + .suspend = max3107_suspend, + .resume = max3107_resume, +}; + +/* Driver init function */ +static int __init max3107_init(void) +{ + return spi_register_driver(&max3107_driver); +} + +/* Driver exit function */ +static void __exit max3107_exit(void) +{ + spi_unregister_driver(&max3107_driver); +} + +module_init(max3107_init); +module_exit(max3107_exit); + +MODULE_DESCRIPTION("MAX3107 driver"); +MODULE_AUTHOR("Aavamobile"); +MODULE_ALIAS("aava-max3107-spi"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/serial/max3107.c b/drivers/serial/max3107.c new file mode 100644 index 0000000..910870e --- /dev/null +++ b/drivers/serial/max3107.c @@ -0,0 +1,1213 @@ +/* + * max3107.c - spi uart protocol driver for Maxim 3107 + * Based on max3100.c + * by Christian Pellegrin <chripell@evolware.org> + * and max3110.c + * by Feng Tang <feng.tang@intel.com> + * + * Copyright (C) Aavamobile 2009 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/gpio.h> +#include <linux/spi/spi.h> +#include <linux/freezer.h> +#include "max3107.h" + +static const struct baud_table brg26_ext[] = { + { 300, MAX3107_BRG26_B300 }, + { 600, MAX3107_BRG26_B600 }, + { 1200, MAX3107_BRG26_B1200 }, + { 2400, MAX3107_BRG26_B2400 }, + { 4800, MAX3107_BRG26_B4800 }, + { 9600, MAX3107_BRG26_B9600 }, + { 19200, MAX3107_BRG26_B19200 }, + { 57600, MAX3107_BRG26_B57600 }, + { 115200, MAX3107_BRG26_B115200 }, + { 230400, MAX3107_BRG26_B230400 }, + { 460800, MAX3107_BRG26_B460800 }, + { 921600, MAX3107_BRG26_B921600 }, + { 0, 0 } +}; + +static const struct baud_table brg13_int[] = { + { 300, MAX3107_BRG13_IB300 }, + { 600, MAX3107_BRG13_IB600 }, + { 1200, MAX3107_BRG13_IB1200 }, + { 2400, MAX3107_BRG13_IB2400 }, + { 4800, MAX3107_BRG13_IB4800 }, + { 9600, MAX3107_BRG13_IB9600 }, + { 19200, MAX3107_BRG13_IB19200 }, + { 57600, MAX3107_BRG13_IB57600 }, + { 115200, MAX3107_BRG13_IB115200 }, + { 230400, MAX3107_BRG13_IB230400 }, + { 460800, MAX3107_BRG13_IB460800 }, + { 921600, MAX3107_BRG13_IB921600 }, + { 0, 0 } +}; + +static u32 get_new_brg(int baud, struct max3107_port *s) +{ + int i; + const struct baud_table *baud_tbl = s->baud_tbl; + + for (i = 0; i < 13; i++) { + if (baud == baud_tbl[i].baud) + return baud_tbl[i].new_brg; + } + + return 0; +} + +/* Perform SPI transfer for write/read of device register(s) */ +int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len) +{ + struct spi_message spi_msg; + struct spi_transfer spi_xfer; + + /* Initialize SPI ,message */ + spi_message_init(&spi_msg); + + /* Initialize SPI transfer */ + memset(&spi_xfer, 0, sizeof spi_xfer); + spi_xfer.len = len; + spi_xfer.tx_buf = tx; + spi_xfer.rx_buf = rx; + spi_xfer.speed_hz = MAX3107_SPI_SPEED; + + /* Add SPI transfer to SPI message */ + spi_message_add_tail(&spi_xfer, &spi_msg); + +#ifdef DBG_TRACE_SPI_DATA + { + int i; + pr_info("tx len %d:\n", spi_xfer.len); + for (i = 0 ; i < spi_xfer.len && i < 32 ; i++) + pr_info(" %x", ((u8 *)spi_xfer.tx_buf)[i]); + pr_info("\n"); + } +#endif + + /* Perform synchronous SPI transfer */ + if (spi_sync(s->spi, &spi_msg)) { + dev_err(&s->spi->dev, "spi_sync failure\n"); + return -EIO; + } + +#ifdef DBG_TRACE_SPI_DATA + if (spi_xfer.rx_buf) { + int i; + pr_info("rx len %d:\n", spi_xfer.len); + for (i = 0 ; i < spi_xfer.len && i < 32 ; i++) + pr_info(" %x", ((u8 *)spi_xfer.rx_buf)[i]); + pr_info("\n"); + } +#endif + return 0; +} +EXPORT_SYMBOL_GPL(max3107_rw); + +/* Puts received data to circular buffer */ +static void put_data_to_circ_buf(struct max3107_port *s, unsigned char *data, + int len) +{ + struct uart_port *port = &s->port; + struct tty_struct *tty; + + if (!port->state) + return; + + tty = port->state->port.tty; + if (!tty) + return; + + /* Insert received data */ + tty_insert_flip_string(tty, data, len); + /* Update RX counter */ + port->icount.rx += len; +} + +/* Handle data receiving */ +static void max3107_handlerx(struct max3107_port *s, u16 rxlvl) +{ + int i; + int j; + int len; /* SPI transfer buffer length */ + u16 *buf; + u8 *valid_str; + + if (!s->rx_enabled) + /* RX is disabled */ + return; + + if (rxlvl == 0) { + /* RX fifo is empty */ + return; + } else if (rxlvl >= MAX3107_RX_FIFO_SIZE) { + dev_warn(&s->spi->dev, "Possible RX FIFO overrun %d\n", rxlvl); + /* Ensure sanity of RX level */ + rxlvl = MAX3107_RX_FIFO_SIZE; + } + if ((s->rxbuf == 0) || (s->rxstr == 0)) { + dev_warn(&s->spi->dev, "Rx buffer/str isn't ready\n"); + return; + } + buf = s->rxbuf; + valid_str = s->rxstr; + while (rxlvl) { + pr_debug("rxlvl %d\n", rxlvl); + /* Clear buffer */ + memset(buf, 0, sizeof(u16) * (MAX3107_RX_FIFO_SIZE + 2)); + len = 0; + if (s->irqen_reg & MAX3107_IRQ_RXFIFO_BIT) { + /* First disable RX FIFO interrupt */ + pr_debug("Disabling RX INT\n"); + buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); + s->irqen_reg &= ~MAX3107_IRQ_RXFIFO_BIT; + buf[0] |= s->irqen_reg; + len++; + } + /* Just increase the length by amount of words in FIFO since + * buffer was zeroed and SPI transfer of 0x0000 means reading + * from RX FIFO + */ + len += rxlvl; + /* Append RX level query */ + buf[len] = MAX3107_RXFIFOLVL_REG; + len++; + + /* Perform the SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, len * 2)) { + dev_err(&s->spi->dev, "SPI transfer for RX h failed\n"); + return; + } + + /* Skip RX FIFO interrupt disabling word if it was added */ + j = ((len - 1) - rxlvl); + /* Read received words */ + for (i = 0; i < rxlvl; i++, j++) + valid_str[i] = (u8)buf[j]; + put_data_to_circ_buf(s, valid_str, rxlvl); + /* Get new RX level */ + rxlvl = (buf[len - 1] & MAX3107_SPI_RX_DATA_MASK); + } + + if (s->rx_enabled) { + /* RX still enabled, re-enable RX FIFO interrupt */ + pr_debug("Enabling RX INT\n"); + buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); + s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT; + buf[0] |= s->irqen_reg; + if (max3107_rw(s, (u8 *)buf, NULL, 2)) + dev_err(&s->spi->dev, "RX FIFO INT enabling failed\n"); + } + + /* Push the received data to receivers */ + if (s->port.state->port.tty) + tty_flip_buffer_push(s->port.state->port.tty); +} + + +/* Handle data sending */ +static void max3107_handletx(struct max3107_port *s) +{ + struct circ_buf *xmit = &s->port.state->xmit; + int i; + unsigned long flags; + int len; /* SPI transfer buffer length */ + u16 *buf; + + if (!s->tx_fifo_empty) + /* Don't send more data before previous data is sent */ + return; + + if (uart_circ_empty(xmit) || uart_tx_stopped(&s->port)) + /* No data to send or TX is stopped */ + return; + + if (!s->txbuf) { + dev_warn(&s->spi->dev, "Txbuf isn't ready\n"); + return; + } + buf = s->txbuf; + /* Get length of data pending in circular buffer */ + len = uart_circ_chars_pending(xmit); + if (len) { + /* Limit to size of TX FIFO */ + if (len > MAX3107_TX_FIFO_SIZE) + len = MAX3107_TX_FIFO_SIZE; + + pr_debug("txlen %d\n", len); + + /* Update TX counter */ + s->port.icount.tx += len; + + /* TX FIFO will no longer be empty */ + s->tx_fifo_empty = 0; + + i = 0; + if (s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT) { + /* First disable TX empty interrupt */ + pr_debug("Disabling TE INT\n"); + buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); + s->irqen_reg &= ~MAX3107_IRQ_TXEMPTY_BIT; + buf[i] |= s->irqen_reg; + i++; + len++; + } + /* Add data to send */ + spin_lock_irqsave(&s->port.lock, flags); + for ( ; i < len ; i++) { + buf[i] = (MAX3107_WRITE_BIT | MAX3107_THR_REG); + buf[i] |= ((u16)xmit->buf[xmit->tail] & + MAX3107_SPI_TX_DATA_MASK); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + } + spin_unlock_irqrestore(&s->port.lock, flags); + if (!(s->irqen_reg & MAX3107_IRQ_TXEMPTY_BIT)) { + /* Enable TX empty interrupt */ + pr_debug("Enabling TE INT\n"); + buf[i] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG); + s->irqen_reg |= MAX3107_IRQ_TXEMPTY_BIT; + buf[i] |= s->irqen_reg; + i++; + len++; + } + if (!s->tx_enabled) { + /* Enable TX */ + pr_debug("Enable TX\n"); + buf[i] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); + spin_lock_irqsave(&s->data_lock, flags); + s->mode1_reg &= ~MAX3107_MODE1_TXDIS_BIT; + buf[i] |= s->mode1_reg; + spin_unlock_irqrestore(&s->data_lock, flags); + s->tx_enabled = 1; + i++; + len++; + } + + /* Perform the SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, len*2)) { + dev_err(&s->spi->dev, + "SPI transfer TX handling failed\n"); + return; + } + } + + /* Indicate wake up if circular buffer is getting low on data */ + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&s->port); + +} + +/* Handle interrupts + * Also reads and returns current RX FIFO level + */ +static u16 handle_interrupt(struct max3107_port *s) +{ + u16 buf[4]; /* Buffer for SPI transfers */ + u8 irq_status; + u16 rx_level; + unsigned long flags; + + /* Read IRQ status register */ + buf[0] = MAX3107_IRQSTS_REG; + /* Read status IRQ status register */ + buf[1] = MAX3107_STS_IRQSTS_REG; + /* Read LSR IRQ status register */ + buf[2] = MAX3107_LSR_IRQSTS_REG; + /* Query RX level */ + buf[3] = MAX3107_RXFIFOLVL_REG; + + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 8)) { + dev_err(&s->spi->dev, + "SPI transfer for INTR handling failed\n"); + return 0; + } + + irq_status = (u8)buf[0]; + pr_debug("IRQSTS %x\n", irq_status); + rx_level = (buf[3] & MAX3107_SPI_RX_DATA_MASK); + + if (irq_status & MAX3107_IRQ_LSR_BIT) { + /* LSR interrupt */ + if (buf[2] & MAX3107_LSR_RXTO_BIT) + /* RX timeout interrupt, + * handled by normal RX handling + */ + pr_debug("RX TO INT\n"); + } + + if (irq_status & MAX3107_IRQ_TXEMPTY_BIT) { + /* Tx empty interrupt, + * disable TX and set tx_fifo_empty flag + */ + pr_debug("TE INT, disabling TX\n"); + buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); + spin_lock_irqsave(&s->data_lock, flags); + s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT; + buf[0] |= s->mode1_reg; + spin_unlock_irqrestore(&s->data_lock, flags); + if (max3107_rw(s, (u8 *)buf, NULL, 2)) + dev_err(&s->spi->dev, "SPI transfer TX dis failed\n"); + s->tx_enabled = 0; + s->tx_fifo_empty = 1; + } + + if (irq_status & MAX3107_IRQ_RXFIFO_BIT) + /* RX FIFO interrupt, + * handled by normal RX handling + */ + pr_debug("RFIFO INT\n"); + + /* Return RX level */ + return rx_level; +} + +/* Trigger work thread*/ +static void max3107_dowork(struct max3107_port *s) +{ + if (!work_pending(&s->work) && !freezing(current) && !s->suspended) + queue_work(s->workqueue, &s->work); + else + dev_warn(&s->spi->dev, "interrup isn't serviced normally!\n"); +} + +/* Work thread */ +static void max3107_work(struct work_struct *w) +{ + struct max3107_port *s = container_of(w, struct max3107_port, work); + u16 rxlvl = 0; + int len; /* SPI transfer buffer length */ + u16 buf[5]; /* Buffer for SPI transfers */ + unsigned long flags; + + /* Start by reading current RX FIFO level */ + buf[0] = MAX3107_RXFIFOLVL_REG; + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, "SPI transfer RX lev failed\n"); + rxlvl = 0; + } else { + rxlvl = (buf[0] & MAX3107_SPI_RX_DATA_MASK); + } + + do { + pr_debug("rxlvl %d\n", rxlvl); + + /* Handle RX */ + max3107_handlerx(s, rxlvl); + rxlvl = 0; + + if (s->handle_irq) { + /* Handle pending interrupts + * We also get new RX FIFO level since new data may + * have been received while pushing received data to + * receivers + */ + s->handle_irq = 0; + rxlvl = handle_interrupt(s); + } + + /* Handle TX */ + max3107_handletx(s); + + /* Handle configuration changes */ + len = 0; + spin_lock_irqsave(&s->data_lock, flags); + if (s->mode1_commit) { + pr_debug("mode1_commit\n"); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); + buf[len++] |= s->mode1_reg; + s->mode1_commit = 0; + } + if (s->lcr_commit) { + pr_debug("lcr_commit\n"); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG); + buf[len++] |= s->lcr_reg; + s->lcr_commit = 0; + } + if (s->brg_commit) { + pr_debug("brg_commit\n"); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG); + buf[len++] |= ((s->brg_cfg >> 16) & + MAX3107_SPI_TX_DATA_MASK); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG); + buf[len++] |= ((s->brg_cfg >> 8) & + MAX3107_SPI_TX_DATA_MASK); + buf[len] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG); + buf[len++] |= ((s->brg_cfg) & 0xff); + s->brg_commit = 0; + } + spin_unlock_irqrestore(&s->data_lock, flags); + + if (len > 0) { + if (max3107_rw(s, (u8 *)buf, NULL, len * 2)) + dev_err(&s->spi->dev, + "SPI transfer config failed\n"); + } + + /* Reloop if interrupt handling indicated data in RX FIFO */ + } while (rxlvl); + +} + +/* Set sleep mode */ +static void max3107_set_sleep(struct max3107_port *s, int mode) +{ + u16 buf[1]; /* Buffer for SPI transfer */ + unsigned long flags; + pr_debug("enter, mode %d\n", mode); + + buf[0] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG); + spin_lock_irqsave(&s->data_lock, flags); + switch (mode) { + case MAX3107_DISABLE_FORCED_SLEEP: + s->mode1_reg &= ~MAX3107_MODE1_FORCESLEEP_BIT; + break; + case MAX3107_ENABLE_FORCED_SLEEP: + s->mode1_reg |= MAX3107_MODE1_FORCESLEEP_BIT; + break; + case MAX3107_DISABLE_AUTOSLEEP: + s->mode1_reg &= ~MAX3107_MODE1_AUTOSLEEP_BIT; + break; + case MAX3107_ENABLE_AUTOSLEEP: + s->mode1_reg |= MAX3107_MODE1_AUTOSLEEP_BIT; + break; + default: + spin_unlock_irqrestore(&s->data_lock, flags); + dev_warn(&s->spi->dev, "invalid sleep mode\n"); + return; + } + buf[0] |= s->mode1_reg; + spin_unlock_irqrestore(&s->data_lock, flags); + + if (max3107_rw(s, (u8 *)buf, NULL, 2)) + dev_err(&s->spi->dev, "SPI transfer sleep mode failed\n"); + + if (mode == MAX3107_DISABLE_AUTOSLEEP || + mode == MAX3107_DISABLE_FORCED_SLEEP) + msleep(MAX3107_WAKEUP_DELAY); +} + +/* Perform full register initialization */ +static void max3107_register_init(struct max3107_port *s) +{ + u16 buf[11]; /* Buffer for SPI transfers */ + + /* 1. Configure baud rate, 9600 as default */ + s->baud = 9600; + /* the below is default*/ + if (s->ext_clk) { + s->brg_cfg = MAX3107_BRG26_B9600; + s->baud_tbl = (struct baud_table *)brg26_ext; + } else { + s->brg_cfg = MAX3107_BRG13_IB9600; + s->baud_tbl = (struct baud_table *)brg13_int; + } + + if (s->pdata->init) + s->pdata->init(s); + + buf[0] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVMSB_REG) + | ((s->brg_cfg >> 16) & MAX3107_SPI_TX_DATA_MASK); + buf[1] = (MAX3107_WRITE_BIT | MAX3107_BRGDIVLSB_REG) + | ((s->brg_cfg >> 8) & MAX3107_SPI_TX_DATA_MASK); + buf[2] = (MAX3107_WRITE_BIT | MAX3107_BRGCFG_REG) + | ((s->brg_cfg) & 0xff); + + /* 2. Configure LCR register, 8N1 mode by default */ + s->lcr_reg = MAX3107_LCR_WORD_LEN_8; + buf[3] = (MAX3107_WRITE_BIT | MAX3107_LCR_REG) + | s->lcr_reg; + + /* 3. Configure MODE 1 register */ + s->mode1_reg = 0; + /* Enable IRQ pin */ + s->mode1_reg |= MAX3107_MODE1_IRQSEL_BIT; + /* Disable TX */ + s->mode1_reg |= MAX3107_MODE1_TXDIS_BIT; + s->tx_enabled = 0; + /* RX is enabled */ + s->rx_enabled = 1; + buf[4] = (MAX3107_WRITE_BIT | MAX3107_MODE1_REG) + | s->mode1_reg; + + /* 4. Configure MODE 2 register */ + buf[5] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG); + if (s->loopback) { + /* Enable loopback */ + buf[5] |= MAX3107_MODE2_LOOPBACK_BIT; + } + /* Reset FIFOs */ + buf[5] |= MAX3107_MODE2_FIFORST_BIT; + s->tx_fifo_empty = 1; + + /* 5. Configure FIFO trigger level register */ + buf[6] = (MAX3107_WRITE_BIT | MAX3107_FIFOTRIGLVL_REG); + /* RX FIFO trigger for 16 words, TX FIFO trigger not used */ + buf[6] |= (MAX3107_FIFOTRIGLVL_RX(16) | MAX3107_FIFOTRIGLVL_TX(0)); + + /* 6. Configure flow control levels */ + buf[7] = (MAX3107_WRITE_BIT | MAX3107_FLOWLVL_REG); + /* Flow control halt level 96, resume level 48 */ + buf[7] |= (MAX3107_FLOWLVL_RES(48) | MAX3107_FLOWLVL_HALT(96)); + + /* 7. Configure flow control */ + buf[8] = (MAX3107_WRITE_BIT | MAX3107_FLOWCTRL_REG); + /* Enable auto CTS and auto RTS flow control */ + buf[8] |= (MAX3107_FLOWCTRL_AUTOCTS_BIT | MAX3107_FLOWCTRL_AUTORTS_BIT); + + /* 8. Configure RX timeout register */ + buf[9] = (MAX3107_WRITE_BIT | MAX3107_RXTO_REG); + /* Timeout after 48 character intervals */ + buf[9] |= 0x0030; + + /* 9. Configure LSR interrupt enable register */ + buf[10] = (MAX3107_WRITE_BIT | MAX3107_LSR_IRQEN_REG); + /* Enable RX timeout interrupt */ + buf[10] |= MAX3107_LSR_RXTO_BIT; + + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 22)) + dev_err(&s->spi->dev, "SPI transfer for init failed\n"); + + /* 10. Clear IRQ status register by reading it */ + buf[0] = MAX3107_IRQSTS_REG; + + /* 11. Configure interrupt enable register */ + /* Enable LSR interrupt */ + s->irqen_reg = MAX3107_IRQ_LSR_BIT; + /* Enable RX FIFO interrupt */ + s->irqen_reg |= MAX3107_IRQ_RXFIFO_BIT; + buf[1] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG) + | s->irqen_reg; + + /* 12. Clear FIFO reset that was set in step 6 */ + buf[2] = (MAX3107_WRITE_BIT | MAX3107_MODE2_REG); + if (s->loopback) { + /* Keep loopback enabled */ + buf[2] |= MAX3107_MODE2_LOOPBACK_BIT; + } + + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 6)) + dev_err(&s->spi->dev, "SPI transfer for init failed\n"); + +} + +/* IRQ handler */ +static irqreturn_t max3107_irq(int irqno, void *dev_id) +{ + struct max3107_port *s = dev_id; + + if (irqno != s->spi->irq) { + /* Unexpected IRQ */ + return IRQ_NONE; + } + + /* Indicate irq */ + s->handle_irq = 1; + + /* Trigger work thread */ + max3107_dowork(s); + + return IRQ_HANDLED; +} + +/* HW suspension function + * + * Currently autosleep is used to decrease current consumption, alternative + * approach would be to set the chip to reset mode if UART is not being + * used but that would mess the GPIOs + * + */ +void max3107_hw_susp(struct max3107_port *s, int suspend) +{ + pr_debug("enter, suspend %d\n", suspend); + + if (suspend) { + /* Suspend requested, + * enable autosleep to decrease current consumption + */ + s->suspended = 1; + max3107_set_sleep(s, MAX3107_ENABLE_AUTOSLEEP); + } else { + /* Resume requested, + * disable autosleep + */ + s->suspended = 0; + max3107_set_sleep(s, MAX3107_DISABLE_AUTOSLEEP); + } +} +EXPORT_SYMBOL_GPL(max3107_hw_susp); + +/* Modem status IRQ enabling */ +static void max3107_enable_ms(struct uart_port *port) +{ + /* Modem status not supported */ +} + +/* Data send function */ +static void max3107_start_tx(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + /* Trigger work thread for sending data */ + max3107_dowork(s); +} + +/* Function for checking that there is no pending transfers */ +static unsigned int max3107_tx_empty(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + pr_debug("returning %d\n", + (s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit))); + return s->tx_fifo_empty && uart_circ_empty(&s->port.state->xmit); +} + +/* Function for stopping RX */ +static void max3107_stop_rx(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + unsigned long flags; + + /* Set RX disabled in MODE 1 register */ + spin_lock_irqsave(&s->data_lock, flags); + s->mode1_reg |= MAX3107_MODE1_RXDIS_BIT; + s->mode1_commit = 1; + spin_unlock_irqrestore(&s->data_lock, flags); + /* Set RX disabled */ + s->rx_enabled = 0; + /* Trigger work thread for doing the actual configuration change */ + max3107_dowork(s); +} + +/* Function for returning control pin states */ +static unsigned int max3107_get_mctrl(struct uart_port *port) +{ + /* DCD and DSR are not wired and CTS/RTS is handled automatically + * so just indicate DSR and CAR asserted + */ + return TIOCM_DSR | TIOCM_CAR; +} + +/* Function for setting control pin states */ +static void max3107_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* DCD and DSR are not wired and CTS/RTS is hadnled automatically + * so do nothing + */ +} + +/* Function for configuring UART parameters */ +static void max3107_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + struct tty_struct *tty; + int baud; + u16 new_lcr = 0; + u32 new_brg = 0; + unsigned long flags; + + if (!port->state) + return; + + tty = port->state->port.tty; + if (!tty) + return; + + /* Get new LCR register values */ + /* Word size */ + if ((termios->c_cflag & CSIZE) == CS7) + new_lcr |= MAX3107_LCR_WORD_LEN_7; + else + new_lcr |= MAX3107_LCR_WORD_LEN_8; + + /* Parity */ + if (termios->c_cflag & PARENB) { + new_lcr |= MAX3107_LCR_PARITY_BIT; + if (!(termios->c_cflag & PARODD)) + new_lcr |= MAX3107_LCR_EVENPARITY_BIT; + } + + /* Stop bits */ + if (termios->c_cflag & CSTOPB) { + /* 2 stop bits */ + new_lcr |= MAX3107_LCR_STOPLEN_BIT; + } + + /* Mask termios capabilities we don't support */ + termios->c_cflag &= ~CMSPAR; + + /* Set status ignore mask */ + s->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + s->port.ignore_status_mask |= MAX3107_ALL_ERRORS; + + /* Set low latency to immediately handle pushed data */ + s->port.state->port.tty->low_latency = 1; + + /* Get new baud rate generator configuration */ + baud = tty_get_baud_rate(tty); + + spin_lock_irqsave(&s->data_lock, flags); + new_brg = get_new_brg(baud, s); + /* if can't find the corrent config, use previous */ + if (!new_brg) { + baud = s->baud; + new_brg = s->brg_cfg; + } + spin_unlock_irqrestore(&s->data_lock, flags); + tty_termios_encode_baud_rate(termios, baud, baud); + s->baud = baud; + + /* Update timeout according to new baud rate */ + uart_update_timeout(port, termios->c_cflag, baud); + + spin_lock_irqsave(&s->data_lock, flags); + if (s->lcr_reg != new_lcr) { + s->lcr_reg = new_lcr; + s->lcr_commit = 1; + } + if (s->brg_cfg != new_brg) { + s->brg_cfg = new_brg; + s->brg_commit = 1; + } + spin_unlock_irqrestore(&s->data_lock, flags); + + /* Trigger work thread for doing the actual configuration change */ + max3107_dowork(s); +} + +/* Port shutdown function */ +static void max3107_shutdown(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + if (s->suspended && s->pdata->hw_suspend) + s->pdata->hw_suspend(s, 0); + + /* Free the interrupt */ + free_irq(s->spi->irq, s); + + if (s->workqueue) { + /* Flush and destroy work queue */ + flush_workqueue(s->workqueue); + destroy_workqueue(s->workqueue); + s->workqueue = NULL; + } + + /* Suspend HW */ + if (s->pdata->hw_suspend) + s->pdata->hw_suspend(s, 1); +} + +/* Port startup function */ +static int max3107_startup(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + + /* Initialize work queue */ + s->workqueue = create_freezeable_workqueue("max3107"); + if (!s->workqueue) { + dev_err(&s->spi->dev, "Workqueue creation failed\n"); + return -EBUSY; + } + INIT_WORK(&s->work, max3107_work); + + /* Setup IRQ */ + if (request_irq(s->spi->irq, max3107_irq, IRQF_TRIGGER_FALLING, + "max3107", s)) { + dev_err(&s->spi->dev, "IRQ reguest failed\n"); + destroy_workqueue(s->workqueue); + s->workqueue = NULL; + return -EBUSY; + } + + /* Resume HW */ + if (s->pdata->hw_suspend) + s->pdata->hw_suspend(s, 0); + + /* Init registers */ + max3107_register_init(s); + + return 0; +} + +/* Port type function */ +static const char *max3107_type(struct uart_port *port) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + return s->spi->modalias; +} + +/* Port release function */ +static void max3107_release_port(struct uart_port *port) +{ + /* Do nothing */ +} + +/* Port request function */ +static int max3107_request_port(struct uart_port *port) +{ + /* Do nothing */ + return 0; +} + +/* Port config function */ +static void max3107_config_port(struct uart_port *port, int flags) +{ + struct max3107_port *s = container_of(port, struct max3107_port, port); + s->port.type = PORT_MAX3107; +} + +/* Port verify function */ +static int max3107_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (ser->type == PORT_UNKNOWN || ser->type == PORT_MAX3107) + return 0; + + return -EINVAL; +} + +/* Port stop TX function */ +static void max3107_stop_tx(struct uart_port *port) +{ + /* Do nothing */ +} + +/* Port break control function */ +static void max3107_break_ctl(struct uart_port *port, int break_state) +{ + /* We don't support break control, do nothing */ +} + + +/* Port functions */ +static struct uart_ops max3107_ops = { + .tx_empty = max3107_tx_empty, + .set_mctrl = max3107_set_mctrl, + .get_mctrl = max3107_get_mctrl, + .stop_tx = max3107_stop_tx, + .start_tx = max3107_start_tx, + .stop_rx = max3107_stop_rx, + .enable_ms = max3107_enable_ms, + .break_ctl = max3107_break_ctl, + .startup = max3107_startup, + .shutdown = max3107_shutdown, + .set_termios = max3107_set_termios, + .type = max3107_type, + .release_port = max3107_release_port, + .request_port = max3107_request_port, + .config_port = max3107_config_port, + .verify_port = max3107_verify_port, +}; + +/* UART driver data */ +static struct uart_driver max3107_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "ttyMAX", + .dev_name = "ttyMAX", + .nr = 1, +}; + +static int driver_registered = 0; + + + +/* 'Generic' platform data */ +static struct max3107_plat generic_plat_data = { + .loopback = 0, + .ext_clk = 1, + .hw_suspend = max3107_hw_susp, + .polled_mode = 0, + .poll_time = 0, +}; + + +/*******************************************************************/ + +/** + * max3107_probe - SPI bus probe entry point + * @spi: the spi device + * + * SPI wants us to probe this device and if appropriate claim it. + * Perform any platform specific requirements and then initialise + * the device. + */ + +int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata) +{ + struct max3107_port *s; + u16 buf[2]; /* Buffer for SPI transfers */ + int retval; + + pr_info("enter max3107 probe\n"); + + /* Allocate port structure */ + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + pr_err("Allocating port structure failed\n"); + return -ENOMEM; + } + + s->pdata = pdata; + + /* SPI Rx buffer + * +2 for RX FIFO interrupt + * disabling and RX level query + */ + s->rxbuf = kzalloc(sizeof(u16) * (MAX3107_RX_FIFO_SIZE+2), GFP_KERNEL); + if (!s->rxbuf) { + pr_err("Allocating RX buffer failed\n"); + retval = -ENOMEM; + goto err_free4; + } + s->rxstr = kzalloc(sizeof(u8) * MAX3107_RX_FIFO_SIZE, GFP_KERNEL); + if (!s->rxstr) { + pr_err("Allocating RX buffer failed\n"); + retval = -ENOMEM; + goto err_free3; + } + /* SPI Tx buffer + * SPI transfer buffer + * +3 for TX FIFO empty + * interrupt disabling and + * enabling and TX enabling + */ + s->txbuf = kzalloc(sizeof(u16) * MAX3107_TX_FIFO_SIZE + 3, GFP_KERNEL); + if (!s->txbuf) { + pr_err("Allocating TX buffer failed\n"); + retval = -ENOMEM; + goto err_free2; + } + /* Initialize shared data lock */ + spin_lock_init(&s->data_lock); + + /* SPI intializations */ + dev_set_drvdata(&spi->dev, s); + spi->mode = SPI_MODE_0; + spi->dev.platform_data = pdata; + spi->bits_per_word = 16; + s->ext_clk = pdata->ext_clk; + s->loopback = pdata->loopback; + spi_setup(spi); + s->spi = spi; + + /* Check REV ID to ensure we are talking to what we expect */ + buf[0] = MAX3107_REVID_REG; + if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) { + dev_err(&s->spi->dev, "SPI transfer for REVID read failed\n"); + retval = -EIO; + goto err_free1; + } + if ((buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID1 && + (buf[0] & MAX3107_SPI_RX_DATA_MASK) != MAX3107_REVID2) { + dev_err(&s->spi->dev, "REVID %x does not match\n", + (buf[0] & MAX3107_SPI_RX_DATA_MASK)); + retval = -ENODEV; + goto err_free1; + } + + /* Disable all interrupts */ + buf[0] = (MAX3107_WRITE_BIT | MAX3107_IRQEN_REG | 0x0000); + buf[0] |= 0x0000; + + /* Configure clock source */ + buf[1] = (MAX3107_WRITE_BIT | MAX3107_CLKSRC_REG); + if (s->ext_clk) { + /* External clock */ + buf[1] |= MAX3107_CLKSRC_EXTCLK_BIT; + } + + /* PLL bypass ON */ + buf[1] |= MAX3107_CLKSRC_PLLBYP_BIT; + + /* Perform SPI transfer */ + if (max3107_rw(s, (u8 *)buf, NULL, 4)) { + dev_err(&s->spi->dev, "SPI transfer for init failed\n"); + retval = -EIO; + goto err_free1; + } + + /* Register UART driver */ + if (!driver_registered) { + retval = uart_register_driver(&max3107_uart_driver); + if (retval) { + dev_err(&s->spi->dev, "Registering UART driver failed\n"); + goto err_free1; + } + driver_registered = 1; + } + + /* Initialize UART port data */ + s->port.fifosize = 128; + s->port.ops = &max3107_ops; + s->port.line = 0; + s->port.dev = &spi->dev; + s->port.uartclk = 9600; + s->port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF; + s->port.irq = s->spi->irq; + s->port.type = PORT_MAX3107; + + /* Add UART port */ + retval = uart_add_one_port(&max3107_uart_driver, &s->port); + if (retval < 0) { + dev_err(&s->spi->dev, "Adding UART port failed\n"); + goto err_free1; + } + + if (pdata->configure) { + retval = pdata->configure(s); + if (retval < 0) + goto err_free1; + } + + /* Go to suspend mode */ + if (pdata->hw_suspend) + pdata->hw_suspend(s, 1); + + return 0; + +err_free1: + kfree(s->txbuf); +err_free2: + kfree(s->rxstr); +err_free3: + kfree(s->rxbuf); +err_free4: + kfree(s); + return retval; +} +EXPORT_SYMBOL_GPL(max3107_probe); + +/* Driver remove function */ +int max3107_remove(struct spi_device *spi) +{ + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + pr_info("enter max3107 remove\n"); + + /* Remove port */ + if (uart_remove_one_port(&max3107_uart_driver, &s->port)) + dev_warn(&s->spi->dev, "Removing UART port failed\n"); + + + /* Free TxRx buffer */ + kfree(s->rxbuf); + kfree(s->rxstr); + kfree(s->txbuf); + + /* Free port structure */ + kfree(s); + + return 0; +} +EXPORT_SYMBOL_GPL(max3107_remove); + +/* Driver suspend function */ +int max3107_suspend(struct spi_device *spi, pm_message_t state) +{ +#ifdef CONFIG_PM + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + pr_debug("enter suspend\n"); + + /* Suspend UART port */ + uart_suspend_port(&max3107_uart_driver, &s->port); + + /* Go to suspend mode */ + if (s->pdata->hw_suspend) + s->pdata->hw_suspend(s, 1); +#endif /* CONFIG_PM */ + return 0; +} +EXPORT_SYMBOL_GPL(max3107_suspend); + +/* Driver resume function */ +int max3107_resume(struct spi_device *spi) +{ +#ifdef CONFIG_PM + struct max3107_port *s = dev_get_drvdata(&spi->dev); + + pr_debug("enter resume\n"); + + /* Resume from suspend */ + if (s->pdata->hw_suspend) + s->pdata->hw_suspend(s, 0); + + /* Resume UART port */ + uart_resume_port(&max3107_uart_driver, &s->port); +#endif /* CONFIG_PM */ + return 0; +} +EXPORT_SYMBOL_GPL(max3107_resume); + +static int max3107_probe_generic(struct spi_device *spi) +{ + return max3107_probe(spi, &generic_plat_data); +} + +/* Spi driver data */ +static struct spi_driver max3107_driver = { + .driver = { + .name = "max3107", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = max3107_probe_generic, + .remove = __devexit_p(max3107_remove), + .suspend = max3107_suspend, + .resume = max3107_resume, +}; + +/* Driver init function */ +static int __init max3107_init(void) +{ + pr_info("enter max3107 init\n"); + return spi_register_driver(&max3107_driver); +} + +/* Driver exit function */ +static void __exit max3107_exit(void) +{ + pr_info("enter max3107 exit\n"); + /* Unregister UART driver */ + if (driver_registered) + uart_unregister_driver(&max3107_uart_driver); + spi_unregister_driver(&max3107_driver); +} + +module_init(max3107_init); +module_exit(max3107_exit); + +MODULE_DESCRIPTION("MAX3107 driver"); +MODULE_AUTHOR("Aavamobile"); +MODULE_ALIAS("max3107-spi"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/serial/max3107.h b/drivers/serial/max3107.h new file mode 100644 index 0000000..7ab63239 --- /dev/null +++ b/drivers/serial/max3107.h @@ -0,0 +1,441 @@ +/* + * max3107.h - spi uart protocol driver header for Maxim 3107 + * + * Copyright (C) Aavamobile 2009 + * Based on serial_max3100.h by Christian Pellegrin + * + * 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. + */ + +#ifndef _MAX3107_H +#define _MAX3107_H + +/* Serial error status definitions */ +#define MAX3107_PARITY_ERROR 1 +#define MAX3107_FRAME_ERROR 2 +#define MAX3107_OVERRUN_ERROR 4 +#define MAX3107_ALL_ERRORS (MAX3107_PARITY_ERROR | \ + MAX3107_FRAME_ERROR | \ + MAX3107_OVERRUN_ERROR) + +/* GPIO definitions */ +#define MAX3107_GPIO_BASE 88 +#define MAX3107_GPIO_COUNT 4 + + +/* GPIO connected to chip's reset pin */ +#define MAX3107_RESET_GPIO 87 + + +/* Chip reset delay */ +#define MAX3107_RESET_DELAY 10 + +/* Chip wakeup delay */ +#define MAX3107_WAKEUP_DELAY 50 + + +/* Sleep mode definitions */ +#define MAX3107_DISABLE_FORCED_SLEEP 0 +#define MAX3107_ENABLE_FORCED_SLEEP 1 +#define MAX3107_DISABLE_AUTOSLEEP 2 +#define MAX3107_ENABLE_AUTOSLEEP 3 + + +/* Definitions for register access with SPI transfers + * + * SPI transfer format: + * + * Master to slave bits xzzzzzzzyyyyyyyy + * Slave to master bits aaaaaaaabbbbbbbb + * + * where: + * x = 0 for reads, 1 for writes + * z = register address + * y = new register value if write, 0 if read + * a = unspecified + * b = register value if read, unspecified if write + */ + +/* SPI speed */ +#define MAX3107_SPI_SPEED (3125000 * 2) + +/* Write bit */ +#define MAX3107_WRITE_BIT (1 << 15) + +/* SPI TX data mask */ +#define MAX3107_SPI_RX_DATA_MASK (0x00ff) + +/* SPI RX data mask */ +#define MAX3107_SPI_TX_DATA_MASK (0x00ff) + +/* Register access masks */ +#define MAX3107_RHR_REG (0x0000) /* RX FIFO */ +#define MAX3107_THR_REG (0x0000) /* TX FIFO */ +#define MAX3107_IRQEN_REG (0x0100) /* IRQ enable */ +#define MAX3107_IRQSTS_REG (0x0200) /* IRQ status */ +#define MAX3107_LSR_IRQEN_REG (0x0300) /* LSR IRQ enable */ +#define MAX3107_LSR_IRQSTS_REG (0x0400) /* LSR IRQ status */ +#define MAX3107_SPCHR_IRQEN_REG (0x0500) /* Special char IRQ enable */ +#define MAX3107_SPCHR_IRQSTS_REG (0x0600) /* Special char IRQ status */ +#define MAX3107_STS_IRQEN_REG (0x0700) /* Status IRQ enable */ +#define MAX3107_STS_IRQSTS_REG (0x0800) /* Status IRQ status */ +#define MAX3107_MODE1_REG (0x0900) /* MODE1 */ +#define MAX3107_MODE2_REG (0x0a00) /* MODE2 */ +#define MAX3107_LCR_REG (0x0b00) /* LCR */ +#define MAX3107_RXTO_REG (0x0c00) /* RX timeout */ +#define MAX3107_HDPIXDELAY_REG (0x0d00) /* Auto transceiver delays */ +#define MAX3107_IRDA_REG (0x0e00) /* IRDA settings */ +#define MAX3107_FLOWLVL_REG (0x0f00) /* Flow control levels */ +#define MAX3107_FIFOTRIGLVL_REG (0x1000) /* FIFO IRQ trigger levels */ +#define MAX3107_TXFIFOLVL_REG (0x1100) /* TX FIFO level */ +#define MAX3107_RXFIFOLVL_REG (0x1200) /* RX FIFO level */ +#define MAX3107_FLOWCTRL_REG (0x1300) /* Flow control */ +#define MAX3107_XON1_REG (0x1400) /* XON1 character */ +#define MAX3107_XON2_REG (0x1500) /* XON2 character */ +#define MAX3107_XOFF1_REG (0x1600) /* XOFF1 character */ +#define MAX3107_XOFF2_REG (0x1700) /* XOFF2 character */ +#define MAX3107_GPIOCFG_REG (0x1800) /* GPIO config */ +#define MAX3107_GPIODATA_REG (0x1900) /* GPIO data */ +#define MAX3107_PLLCFG_REG (0x1a00) /* PLL config */ +#define MAX3107_BRGCFG_REG (0x1b00) /* Baud rate generator conf */ +#define MAX3107_BRGDIVLSB_REG (0x1c00) /* Baud rate divisor LSB */ +#define MAX3107_BRGDIVMSB_REG (0x1d00) /* Baud rate divisor MSB */ +#define MAX3107_CLKSRC_REG (0x1e00) /* Clock source */ +#define MAX3107_REVID_REG (0x1f00) /* Revision identification */ + +/* IRQ register bits */ +#define MAX3107_IRQ_LSR_BIT (1 << 0) /* LSR interrupt */ +#define MAX3107_IRQ_SPCHR_BIT (1 << 1) /* Special char interrupt */ +#define MAX3107_IRQ_STS_BIT (1 << 2) /* Status interrupt */ +#define MAX3107_IRQ_RXFIFO_BIT (1 << 3) /* RX FIFO interrupt */ +#define MAX3107_IRQ_TXFIFO_BIT (1 << 4) /* TX FIFO interrupt */ +#define MAX3107_IRQ_TXEMPTY_BIT (1 << 5) /* TX FIFO empty interrupt */ +#define MAX3107_IRQ_RXEMPTY_BIT (1 << 6) /* RX FIFO empty interrupt */ +#define MAX3107_IRQ_CTS_BIT (1 << 7) /* CTS interrupt */ + +/* LSR register bits */ +#define MAX3107_LSR_RXTO_BIT (1 << 0) /* RX timeout */ +#define MAX3107_LSR_RXOVR_BIT (1 << 1) /* RX overrun */ +#define MAX3107_LSR_RXPAR_BIT (1 << 2) /* RX parity error */ +#define MAX3107_LSR_FRERR_BIT (1 << 3) /* Frame error */ +#define MAX3107_LSR_RXBRK_BIT (1 << 4) /* RX break */ +#define MAX3107_LSR_RXNOISE_BIT (1 << 5) /* RX noise */ +#define MAX3107_LSR_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_LSR_CTS_BIT (1 << 7) /* CTS pin state */ + +/* Special character register bits */ +#define MAX3107_SPCHR_XON1_BIT (1 << 0) /* XON1 character */ +#define MAX3107_SPCHR_XON2_BIT (1 << 1) /* XON2 character */ +#define MAX3107_SPCHR_XOFF1_BIT (1 << 2) /* XOFF1 character */ +#define MAX3107_SPCHR_XOFF2_BIT (1 << 3) /* XOFF2 character */ +#define MAX3107_SPCHR_BREAK_BIT (1 << 4) /* RX break */ +#define MAX3107_SPCHR_MULTIDROP_BIT (1 << 5) /* 9-bit multidrop addr char */ +#define MAX3107_SPCHR_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_SPCHR_UNDEF7_BIT (1 << 7) /* Undefined/not used */ + +/* Status register bits */ +#define MAX3107_STS_GPIO0_BIT (1 << 0) /* GPIO 0 interrupt */ +#define MAX3107_STS_GPIO1_BIT (1 << 1) /* GPIO 1 interrupt */ +#define MAX3107_STS_GPIO2_BIT (1 << 2) /* GPIO 2 interrupt */ +#define MAX3107_STS_GPIO3_BIT (1 << 3) /* GPIO 3 interrupt */ +#define MAX3107_STS_UNDEF4_BIT (1 << 4) /* Undefined/not used */ +#define MAX3107_STS_CLKREADY_BIT (1 << 5) /* Clock ready */ +#define MAX3107_STS_SLEEP_BIT (1 << 6) /* Sleep interrupt */ +#define MAX3107_STS_UNDEF7_BIT (1 << 7) /* Undefined/not used */ + +/* MODE1 register bits */ +#define MAX3107_MODE1_RXDIS_BIT (1 << 0) /* RX disable */ +#define MAX3107_MODE1_TXDIS_BIT (1 << 1) /* TX disable */ +#define MAX3107_MODE1_TXHIZ_BIT (1 << 2) /* TX pin three-state */ +#define MAX3107_MODE1_RTSHIZ_BIT (1 << 3) /* RTS pin three-state */ +#define MAX3107_MODE1_TRNSCVCTRL_BIT (1 << 4) /* Transceiver ctrl enable */ +#define MAX3107_MODE1_FORCESLEEP_BIT (1 << 5) /* Force sleep mode */ +#define MAX3107_MODE1_AUTOSLEEP_BIT (1 << 6) /* Auto sleep enable */ +#define MAX3107_MODE1_IRQSEL_BIT (1 << 7) /* IRQ pin enable */ + +/* MODE2 register bits */ +#define MAX3107_MODE2_RST_BIT (1 << 0) /* Chip reset */ +#define MAX3107_MODE2_FIFORST_BIT (1 << 1) /* FIFO reset */ +#define MAX3107_MODE2_RXTRIGINV_BIT (1 << 2) /* RX FIFO INT invert */ +#define MAX3107_MODE2_RXEMPTINV_BIT (1 << 3) /* RX FIFO empty INT invert */ +#define MAX3107_MODE2_SPCHR_BIT (1 << 4) /* Special chr detect enable */ +#define MAX3107_MODE2_LOOPBACK_BIT (1 << 5) /* Internal loopback enable */ +#define MAX3107_MODE2_MULTIDROP_BIT (1 << 6) /* 9-bit multidrop enable */ +#define MAX3107_MODE2_ECHOSUPR_BIT (1 << 7) /* ECHO suppression enable */ + +/* LCR register bits */ +#define MAX3107_LCR_LENGTH0_BIT (1 << 0) /* Word length bit 0 */ +#define MAX3107_LCR_LENGTH1_BIT (1 << 1) /* Word length bit 1 + * + * Word length bits table: + * 00 -> 5 bit words + * 01 -> 6 bit words + * 10 -> 7 bit words + * 11 -> 8 bit words + */ +#define MAX3107_LCR_STOPLEN_BIT (1 << 2) /* STOP length bit + * + * STOP length bit table: + * 0 -> 1 stop bit + * 1 -> 1-1.5 stop bits if + * word length is 5, + * 2 stop bits otherwise + */ +#define MAX3107_LCR_PARITY_BIT (1 << 3) /* Parity bit enable */ +#define MAX3107_LCR_EVENPARITY_BIT (1 << 4) /* Even parity bit enable */ +#define MAX3107_LCR_FORCEPARITY_BIT (1 << 5) /* 9-bit multidrop parity */ +#define MAX3107_LCR_TXBREAK_BIT (1 << 6) /* TX break enable */ +#define MAX3107_LCR_RTS_BIT (1 << 7) /* RTS pin control */ +#define MAX3107_LCR_WORD_LEN_5 (0x0000) +#define MAX3107_LCR_WORD_LEN_6 (0x0001) +#define MAX3107_LCR_WORD_LEN_7 (0x0002) +#define MAX3107_LCR_WORD_LEN_8 (0x0003) + + +/* IRDA register bits */ +#define MAX3107_IRDA_IRDAEN_BIT (1 << 0) /* IRDA mode enable */ +#define MAX3107_IRDA_SIR_BIT (1 << 1) /* SIR mode enable */ +#define MAX3107_IRDA_SHORTIR_BIT (1 << 2) /* Short SIR mode enable */ +#define MAX3107_IRDA_MIR_BIT (1 << 3) /* MIR mode enable */ +#define MAX3107_IRDA_RXINV_BIT (1 << 4) /* RX logic inversion enable */ +#define MAX3107_IRDA_TXINV_BIT (1 << 5) /* TX logic inversion enable */ +#define MAX3107_IRDA_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_IRDA_UNDEF7_BIT (1 << 7) /* Undefined/not used */ + +/* Flow control trigger level register masks */ +#define MAX3107_FLOWLVL_HALT_MASK (0x000f) /* Flow control halt level */ +#define MAX3107_FLOWLVL_RES_MASK (0x00f0) /* Flow control resume level */ +#define MAX3107_FLOWLVL_HALT(words) ((words/8) & 0x000f) +#define MAX3107_FLOWLVL_RES(words) (((words/8) & 0x000f) << 4) + +/* FIFO interrupt trigger level register masks */ +#define MAX3107_FIFOTRIGLVL_TX_MASK (0x000f) /* TX FIFO trigger level */ +#define MAX3107_FIFOTRIGLVL_RX_MASK (0x00f0) /* RX FIFO trigger level */ +#define MAX3107_FIFOTRIGLVL_TX(words) ((words/8) & 0x000f) +#define MAX3107_FIFOTRIGLVL_RX(words) (((words/8) & 0x000f) << 4) + +/* Flow control register bits */ +#define MAX3107_FLOWCTRL_AUTORTS_BIT (1 << 0) /* Auto RTS flow ctrl enable */ +#define MAX3107_FLOWCTRL_AUTOCTS_BIT (1 << 1) /* Auto CTS flow ctrl enable */ +#define MAX3107_FLOWCTRL_GPIADDR_BIT (1 << 2) /* Enables that GPIO inputs + * are used in conjunction with + * XOFF2 for definition of + * special character */ +#define MAX3107_FLOWCTRL_SWFLOWEN_BIT (1 << 3) /* Auto SW flow ctrl enable */ +#define MAX3107_FLOWCTRL_SWFLOW0_BIT (1 << 4) /* SWFLOW bit 0 */ +#define MAX3107_FLOWCTRL_SWFLOW1_BIT (1 << 5) /* SWFLOW bit 1 + * + * SWFLOW bits 1 & 0 table: + * 00 -> no transmitter flow + * control + * 01 -> receiver compares + * XON2 and XOFF2 + * and controls + * transmitter + * 10 -> receiver compares + * XON1 and XOFF1 + * and controls + * transmitter + * 11 -> receiver compares + * XON1, XON2, XOFF1 and + * XOFF2 and controls + * transmitter + */ +#define MAX3107_FLOWCTRL_SWFLOW2_BIT (1 << 6) /* SWFLOW bit 2 */ +#define MAX3107_FLOWCTRL_SWFLOW3_BIT (1 << 7) /* SWFLOW bit 3 + * + * SWFLOW bits 3 & 2 table: + * 00 -> no received flow + * control + * 01 -> transmitter generates + * XON2 and XOFF2 + * 10 -> transmitter generates + * XON1 and XOFF1 + * 11 -> transmitter generates + * XON1, XON2, XOFF1 and + * XOFF2 + */ + +/* GPIO configuration register bits */ +#define MAX3107_GPIOCFG_GP0OUT_BIT (1 << 0) /* GPIO 0 output enable */ +#define MAX3107_GPIOCFG_GP1OUT_BIT (1 << 1) /* GPIO 1 output enable */ +#define MAX3107_GPIOCFG_GP2OUT_BIT (1 << 2) /* GPIO 2 output enable */ +#define MAX3107_GPIOCFG_GP3OUT_BIT (1 << 3) /* GPIO 3 output enable */ +#define MAX3107_GPIOCFG_GP0OD_BIT (1 << 4) /* GPIO 0 open-drain enable */ +#define MAX3107_GPIOCFG_GP1OD_BIT (1 << 5) /* GPIO 1 open-drain enable */ +#define MAX3107_GPIOCFG_GP2OD_BIT (1 << 6) /* GPIO 2 open-drain enable */ +#define MAX3107_GPIOCFG_GP3OD_BIT (1 << 7) /* GPIO 3 open-drain enable */ + +/* GPIO DATA register bits */ +#define MAX3107_GPIODATA_GP0OUT_BIT (1 << 0) /* GPIO 0 output value */ +#define MAX3107_GPIODATA_GP1OUT_BIT (1 << 1) /* GPIO 1 output value */ +#define MAX3107_GPIODATA_GP2OUT_BIT (1 << 2) /* GPIO 2 output value */ +#define MAX3107_GPIODATA_GP3OUT_BIT (1 << 3) /* GPIO 3 output value */ +#define MAX3107_GPIODATA_GP0IN_BIT (1 << 4) /* GPIO 0 input value */ +#define MAX3107_GPIODATA_GP1IN_BIT (1 << 5) /* GPIO 1 input value */ +#define MAX3107_GPIODATA_GP2IN_BIT (1 << 6) /* GPIO 2 input value */ +#define MAX3107_GPIODATA_GP3IN_BIT (1 << 7) /* GPIO 3 input value */ + +/* PLL configuration register masks */ +#define MAX3107_PLLCFG_PREDIV_MASK (0x003f) /* PLL predivision value */ +#define MAX3107_PLLCFG_PLLFACTOR_MASK (0x00c0) /* PLL multiplication factor */ + +/* Baud rate generator configuration register masks and bits */ +#define MAX3107_BRGCFG_FRACT_MASK (0x000f) /* Fractional portion of + * Baud rate generator divisor + */ +#define MAX3107_BRGCFG_2XMODE_BIT (1 << 4) /* Double baud rate */ +#define MAX3107_BRGCFG_4XMODE_BIT (1 << 5) /* Quadruple baud rate */ +#define MAX3107_BRGCFG_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_BRGCFG_UNDEF7_BIT (1 << 7) /* Undefined/not used */ + +/* Clock source register bits */ +#define MAX3107_CLKSRC_INTOSC_BIT (1 << 0) /* Internal osc enable */ +#define MAX3107_CLKSRC_CRYST_BIT (1 << 1) /* Crystal osc enable */ +#define MAX3107_CLKSRC_PLL_BIT (1 << 2) /* PLL enable */ +#define MAX3107_CLKSRC_PLLBYP_BIT (1 << 3) /* PLL bypass */ +#define MAX3107_CLKSRC_EXTCLK_BIT (1 << 4) /* External clock enable */ +#define MAX3107_CLKSRC_UNDEF5_BIT (1 << 5) /* Undefined/not used */ +#define MAX3107_CLKSRC_UNDEF6_BIT (1 << 6) /* Undefined/not used */ +#define MAX3107_CLKSRC_CLK2RTS_BIT (1 << 7) /* Baud clk to RTS pin */ + + +/* HW definitions */ +#define MAX3107_RX_FIFO_SIZE 128 +#define MAX3107_TX_FIFO_SIZE 128 +#define MAX3107_REVID1 0x00a0 +#define MAX3107_REVID2 0x00a1 + + +/* Baud rate generator configuration values for external clock 13MHz */ +#define MAX3107_BRG13_B300 (0x0A9400 | 0x05) +#define MAX3107_BRG13_B600 (0x054A00 | 0x03) +#define MAX3107_BRG13_B1200 (0x02A500 | 0x01) +#define MAX3107_BRG13_B2400 (0x015200 | 0x09) +#define MAX3107_BRG13_B4800 (0x00A900 | 0x04) +#define MAX3107_BRG13_B9600 (0x005400 | 0x0A) +#define MAX3107_BRG13_B19200 (0x002A00 | 0x05) +#define MAX3107_BRG13_B38400 (0x001500 | 0x03) +#define MAX3107_BRG13_B57600 (0x000E00 | 0x02) +#define MAX3107_BRG13_B115200 (0x000700 | 0x01) +#define MAX3107_BRG13_B230400 (0x000300 | 0x08) +#define MAX3107_BRG13_B460800 (0x000100 | 0x0c) +#define MAX3107_BRG13_B921600 (0x000100 | 0x1c) + +/* Baud rate generator configuration values for external clock 26MHz */ +#define MAX3107_BRG26_B300 (0x152800 | 0x0A) +#define MAX3107_BRG26_B600 (0x0A9400 | 0x05) +#define MAX3107_BRG26_B1200 (0x054A00 | 0x03) +#define MAX3107_BRG26_B2400 (0x02A500 | 0x01) +#define MAX3107_BRG26_B4800 (0x015200 | 0x09) +#define MAX3107_BRG26_B9600 (0x00A900 | 0x04) +#define MAX3107_BRG26_B19200 (0x005400 | 0x0A) +#define MAX3107_BRG26_B38400 (0x002A00 | 0x05) +#define MAX3107_BRG26_B57600 (0x001C00 | 0x03) +#define MAX3107_BRG26_B115200 (0x000E00 | 0x02) +#define MAX3107_BRG26_B230400 (0x000700 | 0x01) +#define MAX3107_BRG26_B460800 (0x000300 | 0x08) +#define MAX3107_BRG26_B921600 (0x000100 | 0x0C) + +/* Baud rate generator configuration values for internal clock */ +#define MAX3107_BRG13_IB300 (0x008000 | 0x00) +#define MAX3107_BRG13_IB600 (0x004000 | 0x00) +#define MAX3107_BRG13_IB1200 (0x002000 | 0x00) +#define MAX3107_BRG13_IB2400 (0x001000 | 0x00) +#define MAX3107_BRG13_IB4800 (0x000800 | 0x00) +#define MAX3107_BRG13_IB9600 (0x000400 | 0x00) +#define MAX3107_BRG13_IB19200 (0x000200 | 0x00) +#define MAX3107_BRG13_IB38400 (0x000100 | 0x00) +#define MAX3107_BRG13_IB57600 (0x000000 | 0x0B) +#define MAX3107_BRG13_IB115200 (0x000000 | 0x05) +#define MAX3107_BRG13_IB230400 (0x000000 | 0x03) +#define MAX3107_BRG13_IB460800 (0x000000 | 0x00) +#define MAX3107_BRG13_IB921600 (0x000000 | 0x00) + + +struct baud_table { + int baud; + u32 new_brg; +}; + +struct max3107_port { + /* UART port structure */ + struct uart_port port; + + /* SPI device structure */ + struct spi_device *spi; + +#if defined(CONFIG_GPIOLIB) + /* GPIO chip stucture */ + struct gpio_chip chip; +#endif + + /* Workqueue that does all the magic */ + struct workqueue_struct *workqueue; + struct work_struct work; + + /* Lock for shared data */ + spinlock_t data_lock; + + /* Device configuration */ + int ext_clk; /* 1 if external clock used */ + int loopback; /* Current loopback mode state */ + int baud; /* Current baud rate */ + + /* State flags */ + int suspended; /* Indicates suspend mode */ + int tx_fifo_empty; /* Flag for TX FIFO state */ + int rx_enabled; /* Flag for receiver state */ + int tx_enabled; /* Flag for transmitter state */ + + u16 irqen_reg; /* Current IRQ enable register value */ + /* Shared data */ + u16 mode1_reg; /* Current mode1 register value*/ + int mode1_commit; /* Flag for setting new mode1 register value */ + u16 lcr_reg; /* Current LCR register value */ + int lcr_commit; /* Flag for setting new LCR register value */ + u32 brg_cfg; /* Current Baud rate generator config */ + int brg_commit; /* Flag for setting new baud rate generator + * config + */ + struct baud_table *baud_tbl; + int handle_irq; /* Indicates that IRQ should be handled */ + + /* Rx buffer and str*/ + u16 *rxbuf; + u8 *rxstr; + /* Tx buffer*/ + u16 *txbuf; + + struct max3107_plat *pdata; /* Platform data */ +}; + +/* Platform data structure */ +struct max3107_plat { + /* Loopback mode enable */ + int loopback; + /* External clock enable */ + int ext_clk; + /* Called during the register initialisation */ + void (*init)(struct max3107_port *s); + /* Called when the port is found and configured */ + int (*configure)(struct max3107_port *s); + /* HW suspend function */ + void (*hw_suspend) (struct max3107_port *s, int suspend); + /* Polling mode enable */ + int polled_mode; + /* Polling period if polling mode enabled */ + int poll_time; +}; + +extern int max3107_rw(struct max3107_port *s, u8 *tx, u8 *rx, int len); +extern void max3107_hw_susp(struct max3107_port *s, int suspend); +extern int max3107_probe(struct spi_device *spi, struct max3107_plat *pdata); +extern int max3107_remove(struct spi_device *spi); +extern int max3107_suspend(struct spi_device *spi, pm_message_t state); +extern int max3107_resume(struct spi_device *spi); + +#endif /* _LINUX_SERIAL_MAX3107_H */ diff --git a/drivers/serial/mcf.c b/drivers/serial/mcf.c index 0eefb07..3394b7c 100644 --- a/drivers/serial/mcf.c +++ b/drivers/serial/mcf.c @@ -70,16 +70,14 @@ static unsigned int mcf_tx_empty(struct uart_port *port) static unsigned int mcf_get_mctrl(struct uart_port *port) { struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - unsigned long flags; unsigned int sigs; - spin_lock_irqsave(&port->lock, flags); sigs = (readb(port->membase + MCFUART_UIPR) & MCFUART_UIPR_CTS) ? 0 : TIOCM_CTS; sigs |= (pp->sigs & TIOCM_RTS); sigs |= (mcf_getppdcd(port->line) ? TIOCM_CD : 0); sigs |= (mcf_getppdtr(port->line) ? TIOCM_DTR : 0); - spin_unlock_irqrestore(&port->lock, flags); + return sigs; } @@ -88,16 +86,13 @@ static unsigned int mcf_get_mctrl(struct uart_port *port) static void mcf_set_mctrl(struct uart_port *port, unsigned int sigs) { struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); pp->sigs = sigs; mcf_setppdtr(port->line, (sigs & TIOCM_DTR)); if (sigs & TIOCM_RTS) writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP1); else writeb(MCFUART_UOP_RTS, port->membase + MCFUART_UOP0); - spin_unlock_irqrestore(&port->lock, flags); } /****************************************************************************/ @@ -105,12 +100,9 @@ static void mcf_set_mctrl(struct uart_port *port, unsigned int sigs) static void mcf_start_tx(struct uart_port *port) { struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); pp->imr |= MCFUART_UIR_TXREADY; writeb(pp->imr, port->membase + MCFUART_UIMR); - spin_unlock_irqrestore(&port->lock, flags); } /****************************************************************************/ @@ -118,12 +110,9 @@ static void mcf_start_tx(struct uart_port *port) static void mcf_stop_tx(struct uart_port *port) { struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); pp->imr &= ~MCFUART_UIR_TXREADY; writeb(pp->imr, port->membase + MCFUART_UIMR); - spin_unlock_irqrestore(&port->lock, flags); } /****************************************************************************/ @@ -131,12 +120,9 @@ static void mcf_stop_tx(struct uart_port *port) static void mcf_stop_rx(struct uart_port *port) { struct mcf_uart *pp = container_of(port, struct mcf_uart, port); - unsigned long flags; - spin_lock_irqsave(&port->lock, flags); pp->imr &= ~MCFUART_UIR_RXREADY; writeb(pp->imr, port->membase + MCFUART_UIMR); - spin_unlock_irqrestore(&port->lock, flags); } /****************************************************************************/ @@ -263,6 +249,7 @@ static void mcf_set_termios(struct uart_port *port, struct ktermios *termios, } spin_lock_irqsave(&port->lock, flags); + uart_update_timeout(port, termios->c_cflag, baud); writeb(MCFUART_UCR_CMDRESETRX, port->membase + MCFUART_UCR); writeb(MCFUART_UCR_CMDRESETTX, port->membase + MCFUART_UCR); writeb(MCFUART_UCR_CMDRESETMRPTR, port->membase + MCFUART_UCR); @@ -323,7 +310,7 @@ static void mcf_rx_chars(struct mcf_uart *pp) uart_insert_char(port, status, MCFUART_USR_RXOVERRUN, ch, flag); } - tty_flip_buffer_push(port->info->port.tty); + tty_flip_buffer_push(port->state->port.tty); } /****************************************************************************/ @@ -331,7 +318,7 @@ static void mcf_rx_chars(struct mcf_uart *pp) static void mcf_tx_chars(struct mcf_uart *pp) { struct uart_port *port = &pp->port; - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; if (port->x_char) { /* Send special char - probably flow control */ @@ -365,13 +352,22 @@ static irqreturn_t mcf_interrupt(int irq, void *data) struct uart_port *port = data; struct mcf_uart *pp = container_of(port, struct mcf_uart, port); unsigned int isr; + irqreturn_t ret = IRQ_NONE; isr = readb(port->membase + MCFUART_UISR) & pp->imr; - if (isr & MCFUART_UIR_RXREADY) + + spin_lock(&port->lock); + if (isr & MCFUART_UIR_RXREADY) { mcf_rx_chars(pp); - if (isr & MCFUART_UIR_TXREADY) + ret = IRQ_HANDLED; + } + if (isr & MCFUART_UIR_TXREADY) { mcf_tx_chars(pp); - return IRQ_HANDLED; + ret = IRQ_HANDLED; + } + spin_unlock(&port->lock); + + return ret; } /****************************************************************************/ @@ -379,6 +375,7 @@ static irqreturn_t mcf_interrupt(int irq, void *data) static void mcf_config_port(struct uart_port *port, int flags) { port->type = PORT_MCF; + port->fifosize = MCFUART_TXFIFOSIZE; /* Clear mask, so no surprise interrupts. */ writeb(0, port->membase + MCFUART_UIMR); @@ -424,7 +421,7 @@ static int mcf_verify_port(struct uart_port *port, struct serial_struct *ser) /* * Define the basic serial functions we support. */ -static struct uart_ops mcf_uart_ops = { +static const struct uart_ops mcf_uart_ops = { .tx_empty = mcf_tx_empty, .get_mctrl = mcf_get_mctrl, .set_mctrl = mcf_set_mctrl, @@ -443,7 +440,7 @@ static struct uart_ops mcf_uart_ops = { .verify_port = mcf_verify_port, }; -static struct mcf_uart mcf_ports[3]; +static struct mcf_uart mcf_ports[4]; #define MCF_MAXPORTS ARRAY_SIZE(mcf_ports) @@ -602,7 +599,7 @@ static int __devinit mcf_probe(struct platform_device *pdev) /****************************************************************************/ -static int mcf_remove(struct platform_device *pdev) +static int __devexit mcf_remove(struct platform_device *pdev) { struct uart_port *port; int i; diff --git a/drivers/serial/mfd.c b/drivers/serial/mfd.c new file mode 100644 index 0000000..d40010a --- /dev/null +++ b/drivers/serial/mfd.c @@ -0,0 +1,1513 @@ +/* + * mfd.c: driver for High Speed UART device of Intel Medfield platform + * + * Refer pxa.c, 8250.c and some other drivers in drivers/serial/ + * + * (C) Copyright 2010 Intel Corporation + * + * 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; version 2 + * of the License. + */ + +/* Notes: + * 1. DMA channel allocation: 0/1 channel are assigned to port 0, + * 2/3 chan to port 1, 4/5 chan to port 3. Even number chans + * are used for RX, odd chans for TX + * + * 2. In A0 stepping, UART will not support TX half empty flag + * + * 3. The RI/DSR/DCD/DTR are not pinned out, DCD & DSR are always + * asserted, only when the HW is reset the DDCD and DDSR will + * be triggered + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/sysrq.h> +#include <linux/slab.h> +#include <linux/serial_reg.h> +#include <linux/circ_buf.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> +#include <linux/serial_mfd.h> +#include <linux/dma-mapping.h> +#include <linux/pci.h> +#include <linux/io.h> +#include <linux/debugfs.h> + +#define MFD_HSU_A0_STEPPING 1 + +#define HSU_DMA_BUF_SIZE 2048 + +#define chan_readl(chan, offset) readl(chan->reg + offset) +#define chan_writel(chan, offset, val) writel(val, chan->reg + offset) + +#define mfd_readl(obj, offset) readl(obj->reg + offset) +#define mfd_writel(obj, offset, val) writel(val, obj->reg + offset) + +#define HSU_DMA_TIMEOUT_CHECK_FREQ (HZ/10) + +struct hsu_dma_buffer { + u8 *buf; + dma_addr_t dma_addr; + u32 dma_size; + u32 ofs; +}; + +struct hsu_dma_chan { + u32 id; + enum dma_data_direction dirt; + struct uart_hsu_port *uport; + void __iomem *reg; + struct timer_list rx_timer; /* only needed by RX channel */ +}; + +struct uart_hsu_port { + struct uart_port port; + unsigned char ier; + unsigned char lcr; + unsigned char mcr; + unsigned int lsr_break_flag; + char name[12]; + int index; + struct device *dev; + + struct hsu_dma_chan *txc; + struct hsu_dma_chan *rxc; + struct hsu_dma_buffer txbuf; + struct hsu_dma_buffer rxbuf; + int use_dma; /* flag for DMA/PIO */ + int running; + int dma_tx_on; +}; + +/* Top level data structure of HSU */ +struct hsu_port { + void __iomem *reg; + unsigned long paddr; + unsigned long iolen; + u32 irq; + + struct uart_hsu_port port[3]; + struct hsu_dma_chan chans[10]; + + struct dentry *debugfs; +}; + +static inline unsigned int serial_in(struct uart_hsu_port *up, int offset) +{ + unsigned int val; + + if (offset > UART_MSR) { + offset <<= 2; + val = readl(up->port.membase + offset); + } else + val = (unsigned int)readb(up->port.membase + offset); + + return val; +} + +static inline void serial_out(struct uart_hsu_port *up, int offset, int value) +{ + if (offset > UART_MSR) { + offset <<= 2; + writel(value, up->port.membase + offset); + } else { + unsigned char val = value & 0xff; + writeb(val, up->port.membase + offset); + } +} + +#ifdef CONFIG_DEBUG_FS + +#define HSU_REGS_BUFSIZE 1024 + +static int hsu_show_regs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t port_show_regs(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct uart_hsu_port *up = file->private_data; + char *buf; + u32 len = 0; + ssize_t ret; + + buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL); + if (!buf) + return 0; + + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "MFD HSU port[%d] regs:\n", up->index); + + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "=================================\n"); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "IER: \t\t0x%08x\n", serial_in(up, UART_IER)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "IIR: \t\t0x%08x\n", serial_in(up, UART_IIR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "LCR: \t\t0x%08x\n", serial_in(up, UART_LCR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "MCR: \t\t0x%08x\n", serial_in(up, UART_MCR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "LSR: \t\t0x%08x\n", serial_in(up, UART_LSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "MSR: \t\t0x%08x\n", serial_in(up, UART_MSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "FOR: \t\t0x%08x\n", serial_in(up, UART_FOR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "PS: \t\t0x%08x\n", serial_in(up, UART_PS)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "MUL: \t\t0x%08x\n", serial_in(up, UART_MUL)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "DIV: \t\t0x%08x\n", serial_in(up, UART_DIV)); + + if (len > HSU_REGS_BUFSIZE) + len = HSU_REGS_BUFSIZE; + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret; +} + +static ssize_t dma_show_regs(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hsu_dma_chan *chan = file->private_data; + char *buf; + u32 len = 0; + ssize_t ret; + + buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL); + if (!buf) + return 0; + + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "MFD HSU DMA channel [%d] regs:\n", chan->id); + + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "=================================\n"); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "CR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_CR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "DCR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_DCR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "BSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_BSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "MOTSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_MOTSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0SAR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0TSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1SAR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1TSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2SAR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2TSR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3SAR)); + len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, + "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3TSR)); + + if (len > HSU_REGS_BUFSIZE) + len = HSU_REGS_BUFSIZE; + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return ret; +} + +static const struct file_operations port_regs_ops = { + .owner = THIS_MODULE, + .open = hsu_show_regs_open, + .read = port_show_regs, + .llseek = default_llseek, +}; + +static const struct file_operations dma_regs_ops = { + .owner = THIS_MODULE, + .open = hsu_show_regs_open, + .read = dma_show_regs, + .llseek = default_llseek, +}; + +static int hsu_debugfs_init(struct hsu_port *hsu) +{ + int i; + char name[32]; + + hsu->debugfs = debugfs_create_dir("hsu", NULL); + if (!hsu->debugfs) + return -ENOMEM; + + for (i = 0; i < 3; i++) { + snprintf(name, sizeof(name), "port_%d_regs", i); + debugfs_create_file(name, S_IFREG | S_IRUGO, + hsu->debugfs, (void *)(&hsu->port[i]), &port_regs_ops); + } + + for (i = 0; i < 6; i++) { + snprintf(name, sizeof(name), "dma_chan_%d_regs", i); + debugfs_create_file(name, S_IFREG | S_IRUGO, + hsu->debugfs, (void *)&hsu->chans[i], &dma_regs_ops); + } + + return 0; +} + +static void hsu_debugfs_remove(struct hsu_port *hsu) +{ + if (hsu->debugfs) + debugfs_remove_recursive(hsu->debugfs); +} + +#else +static inline int hsu_debugfs_init(struct hsu_port *hsu) +{ + return 0; +} + +static inline void hsu_debugfs_remove(struct hsu_port *hsu) +{ +} +#endif /* CONFIG_DEBUG_FS */ + +static void serial_hsu_enable_ms(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +void hsu_dma_tx(struct uart_hsu_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + struct hsu_dma_buffer *dbuf = &up->txbuf; + int count; + + /* test_and_set_bit may be better, but anyway it's in lock protected mode */ + if (up->dma_tx_on) + return; + + /* Update the circ buf info */ + xmit->tail += dbuf->ofs; + xmit->tail &= UART_XMIT_SIZE - 1; + + up->port.icount.tx += dbuf->ofs; + dbuf->ofs = 0; + + /* Disable the channel */ + chan_writel(up->txc, HSU_CH_CR, 0x0); + + if (!uart_circ_empty(xmit) && !uart_tx_stopped(&up->port)) { + dma_sync_single_for_device(up->port.dev, + dbuf->dma_addr, + dbuf->dma_size, + DMA_TO_DEVICE); + + count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + dbuf->ofs = count; + + /* Reprogram the channel */ + chan_writel(up->txc, HSU_CH_D0SAR, dbuf->dma_addr + xmit->tail); + chan_writel(up->txc, HSU_CH_D0TSR, count); + + /* Reenable the channel */ + chan_writel(up->txc, HSU_CH_DCR, 0x1 + | (0x1 << 8) + | (0x1 << 16) + | (0x1 << 24)); + up->dma_tx_on = 1; + chan_writel(up->txc, HSU_CH_CR, 0x1); + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); +} + +/* The buffer is already cache coherent */ +void hsu_dma_start_rx_chan(struct hsu_dma_chan *rxc, struct hsu_dma_buffer *dbuf) +{ + dbuf->ofs = 0; + + chan_writel(rxc, HSU_CH_BSR, 32); + chan_writel(rxc, HSU_CH_MOTSR, 4); + + chan_writel(rxc, HSU_CH_D0SAR, dbuf->dma_addr); + chan_writel(rxc, HSU_CH_D0TSR, dbuf->dma_size); + chan_writel(rxc, HSU_CH_DCR, 0x1 | (0x1 << 8) + | (0x1 << 16) + | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ + ); + chan_writel(rxc, HSU_CH_CR, 0x3); + + mod_timer(&rxc->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ); +} + +/* Protected by spin_lock_irqsave(port->lock) */ +static void serial_hsu_start_tx(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + + if (up->use_dma) { + hsu_dma_tx(up); + } else if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void serial_hsu_stop_tx(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + struct hsu_dma_chan *txc = up->txc; + + if (up->use_dma) + chan_writel(txc, HSU_CH_CR, 0x0); + else if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +/* This is always called in spinlock protected mode, so + * modify timeout timer is safe here */ +void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) +{ + struct hsu_dma_buffer *dbuf = &up->rxbuf; + struct hsu_dma_chan *chan = up->rxc; + struct uart_port *port = &up->port; + struct tty_struct *tty = port->state->port.tty; + int count; + + if (!tty) + return; + + /* + * First need to know how many is already transferred, + * then check if its a timeout DMA irq, and return + * the trail bytes out, push them up and reenable the + * channel + */ + + /* Timeout IRQ, need wait some time, see Errata 2 */ + if (int_sts & 0xf00) + udelay(2); + + /* Stop the channel */ + chan_writel(chan, HSU_CH_CR, 0x0); + + count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; + if (!count) { + /* Restart the channel before we leave */ + chan_writel(chan, HSU_CH_CR, 0x3); + return; + } + del_timer(&chan->rx_timer); + + dma_sync_single_for_cpu(port->dev, dbuf->dma_addr, + dbuf->dma_size, DMA_FROM_DEVICE); + + /* + * Head will only wrap around when we recycle + * the DMA buffer, and when that happens, we + * explicitly set tail to 0. So head will + * always be greater than tail. + */ + tty_insert_flip_string(tty, dbuf->buf, count); + port->icount.rx += count; + + dma_sync_single_for_device(up->port.dev, dbuf->dma_addr, + dbuf->dma_size, DMA_FROM_DEVICE); + + /* Reprogram the channel */ + chan_writel(chan, HSU_CH_D0SAR, dbuf->dma_addr); + chan_writel(chan, HSU_CH_D0TSR, dbuf->dma_size); + chan_writel(chan, HSU_CH_DCR, 0x1 + | (0x1 << 8) + | (0x1 << 16) + | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ + ); + tty_flip_buffer_push(tty); + + chan_writel(chan, HSU_CH_CR, 0x3); + chan->rx_timer.expires = jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ; + add_timer(&chan->rx_timer); + +} + +static void serial_hsu_stop_rx(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + struct hsu_dma_chan *chan = up->rxc; + + if (up->use_dma) + chan_writel(chan, HSU_CH_CR, 0x2); + else { + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); + } +} + +static inline void receive_chars(struct uart_hsu_port *up, int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned int ch, flag; + unsigned int max_count = 256; + + if (!tty) + return; + + do { + ch = serial_in(up, UART_RX); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE))) { + + dev_warn(up->dev, "We really rush into ERR/BI case" + "status = 0x%02x", *status); + /* For statistics only */ + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (*status & UART_LSR_PE) + up->port.icount.parity++; + else if (*status & UART_LSR_FE) + up->port.icount.frame++; + if (*status & UART_LSR_OE) + up->port.icount.overrun++; + + /* Mask off conditions which should be ignored. */ + *status &= up->port.read_status_mask; + +#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE + if (up->port.cons && + up->port.cons->index == up->port.line) { + /* Recover the break flag from console xmit */ + *status |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } +#endif + if (*status & UART_LSR_BI) { + flag = TTY_BREAK; + } else if (*status & UART_LSR_PE) + flag = TTY_PARITY; + else if (*status & UART_LSR_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + + uart_insert_char(&up->port, *status, UART_LSR_OE, ch, flag); + ignore_char: + *status = serial_in(up, UART_LSR); + } while ((*status & UART_LSR_DR) && max_count--); + tty_flip_buffer_push(tty); +} + +static void transmit_chars(struct uart_hsu_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { + serial_out(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + serial_hsu_stop_tx(&up->port); + return; + } + +#ifndef MFD_HSU_A0_STEPPING + count = up->port.fifosize / 2; +#else + /* + * A0 only supports fully empty IRQ, and the first char written + * into it won't clear the EMPT bit, so we may need be cautious + * by useing a shorter buffer + */ + count = up->port.fifosize - 4; +#endif + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) + serial_hsu_stop_tx(&up->port); +} + +static inline void check_modem_status(struct uart_hsu_port *up) +{ + int status; + + status = serial_in(up, UART_MSR); + + if ((status & UART_MSR_ANY_DELTA) == 0) + return; + + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + /* We may only get DDCD when HW init and reset */ + if (status & UART_MSR_DDCD) + uart_handle_dcd_change(&up->port, status & UART_MSR_DCD); + /* Will start/stop_tx accordingly */ + if (status & UART_MSR_DCTS) + uart_handle_cts_change(&up->port, status & UART_MSR_CTS); + + wake_up_interruptible(&up->port.state->port.delta_msr_wait); +} + +/* + * This handles the interrupt from one port. + */ +static irqreturn_t port_irq(int irq, void *dev_id) +{ + struct uart_hsu_port *up = dev_id; + unsigned int iir, lsr; + unsigned long flags; + + if (unlikely(!up->running)) + return IRQ_NONE; + + spin_lock_irqsave(&up->port.lock, flags); + if (up->use_dma) { + lsr = serial_in(up, UART_LSR); + if (unlikely(lsr & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE))) + dev_warn(up->dev, + "Got lsr irq while using DMA, lsr = 0x%2x\n", + lsr); + check_modem_status(up); + spin_unlock_irqrestore(&up->port.lock, flags); + return IRQ_HANDLED; + } + + iir = serial_in(up, UART_IIR); + if (iir & UART_IIR_NO_INT) { + spin_unlock_irqrestore(&up->port.lock, flags); + return IRQ_NONE; + } + + lsr = serial_in(up, UART_LSR); + if (lsr & UART_LSR_DR) + receive_chars(up, &lsr); + check_modem_status(up); + + /* lsr will be renewed during the receive_chars */ + if (lsr & UART_LSR_THRE) + transmit_chars(up); + + spin_unlock_irqrestore(&up->port.lock, flags); + return IRQ_HANDLED; +} + +static inline void dma_chan_irq(struct hsu_dma_chan *chan) +{ + struct uart_hsu_port *up = chan->uport; + unsigned long flags; + u32 int_sts; + + spin_lock_irqsave(&up->port.lock, flags); + + if (!up->use_dma || !up->running) + goto exit; + + /* + * No matter what situation, need read clear the IRQ status + * There is a bug, see Errata 5, HSD 2900918 + */ + int_sts = chan_readl(chan, HSU_CH_SR); + + /* Rx channel */ + if (chan->dirt == DMA_FROM_DEVICE) + hsu_dma_rx(up, int_sts); + + /* Tx channel */ + if (chan->dirt == DMA_TO_DEVICE) { + chan_writel(chan, HSU_CH_CR, 0x0); + up->dma_tx_on = 0; + hsu_dma_tx(up); + } + +exit: + spin_unlock_irqrestore(&up->port.lock, flags); + return; +} + +static irqreturn_t dma_irq(int irq, void *dev_id) +{ + struct hsu_port *hsu = dev_id; + u32 int_sts, i; + + int_sts = mfd_readl(hsu, HSU_GBL_DMAISR); + + /* Currently we only have 6 channels may be used */ + for (i = 0; i < 6; i++) { + if (int_sts & 0x1) + dma_chan_irq(&hsu->chans[i]); + int_sts >>= 1; + } + + return IRQ_HANDLED; +} + +static unsigned int serial_hsu_tx_empty(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int serial_hsu_get_mctrl(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + unsigned char status; + unsigned int ret; + + status = serial_in(up, UART_MSR); + + ret = 0; + if (status & UART_MSR_DCD) + ret |= TIOCM_CAR; + if (status & UART_MSR_RI) + ret |= TIOCM_RNG; + if (status & UART_MSR_DSR) + ret |= TIOCM_DSR; + if (status & UART_MSR_CTS) + ret |= TIOCM_CTS; + return ret; +} + +static void serial_hsu_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + unsigned char mcr = 0; + + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + mcr |= up->mcr; + + serial_out(up, UART_MCR, mcr); +} + +static void serial_hsu_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +/* + * What special to do: + * 1. chose the 64B fifo mode + * 2. make sure not to select half empty mode for A0 stepping + * 3. start dma or pio depends on configuration + * 4. we only allocate dma memory when needed + */ +static int serial_hsu_startup(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + unsigned long flags; + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); + + /* Clear the interrupt registers. */ + (void) serial_in(up, UART_LSR); + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + + /* Now, initialize the UART, default is 8n1 */ + serial_out(up, UART_LCR, UART_LCR_WLEN8); + + spin_lock_irqsave(&up->port.lock, flags); + + up->port.mctrl |= TIOCM_OUT2; + serial_hsu_set_mctrl(&up->port, up->port.mctrl); + + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + */ + if (!up->use_dma) + up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE; + else + up->ier = 0; + serial_out(up, UART_IER, up->ier); + + spin_unlock_irqrestore(&up->port.lock, flags); + + /* DMA init */ + if (up->use_dma) { + struct hsu_dma_buffer *dbuf; + struct circ_buf *xmit = &port->state->xmit; + + up->dma_tx_on = 0; + + /* First allocate the RX buffer */ + dbuf = &up->rxbuf; + dbuf->buf = kzalloc(HSU_DMA_BUF_SIZE, GFP_KERNEL); + if (!dbuf->buf) { + up->use_dma = 0; + goto exit; + } + dbuf->dma_addr = dma_map_single(port->dev, + dbuf->buf, + HSU_DMA_BUF_SIZE, + DMA_FROM_DEVICE); + dbuf->dma_size = HSU_DMA_BUF_SIZE; + + /* Start the RX channel right now */ + hsu_dma_start_rx_chan(up->rxc, dbuf); + + /* Next init the TX DMA */ + dbuf = &up->txbuf; + dbuf->buf = xmit->buf; + dbuf->dma_addr = dma_map_single(port->dev, + dbuf->buf, + UART_XMIT_SIZE, + DMA_TO_DEVICE); + dbuf->dma_size = UART_XMIT_SIZE; + + /* This should not be changed all around */ + chan_writel(up->txc, HSU_CH_BSR, 32); + chan_writel(up->txc, HSU_CH_MOTSR, 4); + dbuf->ofs = 0; + } + +exit: + /* And clear the interrupt registers again for luck. */ + (void) serial_in(up, UART_LSR); + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + + up->running = 1; + return 0; +} + +static void serial_hsu_shutdown(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + unsigned long flags; + + del_timer_sync(&up->rxc->rx_timer); + + /* Disable interrupts from this port */ + up->ier = 0; + serial_out(up, UART_IER, 0); + up->running = 0; + + spin_lock_irqsave(&up->port.lock, flags); + up->port.mctrl &= ~TIOCM_OUT2; + serial_hsu_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* Disable break condition and FIFOs */ + serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); +} + +static void +serial_hsu_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + struct tty_struct *tty = port->state->port.tty; + unsigned char cval, fcr = 0; + unsigned long flags; + unsigned int baud, quot; + u32 ps, mul; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + /* CMSPAR isn't supported by this driver */ + if (tty) + tty->termios->c_cflag &= ~CMSPAR; + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + + /* + * The base clk is 50Mhz, and the baud rate come from: + * baud = 50M * MUL / (DIV * PS * DLAB) + * + * For those basic low baud rate we can get the direct + * scalar from 2746800, like 115200 = 2746800/24. For those + * higher baud rate, we handle them case by case, mainly by + * adjusting the MUL/PS registers, and DIV register is kept + * as default value 0x3d09 to make things simple + */ + baud = uart_get_baud_rate(port, termios, old, 0, 4000000); + + quot = 1; + ps = 0x10; + mul = 0x3600; + switch (baud) { + case 3500000: + mul = 0x3345; + ps = 0xC; + break; + case 1843200: + mul = 0x2400; + break; + case 3000000: + case 2500000: + case 2000000: + case 1500000: + case 1000000: + case 500000: + /* mul/ps/quot = 0x9C4/0x10/0x1 will make a 500000 bps */ + mul = baud / 500000 * 0x9C4; + break; + default: + /* Use uart_get_divisor to get quot for other baud rates */ + quot = 0; + } + + if (!quot) + quot = uart_get_divisor(port, baud); + + if ((up->port.uartclk / quot) < (2400 * 16)) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_1B; + else if ((up->port.uartclk / quot) < (230400 * 16)) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_16B; + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_HSU_64_32B; + + fcr |= UART_FCR_HSU_64B_FIFO; +#ifdef MFD_HSU_A0_STEPPING + /* A0 doesn't support half empty IRQ */ + fcr |= UART_FCR_FULL_EMPT_TXI; +#endif + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* Update the per-port timeout */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* Characters to ignore */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* Ignore all characters if CREAD is not set */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * CTS flow control flag and modem status interrupts, disable + * MSI by default + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + + serial_out(up, UART_IER, up->ier); + + if (termios->c_cflag & CRTSCTS) + up->mcr |= UART_MCR_AFE | UART_MCR_RTS; + else + up->mcr &= ~UART_MCR_AFE; + + serial_out(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ + serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ + serial_out(up, UART_LCR, cval); /* reset DLAB */ + serial_out(up, UART_MUL, mul); /* set MUL */ + serial_out(up, UART_PS, ps); /* set PS */ + up->lcr = cval; /* Save LCR */ + serial_hsu_set_mctrl(&up->port, up->port.mctrl); + serial_out(up, UART_FCR, fcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void +serial_hsu_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ +} + +static void serial_hsu_release_port(struct uart_port *port) +{ +} + +static int serial_hsu_request_port(struct uart_port *port) +{ + return 0; +} + +static void serial_hsu_config_port(struct uart_port *port, int flags) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + up->port.type = PORT_MFD; +} + +static int +serial_hsu_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* We don't want the core code to modify any port params */ + return -EINVAL; +} + +static const char * +serial_hsu_type(struct uart_port *port) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + return up->name; +} + +/* Mainly for uart console use */ +static struct uart_hsu_port *serial_hsu_ports[3]; +static struct uart_driver serial_hsu_reg; + +#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +/* Wait for transmitter & holding register to empty */ +static inline void wait_for_xmitr(struct uart_hsu_port *up) +{ + unsigned int status, tmout = 1000; + + /* Wait up to 1ms for the character to be sent. */ + do { + status = serial_in(up, UART_LSR); + + if (status & UART_LSR_BI) + up->lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + udelay(1); + } while (!(status & BOTH_EMPTY)); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0)) + udelay(1); + } +} + +static void serial_hsu_console_putchar(struct uart_port *port, int ch) +{ + struct uart_hsu_port *up = + container_of(port, struct uart_hsu_port, port); + + wait_for_xmitr(up); + serial_out(up, UART_TX, ch); +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void +serial_hsu_console_write(struct console *co, const char *s, unsigned int count) +{ + struct uart_hsu_port *up = serial_hsu_ports[co->index]; + unsigned long flags; + unsigned int ier; + int locked = 1; + + local_irq_save(flags); + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) { + locked = spin_trylock(&up->port.lock); + } else + spin_lock(&up->port.lock); + + /* First save the IER then disable the interrupts */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + + uart_console_write(&up->port, s, count, serial_hsu_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + serial_out(up, UART_IER, ier); + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +static struct console serial_hsu_console; + +static int __init +serial_hsu_console_setup(struct console *co, char *options) +{ + struct uart_hsu_port *up; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + if (co->index == -1 || co->index >= serial_hsu_reg.nr) + co->index = 0; + up = serial_hsu_ports[co->index]; + if (!up) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + ret = uart_set_options(&up->port, co, baud, parity, bits, flow); + + return ret; +} + +static struct console serial_hsu_console = { + .name = "ttyMFD", + .write = serial_hsu_console_write, + .device = uart_console_device, + .setup = serial_hsu_console_setup, + .flags = CON_PRINTBUFFER, + .index = 2, + .data = &serial_hsu_reg, +}; +#endif + +struct uart_ops serial_hsu_pops = { + .tx_empty = serial_hsu_tx_empty, + .set_mctrl = serial_hsu_set_mctrl, + .get_mctrl = serial_hsu_get_mctrl, + .stop_tx = serial_hsu_stop_tx, + .start_tx = serial_hsu_start_tx, + .stop_rx = serial_hsu_stop_rx, + .enable_ms = serial_hsu_enable_ms, + .break_ctl = serial_hsu_break_ctl, + .startup = serial_hsu_startup, + .shutdown = serial_hsu_shutdown, + .set_termios = serial_hsu_set_termios, + .pm = serial_hsu_pm, + .type = serial_hsu_type, + .release_port = serial_hsu_release_port, + .request_port = serial_hsu_request_port, + .config_port = serial_hsu_config_port, + .verify_port = serial_hsu_verify_port, +}; + +static struct uart_driver serial_hsu_reg = { + .owner = THIS_MODULE, + .driver_name = "MFD serial", + .dev_name = "ttyMFD", + .major = TTY_MAJOR, + .minor = 128, + .nr = 3, +}; + +#ifdef CONFIG_PM +static int serial_hsu_suspend(struct pci_dev *pdev, pm_message_t state) +{ + void *priv = pci_get_drvdata(pdev); + struct uart_hsu_port *up; + + /* Make sure this is not the internal dma controller */ + if (priv && (pdev->device != 0x081E)) { + up = priv; + uart_suspend_port(&serial_hsu_reg, &up->port); + } + + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int serial_hsu_resume(struct pci_dev *pdev) +{ + void *priv = pci_get_drvdata(pdev); + struct uart_hsu_port *up; + int ret; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + ret = pci_enable_device(pdev); + if (ret) + dev_warn(&pdev->dev, + "HSU: can't re-enable device, try to continue\n"); + + if (priv && (pdev->device != 0x081E)) { + up = priv; + uart_resume_port(&serial_hsu_reg, &up->port); + } + return 0; +} +#else +#define serial_hsu_suspend NULL +#define serial_hsu_resume NULL +#endif + +/* temp global pointer before we settle down on using one or four PCI dev */ +static struct hsu_port *phsu; + +static int serial_hsu_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct uart_hsu_port *uport; + int index, ret; + + printk(KERN_INFO "HSU: found PCI Serial controller(ID: %04x:%04x)\n", + pdev->vendor, pdev->device); + + switch (pdev->device) { + case 0x081B: + index = 0; + break; + case 0x081C: + index = 1; + break; + case 0x081D: + index = 2; + break; + case 0x081E: + /* internal DMA controller */ + index = 3; + break; + default: + dev_err(&pdev->dev, "HSU: out of index!"); + return -ENODEV; + } + + ret = pci_enable_device(pdev); + if (ret) + return ret; + + if (index == 3) { + /* DMA controller */ + ret = request_irq(pdev->irq, dma_irq, 0, "hsu_dma", phsu); + if (ret) { + dev_err(&pdev->dev, "can not get IRQ\n"); + goto err_disable; + } + pci_set_drvdata(pdev, phsu); + } else { + /* UART port 0~2 */ + uport = &phsu->port[index]; + uport->port.irq = pdev->irq; + uport->port.dev = &pdev->dev; + uport->dev = &pdev->dev; + + ret = request_irq(pdev->irq, port_irq, 0, uport->name, uport); + if (ret) { + dev_err(&pdev->dev, "can not get IRQ\n"); + goto err_disable; + } + uart_add_one_port(&serial_hsu_reg, &uport->port); + +#ifdef CONFIG_SERIAL_MFD_HSU_CONSOLE + if (index == 2) { + register_console(&serial_hsu_console); + uport->port.cons = &serial_hsu_console; + } +#endif + pci_set_drvdata(pdev, uport); + } + + return 0; + +err_disable: + pci_disable_device(pdev); + return ret; +} + +static void hsu_dma_rx_timeout(unsigned long data) +{ + struct hsu_dma_chan *chan = (void *)data; + struct uart_hsu_port *up = chan->uport; + struct hsu_dma_buffer *dbuf = &up->rxbuf; + int count = 0; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + + count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; + + if (!count) { + mod_timer(&chan->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ); + goto exit; + } + + hsu_dma_rx(up, 0); +exit: + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void hsu_global_init(void) +{ + struct hsu_port *hsu; + struct uart_hsu_port *uport; + struct hsu_dma_chan *dchan; + int i, ret; + + hsu = kzalloc(sizeof(struct hsu_port), GFP_KERNEL); + if (!hsu) + return; + + /* Get basic io resource and map it */ + hsu->paddr = 0xffa28000; + hsu->iolen = 0x1000; + + if (!(request_mem_region(hsu->paddr, hsu->iolen, "HSU global"))) + pr_warning("HSU: error in request mem region\n"); + + hsu->reg = ioremap_nocache((unsigned long)hsu->paddr, hsu->iolen); + if (!hsu->reg) { + pr_err("HSU: error in ioremap\n"); + ret = -ENOMEM; + goto err_free_region; + } + + /* Initialise the 3 UART ports */ + uport = hsu->port; + for (i = 0; i < 3; i++) { + uport->port.type = PORT_MFD; + uport->port.iotype = UPIO_MEM; + uport->port.mapbase = (resource_size_t)hsu->paddr + + HSU_PORT_REG_OFFSET + + i * HSU_PORT_REG_LENGTH; + uport->port.membase = hsu->reg + HSU_PORT_REG_OFFSET + + i * HSU_PORT_REG_LENGTH; + + sprintf(uport->name, "hsu_port%d", i); + uport->port.fifosize = 64; + uport->port.ops = &serial_hsu_pops; + uport->port.line = i; + uport->port.flags = UPF_IOREMAP; + /* set the scalable maxim support rate to 2746800 bps */ + uport->port.uartclk = 115200 * 24 * 16; + + uport->running = 0; + uport->txc = &hsu->chans[i * 2]; + uport->rxc = &hsu->chans[i * 2 + 1]; + + serial_hsu_ports[i] = uport; + uport->index = i; + uport++; + } + + /* Initialise 6 dma channels */ + dchan = hsu->chans; + for (i = 0; i < 6; i++) { + dchan->id = i; + dchan->dirt = (i & 0x1) ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + dchan->uport = &hsu->port[i/2]; + dchan->reg = hsu->reg + HSU_DMA_CHANS_REG_OFFSET + + i * HSU_DMA_CHANS_REG_LENGTH; + + /* Work around for RX */ + if (dchan->dirt == DMA_FROM_DEVICE) { + init_timer(&dchan->rx_timer); + dchan->rx_timer.function = hsu_dma_rx_timeout; + dchan->rx_timer.data = (unsigned long)dchan; + } + dchan++; + } + + phsu = hsu; + hsu_debugfs_init(hsu); + return; + +err_free_region: + release_mem_region(hsu->paddr, hsu->iolen); + kfree(hsu); + return; +} + +static void serial_hsu_remove(struct pci_dev *pdev) +{ + void *priv = pci_get_drvdata(pdev); + struct uart_hsu_port *up; + + if (!priv) + return; + + /* For port 0/1/2, priv is the address of uart_hsu_port */ + if (pdev->device != 0x081E) { + up = priv; + uart_remove_one_port(&serial_hsu_reg, &up->port); + } + + pci_set_drvdata(pdev, NULL); + free_irq(pdev->irq, priv); + pci_disable_device(pdev); +} + +/* First 3 are UART ports, and the 4th is the DMA */ +static const struct pci_device_id pci_ids[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081B) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081C) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081D) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081E) }, + {}, +}; + +static struct pci_driver hsu_pci_driver = { + .name = "HSU serial", + .id_table = pci_ids, + .probe = serial_hsu_probe, + .remove = __devexit_p(serial_hsu_remove), + .suspend = serial_hsu_suspend, + .resume = serial_hsu_resume, +}; + +static int __init hsu_pci_init(void) +{ + int ret; + + hsu_global_init(); + + ret = uart_register_driver(&serial_hsu_reg); + if (ret) + return ret; + + return pci_register_driver(&hsu_pci_driver); +} + +static void __exit hsu_pci_exit(void) +{ + pci_unregister_driver(&hsu_pci_driver); + uart_unregister_driver(&serial_hsu_reg); + + hsu_debugfs_remove(phsu); + + kfree(phsu); +} + +module_init(hsu_pci_init); +module_exit(hsu_pci_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:medfield-hsu"); diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index abbd146..126ec7f 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -29,39 +29,6 @@ * kind, whether express or implied. */ -/* Platform device Usage : - * - * Since PSCs can have multiple function, the correct driver for each one - * is selected by calling mpc52xx_match_psc_function(...). The function - * handled by this driver is "uart". - * - * The driver init all necessary registers to place the PSC in uart mode without - * DCD. However, the pin multiplexing aren't changed and should be set either - * by the bootloader or in the platform init code. - * - * The idx field must be equal to the PSC index (e.g. 0 for PSC1, 1 for PSC2, - * and so on). So the PSC1 is mapped to /dev/ttyPSC0, PSC2 to /dev/ttyPSC1 and - * so on. But be warned, it's an ABSOLUTE REQUIREMENT ! This is needed mainly - * fpr the console code : without this 1:1 mapping, at early boot time, when we - * are parsing the kernel args console=ttyPSC?, we wouldn't know which PSC it - * will be mapped to. - */ - -/* OF Platform device Usage : - * - * This driver is only used for PSCs configured in uart mode. The device - * tree will have a node for each PSC with "mpc52xx-psc-uart" in the compatible - * list. - * - * By default, PSC devices are enumerated in the order they are found. However - * a particular PSC number can be forces by adding 'device_no = <port#>' - * to the device node. - * - * The driver init all necessary registers to place the PSC in uart mode without - * DCD. However, the pin multiplexing aren't changed and should be set either - * by the bootloader or in the platform init code. - */ - #undef DEBUG #include <linux/device.h> @@ -74,6 +41,7 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_platform.h> +#include <linux/clk.h> #include <asm/mpc52xx.h> #include <asm/mpc52xx_psc.h> @@ -113,6 +81,7 @@ static void mpc52xx_uart_of_enumerate(void); /* Forward declaration of the interruption handling routine */ static irqreturn_t mpc52xx_uart_int(int irq, void *dev_id); +static irqreturn_t mpc5xxx_uart_process_int(struct uart_port *port); /* Simple macro to test if a port is console or not. This one is taken @@ -144,9 +113,26 @@ struct psc_ops { unsigned char (*read_char)(struct uart_port *port); void (*cw_disable_ints)(struct uart_port *port); void (*cw_restore_ints)(struct uart_port *port); - unsigned long (*getuartclk)(void *p); + unsigned int (*set_baudrate)(struct uart_port *port, + struct ktermios *new, + struct ktermios *old); + int (*clock)(struct uart_port *port, int enable); + int (*fifoc_init)(void); + void (*fifoc_uninit)(void); + void (*get_irq)(struct uart_port *, struct device_node *); + irqreturn_t (*handle_irq)(struct uart_port *port); }; +/* setting the prescaler and divisor reg is common for all chips */ +static inline void mpc52xx_set_divisor(struct mpc52xx_psc __iomem *psc, + u16 prescaler, unsigned int divisor) +{ + /* select prescaler */ + out_be16(&psc->mpc52xx_psc_clock_select, prescaler); + out_8(&psc->ctur, divisor >> 8); + out_8(&psc->ctlr, divisor & 0xff); +} + #ifdef CONFIG_PPC_MPC52xx #define FIFO_52xx(port) ((struct mpc52xx_psc_fifo __iomem *)(PSC(port)+1)) static void mpc52xx_psc_fifo_init(struct uart_port *port) @@ -154,9 +140,6 @@ static void mpc52xx_psc_fifo_init(struct uart_port *port) struct mpc52xx_psc __iomem *psc = PSC(port); struct mpc52xx_psc_fifo __iomem *fifo = FIFO_52xx(port); - /* /32 prescaler */ - out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00); - out_8(&fifo->rfcntl, 0x00); out_be16(&fifo->rfalarm, 0x1ff); out_8(&fifo->tfcntl, 0x07); @@ -245,15 +228,59 @@ static void mpc52xx_psc_cw_restore_ints(struct uart_port *port) out_be16(&PSC(port)->mpc52xx_psc_imr, port->read_status_mask); } -/* Search for bus-frequency property in this node or a parent */ -static unsigned long mpc52xx_getuartclk(void *p) +static unsigned int mpc5200_psc_set_baudrate(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) { - /* - * 5200 UARTs have a / 32 prescaler - * but the generic serial code assumes 16 - * so return ipb freq / 2 - */ - return mpc5xxx_get_bus_frequency(p) / 2; + unsigned int baud; + unsigned int divisor; + + /* The 5200 has a fixed /32 prescaler, uartclk contains the ipb freq */ + baud = uart_get_baud_rate(port, new, old, + port->uartclk / (32 * 0xffff) + 1, + port->uartclk / 32); + divisor = (port->uartclk + 16 * baud) / (32 * baud); + + /* enable the /32 prescaler and set the divisor */ + mpc52xx_set_divisor(PSC(port), 0xdd00, divisor); + return baud; +} + +static unsigned int mpc5200b_psc_set_baudrate(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ + unsigned int baud; + unsigned int divisor; + u16 prescaler; + + /* The 5200B has a selectable /4 or /32 prescaler, uartclk contains the + * ipb freq */ + baud = uart_get_baud_rate(port, new, old, + port->uartclk / (32 * 0xffff) + 1, + port->uartclk / 4); + divisor = (port->uartclk + 2 * baud) / (4 * baud); + + /* select the proper prescaler and set the divisor */ + if (divisor > 0xffff) { + divisor = (divisor + 4) / 8; + prescaler = 0xdd00; /* /32 */ + } else + prescaler = 0xff00; /* /4 */ + mpc52xx_set_divisor(PSC(port), prescaler, divisor); + return baud; +} + +static void mpc52xx_psc_get_irq(struct uart_port *port, struct device_node *np) +{ + port->irqflags = IRQF_DISABLED; + port->irq = irq_of_parse_and_map(np, 0); +} + +/* 52xx specific interrupt handler. The caller holds the port lock */ +static irqreturn_t mpc52xx_psc_handle_irq(struct uart_port *port) +{ + return mpc5xxx_uart_process_int(port); } static struct psc_ops mpc52xx_psc_ops = { @@ -272,15 +299,54 @@ static struct psc_ops mpc52xx_psc_ops = { .read_char = mpc52xx_psc_read_char, .cw_disable_ints = mpc52xx_psc_cw_disable_ints, .cw_restore_ints = mpc52xx_psc_cw_restore_ints, - .getuartclk = mpc52xx_getuartclk, + .set_baudrate = mpc5200_psc_set_baudrate, + .get_irq = mpc52xx_psc_get_irq, + .handle_irq = mpc52xx_psc_handle_irq, +}; + +static struct psc_ops mpc5200b_psc_ops = { + .fifo_init = mpc52xx_psc_fifo_init, + .raw_rx_rdy = mpc52xx_psc_raw_rx_rdy, + .raw_tx_rdy = mpc52xx_psc_raw_tx_rdy, + .rx_rdy = mpc52xx_psc_rx_rdy, + .tx_rdy = mpc52xx_psc_tx_rdy, + .tx_empty = mpc52xx_psc_tx_empty, + .stop_rx = mpc52xx_psc_stop_rx, + .start_tx = mpc52xx_psc_start_tx, + .stop_tx = mpc52xx_psc_stop_tx, + .rx_clr_irq = mpc52xx_psc_rx_clr_irq, + .tx_clr_irq = mpc52xx_psc_tx_clr_irq, + .write_char = mpc52xx_psc_write_char, + .read_char = mpc52xx_psc_read_char, + .cw_disable_ints = mpc52xx_psc_cw_disable_ints, + .cw_restore_ints = mpc52xx_psc_cw_restore_ints, + .set_baudrate = mpc5200b_psc_set_baudrate, + .get_irq = mpc52xx_psc_get_irq, + .handle_irq = mpc52xx_psc_handle_irq, }; #endif /* CONFIG_MPC52xx */ #ifdef CONFIG_PPC_MPC512x #define FIFO_512x(port) ((struct mpc512x_psc_fifo __iomem *)(PSC(port)+1)) + +/* PSC FIFO Controller for mpc512x */ +struct psc_fifoc { + u32 fifoc_cmd; + u32 fifoc_int; + u32 fifoc_dma; + u32 fifoc_axe; + u32 fifoc_debug; +}; + +static struct psc_fifoc __iomem *psc_fifoc; +static unsigned int psc_fifoc_irq; + static void mpc512x_psc_fifo_init(struct uart_port *port) { + /* /32 prescaler */ + out_be16(&PSC(port)->mpc52xx_psc_clock_select, 0xdd00); + out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_RESET_SLICE); out_be32(&FIFO_512x(port)->txcmd, MPC512x_PSC_FIFO_ENABLE_SLICE); out_be32(&FIFO_512x(port)->txalarm, 1); @@ -388,9 +454,121 @@ static void mpc512x_psc_cw_restore_ints(struct uart_port *port) out_be32(&FIFO_512x(port)->rximr, port->read_status_mask & 0x7f); } -static unsigned long mpc512x_getuartclk(void *p) +static unsigned int mpc512x_psc_set_baudrate(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) { - return mpc5xxx_get_bus_frequency(p); + unsigned int baud; + unsigned int divisor; + + /* + * The "MPC5121e Microcontroller Reference Manual, Rev. 3" says on + * pg. 30-10 that the chip supports a /32 and a /10 prescaler. + * Furthermore, it states that "After reset, the prescaler by 10 + * for the UART mode is selected", but the reset register value is + * 0x0000 which means a /32 prescaler. This is wrong. + * + * In reality using /32 prescaler doesn't work, as it is not supported! + * Use /16 or /10 prescaler, see "MPC5121e Hardware Design Guide", + * Chapter 4.1 PSC in UART Mode. + * Calculate with a /16 prescaler here. + */ + + /* uartclk contains the ips freq */ + baud = uart_get_baud_rate(port, new, old, + port->uartclk / (16 * 0xffff) + 1, + port->uartclk / 16); + divisor = (port->uartclk + 8 * baud) / (16 * baud); + + /* enable the /16 prescaler and set the divisor */ + mpc52xx_set_divisor(PSC(port), 0xdd00, divisor); + return baud; +} + +/* Init PSC FIFO Controller */ +static int __init mpc512x_psc_fifoc_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, + "fsl,mpc5121-psc-fifo"); + if (!np) { + pr_err("%s: Can't find FIFOC node\n", __func__); + return -ENODEV; + } + + psc_fifoc = of_iomap(np, 0); + if (!psc_fifoc) { + pr_err("%s: Can't map FIFOC\n", __func__); + of_node_put(np); + return -ENODEV; + } + + psc_fifoc_irq = irq_of_parse_and_map(np, 0); + of_node_put(np); + if (psc_fifoc_irq == NO_IRQ) { + pr_err("%s: Can't get FIFOC irq\n", __func__); + iounmap(psc_fifoc); + return -ENODEV; + } + + return 0; +} + +static void __exit mpc512x_psc_fifoc_uninit(void) +{ + iounmap(psc_fifoc); +} + +/* 512x specific interrupt handler. The caller holds the port lock */ +static irqreturn_t mpc512x_psc_handle_irq(struct uart_port *port) +{ + unsigned long fifoc_int; + int psc_num; + + /* Read pending PSC FIFOC interrupts */ + fifoc_int = in_be32(&psc_fifoc->fifoc_int); + + /* Check if it is an interrupt for this port */ + psc_num = (port->mapbase & 0xf00) >> 8; + if (test_bit(psc_num, &fifoc_int) || + test_bit(psc_num + 16, &fifoc_int)) + return mpc5xxx_uart_process_int(port); + + return IRQ_NONE; +} + +static int mpc512x_psc_clock(struct uart_port *port, int enable) +{ + struct clk *psc_clk; + int psc_num; + char clk_name[10]; + + if (uart_console(port)) + return 0; + + psc_num = (port->mapbase & 0xf00) >> 8; + snprintf(clk_name, sizeof(clk_name), "psc%d_clk", psc_num); + psc_clk = clk_get(port->dev, clk_name); + if (IS_ERR(psc_clk)) { + dev_err(port->dev, "Failed to get PSC clock entry!\n"); + return -ENODEV; + } + + dev_dbg(port->dev, "%s %sable\n", clk_name, enable ? "en" : "dis"); + + if (enable) + clk_enable(psc_clk); + else + clk_disable(psc_clk); + + return 0; +} + +static void mpc512x_psc_get_irq(struct uart_port *port, struct device_node *np) +{ + port->irqflags = IRQF_SHARED; + port->irq = psc_fifoc_irq; } static struct psc_ops mpc512x_psc_ops = { @@ -409,7 +587,12 @@ static struct psc_ops mpc512x_psc_ops = { .read_char = mpc512x_psc_read_char, .cw_disable_ints = mpc512x_psc_cw_disable_ints, .cw_restore_ints = mpc512x_psc_cw_restore_ints, - .getuartclk = mpc512x_getuartclk, + .set_baudrate = mpc512x_psc_set_baudrate, + .clock = mpc512x_psc_clock, + .fifoc_init = mpc512x_psc_fifoc_init, + .fifoc_uninit = mpc512x_psc_fifoc_uninit, + .get_irq = mpc512x_psc_get_irq, + .handle_irq = mpc512x_psc_handle_irq, }; #endif @@ -519,10 +702,15 @@ mpc52xx_uart_startup(struct uart_port *port) struct mpc52xx_psc __iomem *psc = PSC(port); int ret; + if (psc_ops->clock) { + ret = psc_ops->clock(port, 1); + if (ret) + return ret; + } + /* Request IRQ */ ret = request_irq(port->irq, mpc52xx_uart_int, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, - "mpc52xx_psc_uart", port); + port->irqflags, "mpc52xx_psc_uart", port); if (ret) return ret; @@ -553,6 +741,9 @@ mpc52xx_uart_shutdown(struct uart_port *port) port->read_status_mask = 0; out_be16(&psc->mpc52xx_psc_imr, port->read_status_mask); + if (psc_ops->clock) + psc_ops->clock(port, 0); + /* Release interrupt */ free_irq(port->irq, port); } @@ -564,8 +755,8 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, struct mpc52xx_psc __iomem *psc = PSC(port); unsigned long flags; unsigned char mr1, mr2; - unsigned short ctr; - unsigned int j, baud, quot; + unsigned int j; + unsigned int baud; /* Prepare what we're gonna write */ mr1 = 0; @@ -602,16 +793,9 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, mr2 |= MPC52xx_PSC_MODE_TXCTS; } - baud = uart_get_baud_rate(port, new, old, 0, port->uartclk/16); - quot = uart_get_divisor(port, baud); - ctr = quot & 0xffff; - /* Get the lock */ spin_lock_irqsave(&port->lock, flags); - /* Update the per-port timeout */ - uart_update_timeout(port, new->c_cflag, baud); - /* Do our best to flush TX & RX, so we don't lose anything */ /* But we don't wait indefinitely ! */ j = 5000000; /* Maximum wait */ @@ -635,8 +819,10 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1); out_8(&psc->mode, mr1); out_8(&psc->mode, mr2); - out_8(&psc->ctur, ctr >> 8); - out_8(&psc->ctlr, ctr & 0xff); + baud = psc_ops->set_baudrate(port, new, old); + + /* Update the per-port timeout */ + uart_update_timeout(port, new->c_cflag, baud); if (UART_ENABLE_MS(port, new->c_cflag)) mpc52xx_uart_enable_ms(port); @@ -652,7 +838,11 @@ mpc52xx_uart_set_termios(struct uart_port *port, struct ktermios *new, static const char * mpc52xx_uart_type(struct uart_port *port) { - return port->type == PORT_MPC52xx ? "MPC52xx PSC" : NULL; + /* + * We keep using PORT_MPC52xx for historic reasons although it applies + * for MPC512x, too, but print "MPC5xxx" to not irritate users + */ + return port->type == PORT_MPC52xx ? "MPC5xxx PSC" : NULL; } static void @@ -705,7 +895,7 @@ mpc52xx_uart_verify_port(struct uart_port *port, struct serial_struct *ser) return -EINVAL; if ((ser->irq != port->irq) || - (ser->io_type != SERIAL_IO_MEM) || + (ser->io_type != UPIO_MEM) || (ser->baud_base != port->uartclk) || (ser->iomem_base != (void *)port->mapbase) || (ser->hub6 != 0)) @@ -745,7 +935,7 @@ static struct uart_ops mpc52xx_uart_ops = { static inline int mpc52xx_uart_int_rx_chars(struct uart_port *port) { - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; unsigned char ch, flag; unsigned short status; @@ -812,7 +1002,7 @@ mpc52xx_uart_int_rx_chars(struct uart_port *port) static inline int mpc52xx_uart_int_tx_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; /* Process out of band chars */ if (port->x_char) { @@ -851,15 +1041,12 @@ mpc52xx_uart_int_tx_chars(struct uart_port *port) } static irqreturn_t -mpc52xx_uart_int(int irq, void *dev_id) +mpc5xxx_uart_process_int(struct uart_port *port) { - struct uart_port *port = dev_id; unsigned long pass = ISR_PASS_LIMIT; unsigned int keepgoing; u8 status; - spin_lock(&port->lock); - /* While we have stuff to do, we continue */ do { /* If we don't find anything to do, we stop */ @@ -886,11 +1073,23 @@ mpc52xx_uart_int(int irq, void *dev_id) } while (keepgoing); - spin_unlock(&port->lock); - return IRQ_HANDLED; } +static irqreturn_t +mpc52xx_uart_int(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + irqreturn_t ret; + + spin_lock(&port->lock); + + ret = psc_ops->handle_irq(port); + + spin_unlock(&port->lock); + + return ret; +} /* ======================================================================== */ /* Console ( if applicable ) */ @@ -1007,7 +1206,7 @@ mpc52xx_console_setup(struct console *co, char *options) return ret; } - uartclk = psc_ops->getuartclk(np); + uartclk = mpc5xxx_get_bus_frequency(np); if (uartclk == 0) { pr_debug("Could not find uart clock frequency!\n"); return -EINVAL; @@ -1090,6 +1289,7 @@ static struct uart_driver mpc52xx_uart_driver = { static struct of_device_id mpc52xx_uart_of_match[] = { #ifdef CONFIG_PPC_MPC52xx + { .compatible = "fsl,mpc5200b-psc-uart", .data = &mpc5200b_psc_ops, }, { .compatible = "fsl,mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, /* binding used by old lite5200 device trees: */ { .compatible = "mpc5200-psc-uart", .data = &mpc52xx_psc_ops, }, @@ -1103,7 +1303,7 @@ static struct of_device_id mpc52xx_uart_of_match[] = { }; static int __devinit -mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) +mpc52xx_uart_of_probe(struct platform_device *op, const struct of_device_id *match) { int idx = -1; unsigned int uartclk; @@ -1115,14 +1315,17 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) /* Check validity & presence */ for (idx = 0; idx < MPC52xx_PSC_MAXNUM; idx++) - if (mpc52xx_uart_nodes[idx] == op->node) + if (mpc52xx_uart_nodes[idx] == op->dev.of_node) break; if (idx >= MPC52xx_PSC_MAXNUM) return -EINVAL; pr_debug("Found %s assigned to ttyPSC%x\n", mpc52xx_uart_nodes[idx]->full_name, idx); - uartclk = psc_ops->getuartclk(op->node); + /* set the uart clock to the input clock of the psc, the different + * prescalers are taken into account in the set_baudrate() methods + * of the respective chip */ + uartclk = mpc5xxx_get_bus_frequency(op->dev.of_node); if (uartclk == 0) { dev_dbg(&op->dev, "Could not find uart clock frequency!\n"); return -EINVAL; @@ -1142,7 +1345,7 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) port->dev = &op->dev; /* Search for IRQ and mapbase */ - ret = of_address_to_resource(op->node, 0, &res); + ret = of_address_to_resource(op->dev.of_node, 0, &res); if (ret) return ret; @@ -1152,7 +1355,7 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) return -EINVAL; } - port->irq = irq_of_parse_and_map(op->node, 0); + psc_ops->get_irq(port, op->dev.of_node); if (port->irq == NO_IRQ) { dev_dbg(&op->dev, "Could not get irq\n"); return -EINVAL; @@ -1163,32 +1366,28 @@ mpc52xx_uart_of_probe(struct of_device *op, const struct of_device_id *match) /* Add the port to the uart sub-system */ ret = uart_add_one_port(&mpc52xx_uart_driver, port); - if (ret) { - irq_dispose_mapping(port->irq); + if (ret) return ret; - } dev_set_drvdata(&op->dev, (void *)port); return 0; } static int -mpc52xx_uart_of_remove(struct of_device *op) +mpc52xx_uart_of_remove(struct platform_device *op) { struct uart_port *port = dev_get_drvdata(&op->dev); dev_set_drvdata(&op->dev, NULL); - if (port) { + if (port) uart_remove_one_port(&mpc52xx_uart_driver, port); - irq_dispose_mapping(port->irq); - } return 0; } #ifdef CONFIG_PM static int -mpc52xx_uart_of_suspend(struct of_device *op, pm_message_t state) +mpc52xx_uart_of_suspend(struct platform_device *op, pm_message_t state) { struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); @@ -1199,7 +1398,7 @@ mpc52xx_uart_of_suspend(struct of_device *op, pm_message_t state) } static int -mpc52xx_uart_of_resume(struct of_device *op) +mpc52xx_uart_of_resume(struct platform_device *op) { struct uart_port *port = (struct uart_port *) dev_get_drvdata(&op->dev); @@ -1255,15 +1454,16 @@ mpc52xx_uart_of_enumerate(void) MODULE_DEVICE_TABLE(of, mpc52xx_uart_of_match); static struct of_platform_driver mpc52xx_uart_of_driver = { - .match_table = mpc52xx_uart_of_match, .probe = mpc52xx_uart_of_probe, .remove = mpc52xx_uart_of_remove, #ifdef CONFIG_PM .suspend = mpc52xx_uart_of_suspend, .resume = mpc52xx_uart_of_resume, #endif - .driver = { - .name = "mpc52xx-psc-uart", + .driver = { + .name = "mpc52xx-psc-uart", + .owner = THIS_MODULE, + .of_match_table = mpc52xx_uart_of_match, }, }; @@ -1288,6 +1488,15 @@ mpc52xx_uart_init(void) mpc52xx_uart_of_enumerate(); + /* + * Map the PSC FIFO Controller and init if on MPC512x. + */ + if (psc_ops && psc_ops->fifoc_init) { + ret = psc_ops->fifoc_init(); + if (ret) + return ret; + } + ret = of_register_platform_driver(&mpc52xx_uart_of_driver); if (ret) { printk(KERN_ERR "%s: of_register_platform_driver failed (%i)\n", @@ -1302,6 +1511,9 @@ mpc52xx_uart_init(void) static void __exit mpc52xx_uart_exit(void) { + if (psc_ops->fifoc_uninit) + psc_ops->fifoc_uninit(); + of_unregister_platform_driver(&mpc52xx_uart_of_driver); uart_unregister_driver(&mpc52xx_uart_driver); } diff --git a/drivers/serial/mpsc.c b/drivers/serial/mpsc.c index 61d3ade..6a9c660 100644 --- a/drivers/serial/mpsc.c +++ b/drivers/serial/mpsc.c @@ -70,6 +70,7 @@ #include <linux/dma-mapping.h> #include <linux/mv643xx.h> #include <linux/platform_device.h> +#include <linux/gfp.h> #include <asm/io.h> #include <asm/irq.h> @@ -936,7 +937,7 @@ static int serial_polled; static int mpsc_rx_intr(struct mpsc_port_info *pi) { struct mpsc_rx_desc *rxre; - struct tty_struct *tty = pi->port.info->port.tty; + struct tty_struct *tty = pi->port.state->port.tty; u32 cmdstat, bytes_in, i; int rc = 0; u8 *bp; @@ -1109,7 +1110,7 @@ static void mpsc_setup_tx_desc(struct mpsc_port_info *pi, u32 count, u32 intr) static void mpsc_copy_tx_data(struct mpsc_port_info *pi) { - struct circ_buf *xmit = &pi->port.info->xmit; + struct circ_buf *xmit = &pi->port.state->xmit; u8 *bp; u32 i; @@ -2070,6 +2071,7 @@ static int mpsc_drv_probe(struct platform_device *dev) if (!(rc = mpsc_drv_map_regs(pi, dev))) { mpsc_drv_get_platform_data(pi, dev, dev->id); + pi->port.dev = &dev->dev; if (!(rc = mpsc_make_ready(pi))) { spin_lock_init(&pi->tx_lock); diff --git a/drivers/serial/mrst_max3110.c b/drivers/serial/mrst_max3110.c new file mode 100644 index 0000000..b62857b --- /dev/null +++ b/drivers/serial/mrst_max3110.c @@ -0,0 +1,919 @@ +/* + * mrst_max3110.c - spi uart protocol driver for Maxim 3110 + * + * Copyright (c) 2008-2010, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * Note: + * 1. From Max3110 spec, the Rx FIFO has 8 words, while the Tx FIFO only has + * 1 word. If SPI master controller doesn't support sclk frequency change, + * then the char need be sent out one by one with some delay + * + * 2. Currently only RX availabe interrrupt is used, no need for waiting TXE + * interrupt for a low speed UART device + */ + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/irq.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> + +#include <linux/kthread.h> +#include <linux/spi/spi.h> + +#include "mrst_max3110.h" + +#define PR_FMT "mrst_max3110: " + +#define UART_TX_NEEDED 1 +#define CON_TX_NEEDED 2 +#define BIT_IRQ_PENDING 3 + +struct uart_max3110 { + struct uart_port port; + struct spi_device *spi; + char name[24]; + + wait_queue_head_t wq; + struct task_struct *main_thread; + struct task_struct *read_thread; + struct mutex thread_mutex;; + + u32 baud; + u16 cur_conf; + u8 clock; + u8 parity, word_7bits; + u16 irq; + + unsigned long uart_flags; + + /* console related */ + struct circ_buf con_xmit; +}; + +/* global data structure, may need be removed */ +static struct uart_max3110 *pmax; + +static void receive_chars(struct uart_max3110 *max, + unsigned char *str, int len); +static int max3110_read_multi(struct uart_max3110 *max, u8 *buf); +static void max3110_con_receive(struct uart_max3110 *max); + +static int max3110_write_then_read(struct uart_max3110 *max, + const void *txbuf, void *rxbuf, unsigned len, int always_fast) +{ + struct spi_device *spi = max->spi; + struct spi_message message; + struct spi_transfer x; + int ret; + + spi_message_init(&message); + memset(&x, 0, sizeof x); + x.len = len; + x.tx_buf = txbuf; + x.rx_buf = rxbuf; + spi_message_add_tail(&x, &message); + + if (always_fast) + x.speed_hz = spi->max_speed_hz; + else if (max->baud) + x.speed_hz = max->baud; + + /* Do the i/o */ + ret = spi_sync(spi, &message); + return ret; +} + +/* Write a 16b word to the device */ +static int max3110_out(struct uart_max3110 *max, const u16 out) +{ + void *buf; + u16 *obuf, *ibuf; + u8 ch; + int ret; + + buf = kzalloc(8, GFP_KERNEL | GFP_DMA); + if (!buf) + return -ENOMEM; + + obuf = buf; + ibuf = buf + 4; + *obuf = out; + ret = max3110_write_then_read(max, obuf, ibuf, 2, 1); + if (ret) { + pr_warning(PR_FMT "%s(): get err msg %d when sending 0x%x\n", + __func__, ret, out); + goto exit; + } + + /* If some valid data is read back */ + if (*ibuf & MAX3110_READ_DATA_AVAILABLE) { + ch = *ibuf & 0xff; + receive_chars(max, &ch, 1); + } + +exit: + kfree(buf); + return ret; +} + +/* + * This is usually used to read data from SPIC RX FIFO, which doesn't + * need any delay like flushing character out. + * + * Return how many valide bytes are read back + */ +static int max3110_read_multi(struct uart_max3110 *max, u8 *rxbuf) +{ + void *buf; + u16 *obuf, *ibuf; + u8 *pbuf, valid_str[M3110_RX_FIFO_DEPTH]; + int i, j, blen; + + blen = M3110_RX_FIFO_DEPTH * sizeof(u16); + buf = kzalloc(blen * 2, GFP_KERNEL | GFP_DMA); + if (!buf) { + pr_warning(PR_FMT "%s(): fail to alloc dma buffer\n", __func__); + return 0; + } + + /* tx/rx always have the same length */ + obuf = buf; + ibuf = buf + blen; + + if (max3110_write_then_read(max, obuf, ibuf, blen, 1)) { + kfree(buf); + return 0; + } + + /* If caller doesn't provide a buffer, then handle received char */ + pbuf = rxbuf ? rxbuf : valid_str; + + for (i = 0, j = 0; i < M3110_RX_FIFO_DEPTH; i++) { + if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE) + pbuf[j++] = ibuf[i] & 0xff; + } + + if (j && (pbuf == valid_str)) + receive_chars(max, valid_str, j); + + kfree(buf); + return j; +} + +static void serial_m3110_con_putchar(struct uart_port *port, int ch) +{ + struct uart_max3110 *max = + container_of(port, struct uart_max3110, port); + struct circ_buf *xmit = &max->con_xmit; + + if (uart_circ_chars_free(xmit)) { + xmit->buf[xmit->head] = (char)ch; + xmit->head = (xmit->head + 1) & (PAGE_SIZE - 1); + } +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void serial_m3110_con_write(struct console *co, + const char *s, unsigned int count) +{ + if (!pmax) + return; + + uart_console_write(&pmax->port, s, count, serial_m3110_con_putchar); + + if (!test_and_set_bit(CON_TX_NEEDED, &pmax->uart_flags)) + wake_up_process(pmax->main_thread); +} + +static int __init +serial_m3110_con_setup(struct console *co, char *options) +{ + struct uart_max3110 *max = pmax; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + pr_info(PR_FMT "setting up console\n"); + + if (co->index == -1) + co->index = 0; + + if (!max) { + pr_err(PR_FMT "pmax is NULL, return"); + return -ENODEV; + } + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&max->port, co, baud, parity, bits, flow); +} + +static struct tty_driver *serial_m3110_con_device(struct console *co, + int *index) +{ + struct uart_driver *p = co->data; + *index = co->index; + return p->tty_driver; +} + +static struct uart_driver serial_m3110_reg; +static struct console serial_m3110_console = { + .name = "ttyS", + .write = serial_m3110_con_write, + .device = serial_m3110_con_device, + .setup = serial_m3110_con_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &serial_m3110_reg, +}; + +static unsigned int serial_m3110_tx_empty(struct uart_port *port) +{ + return 1; +} + +static void serial_m3110_stop_tx(struct uart_port *port) +{ + return; +} + +/* stop_rx will be called in spin_lock env */ +static void serial_m3110_stop_rx(struct uart_port *port) +{ + return; +} + +#define WORDS_PER_XFER 128 +static void send_circ_buf(struct uart_max3110 *max, + struct circ_buf *xmit) +{ + void *buf; + u16 *obuf, *ibuf; + u8 valid_str[WORDS_PER_XFER]; + int i, j, len, blen, dma_size, left, ret = 0; + + + dma_size = WORDS_PER_XFER * sizeof(u16) * 2; + buf = kzalloc(dma_size, GFP_KERNEL | GFP_DMA); + if (!buf) + return; + obuf = buf; + ibuf = buf + dma_size/2; + + while (!uart_circ_empty(xmit)) { + left = uart_circ_chars_pending(xmit); + while (left) { + len = min(left, WORDS_PER_XFER); + blen = len * sizeof(u16); + memset(ibuf, 0, blen); + + for (i = 0; i < len; i++) { + obuf[i] = (u8)xmit->buf[xmit->tail] | WD_TAG; + xmit->tail = (xmit->tail + 1) & + (UART_XMIT_SIZE - 1); + } + + /* Fail to send msg to console is not very critical */ + ret = max3110_write_then_read(max, obuf, ibuf, blen, 0); + if (ret) + pr_warning(PR_FMT "%s(): get err msg %d\n", + __func__, ret); + + for (i = 0, j = 0; i < len; i++) { + if (ibuf[i] & MAX3110_READ_DATA_AVAILABLE) + valid_str[j++] = ibuf[i] & 0xff; + } + + if (j) + receive_chars(max, valid_str, j); + + max->port.icount.tx += len; + left -= len; + } + } + + kfree(buf); +} + +static void transmit_char(struct uart_max3110 *max) +{ + struct uart_port *port = &max->port; + struct circ_buf *xmit = &port->state->xmit; + + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) + return; + + send_circ_buf(max, xmit); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (uart_circ_empty(xmit)) + serial_m3110_stop_tx(port); +} + +/* + * This will be called by uart_write() and tty_write, can't + * go to sleep + */ +static void serial_m3110_start_tx(struct uart_port *port) +{ + struct uart_max3110 *max = + container_of(port, struct uart_max3110, port); + + if (!test_and_set_bit(UART_TX_NEEDED, &max->uart_flags)) + wake_up_process(max->main_thread); +} + +static void receive_chars(struct uart_max3110 *max, unsigned char *str, int len) +{ + struct uart_port *port = &max->port; + struct tty_struct *tty; + int usable; + + /* If uart is not opened, just return */ + if (!port->state) + return; + + tty = port->state->port.tty; + if (!tty) + return; + + while (len) { + usable = tty_buffer_request_room(tty, len); + if (usable) { + tty_insert_flip_string(tty, str, usable); + str += usable; + port->icount.rx += usable; + } + len -= usable; + } + tty_flip_buffer_push(tty); +} + +/* + * This routine will be used in read_thread or RX IRQ handling, + * it will first do one round buffer read(8 words), if there is some + * valid RX data, will try to read 5 more rounds till all data + * is read out. + * + * Use stack space as data buffer to save some system load, and chose + * 504 Btyes as a threadhold to do a bulk push to upper tty layer when + * receiving bulk data, a much bigger buffer may cause stack overflow + */ +static void max3110_con_receive(struct uart_max3110 *max) +{ + int loop = 1, num, total = 0; + u8 recv_buf[512], *pbuf; + + pbuf = recv_buf; + do { + num = max3110_read_multi(max, pbuf); + + if (num) { + loop = 5; + pbuf += num; + total += num; + + if (total >= 504) { + receive_chars(max, recv_buf, total); + pbuf = recv_buf; + total = 0; + } + } + } while (--loop); + + if (total) + receive_chars(max, recv_buf, total); +} + +static int max3110_main_thread(void *_max) +{ + struct uart_max3110 *max = _max; + wait_queue_head_t *wq = &max->wq; + int ret = 0; + struct circ_buf *xmit = &max->con_xmit; + + init_waitqueue_head(wq); + pr_info(PR_FMT "start main thread\n"); + + do { + wait_event_interruptible(*wq, max->uart_flags || kthread_should_stop()); + + mutex_lock(&max->thread_mutex); + + if (test_and_clear_bit(BIT_IRQ_PENDING, &max->uart_flags)) + max3110_con_receive(max); + + /* first handle console output */ + if (test_and_clear_bit(CON_TX_NEEDED, &max->uart_flags)) + send_circ_buf(max, xmit); + + /* handle uart output */ + if (test_and_clear_bit(UART_TX_NEEDED, &max->uart_flags)) + transmit_char(max); + + mutex_unlock(&max->thread_mutex); + + } while (!kthread_should_stop()); + + return ret; +} + +static irqreturn_t serial_m3110_irq(int irq, void *dev_id) +{ + struct uart_max3110 *max = dev_id; + + /* max3110's irq is a falling edge, not level triggered, + * so no need to disable the irq */ + if (!test_and_set_bit(BIT_IRQ_PENDING, &max->uart_flags)) + wake_up_process(max->main_thread); + + return IRQ_HANDLED; +} + +/* if don't use RX IRQ, then need a thread to polling read */ +static int max3110_read_thread(void *_max) +{ + struct uart_max3110 *max = _max; + + pr_info(PR_FMT "start read thread\n"); + do { + /* + * If can't acquire the mutex, it means the main thread + * is running which will also perform the rx job + */ + if (mutex_trylock(&max->thread_mutex)) { + max3110_con_receive(max); + mutex_unlock(&max->thread_mutex); + } + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 20); + } while (!kthread_should_stop()); + + return 0; +} + +static int serial_m3110_startup(struct uart_port *port) +{ + struct uart_max3110 *max = + container_of(port, struct uart_max3110, port); + u16 config = 0; + int ret = 0; + + if (port->line != 0) { + pr_err(PR_FMT "uart port startup failed\n"); + return -1; + } + + /* Disable all IRQ and config it to 115200, 8n1 */ + config = WC_TAG | WC_FIFO_ENABLE + | WC_1_STOPBITS + | WC_8BIT_WORD + | WC_BAUD_DR2; + + /* as we use thread to handle tx/rx, need set low latency */ + port->state->port.tty->low_latency = 1; + + if (max->irq) { + max->read_thread = NULL; + ret = request_irq(max->irq, serial_m3110_irq, + IRQ_TYPE_EDGE_FALLING, "max3110", max); + if (ret) { + max->irq = 0; + pr_err(PR_FMT "unable to allocate IRQ, polling\n"); + } else { + /* Enable RX IRQ only */ + config |= WC_RXA_IRQ_ENABLE; + } + } + + if (max->irq == 0) { + /* If IRQ is disabled, start a read thread for input data */ + max->read_thread = + kthread_run(max3110_read_thread, max, "max3110_read"); + if (IS_ERR(max->read_thread)) { + ret = PTR_ERR(max->read_thread); + max->read_thread = NULL; + pr_err(PR_FMT "Can't create read thread!\n"); + return ret; + } + } + + ret = max3110_out(max, config); + if (ret) { + if (max->irq) + free_irq(max->irq, max); + if (max->read_thread) + kthread_stop(max->read_thread); + max->read_thread = NULL; + return ret; + } + + max->cur_conf = config; + return 0; +} + +static void serial_m3110_shutdown(struct uart_port *port) +{ + struct uart_max3110 *max = + container_of(port, struct uart_max3110, port); + u16 config; + + if (max->read_thread) { + kthread_stop(max->read_thread); + max->read_thread = NULL; + } + + if (max->irq) + free_irq(max->irq, max); + + /* Disable interrupts from this port */ + config = WC_TAG | WC_SW_SHDI; + max3110_out(max, config); +} + +static void serial_m3110_release_port(struct uart_port *port) +{ +} + +static int serial_m3110_request_port(struct uart_port *port) +{ + return 0; +} + +static void serial_m3110_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_MAX3100; +} + +static int +serial_m3110_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* we don't want the core code to modify any port params */ + return -EINVAL; +} + + +static const char *serial_m3110_type(struct uart_port *port) +{ + struct uart_max3110 *max = + container_of(port, struct uart_max3110, port); + return max->name; +} + +static void +serial_m3110_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_max3110 *max = + container_of(port, struct uart_max3110, port); + unsigned char cval; + unsigned int baud, parity = 0; + int clk_div = -1; + u16 new_conf = max->cur_conf; + + switch (termios->c_cflag & CSIZE) { + case CS7: + cval = UART_LCR_WLEN7; + new_conf |= WC_7BIT_WORD; + break; + default: + /* We only support CS7 & CS8 */ + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + case CS8: + cval = UART_LCR_WLEN8; + new_conf |= WC_8BIT_WORD; + break; + } + + baud = uart_get_baud_rate(port, termios, old, 0, 230400); + + /* First calc the div for 1.8MHZ clock case */ + switch (baud) { + case 300: + clk_div = WC_BAUD_DR384; + break; + case 600: + clk_div = WC_BAUD_DR192; + break; + case 1200: + clk_div = WC_BAUD_DR96; + break; + case 2400: + clk_div = WC_BAUD_DR48; + break; + case 4800: + clk_div = WC_BAUD_DR24; + break; + case 9600: + clk_div = WC_BAUD_DR12; + break; + case 19200: + clk_div = WC_BAUD_DR6; + break; + case 38400: + clk_div = WC_BAUD_DR3; + break; + case 57600: + clk_div = WC_BAUD_DR2; + break; + case 115200: + clk_div = WC_BAUD_DR1; + break; + case 230400: + if (max->clock & MAX3110_HIGH_CLK) + break; + default: + /* Pick the previous baud rate */ + baud = max->baud; + clk_div = max->cur_conf & WC_BAUD_DIV_MASK; + tty_termios_encode_baud_rate(termios, baud, baud); + } + + if (max->clock & MAX3110_HIGH_CLK) { + clk_div += 1; + /* High clk version max3110 doesn't support B300 */ + if (baud == 300) { + baud = 600; + clk_div = WC_BAUD_DR384; + } + if (baud == 230400) + clk_div = WC_BAUD_DR1; + tty_termios_encode_baud_rate(termios, baud, baud); + } + + new_conf = (new_conf & ~WC_BAUD_DIV_MASK) | clk_div; + + if (unlikely(termios->c_cflag & CMSPAR)) + termios->c_cflag &= ~CMSPAR; + + if (termios->c_cflag & CSTOPB) + new_conf |= WC_2_STOPBITS; + else + new_conf &= ~WC_2_STOPBITS; + + if (termios->c_cflag & PARENB) { + new_conf |= WC_PARITY_ENABLE; + parity |= UART_LCR_PARITY; + } else + new_conf &= ~WC_PARITY_ENABLE; + + if (!(termios->c_cflag & PARODD)) + parity |= UART_LCR_EPAR; + max->parity = parity; + + uart_update_timeout(port, termios->c_cflag, baud); + + new_conf |= WC_TAG; + if (new_conf != max->cur_conf) { + if (!max3110_out(max, new_conf)) { + max->cur_conf = new_conf; + max->baud = baud; + } + } +} + +/* Don't handle hw handshaking */ +static unsigned int serial_m3110_get_mctrl(struct uart_port *port) +{ + return TIOCM_DSR | TIOCM_CAR | TIOCM_DSR; +} + +static void serial_m3110_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static void serial_m3110_break_ctl(struct uart_port *port, int break_state) +{ +} + +static void serial_m3110_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ +} + +static void serial_m3110_enable_ms(struct uart_port *port) +{ +} + +struct uart_ops serial_m3110_ops = { + .tx_empty = serial_m3110_tx_empty, + .set_mctrl = serial_m3110_set_mctrl, + .get_mctrl = serial_m3110_get_mctrl, + .stop_tx = serial_m3110_stop_tx, + .start_tx = serial_m3110_start_tx, + .stop_rx = serial_m3110_stop_rx, + .enable_ms = serial_m3110_enable_ms, + .break_ctl = serial_m3110_break_ctl, + .startup = serial_m3110_startup, + .shutdown = serial_m3110_shutdown, + .set_termios = serial_m3110_set_termios, + .pm = serial_m3110_pm, + .type = serial_m3110_type, + .release_port = serial_m3110_release_port, + .request_port = serial_m3110_request_port, + .config_port = serial_m3110_config_port, + .verify_port = serial_m3110_verify_port, +}; + +static struct uart_driver serial_m3110_reg = { + .owner = THIS_MODULE, + .driver_name = "MRST serial", + .dev_name = "ttyS", + .major = TTY_MAJOR, + .minor = 64, + .nr = 1, + .cons = &serial_m3110_console, +}; + +#ifdef CONFIG_PM +static int serial_m3110_suspend(struct spi_device *spi, pm_message_t state) +{ + struct uart_max3110 *max = spi_get_drvdata(spi); + + disable_irq(max->irq); + uart_suspend_port(&serial_m3110_reg, &max->port); + max3110_out(max, max->cur_conf | WC_SW_SHDI); + return 0; +} + +static int serial_m3110_resume(struct spi_device *spi) +{ + struct uart_max3110 *max = spi_get_drvdata(spi); + + max3110_out(max, max->cur_conf); + uart_resume_port(&serial_m3110_reg, &max->port); + enable_irq(max->irq); + return 0; +} +#else +#define serial_m3110_suspend NULL +#define serial_m3110_resume NULL +#endif + +static int __devinit serial_m3110_probe(struct spi_device *spi) +{ + struct uart_max3110 *max; + void *buffer; + u16 res; + int ret = 0; + + max = kzalloc(sizeof(*max), GFP_KERNEL); + if (!max) + return -ENOMEM; + + /* Set spi info */ + spi->bits_per_word = 16; + max->clock = MAX3110_HIGH_CLK; + + spi_setup(spi); + + max->port.type = PORT_MAX3100; + max->port.fifosize = 2; /* Only have 16b buffer */ + max->port.ops = &serial_m3110_ops; + max->port.line = 0; + max->port.dev = &spi->dev; + max->port.uartclk = 115200; + + max->spi = spi; + strcpy(max->name, spi->modalias); + max->irq = (u16)spi->irq; + + mutex_init(&max->thread_mutex); + + max->word_7bits = 0; + max->parity = 0; + max->baud = 0; + + max->cur_conf = 0; + max->uart_flags = 0; + + /* Check if reading configuration register returns something sane */ + + res = RC_TAG; + ret = max3110_write_then_read(max, (u8 *)&res, (u8 *)&res, 2, 0); + if (ret < 0 || res == 0 || res == 0xffff) { + printk(KERN_ERR "MAX3111 deemed not present (conf reg %04x)", + res); + ret = -ENODEV; + goto err_get_page; + } + + buffer = (void *)__get_free_page(GFP_KERNEL); + if (!buffer) { + ret = -ENOMEM; + goto err_get_page; + } + max->con_xmit.buf = buffer; + max->con_xmit.head = 0; + max->con_xmit.tail = 0; + + max->main_thread = kthread_run(max3110_main_thread, + max, "max3110_main"); + if (IS_ERR(max->main_thread)) { + ret = PTR_ERR(max->main_thread); + goto err_kthread; + } + + spi_set_drvdata(spi, max); + pmax = max; + + /* Give membase a psudo value to pass serial_core's check */ + max->port.membase = (void *)0xff110000; + uart_add_one_port(&serial_m3110_reg, &max->port); + + return 0; + +err_kthread: + free_page((unsigned long)buffer); +err_get_page: + kfree(max); + return ret; +} + +static int __devexit serial_m3110_remove(struct spi_device *dev) +{ + struct uart_max3110 *max = spi_get_drvdata(dev); + + if (!max) + return 0; + + uart_remove_one_port(&serial_m3110_reg, &max->port); + + free_page((unsigned long)max->con_xmit.buf); + + if (max->main_thread) + kthread_stop(max->main_thread); + + kfree(max); + return 0; +} + +static struct spi_driver uart_max3110_driver = { + .driver = { + .name = "spi_max3111", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = serial_m3110_probe, + .remove = __devexit_p(serial_m3110_remove), + .suspend = serial_m3110_suspend, + .resume = serial_m3110_resume, +}; + +static int __init serial_m3110_init(void) +{ + int ret = 0; + + ret = uart_register_driver(&serial_m3110_reg); + if (ret) + return ret; + + ret = spi_register_driver(&uart_max3110_driver); + if (ret) + uart_unregister_driver(&serial_m3110_reg); + + return ret; +} + +static void __exit serial_m3110_exit(void) +{ + spi_unregister_driver(&uart_max3110_driver); + uart_unregister_driver(&serial_m3110_reg); +} + +module_init(serial_m3110_init); +module_exit(serial_m3110_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("max3110-uart"); diff --git a/drivers/serial/mrst_max3110.h b/drivers/serial/mrst_max3110.h new file mode 100644 index 0000000..d1ef43a --- /dev/null +++ b/drivers/serial/mrst_max3110.h @@ -0,0 +1,60 @@ +#ifndef _MRST_MAX3110_H +#define _MRST_MAX3110_H + +#define MAX3110_HIGH_CLK 0x1 /* 3.6864 MHZ */ +#define MAX3110_LOW_CLK 0x0 /* 1.8432 MHZ */ + +/* status bits for all 4 MAX3110 operate modes */ +#define MAX3110_READ_DATA_AVAILABLE (1 << 15) +#define MAX3110_WRITE_BUF_EMPTY (1 << 14) + +#define WC_TAG (3 << 14) +#define RC_TAG (1 << 14) +#define WD_TAG (2 << 14) +#define RD_TAG (0 << 14) + +/* bits def for write configuration */ +#define WC_FIFO_ENABLE_MASK (1 << 13) +#define WC_FIFO_ENABLE (0 << 13) + +#define WC_SW_SHDI (1 << 12) + +#define WC_IRQ_MASK (0xF << 8) +#define WC_TXE_IRQ_ENABLE (1 << 11) /* TX empty irq */ +#define WC_RXA_IRQ_ENABLE (1 << 10) /* RX availabe irq */ +#define WC_PAR_HIGH_IRQ_ENABLE (1 << 9) +#define WC_REC_ACT_IRQ_ENABLE (1 << 8) + +#define WC_IRDA_ENABLE (1 << 7) + +#define WC_STOPBITS_MASK (1 << 6) +#define WC_2_STOPBITS (1 << 6) +#define WC_1_STOPBITS (0 << 6) + +#define WC_PARITY_ENABLE_MASK (1 << 5) +#define WC_PARITY_ENABLE (1 << 5) + +#define WC_WORDLEN_MASK (1 << 4) +#define WC_7BIT_WORD (1 << 4) +#define WC_8BIT_WORD (0 << 4) + +#define WC_BAUD_DIV_MASK (0xF) +#define WC_BAUD_DR1 (0x0) +#define WC_BAUD_DR2 (0x1) +#define WC_BAUD_DR4 (0x2) +#define WC_BAUD_DR8 (0x3) +#define WC_BAUD_DR16 (0x4) +#define WC_BAUD_DR32 (0x5) +#define WC_BAUD_DR64 (0x6) +#define WC_BAUD_DR128 (0x7) +#define WC_BAUD_DR3 (0x8) +#define WC_BAUD_DR6 (0x9) +#define WC_BAUD_DR12 (0xA) +#define WC_BAUD_DR24 (0xB) +#define WC_BAUD_DR48 (0xC) +#define WC_BAUD_DR96 (0xD) +#define WC_BAUD_DR192 (0xE) +#define WC_BAUD_DR384 (0xF) + +#define M3110_RX_FIFO_DEPTH 8 +#endif diff --git a/drivers/serial/msm_serial.c b/drivers/serial/msm_serial.c index 698048f..8e43a7b 100644 --- a/drivers/serial/msm_serial.c +++ b/drivers/serial/msm_serial.c @@ -41,19 +41,6 @@ struct msm_port { unsigned int imr; }; -#define UART_TO_MSM(uart_port) ((struct msm_port *) uart_port) - -static inline void msm_write(struct uart_port *port, unsigned int val, - unsigned int off) -{ - __raw_writel(val, port->membase + off); -} - -static inline unsigned int msm_read(struct uart_port *port, unsigned int off) -{ - return __raw_readl(port->membase + off); -} - static void msm_stop_tx(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); @@ -88,7 +75,7 @@ static void msm_enable_ms(struct uart_port *port) static void handle_rx(struct uart_port *port) { - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; unsigned int sr; /* @@ -136,7 +123,7 @@ static void handle_rx(struct uart_port *port) static void handle_tx(struct uart_port *port) { - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; struct msm_port *msm_port = UART_TO_MSM(port); int sent_tx; @@ -169,7 +156,7 @@ static void handle_delta_cts(struct uart_port *port) { msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); port->icount.cts++; - wake_up_interruptible(&port->info->delta_msr_wait); + wake_up_interruptible(&port->state->port.delta_msr_wait); } static irqreturn_t msm_irq(int irq, void *dev_id) @@ -320,11 +307,7 @@ static void msm_init_clock(struct uart_port *port) struct msm_port *msm_port = UART_TO_MSM(port); clk_enable(msm_port->clk); - - msm_write(port, 0xC0, UART_MREG); - msm_write(port, 0xB2, UART_NREG); - msm_write(port, 0x7D, UART_DREG); - msm_write(port, 0x1C, UART_MNDREG); + msm_serial_set_mnd_regs(port); } static int msm_startup(struct uart_port *port) @@ -691,6 +674,7 @@ static int __init msm_serial_probe(struct platform_device *pdev) struct msm_port *msm_port; struct resource *resource; struct uart_port *port; + int irq; if (unlikely(pdev->id < 0 || pdev->id >= UART_NR)) return -ENXIO; @@ -702,18 +686,21 @@ static int __init msm_serial_probe(struct platform_device *pdev) msm_port = UART_TO_MSM(port); msm_port->clk = clk_get(&pdev->dev, "uart_clk"); - if (unlikely(IS_ERR(msm_port->clk))) + if (IS_ERR(msm_port->clk)) return PTR_ERR(msm_port->clk); port->uartclk = clk_get_rate(msm_port->clk); + printk(KERN_INFO "uartclk = %d\n", port->uartclk); + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (unlikely(!resource)) return -ENXIO; port->mapbase = resource->start; - port->irq = platform_get_irq(pdev, 0); - if (unlikely(port->irq < 0)) + irq = platform_get_irq(pdev, 0); + if (unlikely(irq < 0)) return -ENXIO; + port->irq = irq; platform_set_drvdata(pdev, port); @@ -730,7 +717,6 @@ static int __devexit msm_serial_remove(struct platform_device *pdev) } static struct platform_driver msm_platform_driver = { - .probe = msm_serial_probe, .remove = msm_serial_remove, .driver = { .name = "msm_serial", diff --git a/drivers/serial/msm_serial.h b/drivers/serial/msm_serial.h index 689f1fa..f6ca9ca 100644 --- a/drivers/serial/msm_serial.h +++ b/drivers/serial/msm_serial.h @@ -114,4 +114,60 @@ #define UART_MISR 0x0010 #define UART_ISR 0x0014 +#define UART_TO_MSM(uart_port) ((struct msm_port *) uart_port) + +static inline +void msm_write(struct uart_port *port, unsigned int val, unsigned int off) +{ + __raw_writel(val, port->membase + off); +} + +static inline +unsigned int msm_read(struct uart_port *port, unsigned int off) +{ + return __raw_readl(port->membase + off); +} + +/* + * Setup the MND registers to use the TCXO clock. + */ +static inline void msm_serial_set_mnd_regs_tcxo(struct uart_port *port) +{ + msm_write(port, 0x06, UART_MREG); + msm_write(port, 0xF1, UART_NREG); + msm_write(port, 0x0F, UART_DREG); + msm_write(port, 0x1A, UART_MNDREG); +} + +/* + * Setup the MND registers to use the TCXO clock divided by 4. + */ +static inline void msm_serial_set_mnd_regs_tcxoby4(struct uart_port *port) +{ + msm_write(port, 0x18, UART_MREG); + msm_write(port, 0xF6, UART_NREG); + msm_write(port, 0x0F, UART_DREG); + msm_write(port, 0x0A, UART_MNDREG); +} + +static inline +void msm_serial_set_mnd_regs_from_uartclk(struct uart_port *port) +{ + if (port->uartclk == 19200000) + msm_serial_set_mnd_regs_tcxo(port); + else + msm_serial_set_mnd_regs_tcxoby4(port); +} + +/* + * TROUT has a specific defect that makes it report it's uartclk + * as 19.2Mhz (TCXO) when it's actually 4.8Mhz (TCXO/4). This special + * cases TROUT to use the right clock. + */ +#ifdef CONFIG_MACH_TROUT +#define msm_serial_set_mnd_regs msm_serial_set_mnd_regs_tcxoby4 +#else +#define msm_serial_set_mnd_regs msm_serial_set_mnd_regs_from_uartclk +#endif + #endif /* __DRIVERS_SERIAL_MSM_SERIAL_H */ diff --git a/drivers/serial/mux.c b/drivers/serial/mux.c index 953a5ff..9711e06 100644 --- a/drivers/serial/mux.c +++ b/drivers/serial/mux.c @@ -22,7 +22,6 @@ #include <linux/init.h> #include <linux/serial.h> #include <linux/console.h> -#include <linux/slab.h> #include <linux/delay.h> /* for udelay */ #include <linux/device.h> #include <asm/io.h> @@ -199,7 +198,7 @@ static void mux_break_ctl(struct uart_port *port, int break_state) static void mux_write(struct uart_port *port) { int count; - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; if(port->x_char) { UART_PUT_CHAR(port, port->x_char); @@ -243,7 +242,7 @@ static void mux_write(struct uart_port *port) static void mux_read(struct uart_port *port) { int data; - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; __u32 start_count = port->icount.rx; while(1) { diff --git a/drivers/serial/netx-serial.c b/drivers/serial/netx-serial.c index 3e5dda8..7735c9f 100644 --- a/drivers/serial/netx-serial.c +++ b/drivers/serial/netx-serial.c @@ -140,7 +140,7 @@ static void netx_enable_ms(struct uart_port *port) static inline void netx_transmit_buffer(struct uart_port *port) { - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; if (port->x_char) { writel(port->x_char, port->membase + UART_DR); @@ -185,7 +185,7 @@ static unsigned int netx_tx_empty(struct uart_port *port) static void netx_txint(struct uart_port *port) { - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { netx_stop_tx(port); @@ -201,7 +201,7 @@ static void netx_txint(struct uart_port *port) static void netx_rxint(struct uart_port *port) { unsigned char rx, flg, status; - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; while (!(readl(port->membase + UART_FR) & FR_RXFE)) { rx = readl(port->membase + UART_DR); diff --git a/drivers/serial/nwpserial.c b/drivers/serial/nwpserial.c index 9e150b1..de17367 100644 --- a/drivers/serial/nwpserial.c +++ b/drivers/serial/nwpserial.c @@ -81,7 +81,7 @@ nwpserial_console_write(struct console *co, const char *s, unsigned int count) uart_console_write(&up->port, s, count, nwpserial_console_putchar); - /* wait for transmitter to become emtpy */ + /* wait for transmitter to become empty */ while ((dcr_read(up->dcr_host, UART_LSR) & UART_LSR_THRE) == 0) cpu_relax(); @@ -126,7 +126,7 @@ static void nwpserial_config_port(struct uart_port *port, int flags) static irqreturn_t nwpserial_interrupt(int irq, void *dev_id) { struct nwpserial_port *up = dev_id; - struct tty_struct *tty = up->port.info->port.tty; + struct tty_struct *tty = up->port.state->port.tty; irqreturn_t ret; unsigned int iir; unsigned char ch; @@ -261,7 +261,7 @@ static void nwpserial_start_tx(struct uart_port *port) struct nwpserial_port *up; struct circ_buf *xmit; up = container_of(port, struct nwpserial_port, port); - xmit = &up->port.info->xmit; + xmit = &up->port.state->xmit; if (port->x_char) { nwpserial_putchar(up, up->port.x_char); @@ -344,7 +344,7 @@ int nwpserial_register_port(struct uart_port *port) mutex_lock(&nwpserial_mutex); - dn = to_of_device(port->dev)->node; + dn = port->dev->of_node; if (dn == NULL) goto out; diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c index 02406ba..5c7abe4 100644 --- a/drivers/serial/of_serial.c +++ b/drivers/serial/of_serial.c @@ -11,13 +11,14 @@ */ #include <linux/init.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/serial_core.h> #include <linux/serial_8250.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/nwpserial.h> -#include <asm/prom.h> - struct of_serial_info { int type; int line; @@ -26,13 +27,13 @@ struct of_serial_info { /* * Fill a struct uart_port for a given device node */ -static int __devinit of_platform_serial_setup(struct of_device *ofdev, +static int __devinit of_platform_serial_setup(struct platform_device *ofdev, int type, struct uart_port *port) { struct resource resource; - struct device_node *np = ofdev->node; - const unsigned int *clk, *spd; - const u32 *prop; + struct device_node *np = ofdev->dev.of_node; + const __be32 *clk, *spd; + const __be32 *prop; int ret, prop_size; memset(port, 0, sizeof *port); @@ -55,23 +56,23 @@ static int __devinit of_platform_serial_setup(struct of_device *ofdev, /* Check for shifted address mapping */ prop = of_get_property(np, "reg-offset", &prop_size); if (prop && (prop_size == sizeof(u32))) - port->mapbase += *prop; + port->mapbase += be32_to_cpup(prop); /* Check for registers offset within the devices address range */ prop = of_get_property(np, "reg-shift", &prop_size); if (prop && (prop_size == sizeof(u32))) - port->regshift = *prop; + port->regshift = be32_to_cpup(prop); port->irq = irq_of_parse_and_map(np, 0); port->iotype = UPIO_MEM; port->type = type; - port->uartclk = *clk; + port->uartclk = be32_to_cpup(clk); port->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_IOREMAP | UPF_FIXED_PORT | UPF_FIXED_TYPE; port->dev = &ofdev->dev; /* If current-speed was set, then try not to change it. */ if (spd) - port->custom_divisor = *clk / (16 * (*spd)); + port->custom_divisor = be32_to_cpup(clk) / (16 * (be32_to_cpup(spd))); return 0; } @@ -79,7 +80,7 @@ static int __devinit of_platform_serial_setup(struct of_device *ofdev, /* * Try to register a serial port */ -static int __devinit of_platform_serial_probe(struct of_device *ofdev, +static int __devinit of_platform_serial_probe(struct platform_device *ofdev, const struct of_device_id *id) { struct of_serial_info *info; @@ -87,7 +88,7 @@ static int __devinit of_platform_serial_probe(struct of_device *ofdev, int port_type; int ret; - if (of_find_property(ofdev->node, "used-by-rtas", NULL)) + if (of_find_property(ofdev->dev.of_node, "used-by-rtas", NULL)) return -EBUSY; info = kmalloc(sizeof(*info), GFP_KERNEL); @@ -133,7 +134,7 @@ out: /* * Release a line */ -static int of_platform_serial_remove(struct of_device *ofdev) +static int of_platform_serial_remove(struct platform_device *ofdev) { struct of_serial_info *info = dev_get_drvdata(&ofdev->dev); switch (info->type) { @@ -161,6 +162,7 @@ static int of_platform_serial_remove(struct of_device *ofdev) static struct of_device_id __devinitdata of_platform_serial_table[] = { { .type = "serial", .compatible = "ns8250", .data = (void *)PORT_8250, }, { .type = "serial", .compatible = "ns16450", .data = (void *)PORT_16450, }, + { .type = "serial", .compatible = "ns16550a", .data = (void *)PORT_16550A, }, { .type = "serial", .compatible = "ns16550", .data = (void *)PORT_16550, }, { .type = "serial", .compatible = "ns16750", .data = (void *)PORT_16750, }, { .type = "serial", .compatible = "ns16850", .data = (void *)PORT_16850, }, @@ -173,11 +175,13 @@ static struct of_device_id __devinitdata of_platform_serial_table[] = { }; static struct of_platform_driver of_platform_serial_driver = { - .owner = THIS_MODULE, - .name = "of_serial", + .driver = { + .name = "of_serial", + .owner = THIS_MODULE, + .of_match_table = of_platform_serial_table, + }, .probe = of_platform_serial_probe, .remove = of_platform_serial_remove, - .match_table = of_platform_serial_table, }; static int __init of_platform_serial_init(void) diff --git a/drivers/serial/omap-serial.c b/drivers/serial/omap-serial.c new file mode 100644 index 0000000..7f2f010 --- /dev/null +++ b/drivers/serial/omap-serial.c @@ -0,0 +1,1359 @@ +/* + * Driver for OMAP-UART controller. + * Based on drivers/serial/8250.c + * + * Copyright (C) 2010 Texas Instruments. + * + * Authors: + * Govindraj R <govindraj.raja@ti.com> + * Thara Gopinath <thara@ti.com> + * + * 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. + * + * Note: This driver is made seperate from 8250 driver as we cannot + * over load 8250 driver with omap platform specific configuration for + * features like DMA, it makes easier to implement features like DMA and + * hardware flow control and software flow control configuration with + * this driver as required for the omap-platform. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/serial_reg.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> +#include <linux/serial_core.h> +#include <linux/irq.h> + +#include <plat/dma.h> +#include <plat/dmtimer.h> +#include <plat/omap-serial.h> + +static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS]; + +/* Forward declaration of functions */ +static void uart_tx_dma_callback(int lch, u16 ch_status, void *data); +static void serial_omap_rx_timeout(unsigned long uart_no); +static int serial_omap_start_rxdma(struct uart_omap_port *up); + +static inline unsigned int serial_in(struct uart_omap_port *up, int offset) +{ + offset <<= up->port.regshift; + return readw(up->port.membase + offset); +} + +static inline void serial_out(struct uart_omap_port *up, int offset, int value) +{ + offset <<= up->port.regshift; + writew(value, up->port.membase + offset); +} + +static inline void serial_omap_clear_fifos(struct uart_omap_port *up) +{ + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); +} + +/* + * serial_omap_get_divisor - calculate divisor value + * @port: uart port info + * @baud: baudrate for which divisor needs to be calculated. + * + * We have written our own function to get the divisor so as to support + * 13x mode. 3Mbps Baudrate as an different divisor. + * Reference OMAP TRM Chapter 17: + * Table 17-1. UART Mode Baud Rates, Divisor Values, and Error Rates + * referring to oversampling - divisor value + * baudrate 460,800 to 3,686,400 all have divisor 13 + * except 3,000,000 which has divisor value 16 + */ +static unsigned int +serial_omap_get_divisor(struct uart_port *port, unsigned int baud) +{ + unsigned int divisor; + + if (baud > OMAP_MODE13X_SPEED && baud != 3000000) + divisor = 13; + else + divisor = 16; + return port->uartclk/(baud * divisor); +} + +static void serial_omap_stop_rxdma(struct uart_omap_port *up) +{ + if (up->uart_dma.rx_dma_used) { + del_timer(&up->uart_dma.rx_timer); + omap_stop_dma(up->uart_dma.rx_dma_channel); + omap_free_dma(up->uart_dma.rx_dma_channel); + up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; + up->uart_dma.rx_dma_used = false; + } +} + +static void serial_omap_enable_ms(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + dev_dbg(up->port.dev, "serial_omap_enable_ms+%d\n", up->pdev->id); + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); +} + +static void serial_omap_stop_tx(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + if (up->use_dma && + up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) { + /* + * Check if dma is still active. If yes do nothing, + * return. Else stop dma + */ + if (omap_get_dma_active_status(up->uart_dma.tx_dma_channel)) + return; + omap_stop_dma(up->uart_dma.tx_dma_channel); + omap_free_dma(up->uart_dma.tx_dma_channel); + up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; + } + + if (up->ier & UART_IER_THRI) { + up->ier &= ~UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void serial_omap_stop_rx(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + if (up->use_dma) + serial_omap_stop_rxdma(up); + up->ier &= ~UART_IER_RLSI; + up->port.read_status_mask &= ~UART_LSR_DR; + serial_out(up, UART_IER, up->ier); +} + +static inline void receive_chars(struct uart_omap_port *up, int *status) +{ + struct tty_struct *tty = up->port.state->port.tty; + unsigned int flag; + unsigned char ch, lsr = *status; + int max_count = 256; + + do { + if (likely(lsr & UART_LSR_DR)) + ch = serial_in(up, UART_RX); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) { + /* + * For statistics only + */ + if (lsr & UART_LSR_BI) { + lsr &= ~(UART_LSR_FE | UART_LSR_PE); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (lsr & UART_LSR_PE) { + up->port.icount.parity++; + } else if (lsr & UART_LSR_FE) { + up->port.icount.frame++; + } + + if (lsr & UART_LSR_OE) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ignored. + */ + lsr &= up->port.read_status_mask; + +#ifdef CONFIG_SERIAL_OMAP_CONSOLE + if (up->port.line == up->port.cons->index) { + /* Recover the break flag from console xmit */ + lsr |= up->lsr_break_flag; + up->lsr_break_flag = 0; + } +#endif + if (lsr & UART_LSR_BI) + flag = TTY_BREAK; + else if (lsr & UART_LSR_PE) + flag = TTY_PARITY; + else if (lsr & UART_LSR_FE) + flag = TTY_FRAME; + } + + if (uart_handle_sysrq_char(&up->port, ch)) + goto ignore_char; + uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag); +ignore_char: + lsr = serial_in(up, UART_LSR); + } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0)); + spin_unlock(&up->port.lock); + tty_flip_buffer_push(tty); + spin_lock(&up->port.lock); +} + +static void transmit_chars(struct uart_omap_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + int count; + + if (up->port.x_char) { + serial_out(up, UART_TX, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + serial_omap_stop_tx(&up->port); + return; + } + count = up->port.fifosize / 4; + do { + serial_out(up, UART_TX, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) + serial_omap_stop_tx(&up->port); +} + +static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up) +{ + if (!(up->ier & UART_IER_THRI)) { + up->ier |= UART_IER_THRI; + serial_out(up, UART_IER, up->ier); + } +} + +static void serial_omap_start_tx(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + struct circ_buf *xmit; + unsigned int start; + int ret = 0; + + if (!up->use_dma) { + serial_omap_enable_ier_thri(up); + return; + } + + if (up->uart_dma.tx_dma_used) + return; + + xmit = &up->port.state->xmit; + + if (up->uart_dma.tx_dma_channel == OMAP_UART_DMA_CH_FREE) { + ret = omap_request_dma(up->uart_dma.uart_dma_tx, + "UART Tx DMA", + (void *)uart_tx_dma_callback, up, + &(up->uart_dma.tx_dma_channel)); + + if (ret < 0) { + serial_omap_enable_ier_thri(up); + return; + } + } + spin_lock(&(up->uart_dma.tx_lock)); + up->uart_dma.tx_dma_used = true; + spin_unlock(&(up->uart_dma.tx_lock)); + + start = up->uart_dma.tx_buf_dma_phys + + (xmit->tail & (UART_XMIT_SIZE - 1)); + + up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit); + /* + * It is a circular buffer. See if the buffer has wounded back. + * If yes it will have to be transferred in two separate dma + * transfers + */ + if (start + up->uart_dma.tx_buf_size >= + up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) + up->uart_dma.tx_buf_size = + (up->uart_dma.tx_buf_dma_phys + + UART_XMIT_SIZE) - start; + + omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + up->uart_dma.uart_base, 0, 0); + omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_POST_INC, start, 0, 0); + omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel, + OMAP_DMA_DATA_TYPE_S8, + up->uart_dma.tx_buf_size, 1, + OMAP_DMA_SYNC_ELEMENT, + up->uart_dma.uart_dma_tx, 0); + /* FIXME: Cache maintenance needed here? */ + omap_start_dma(up->uart_dma.tx_dma_channel); +} + +static unsigned int check_modem_status(struct uart_omap_port *up) +{ + unsigned int status; + + status = serial_in(up, UART_MSR); + status |= up->msr_saved_flags; + up->msr_saved_flags = 0; + if ((status & UART_MSR_ANY_DELTA) == 0) + return status; + + if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI && + up->port.state != NULL) { + if (status & UART_MSR_TERI) + up->port.icount.rng++; + if (status & UART_MSR_DDSR) + up->port.icount.dsr++; + if (status & UART_MSR_DDCD) + uart_handle_dcd_change + (&up->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + uart_handle_cts_change + (&up->port, status & UART_MSR_CTS); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); + } + + return status; +} + +/** + * serial_omap_irq() - This handles the interrupt from one port + * @irq: uart port irq number + * @dev_id: uart port info + */ +static inline irqreturn_t serial_omap_irq(int irq, void *dev_id) +{ + struct uart_omap_port *up = dev_id; + unsigned int iir, lsr; + unsigned long flags; + + iir = serial_in(up, UART_IIR); + if (iir & UART_IIR_NO_INT) + return IRQ_NONE; + + spin_lock_irqsave(&up->port.lock, flags); + lsr = serial_in(up, UART_LSR); + if (iir & UART_IIR_RLSI) { + if (!up->use_dma) { + if (lsr & UART_LSR_DR) + receive_chars(up, &lsr); + } else { + up->ier &= ~(UART_IER_RDI | UART_IER_RLSI); + serial_out(up, UART_IER, up->ier); + if ((serial_omap_start_rxdma(up) != 0) && + (lsr & UART_LSR_DR)) + receive_chars(up, &lsr); + } + } + + check_modem_status(up); + if ((lsr & UART_LSR_THRE) && (iir & UART_IIR_THRI)) + transmit_chars(up); + + spin_unlock_irqrestore(&up->port.lock, flags); + up->port_activity = jiffies; + return IRQ_HANDLED; +} + +static unsigned int serial_omap_tx_empty(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + unsigned int ret = 0; + + dev_dbg(up->port.dev, "serial_omap_tx_empty+%d\n", up->pdev->id); + spin_lock_irqsave(&up->port.lock, flags); + ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int serial_omap_get_mctrl(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char status; + unsigned int ret = 0; + + status = check_modem_status(up); + dev_dbg(up->port.dev, "serial_omap_get_mctrl+%d\n", up->pdev->id); + + if (status & UART_MSR_DCD) + ret |= TIOCM_CAR; + if (status & UART_MSR_RI) + ret |= TIOCM_RNG; + if (status & UART_MSR_DSR) + ret |= TIOCM_DSR; + if (status & UART_MSR_CTS) + ret |= TIOCM_CTS; + return ret; +} + +static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char mcr = 0; + + dev_dbg(up->port.dev, "serial_omap_set_mctrl+%d\n", up->pdev->id); + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + mcr |= up->mcr; + serial_out(up, UART_MCR, mcr); +} + +static void serial_omap_break_ctl(struct uart_port *port, int break_state) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + + dev_dbg(up->port.dev, "serial_omap_break_ctl+%d\n", up->pdev->id); + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + up->lcr |= UART_LCR_SBC; + else + up->lcr &= ~UART_LCR_SBC; + serial_out(up, UART_LCR, up->lcr); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static int serial_omap_startup(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + int retval; + + /* + * Allocate the IRQ + */ + retval = request_irq(up->port.irq, serial_omap_irq, up->port.irqflags, + up->name, up); + if (retval) + return retval; + + dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->pdev->id); + + /* + * Clear the FIFO buffers and disable them. + * (they will be reenabled in set_termios()) + */ + serial_omap_clear_fifos(up); + /* For Hardware flow control */ + serial_out(up, UART_MCR, UART_MCR_RTS); + + /* + * Clear the interrupt registers. + */ + (void) serial_in(up, UART_LSR); + if (serial_in(up, UART_LSR) & UART_LSR_DR) + (void) serial_in(up, UART_RX); + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + + /* + * Now, initialize the UART + */ + serial_out(up, UART_LCR, UART_LCR_WLEN8); + spin_lock_irqsave(&up->port.lock, flags); + /* + * Most PC uarts need OUT2 raised to enable interrupts. + */ + up->port.mctrl |= TIOCM_OUT2; + serial_omap_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + up->msr_saved_flags = 0; + if (up->use_dma) { + free_page((unsigned long)up->port.state->xmit.buf); + up->port.state->xmit.buf = dma_alloc_coherent(NULL, + UART_XMIT_SIZE, + (dma_addr_t *)&(up->uart_dma.tx_buf_dma_phys), + 0); + init_timer(&(up->uart_dma.rx_timer)); + up->uart_dma.rx_timer.function = serial_omap_rx_timeout; + up->uart_dma.rx_timer.data = up->pdev->id; + /* Currently the buffer size is 4KB. Can increase it */ + up->uart_dma.rx_buf = dma_alloc_coherent(NULL, + up->uart_dma.rx_buf_size, + (dma_addr_t *)&(up->uart_dma.rx_buf_dma_phys), 0); + } + /* + * Finally, enable interrupts. Note: Modem status interrupts + * are set via set_termios(), which will be occurring imminently + * anyway, so we don't enable them here. + */ + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); + + up->port_activity = jiffies; + return 0; +} + +static void serial_omap_shutdown(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned long flags = 0; + + dev_dbg(up->port.dev, "serial_omap_shutdown+%d\n", up->pdev->id); + /* + * Disable interrupts from this port + */ + up->ier = 0; + serial_out(up, UART_IER, 0); + + spin_lock_irqsave(&up->port.lock, flags); + up->port.mctrl &= ~TIOCM_OUT2; + serial_omap_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition and FIFOs + */ + serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); + serial_omap_clear_fifos(up); + + /* + * Read data port to reset things, and then free the irq + */ + if (serial_in(up, UART_LSR) & UART_LSR_DR) + (void) serial_in(up, UART_RX); + if (up->use_dma) { + dma_free_coherent(up->port.dev, + UART_XMIT_SIZE, up->port.state->xmit.buf, + up->uart_dma.tx_buf_dma_phys); + up->port.state->xmit.buf = NULL; + serial_omap_stop_rx(port); + dma_free_coherent(up->port.dev, + up->uart_dma.rx_buf_size, up->uart_dma.rx_buf, + up->uart_dma.rx_buf_dma_phys); + up->uart_dma.rx_buf = NULL; + } + free_irq(up->port.irq, up); +} + +static inline void +serial_omap_configure_xonxoff + (struct uart_omap_port *up, struct ktermios *termios) +{ + unsigned char efr = 0; + + up->lcr = serial_in(up, UART_LCR); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr & ~UART_EFR_ECB); + + serial_out(up, UART_XON1, termios->c_cc[VSTART]); + serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]); + + /* clear SW control mode bits */ + efr = up->efr; + efr &= OMAP_UART_SW_CLR; + + /* + * IXON Flag: + * Enable XON/XOFF flow control on output. + * Transmit XON1, XOFF1 + */ + if (termios->c_iflag & IXON) + efr |= OMAP_UART_SW_TX; + + /* + * IXOFF Flag: + * Enable XON/XOFF flow control on input. + * Receiver compares XON1, XOFF1. + */ + if (termios->c_iflag & IXOFF) + efr |= OMAP_UART_SW_RX; + + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + + up->mcr = serial_in(up, UART_MCR); + + /* + * IXANY Flag: + * Enable any character to restart output. + * Operation resumes after receiving any + * character after recognition of the XOFF character + */ + if (termios->c_iflag & IXANY) + up->mcr |= UART_MCR_XONANY; + + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); + /* Enable special char function UARTi.EFR_REG[5] and + * load the new software flow control mode IXON or IXOFF + * and restore the UARTi.EFR_REG[4] ENHANCED_EN value. + */ + serial_out(up, UART_EFR, efr | UART_EFR_SCD); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + + serial_out(up, UART_MCR, up->mcr & ~UART_MCR_TCRTLR); + serial_out(up, UART_LCR, up->lcr); +} + +static void +serial_omap_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char cval = 0; + unsigned char efr = 0; + unsigned long flags = 0; + unsigned int baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = UART_LCR_WLEN5; + break; + case CS6: + cval = UART_LCR_WLEN6; + break; + case CS7: + cval = UART_LCR_WLEN7; + break; + default: + case CS8: + cval = UART_LCR_WLEN8; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= UART_LCR_STOP; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + + /* + * Ask the core to calculate the divisor for us. + */ + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/13); + quot = serial_omap_get_divisor(port, baud); + + up->fcr = UART_FCR_R_TRIG_01 | UART_FCR_T_TRIG_01 | + UART_FCR_ENABLE_FIFO; + if (up->use_dma) + up->fcr |= UART_FCR_DMA_SELECT; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= UART_LSR_BI; + + /* + * Characters to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= UART_LSR_BI; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= UART_LSR_OE; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= UART_LSR_DR; + + /* + * Modem status interrupts + */ + up->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&up->port, termios->c_cflag)) + up->ier |= UART_IER_MSI; + serial_out(up, UART_IER, up->ier); + serial_out(up, UART_LCR, cval); /* reset DLAB */ + + /* FIFOs and DMA Settings */ + + /* FCR can be changed only when the + * baud clock is not running + * DLL_REG and DLH_REG set to 0. + */ + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_out(up, UART_DLL, 0); + serial_out(up, UART_DLM, 0); + serial_out(up, UART_LCR, 0); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + up->mcr = serial_in(up, UART_MCR); + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); + /* FIFO ENABLE, DMA MODE */ + serial_out(up, UART_FCR, up->fcr); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + if (up->use_dma) { + serial_out(up, UART_TI752_TLR, 0); + serial_out(up, UART_OMAP_SCR, + (UART_FCR_TRIGGER_4 | UART_FCR_TRIGGER_8)); + } + + serial_out(up, UART_EFR, up->efr); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_out(up, UART_MCR, up->mcr); + + /* Protocol, Baud Rate, and Interrupt Settings */ + + serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_DISABLE); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + + serial_out(up, UART_LCR, 0); + serial_out(up, UART_IER, 0); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ + + serial_out(up, UART_LCR, 0); + serial_out(up, UART_IER, up->ier); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + + serial_out(up, UART_EFR, up->efr); + serial_out(up, UART_LCR, cval); + + if (baud > 230400 && baud != 3000000) + serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_13X_MODE); + else + serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_16X_MODE); + + /* Hardware Flow Control Configuration */ + + if (termios->c_cflag & CRTSCTS) { + efr |= (UART_EFR_CTS | UART_EFR_RTS); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + + up->mcr = serial_in(up, UART_MCR); + serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR); + + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + up->efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, up->efr | UART_EFR_ECB); + + serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG); + serial_out(up, UART_EFR, efr); /* Enable AUTORTS and AUTOCTS */ + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A); + serial_out(up, UART_MCR, up->mcr | UART_MCR_RTS); + serial_out(up, UART_LCR, cval); + } + + serial_omap_set_mctrl(&up->port, up->port.mctrl); + /* Software Flow Control Configuration */ + if (termios->c_iflag & (IXON | IXOFF)) + serial_omap_configure_xonxoff(up, termios); + + spin_unlock_irqrestore(&up->port.lock, flags); + dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->pdev->id); +} + +static void +serial_omap_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned char efr; + + dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->pdev->id); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + efr = serial_in(up, UART_EFR); + serial_out(up, UART_EFR, efr | UART_EFR_ECB); + serial_out(up, UART_LCR, 0); + + serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + serial_out(up, UART_EFR, efr); + serial_out(up, UART_LCR, 0); + /* Enable module level wake up */ + serial_out(up, UART_OMAP_WER, + (state != 0) ? OMAP_UART_WER_MOD_WKUP : 0); +} + +static void serial_omap_release_port(struct uart_port *port) +{ + dev_dbg(port->dev, "serial_omap_release_port+\n"); +} + +static int serial_omap_request_port(struct uart_port *port) +{ + dev_dbg(port->dev, "serial_omap_request_port+\n"); + return 0; +} + +static void serial_omap_config_port(struct uart_port *port, int flags) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + dev_dbg(up->port.dev, "serial_omap_config_port+%d\n", + up->pdev->id); + up->port.type = PORT_OMAP; +} + +static int +serial_omap_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + /* we don't want the core code to modify any port params */ + dev_dbg(port->dev, "serial_omap_verify_port+\n"); + return -EINVAL; +} + +static const char * +serial_omap_type(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + dev_dbg(up->port.dev, "serial_omap_type+%d\n", up->pdev->id); + return up->name; +} + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static inline void wait_for_xmitr(struct uart_omap_port *up) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = serial_in(up, UART_LSR); + + if (status & UART_LSR_BI) + up->lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + udelay(1); + } while ((status & BOTH_EMPTY) != BOTH_EMPTY); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + for (tmout = 1000000; tmout; tmout--) { + unsigned int msr = serial_in(up, UART_MSR); + + up->msr_saved_flags |= msr & MSR_SAVE_FLAGS; + if (msr & UART_MSR_CTS) + break; + + udelay(1); + } + } +} + +#ifdef CONFIG_CONSOLE_POLL + +static void serial_omap_poll_put_char(struct uart_port *port, unsigned char ch) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + wait_for_xmitr(up); + serial_out(up, UART_TX, ch); +} + +static int serial_omap_poll_get_char(struct uart_port *port) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + unsigned int status = serial_in(up, UART_LSR); + + if (!(status & UART_LSR_DR)) + return NO_POLL_CHAR; + + return serial_in(up, UART_RX); +} + +#endif /* CONFIG_CONSOLE_POLL */ + +#ifdef CONFIG_SERIAL_OMAP_CONSOLE + +static struct uart_omap_port *serial_omap_console_ports[4]; + +static struct uart_driver serial_omap_reg; + +static void serial_omap_console_putchar(struct uart_port *port, int ch) +{ + struct uart_omap_port *up = (struct uart_omap_port *)port; + + wait_for_xmitr(up); + serial_out(up, UART_TX, ch); +} + +static void +serial_omap_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_omap_port *up = serial_omap_console_ports[co->index]; + unsigned long flags; + unsigned int ier; + int locked = 1; + + local_irq_save(flags); + if (up->port.sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&up->port.lock); + else + spin_lock(&up->port.lock); + + /* + * First save the IER then disable the interrupts + */ + ier = serial_in(up, UART_IER); + serial_out(up, UART_IER, 0); + + uart_console_write(&up->port, s, count, serial_omap_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + serial_out(up, UART_IER, ier); + /* + * The receive handling will happen properly because the + * receive ready bit will still be set; it is not cleared + * on read. However, modem control will not, we must + * call it if we have saved something in the saved flags + * while processing with interrupts off. + */ + if (up->msr_saved_flags) + check_modem_status(up); + + if (locked) + spin_unlock(&up->port.lock); + local_irq_restore(flags); +} + +static int __init +serial_omap_console_setup(struct console *co, char *options) +{ + struct uart_omap_port *up; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (serial_omap_console_ports[co->index] == NULL) + return -ENODEV; + up = serial_omap_console_ports[co->index]; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&up->port, co, baud, parity, bits, flow); +} + +static struct console serial_omap_console = { + .name = OMAP_SERIAL_NAME, + .write = serial_omap_console_write, + .device = uart_console_device, + .setup = serial_omap_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &serial_omap_reg, +}; + +static void serial_omap_add_console_port(struct uart_omap_port *up) +{ + serial_omap_console_ports[up->pdev->id] = up; +} + +#define OMAP_CONSOLE (&serial_omap_console) + +#else + +#define OMAP_CONSOLE NULL + +static inline void serial_omap_add_console_port(struct uart_omap_port *up) +{} + +#endif + +static struct uart_ops serial_omap_pops = { + .tx_empty = serial_omap_tx_empty, + .set_mctrl = serial_omap_set_mctrl, + .get_mctrl = serial_omap_get_mctrl, + .stop_tx = serial_omap_stop_tx, + .start_tx = serial_omap_start_tx, + .stop_rx = serial_omap_stop_rx, + .enable_ms = serial_omap_enable_ms, + .break_ctl = serial_omap_break_ctl, + .startup = serial_omap_startup, + .shutdown = serial_omap_shutdown, + .set_termios = serial_omap_set_termios, + .pm = serial_omap_pm, + .type = serial_omap_type, + .release_port = serial_omap_release_port, + .request_port = serial_omap_request_port, + .config_port = serial_omap_config_port, + .verify_port = serial_omap_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_put_char = serial_omap_poll_put_char, + .poll_get_char = serial_omap_poll_get_char, +#endif +}; + +static struct uart_driver serial_omap_reg = { + .owner = THIS_MODULE, + .driver_name = "OMAP-SERIAL", + .dev_name = OMAP_SERIAL_NAME, + .nr = OMAP_MAX_HSUART_PORTS, + .cons = OMAP_CONSOLE, +}; + +static int +serial_omap_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct uart_omap_port *up = platform_get_drvdata(pdev); + + if (up) + uart_suspend_port(&serial_omap_reg, &up->port); + return 0; +} + +static int serial_omap_resume(struct platform_device *dev) +{ + struct uart_omap_port *up = platform_get_drvdata(dev); + + if (up) + uart_resume_port(&serial_omap_reg, &up->port); + return 0; +} + +static void serial_omap_rx_timeout(unsigned long uart_no) +{ + struct uart_omap_port *up = ui[uart_no]; + unsigned int curr_dma_pos, curr_transmitted_size; + int ret = 0; + + curr_dma_pos = omap_get_dma_dst_pos(up->uart_dma.rx_dma_channel); + if ((curr_dma_pos == up->uart_dma.prev_rx_dma_pos) || + (curr_dma_pos == 0)) { + if (jiffies_to_msecs(jiffies - up->port_activity) < + RX_TIMEOUT) { + mod_timer(&up->uart_dma.rx_timer, jiffies + + usecs_to_jiffies(up->uart_dma.rx_timeout)); + } else { + serial_omap_stop_rxdma(up); + up->ier |= (UART_IER_RDI | UART_IER_RLSI); + serial_out(up, UART_IER, up->ier); + } + return; + } + + curr_transmitted_size = curr_dma_pos - + up->uart_dma.prev_rx_dma_pos; + up->port.icount.rx += curr_transmitted_size; + tty_insert_flip_string(up->port.state->port.tty, + up->uart_dma.rx_buf + + (up->uart_dma.prev_rx_dma_pos - + up->uart_dma.rx_buf_dma_phys), + curr_transmitted_size); + tty_flip_buffer_push(up->port.state->port.tty); + up->uart_dma.prev_rx_dma_pos = curr_dma_pos; + if (up->uart_dma.rx_buf_size + + up->uart_dma.rx_buf_dma_phys == curr_dma_pos) { + ret = serial_omap_start_rxdma(up); + if (ret < 0) { + serial_omap_stop_rxdma(up); + up->ier |= (UART_IER_RDI | UART_IER_RLSI); + serial_out(up, UART_IER, up->ier); + } + } else { + mod_timer(&up->uart_dma.rx_timer, jiffies + + usecs_to_jiffies(up->uart_dma.rx_timeout)); + } + up->port_activity = jiffies; +} + +static void uart_rx_dma_callback(int lch, u16 ch_status, void *data) +{ + return; +} + +static int serial_omap_start_rxdma(struct uart_omap_port *up) +{ + int ret = 0; + + if (up->uart_dma.rx_dma_channel == -1) { + ret = omap_request_dma(up->uart_dma.uart_dma_rx, + "UART Rx DMA", + (void *)uart_rx_dma_callback, up, + &(up->uart_dma.rx_dma_channel)); + if (ret < 0) + return ret; + + omap_set_dma_src_params(up->uart_dma.rx_dma_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + up->uart_dma.uart_base, 0, 0); + omap_set_dma_dest_params(up->uart_dma.rx_dma_channel, 0, + OMAP_DMA_AMODE_POST_INC, + up->uart_dma.rx_buf_dma_phys, 0, 0); + omap_set_dma_transfer_params(up->uart_dma.rx_dma_channel, + OMAP_DMA_DATA_TYPE_S8, + up->uart_dma.rx_buf_size, 1, + OMAP_DMA_SYNC_ELEMENT, + up->uart_dma.uart_dma_rx, 0); + } + up->uart_dma.prev_rx_dma_pos = up->uart_dma.rx_buf_dma_phys; + /* FIXME: Cache maintenance needed here? */ + omap_start_dma(up->uart_dma.rx_dma_channel); + mod_timer(&up->uart_dma.rx_timer, jiffies + + usecs_to_jiffies(up->uart_dma.rx_timeout)); + up->uart_dma.rx_dma_used = true; + return ret; +} + +static void serial_omap_continue_tx(struct uart_omap_port *up) +{ + struct circ_buf *xmit = &up->port.state->xmit; + unsigned int start = up->uart_dma.tx_buf_dma_phys + + (xmit->tail & (UART_XMIT_SIZE - 1)); + + if (uart_circ_empty(xmit)) + return; + + up->uart_dma.tx_buf_size = uart_circ_chars_pending(xmit); + /* + * It is a circular buffer. See if the buffer has wounded back. + * If yes it will have to be transferred in two separate dma + * transfers + */ + if (start + up->uart_dma.tx_buf_size >= + up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) + up->uart_dma.tx_buf_size = + (up->uart_dma.tx_buf_dma_phys + UART_XMIT_SIZE) - start; + omap_set_dma_dest_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_CONSTANT, + up->uart_dma.uart_base, 0, 0); + omap_set_dma_src_params(up->uart_dma.tx_dma_channel, 0, + OMAP_DMA_AMODE_POST_INC, start, 0, 0); + omap_set_dma_transfer_params(up->uart_dma.tx_dma_channel, + OMAP_DMA_DATA_TYPE_S8, + up->uart_dma.tx_buf_size, 1, + OMAP_DMA_SYNC_ELEMENT, + up->uart_dma.uart_dma_tx, 0); + /* FIXME: Cache maintenance needed here? */ + omap_start_dma(up->uart_dma.tx_dma_channel); +} + +static void uart_tx_dma_callback(int lch, u16 ch_status, void *data) +{ + struct uart_omap_port *up = (struct uart_omap_port *)data; + struct circ_buf *xmit = &up->port.state->xmit; + + xmit->tail = (xmit->tail + up->uart_dma.tx_buf_size) & \ + (UART_XMIT_SIZE - 1); + up->port.icount.tx += up->uart_dma.tx_buf_size; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) { + spin_lock(&(up->uart_dma.tx_lock)); + serial_omap_stop_tx(&up->port); + up->uart_dma.tx_dma_used = false; + spin_unlock(&(up->uart_dma.tx_lock)); + } else { + omap_stop_dma(up->uart_dma.tx_dma_channel); + serial_omap_continue_tx(up); + } + up->port_activity = jiffies; + return; +} + +static int serial_omap_probe(struct platform_device *pdev) +{ + struct uart_omap_port *up; + struct resource *mem, *irq, *dma_tx, *dma_rx; + struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data; + int ret = -ENOSPC; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "no irq resource?\n"); + return -ENODEV; + } + + if (!request_mem_region(mem->start, (mem->end - mem->start) + 1, + pdev->dev.driver->name)) { + dev_err(&pdev->dev, "memory region already claimed\n"); + return -EBUSY; + } + + dma_rx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); + if (!dma_rx) { + ret = -EINVAL; + goto err; + } + + dma_tx = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); + if (!dma_tx) { + ret = -EINVAL; + goto err; + } + + up = kzalloc(sizeof(*up), GFP_KERNEL); + if (up == NULL) { + ret = -ENOMEM; + goto do_release_region; + } + sprintf(up->name, "OMAP UART%d", pdev->id); + up->pdev = pdev; + up->port.dev = &pdev->dev; + up->port.type = PORT_OMAP; + up->port.iotype = UPIO_MEM; + up->port.irq = irq->start; + + up->port.regshift = 2; + up->port.fifosize = 64; + up->port.ops = &serial_omap_pops; + up->port.line = pdev->id; + + up->port.membase = omap_up_info->membase; + up->port.mapbase = omap_up_info->mapbase; + up->port.flags = omap_up_info->flags; + up->port.irqflags = omap_up_info->irqflags; + up->port.uartclk = omap_up_info->uartclk; + up->uart_dma.uart_base = mem->start; + + if (omap_up_info->dma_enabled) { + up->uart_dma.uart_dma_tx = dma_tx->start; + up->uart_dma.uart_dma_rx = dma_rx->start; + up->use_dma = 1; + up->uart_dma.rx_buf_size = 4096; + up->uart_dma.rx_timeout = 2; + spin_lock_init(&(up->uart_dma.tx_lock)); + spin_lock_init(&(up->uart_dma.rx_lock)); + up->uart_dma.tx_dma_channel = OMAP_UART_DMA_CH_FREE; + up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE; + } + + ui[pdev->id] = up; + serial_omap_add_console_port(up); + + ret = uart_add_one_port(&serial_omap_reg, &up->port); + if (ret != 0) + goto do_release_region; + + platform_set_drvdata(pdev, up); + return 0; +err: + dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", + pdev->id, __func__, ret); +do_release_region: + release_mem_region(mem->start, (mem->end - mem->start) + 1); + return ret; +} + +static int serial_omap_remove(struct platform_device *dev) +{ + struct uart_omap_port *up = platform_get_drvdata(dev); + + platform_set_drvdata(dev, NULL); + if (up) { + uart_remove_one_port(&serial_omap_reg, &up->port); + kfree(up); + } + return 0; +} + +static struct platform_driver serial_omap_driver = { + .probe = serial_omap_probe, + .remove = serial_omap_remove, + + .suspend = serial_omap_suspend, + .resume = serial_omap_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init serial_omap_init(void) +{ + int ret; + + ret = uart_register_driver(&serial_omap_reg); + if (ret != 0) + return ret; + ret = platform_driver_register(&serial_omap_driver); + if (ret != 0) + uart_unregister_driver(&serial_omap_reg); + return ret; +} + +static void __exit serial_omap_exit(void) +{ + platform_driver_unregister(&serial_omap_driver); + uart_unregister_driver(&serial_omap_reg); +} + +module_init(serial_omap_init); +module_exit(serial_omap_exit); + +MODULE_DESCRIPTION("OMAP High Speed UART driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments Inc"); diff --git a/drivers/serial/pch_uart.c b/drivers/serial/pch_uart.c new file mode 100644 index 0000000..70a6145 --- /dev/null +++ b/drivers/serial/pch_uart.c @@ -0,0 +1,1451 @@ +/* + *Copyright (C) 2010 OKI SEMICONDUCTOR CO., LTD. + * + *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; version 2 of the License. + * + *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. + */ +#include <linux/serial_reg.h> +#include <linux/pci.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/serial_core.h> +#include <linux/interrupt.h> +#include <linux/io.h> + +#include <linux/dmaengine.h> +#include <linux/pch_dma.h> + +enum { + PCH_UART_HANDLED_RX_INT_SHIFT, + PCH_UART_HANDLED_TX_INT_SHIFT, + PCH_UART_HANDLED_RX_ERR_INT_SHIFT, + PCH_UART_HANDLED_RX_TRG_INT_SHIFT, + PCH_UART_HANDLED_MS_INT_SHIFT, +}; + +enum { + PCH_UART_8LINE, + PCH_UART_2LINE, +}; + +#define PCH_UART_DRIVER_DEVICE "ttyPCH" + +#define PCH_UART_NR_GE_256FIFO 1 +#define PCH_UART_NR_GE_64FIFO 3 +#define PCH_UART_NR_GE (PCH_UART_NR_GE_256FIFO+PCH_UART_NR_GE_64FIFO) +#define PCH_UART_NR PCH_UART_NR_GE + +#define PCH_UART_HANDLED_RX_INT (1<<((PCH_UART_HANDLED_RX_INT_SHIFT)<<1)) +#define PCH_UART_HANDLED_TX_INT (1<<((PCH_UART_HANDLED_TX_INT_SHIFT)<<1)) +#define PCH_UART_HANDLED_RX_ERR_INT (1<<((\ + PCH_UART_HANDLED_RX_ERR_INT_SHIFT)<<1)) +#define PCH_UART_HANDLED_RX_TRG_INT (1<<((\ + PCH_UART_HANDLED_RX_TRG_INT_SHIFT)<<1)) +#define PCH_UART_HANDLED_MS_INT (1<<((PCH_UART_HANDLED_MS_INT_SHIFT)<<1)) + +#define PCH_UART_RBR 0x00 +#define PCH_UART_THR 0x00 + +#define PCH_UART_IER_MASK (PCH_UART_IER_ERBFI|PCH_UART_IER_ETBEI|\ + PCH_UART_IER_ELSI|PCH_UART_IER_EDSSI) +#define PCH_UART_IER_ERBFI 0x00000001 +#define PCH_UART_IER_ETBEI 0x00000002 +#define PCH_UART_IER_ELSI 0x00000004 +#define PCH_UART_IER_EDSSI 0x00000008 + +#define PCH_UART_IIR_IP 0x00000001 +#define PCH_UART_IIR_IID 0x00000006 +#define PCH_UART_IIR_MSI 0x00000000 +#define PCH_UART_IIR_TRI 0x00000002 +#define PCH_UART_IIR_RRI 0x00000004 +#define PCH_UART_IIR_REI 0x00000006 +#define PCH_UART_IIR_TOI 0x00000008 +#define PCH_UART_IIR_FIFO256 0x00000020 +#define PCH_UART_IIR_FIFO64 PCH_UART_IIR_FIFO256 +#define PCH_UART_IIR_FE 0x000000C0 + +#define PCH_UART_FCR_FIFOE 0x00000001 +#define PCH_UART_FCR_RFR 0x00000002 +#define PCH_UART_FCR_TFR 0x00000004 +#define PCH_UART_FCR_DMS 0x00000008 +#define PCH_UART_FCR_FIFO256 0x00000020 +#define PCH_UART_FCR_RFTL 0x000000C0 + +#define PCH_UART_FCR_RFTL1 0x00000000 +#define PCH_UART_FCR_RFTL64 0x00000040 +#define PCH_UART_FCR_RFTL128 0x00000080 +#define PCH_UART_FCR_RFTL224 0x000000C0 +#define PCH_UART_FCR_RFTL16 PCH_UART_FCR_RFTL64 +#define PCH_UART_FCR_RFTL32 PCH_UART_FCR_RFTL128 +#define PCH_UART_FCR_RFTL56 PCH_UART_FCR_RFTL224 +#define PCH_UART_FCR_RFTL4 PCH_UART_FCR_RFTL64 +#define PCH_UART_FCR_RFTL8 PCH_UART_FCR_RFTL128 +#define PCH_UART_FCR_RFTL14 PCH_UART_FCR_RFTL224 +#define PCH_UART_FCR_RFTL_SHIFT 6 + +#define PCH_UART_LCR_WLS 0x00000003 +#define PCH_UART_LCR_STB 0x00000004 +#define PCH_UART_LCR_PEN 0x00000008 +#define PCH_UART_LCR_EPS 0x00000010 +#define PCH_UART_LCR_SP 0x00000020 +#define PCH_UART_LCR_SB 0x00000040 +#define PCH_UART_LCR_DLAB 0x00000080 +#define PCH_UART_LCR_NP 0x00000000 +#define PCH_UART_LCR_OP PCH_UART_LCR_PEN +#define PCH_UART_LCR_EP (PCH_UART_LCR_PEN | PCH_UART_LCR_EPS) +#define PCH_UART_LCR_1P (PCH_UART_LCR_PEN | PCH_UART_LCR_SP) +#define PCH_UART_LCR_0P (PCH_UART_LCR_PEN | PCH_UART_LCR_EPS |\ + PCH_UART_LCR_SP) + +#define PCH_UART_LCR_5BIT 0x00000000 +#define PCH_UART_LCR_6BIT 0x00000001 +#define PCH_UART_LCR_7BIT 0x00000002 +#define PCH_UART_LCR_8BIT 0x00000003 + +#define PCH_UART_MCR_DTR 0x00000001 +#define PCH_UART_MCR_RTS 0x00000002 +#define PCH_UART_MCR_OUT 0x0000000C +#define PCH_UART_MCR_LOOP 0x00000010 +#define PCH_UART_MCR_AFE 0x00000020 + +#define PCH_UART_LSR_DR 0x00000001 +#define PCH_UART_LSR_ERR (1<<7) + +#define PCH_UART_MSR_DCTS 0x00000001 +#define PCH_UART_MSR_DDSR 0x00000002 +#define PCH_UART_MSR_TERI 0x00000004 +#define PCH_UART_MSR_DDCD 0x00000008 +#define PCH_UART_MSR_CTS 0x00000010 +#define PCH_UART_MSR_DSR 0x00000020 +#define PCH_UART_MSR_RI 0x00000040 +#define PCH_UART_MSR_DCD 0x00000080 +#define PCH_UART_MSR_DELTA (PCH_UART_MSR_DCTS | PCH_UART_MSR_DDSR |\ + PCH_UART_MSR_TERI | PCH_UART_MSR_DDCD) + +#define PCH_UART_DLL 0x00 +#define PCH_UART_DLM 0x01 + +#define DIV_ROUND(a, b) (((a) + ((b)/2)) / (b)) + +#define PCH_UART_IID_RLS (PCH_UART_IIR_REI) +#define PCH_UART_IID_RDR (PCH_UART_IIR_RRI) +#define PCH_UART_IID_RDR_TO (PCH_UART_IIR_RRI | PCH_UART_IIR_TOI) +#define PCH_UART_IID_THRE (PCH_UART_IIR_TRI) +#define PCH_UART_IID_MS (PCH_UART_IIR_MSI) + +#define PCH_UART_HAL_PARITY_NONE (PCH_UART_LCR_NP) +#define PCH_UART_HAL_PARITY_ODD (PCH_UART_LCR_OP) +#define PCH_UART_HAL_PARITY_EVEN (PCH_UART_LCR_EP) +#define PCH_UART_HAL_PARITY_FIX1 (PCH_UART_LCR_1P) +#define PCH_UART_HAL_PARITY_FIX0 (PCH_UART_LCR_0P) +#define PCH_UART_HAL_5BIT (PCH_UART_LCR_5BIT) +#define PCH_UART_HAL_6BIT (PCH_UART_LCR_6BIT) +#define PCH_UART_HAL_7BIT (PCH_UART_LCR_7BIT) +#define PCH_UART_HAL_8BIT (PCH_UART_LCR_8BIT) +#define PCH_UART_HAL_STB1 0 +#define PCH_UART_HAL_STB2 (PCH_UART_LCR_STB) + +#define PCH_UART_HAL_CLR_TX_FIFO (PCH_UART_FCR_TFR) +#define PCH_UART_HAL_CLR_RX_FIFO (PCH_UART_FCR_RFR) +#define PCH_UART_HAL_CLR_ALL_FIFO (PCH_UART_HAL_CLR_TX_FIFO | \ + PCH_UART_HAL_CLR_RX_FIFO) + +#define PCH_UART_HAL_DMA_MODE0 0 +#define PCH_UART_HAL_FIFO_DIS 0 +#define PCH_UART_HAL_FIFO16 (PCH_UART_FCR_FIFOE) +#define PCH_UART_HAL_FIFO256 (PCH_UART_FCR_FIFOE | \ + PCH_UART_FCR_FIFO256) +#define PCH_UART_HAL_FIFO64 (PCH_UART_HAL_FIFO256) +#define PCH_UART_HAL_TRIGGER1 (PCH_UART_FCR_RFTL1) +#define PCH_UART_HAL_TRIGGER64 (PCH_UART_FCR_RFTL64) +#define PCH_UART_HAL_TRIGGER128 (PCH_UART_FCR_RFTL128) +#define PCH_UART_HAL_TRIGGER224 (PCH_UART_FCR_RFTL224) +#define PCH_UART_HAL_TRIGGER16 (PCH_UART_FCR_RFTL16) +#define PCH_UART_HAL_TRIGGER32 (PCH_UART_FCR_RFTL32) +#define PCH_UART_HAL_TRIGGER56 (PCH_UART_FCR_RFTL56) +#define PCH_UART_HAL_TRIGGER4 (PCH_UART_FCR_RFTL4) +#define PCH_UART_HAL_TRIGGER8 (PCH_UART_FCR_RFTL8) +#define PCH_UART_HAL_TRIGGER14 (PCH_UART_FCR_RFTL14) +#define PCH_UART_HAL_TRIGGER_L (PCH_UART_FCR_RFTL64) +#define PCH_UART_HAL_TRIGGER_M (PCH_UART_FCR_RFTL128) +#define PCH_UART_HAL_TRIGGER_H (PCH_UART_FCR_RFTL224) + +#define PCH_UART_HAL_RX_INT (PCH_UART_IER_ERBFI) +#define PCH_UART_HAL_TX_INT (PCH_UART_IER_ETBEI) +#define PCH_UART_HAL_RX_ERR_INT (PCH_UART_IER_ELSI) +#define PCH_UART_HAL_MS_INT (PCH_UART_IER_EDSSI) +#define PCH_UART_HAL_ALL_INT (PCH_UART_IER_MASK) + +#define PCH_UART_HAL_DTR (PCH_UART_MCR_DTR) +#define PCH_UART_HAL_RTS (PCH_UART_MCR_RTS) +#define PCH_UART_HAL_OUT (PCH_UART_MCR_OUT) +#define PCH_UART_HAL_LOOP (PCH_UART_MCR_LOOP) +#define PCH_UART_HAL_AFE (PCH_UART_MCR_AFE) + +struct pch_uart_buffer { + unsigned char *buf; + int size; +}; + +struct eg20t_port { + struct uart_port port; + int port_type; + void __iomem *membase; + resource_size_t mapbase; + unsigned int iobase; + struct pci_dev *pdev; + int fifo_size; + int base_baud; + int start_tx; + int start_rx; + int tx_empty; + int int_dis_flag; + int trigger; + int trigger_level; + struct pch_uart_buffer rxbuf; + unsigned int dmsr; + unsigned int fcr; + unsigned int use_dma; + unsigned int use_dma_flag; + struct dma_async_tx_descriptor *desc_tx; + struct dma_async_tx_descriptor *desc_rx; + struct pch_dma_slave param_tx; + struct pch_dma_slave param_rx; + struct dma_chan *chan_tx; + struct dma_chan *chan_rx; + struct scatterlist sg_tx; + struct scatterlist sg_rx; + int tx_dma_use; + void *rx_buf_virt; + dma_addr_t rx_buf_dma; +}; + +static unsigned int default_baud = 9600; +static const int trigger_level_256[4] = { 1, 64, 128, 224 }; +static const int trigger_level_64[4] = { 1, 16, 32, 56 }; +static const int trigger_level_16[4] = { 1, 4, 8, 14 }; +static const int trigger_level_1[4] = { 1, 1, 1, 1 }; + +static void pch_uart_hal_request(struct pci_dev *pdev, int fifosize, + int base_baud) +{ + struct eg20t_port *priv = pci_get_drvdata(pdev); + + priv->trigger_level = 1; + priv->fcr = 0; +} + +static unsigned int get_msr(struct eg20t_port *priv, void __iomem *base) +{ + unsigned int msr = ioread8(base + UART_MSR); + priv->dmsr |= msr & PCH_UART_MSR_DELTA; + + return msr; +} + +static void pch_uart_hal_enable_interrupt(struct eg20t_port *priv, + unsigned int flag) +{ + u8 ier = ioread8(priv->membase + UART_IER); + ier |= flag & PCH_UART_IER_MASK; + iowrite8(ier, priv->membase + UART_IER); +} + +static void pch_uart_hal_disable_interrupt(struct eg20t_port *priv, + unsigned int flag) +{ + u8 ier = ioread8(priv->membase + UART_IER); + ier &= ~(flag & PCH_UART_IER_MASK); + iowrite8(ier, priv->membase + UART_IER); +} + +static int pch_uart_hal_set_line(struct eg20t_port *priv, int baud, + unsigned int parity, unsigned int bits, + unsigned int stb) +{ + unsigned int dll, dlm, lcr; + int div; + + div = DIV_ROUND(priv->base_baud / 16, baud); + if (div < 0 || USHRT_MAX <= div) { + pr_err("Invalid Baud(div=0x%x)\n", div); + return -EINVAL; + } + + dll = (unsigned int)div & 0x00FFU; + dlm = ((unsigned int)div >> 8) & 0x00FFU; + + if (parity & ~(PCH_UART_LCR_PEN | PCH_UART_LCR_EPS | PCH_UART_LCR_SP)) { + pr_err("Invalid parity(0x%x)\n", parity); + return -EINVAL; + } + + if (bits & ~PCH_UART_LCR_WLS) { + pr_err("Invalid bits(0x%x)\n", bits); + return -EINVAL; + } + + if (stb & ~PCH_UART_LCR_STB) { + pr_err("Invalid STB(0x%x)\n", stb); + return -EINVAL; + } + + lcr = parity; + lcr |= bits; + lcr |= stb; + + pr_debug("%s:baud = %d, div = %04x, lcr = %02x (%lu)\n", + __func__, baud, div, lcr, jiffies); + iowrite8(PCH_UART_LCR_DLAB, priv->membase + UART_LCR); + iowrite8(dll, priv->membase + PCH_UART_DLL); + iowrite8(dlm, priv->membase + PCH_UART_DLM); + iowrite8(lcr, priv->membase + UART_LCR); + + return 0; +} + +static int pch_uart_hal_fifo_reset(struct eg20t_port *priv, + unsigned int flag) +{ + if (flag & ~(PCH_UART_FCR_TFR | PCH_UART_FCR_RFR)) { + pr_err("%s:Invalid flag(0x%x)\n", __func__, flag); + return -EINVAL; + } + + iowrite8(PCH_UART_FCR_FIFOE | priv->fcr, priv->membase + UART_FCR); + iowrite8(PCH_UART_FCR_FIFOE | priv->fcr | flag, + priv->membase + UART_FCR); + iowrite8(priv->fcr, priv->membase + UART_FCR); + + return 0; +} + +static int pch_uart_hal_set_fifo(struct eg20t_port *priv, + unsigned int dmamode, + unsigned int fifo_size, unsigned int trigger) +{ + u8 fcr; + + if (dmamode & ~PCH_UART_FCR_DMS) { + pr_err("%s:Invalid DMA Mode(0x%x)\n", __func__, dmamode); + return -EINVAL; + } + + if (fifo_size & ~(PCH_UART_FCR_FIFOE | PCH_UART_FCR_FIFO256)) { + pr_err("%s:Invalid FIFO SIZE(0x%x)\n", __func__, fifo_size); + return -EINVAL; + } + + if (trigger & ~PCH_UART_FCR_RFTL) { + pr_err("%s:Invalid TRIGGER(0x%x)\n", __func__, trigger); + return -EINVAL; + } + + switch (priv->fifo_size) { + case 256: + priv->trigger_level = + trigger_level_256[trigger >> PCH_UART_FCR_RFTL_SHIFT]; + break; + case 64: + priv->trigger_level = + trigger_level_64[trigger >> PCH_UART_FCR_RFTL_SHIFT]; + break; + case 16: + priv->trigger_level = + trigger_level_16[trigger >> PCH_UART_FCR_RFTL_SHIFT]; + break; + default: + priv->trigger_level = + trigger_level_1[trigger >> PCH_UART_FCR_RFTL_SHIFT]; + break; + } + fcr = + dmamode | fifo_size | trigger | PCH_UART_FCR_RFR | PCH_UART_FCR_TFR; + iowrite8(PCH_UART_FCR_FIFOE, priv->membase + UART_FCR); + iowrite8(PCH_UART_FCR_FIFOE | PCH_UART_FCR_RFR | PCH_UART_FCR_TFR, + priv->membase + UART_FCR); + iowrite8(fcr, priv->membase + UART_FCR); + priv->fcr = fcr; + + return 0; +} + +static u8 pch_uart_hal_get_modem(struct eg20t_port *priv) +{ + priv->dmsr = 0; + return get_msr(priv, priv->membase); +} + +static int pch_uart_hal_write(struct eg20t_port *priv, + const unsigned char *buf, int tx_size) +{ + int i; + unsigned int thr; + + for (i = 0; i < tx_size;) { + thr = buf[i++]; + iowrite8(thr, priv->membase + PCH_UART_THR); + } + return i; +} + +static int pch_uart_hal_read(struct eg20t_port *priv, unsigned char *buf, + int rx_size) +{ + int i; + u8 rbr, lsr; + + lsr = ioread8(priv->membase + UART_LSR); + for (i = 0, lsr = ioread8(priv->membase + UART_LSR); + i < rx_size && lsr & UART_LSR_DR; + lsr = ioread8(priv->membase + UART_LSR)) { + rbr = ioread8(priv->membase + PCH_UART_RBR); + buf[i++] = rbr; + } + return i; +} + +static unsigned int pch_uart_hal_get_iid(struct eg20t_port *priv) +{ + unsigned int iir; + int ret; + + iir = ioread8(priv->membase + UART_IIR); + ret = (iir & (PCH_UART_IIR_IID | PCH_UART_IIR_TOI | PCH_UART_IIR_IP)); + return ret; +} + +static u8 pch_uart_hal_get_line_status(struct eg20t_port *priv) +{ + return ioread8(priv->membase + UART_LSR); +} + +static void pch_uart_hal_set_break(struct eg20t_port *priv, int on) +{ + unsigned int lcr; + + lcr = ioread8(priv->membase + UART_LCR); + if (on) + lcr |= PCH_UART_LCR_SB; + else + lcr &= ~PCH_UART_LCR_SB; + + iowrite8(lcr, priv->membase + UART_LCR); +} + +static int push_rx(struct eg20t_port *priv, const unsigned char *buf, + int size) +{ + struct uart_port *port; + struct tty_struct *tty; + + port = &priv->port; + tty = tty_port_tty_get(&port->state->port); + if (!tty) { + pr_debug("%s:tty is busy now", __func__); + return -EBUSY; + } + + tty_insert_flip_string(tty, buf, size); + tty_flip_buffer_push(tty); + tty_kref_put(tty); + + return 0; +} + +static int pop_tx_x(struct eg20t_port *priv, unsigned char *buf) +{ + int ret; + struct uart_port *port = &priv->port; + + if (port->x_char) { + pr_debug("%s:X character send %02x (%lu)\n", __func__, + port->x_char, jiffies); + buf[0] = port->x_char; + port->x_char = 0; + ret = 1; + } else { + ret = 0; + } + + return ret; +} + +static int dma_push_rx(struct eg20t_port *priv, int size) +{ + struct tty_struct *tty; + int room; + struct uart_port *port = &priv->port; + + port = &priv->port; + tty = tty_port_tty_get(&port->state->port); + if (!tty) { + pr_debug("%s:tty is busy now", __func__); + return 0; + } + + room = tty_buffer_request_room(tty, size); + + if (room < size) + dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", + size - room); + if (!room) + return room; + + tty_insert_flip_string(tty, sg_virt(&priv->sg_rx), size); + + port->icount.rx += room; + tty_kref_put(tty); + + return room; +} + +static void pch_free_dma(struct uart_port *port) +{ + struct eg20t_port *priv; + priv = container_of(port, struct eg20t_port, port); + + if (priv->chan_tx) { + dma_release_channel(priv->chan_tx); + priv->chan_tx = NULL; + } + if (priv->chan_rx) { + dma_release_channel(priv->chan_rx); + priv->chan_rx = NULL; + } + if (sg_dma_address(&priv->sg_rx)) + dma_free_coherent(port->dev, port->fifosize, + sg_virt(&priv->sg_rx), + sg_dma_address(&priv->sg_rx)); + + return; +} + +static bool filter(struct dma_chan *chan, void *slave) +{ + struct pch_dma_slave *param = slave; + + if ((chan->chan_id == param->chan_id) && (param->dma_dev == + chan->device->dev)) { + chan->private = param; + return true; + } else { + return false; + } +} + +static void pch_request_dma(struct uart_port *port) +{ + dma_cap_mask_t mask; + struct dma_chan *chan; + struct pci_dev *dma_dev; + struct pch_dma_slave *param; + struct eg20t_port *priv = + container_of(port, struct eg20t_port, port); + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + dma_dev = pci_get_bus_and_slot(2, PCI_DEVFN(0xa, 0)); /* Get DMA's dev + information */ + /* Set Tx DMA */ + param = &priv->param_tx; + param->dma_dev = &dma_dev->dev; + param->chan_id = priv->port.line; + param->tx_reg = port->mapbase + UART_TX; + chan = dma_request_channel(mask, filter, param); + if (!chan) { + pr_err("%s:dma_request_channel FAILS(Tx)\n", __func__); + return; + } + priv->chan_tx = chan; + + /* Set Rx DMA */ + param = &priv->param_rx; + param->dma_dev = &dma_dev->dev; + param->chan_id = priv->port.line + 1; /* Rx = Tx + 1 */ + param->rx_reg = port->mapbase + UART_RX; + chan = dma_request_channel(mask, filter, param); + if (!chan) { + pr_err("%s:dma_request_channel FAILS(Rx)\n", __func__); + dma_release_channel(priv->chan_tx); + return; + } + + /* Get Consistent memory for DMA */ + priv->rx_buf_virt = dma_alloc_coherent(port->dev, port->fifosize, + &priv->rx_buf_dma, GFP_KERNEL); + priv->chan_rx = chan; +} + +static void pch_dma_rx_complete(void *arg) +{ + struct eg20t_port *priv = arg; + struct uart_port *port = &priv->port; + struct tty_struct *tty = tty_port_tty_get(&port->state->port); + + if (!tty) { + pr_debug("%s:tty is busy now", __func__); + return; + } + + if (dma_push_rx(priv, priv->trigger_level)) + tty_flip_buffer_push(tty); + + tty_kref_put(tty); +} + +static void pch_dma_tx_complete(void *arg) +{ + struct eg20t_port *priv = arg; + struct uart_port *port = &priv->port; + struct circ_buf *xmit = &port->state->xmit; + + xmit->tail += sg_dma_len(&priv->sg_tx); + xmit->tail &= UART_XMIT_SIZE - 1; + port->icount.tx += sg_dma_len(&priv->sg_tx); + + async_tx_ack(priv->desc_tx); + priv->tx_dma_use = 0; +} + +static int pop_tx(struct eg20t_port *priv, unsigned char *buf, int size) +{ + int count = 0; + struct uart_port *port = &priv->port; + struct circ_buf *xmit = &port->state->xmit; + + if (uart_tx_stopped(port) || uart_circ_empty(xmit) || count >= size) + goto pop_tx_end; + + do { + int cnt_to_end = + CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + int sz = min(size - count, cnt_to_end); + memcpy(&buf[count], &xmit->buf[xmit->tail], sz); + xmit->tail = (xmit->tail + sz) & (UART_XMIT_SIZE - 1); + count += sz; + } while (!uart_circ_empty(xmit) && count < size); + +pop_tx_end: + pr_debug("%d characters. Remained %d characters. (%lu)\n", + count, size - count, jiffies); + + return count; +} + +static int handle_rx_to(struct eg20t_port *priv) +{ + struct pch_uart_buffer *buf; + int rx_size; + int ret; + if (!priv->start_rx) { + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT); + return 0; + } + buf = &priv->rxbuf; + do { + rx_size = pch_uart_hal_read(priv, buf->buf, buf->size); + ret = push_rx(priv, buf->buf, rx_size); + if (ret) + return 0; + } while (rx_size == buf->size); + + return PCH_UART_HANDLED_RX_INT; +} + +static int handle_rx(struct eg20t_port *priv) +{ + return handle_rx_to(priv); +} + +static int dma_handle_rx(struct eg20t_port *priv) +{ + struct uart_port *port = &priv->port; + struct dma_async_tx_descriptor *desc; + struct scatterlist *sg; + + priv = container_of(port, struct eg20t_port, port); + sg = &priv->sg_rx; + + sg_init_table(&priv->sg_rx, 1); /* Initialize SG table */ + + sg_dma_len(sg) = priv->fifo_size; + + sg_set_page(&priv->sg_rx, virt_to_page(priv->rx_buf_virt), + sg_dma_len(sg), (unsigned long)priv->rx_buf_virt & + ~PAGE_MASK); + + sg_dma_address(sg) = priv->rx_buf_dma; + + desc = priv->chan_rx->device->device_prep_slave_sg(priv->chan_rx, + sg, 1, DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT); + if (!desc) + return 0; + + priv->desc_rx = desc; + desc->callback = pch_dma_rx_complete; + desc->callback_param = priv; + desc->tx_submit(desc); + dma_async_issue_pending(priv->chan_rx); + + return PCH_UART_HANDLED_RX_INT; +} + +static unsigned int handle_tx(struct eg20t_port *priv) +{ + struct uart_port *port = &priv->port; + struct circ_buf *xmit = &port->state->xmit; + int ret; + int fifo_size; + int tx_size; + int size; + int tx_empty; + + if (!priv->start_tx) { + pr_info("%s:Tx isn't started. (%lu)\n", __func__, jiffies); + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); + priv->tx_empty = 1; + return 0; + } + + fifo_size = max(priv->fifo_size, 1); + tx_empty = 1; + if (pop_tx_x(priv, xmit->buf)) { + pch_uart_hal_write(priv, xmit->buf, 1); + port->icount.tx++; + tx_empty = 0; + fifo_size--; + } + size = min(xmit->head - xmit->tail, fifo_size); + tx_size = pop_tx(priv, xmit->buf, size); + if (tx_size > 0) { + ret = pch_uart_hal_write(priv, xmit->buf, tx_size); + port->icount.tx += ret; + tx_empty = 0; + } + + priv->tx_empty = tx_empty; + + if (tx_empty) + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); + + return PCH_UART_HANDLED_TX_INT; +} + +static unsigned int dma_handle_tx(struct eg20t_port *priv) +{ + struct uart_port *port = &priv->port; + struct circ_buf *xmit = &port->state->xmit; + struct scatterlist *sg = &priv->sg_tx; + int nent; + int fifo_size; + int tx_empty; + struct dma_async_tx_descriptor *desc; + + if (!priv->start_tx) { + pr_info("%s:Tx isn't started. (%lu)\n", __func__, jiffies); + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); + priv->tx_empty = 1; + return 0; + } + + fifo_size = max(priv->fifo_size, 1); + tx_empty = 1; + if (pop_tx_x(priv, xmit->buf)) { + pch_uart_hal_write(priv, xmit->buf, 1); + port->icount.tx++; + tx_empty = 0; + fifo_size--; + } + + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_TX_INT); + + priv->tx_dma_use = 1; + + sg_init_table(&priv->sg_tx, 1); /* Initialize SG table */ + + sg_set_page(&priv->sg_tx, virt_to_page(xmit->buf), + UART_XMIT_SIZE, (int)xmit->buf & ~PAGE_MASK); + + nent = dma_map_sg(port->dev, &priv->sg_tx, 1, DMA_TO_DEVICE); + if (!nent) { + pr_err("%s:dma_map_sg Failed\n", __func__); + return 0; + } + + sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); + sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + + sg->offset; + sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, + UART_XMIT_SIZE), CIRC_CNT_TO_END(xmit->head, + xmit->tail, UART_XMIT_SIZE)); + + desc = priv->chan_tx->device->device_prep_slave_sg(priv->chan_tx, + sg, nent, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + pr_err("%s:device_prep_slave_sg Failed\n", __func__); + return 0; + } + + dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE); + + priv->desc_tx = desc; + desc->callback = pch_dma_tx_complete; + desc->callback_param = priv; + + desc->tx_submit(desc); + + dma_async_issue_pending(priv->chan_tx); + + return PCH_UART_HANDLED_TX_INT; +} + +static void pch_uart_err_ir(struct eg20t_port *priv, unsigned int lsr) +{ + u8 fcr = ioread8(priv->membase + UART_FCR); + + /* Reset FIFO */ + fcr |= UART_FCR_CLEAR_RCVR; + iowrite8(fcr, priv->membase + UART_FCR); + + if (lsr & PCH_UART_LSR_ERR) + dev_err(&priv->pdev->dev, "Error data in FIFO\n"); + + if (lsr & UART_LSR_FE) + dev_err(&priv->pdev->dev, "Framing Error\n"); + + if (lsr & UART_LSR_PE) + dev_err(&priv->pdev->dev, "Parity Error\n"); + + if (lsr & UART_LSR_OE) + dev_err(&priv->pdev->dev, "Overrun Error\n"); +} + +static irqreturn_t pch_uart_interrupt(int irq, void *dev_id) +{ + struct eg20t_port *priv = dev_id; + unsigned int handled; + u8 lsr; + int ret = 0; + unsigned int iid; + unsigned long flags; + + spin_lock_irqsave(&priv->port.lock, flags); + handled = 0; + while ((iid = pch_uart_hal_get_iid(priv)) > 1) { + switch (iid) { + case PCH_UART_IID_RLS: /* Receiver Line Status */ + lsr = pch_uart_hal_get_line_status(priv); + if (lsr & (PCH_UART_LSR_ERR | UART_LSR_FE | + UART_LSR_PE | UART_LSR_OE)) { + pch_uart_err_ir(priv, lsr); + ret = PCH_UART_HANDLED_RX_ERR_INT; + } + break; + case PCH_UART_IID_RDR: /* Received Data Ready */ + if (priv->use_dma) + ret = dma_handle_rx(priv); + else + ret = handle_rx(priv); + break; + case PCH_UART_IID_RDR_TO: /* Received Data Ready + (FIFO Timeout) */ + ret = handle_rx_to(priv); + break; + case PCH_UART_IID_THRE: /* Transmitter Holding Register + Empty */ + if (priv->use_dma) + ret = dma_handle_tx(priv); + else + ret = handle_tx(priv); + break; + case PCH_UART_IID_MS: /* Modem Status */ + ret = PCH_UART_HANDLED_MS_INT; + break; + default: /* Never junp to this label */ + pr_err("%s:iid=%d (%lu)\n", __func__, iid, jiffies); + ret = -1; + break; + } + handled |= (unsigned int)ret; + } + if (handled == 0 && iid <= 1) { + if (priv->int_dis_flag) + priv->int_dis_flag = 0; + } + + spin_unlock_irqrestore(&priv->port.lock, flags); + return IRQ_RETVAL(handled); +} + +/* This function tests whether the transmitter fifo and shifter for the port + described by 'port' is empty. */ +static unsigned int pch_uart_tx_empty(struct uart_port *port) +{ + struct eg20t_port *priv; + int ret; + priv = container_of(port, struct eg20t_port, port); + if (priv->tx_empty) + ret = TIOCSER_TEMT; + else + ret = 0; + + return ret; +} + +/* Returns the current state of modem control inputs. */ +static unsigned int pch_uart_get_mctrl(struct uart_port *port) +{ + struct eg20t_port *priv; + u8 modem; + unsigned int ret = 0; + + priv = container_of(port, struct eg20t_port, port); + modem = pch_uart_hal_get_modem(priv); + + if (modem & UART_MSR_DCD) + ret |= TIOCM_CAR; + + if (modem & UART_MSR_RI) + ret |= TIOCM_RNG; + + if (modem & UART_MSR_DSR) + ret |= TIOCM_DSR; + + if (modem & UART_MSR_CTS) + ret |= TIOCM_CTS; + + return ret; +} + +static void pch_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + u32 mcr = 0; + unsigned int dat; + struct eg20t_port *priv = container_of(port, struct eg20t_port, port); + + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + if (mctrl) { + dat = pch_uart_get_mctrl(port); + dat |= mcr; + iowrite8(dat, priv->membase + UART_MCR); + } +} + +static void pch_uart_stop_tx(struct uart_port *port) +{ + struct eg20t_port *priv; + priv = container_of(port, struct eg20t_port, port); + priv->start_tx = 0; + priv->tx_dma_use = 0; +} + +static void pch_uart_start_tx(struct uart_port *port) +{ + struct eg20t_port *priv; + + priv = container_of(port, struct eg20t_port, port); + + if (priv->use_dma) + if (priv->tx_dma_use) + return; + + priv->start_tx = 1; + pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_TX_INT); +} + +static void pch_uart_stop_rx(struct uart_port *port) +{ + struct eg20t_port *priv; + priv = container_of(port, struct eg20t_port, port); + priv->start_rx = 0; + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_RX_INT); + priv->int_dis_flag = 1; +} + +/* Enable the modem status interrupts. */ +static void pch_uart_enable_ms(struct uart_port *port) +{ + struct eg20t_port *priv; + priv = container_of(port, struct eg20t_port, port); + pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_MS_INT); +} + +/* Control the transmission of a break signal. */ +static void pch_uart_break_ctl(struct uart_port *port, int ctl) +{ + struct eg20t_port *priv; + unsigned long flags; + + priv = container_of(port, struct eg20t_port, port); + spin_lock_irqsave(&port->lock, flags); + pch_uart_hal_set_break(priv, ctl); + spin_unlock_irqrestore(&port->lock, flags); +} + +/* Grab any interrupt resources and initialise any low level driver state. */ +static int pch_uart_startup(struct uart_port *port) +{ + struct eg20t_port *priv; + int ret; + int fifo_size; + int trigger_level; + + priv = container_of(port, struct eg20t_port, port); + priv->tx_empty = 1; + port->uartclk = priv->base_baud; + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT); + ret = pch_uart_hal_set_line(priv, default_baud, + PCH_UART_HAL_PARITY_NONE, PCH_UART_HAL_8BIT, + PCH_UART_HAL_STB1); + if (ret) + return ret; + + switch (priv->fifo_size) { + case 256: + fifo_size = PCH_UART_HAL_FIFO256; + break; + case 64: + fifo_size = PCH_UART_HAL_FIFO64; + break; + case 16: + fifo_size = PCH_UART_HAL_FIFO16; + case 1: + default: + fifo_size = PCH_UART_HAL_FIFO_DIS; + break; + } + + switch (priv->trigger) { + case PCH_UART_HAL_TRIGGER1: + trigger_level = 1; + break; + case PCH_UART_HAL_TRIGGER_L: + trigger_level = priv->fifo_size / 4; + break; + case PCH_UART_HAL_TRIGGER_M: + trigger_level = priv->fifo_size / 2; + break; + case PCH_UART_HAL_TRIGGER_H: + default: + trigger_level = priv->fifo_size - (priv->fifo_size / 8); + break; + } + + priv->trigger_level = trigger_level; + ret = pch_uart_hal_set_fifo(priv, PCH_UART_HAL_DMA_MODE0, + fifo_size, priv->trigger); + if (ret < 0) + return ret; + + ret = request_irq(priv->port.irq, pch_uart_interrupt, IRQF_SHARED, + KBUILD_MODNAME, priv); + if (ret < 0) + return ret; + + if (priv->use_dma) + pch_request_dma(port); + + priv->start_rx = 1; + pch_uart_hal_enable_interrupt(priv, PCH_UART_HAL_RX_INT); + uart_update_timeout(port, CS8, default_baud); + + return 0; +} + +static void pch_uart_shutdown(struct uart_port *port) +{ + struct eg20t_port *priv; + int ret; + + priv = container_of(port, struct eg20t_port, port); + pch_uart_hal_disable_interrupt(priv, PCH_UART_HAL_ALL_INT); + pch_uart_hal_fifo_reset(priv, PCH_UART_HAL_CLR_ALL_FIFO); + ret = pch_uart_hal_set_fifo(priv, PCH_UART_HAL_DMA_MODE0, + PCH_UART_HAL_FIFO_DIS, PCH_UART_HAL_TRIGGER1); + if (ret) + pr_err("pch_uart_hal_set_fifo Failed(ret=%d)\n", ret); + + if (priv->use_dma_flag) + pch_free_dma(port); + + free_irq(priv->port.irq, priv); +} + +/* Change the port parameters, including word length, parity, stop + *bits. Update read_status_mask and ignore_status_mask to indicate + *the types of events we are interested in receiving. */ +static void pch_uart_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + int baud; + int rtn; + unsigned int parity, bits, stb; + struct eg20t_port *priv; + unsigned long flags; + + priv = container_of(port, struct eg20t_port, port); + switch (termios->c_cflag & CSIZE) { + case CS5: + bits = PCH_UART_HAL_5BIT; + break; + case CS6: + bits = PCH_UART_HAL_6BIT; + break; + case CS7: + bits = PCH_UART_HAL_7BIT; + break; + default: /* CS8 */ + bits = PCH_UART_HAL_8BIT; + break; + } + if (termios->c_cflag & CSTOPB) + stb = PCH_UART_HAL_STB2; + else + stb = PCH_UART_HAL_STB1; + + if (termios->c_cflag & PARENB) { + if (!(termios->c_cflag & PARODD)) + parity = PCH_UART_HAL_PARITY_ODD; + else + parity = PCH_UART_HAL_PARITY_EVEN; + + } else { + parity = PCH_UART_HAL_PARITY_NONE; + } + termios->c_cflag &= ~CMSPAR; /* Mark/Space parity is not supported */ + + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16); + + spin_lock_irqsave(&port->lock, flags); + + uart_update_timeout(port, termios->c_cflag, baud); + rtn = pch_uart_hal_set_line(priv, baud, parity, bits, stb); + if (rtn) + goto out; + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + +out: + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *pch_uart_type(struct uart_port *port) +{ + return KBUILD_MODNAME; +} + +static void pch_uart_release_port(struct uart_port *port) +{ + struct eg20t_port *priv; + + priv = container_of(port, struct eg20t_port, port); + pci_iounmap(priv->pdev, priv->membase); + pci_release_regions(priv->pdev); +} + +static int pch_uart_request_port(struct uart_port *port) +{ + struct eg20t_port *priv; + int ret; + void __iomem *membase; + + priv = container_of(port, struct eg20t_port, port); + ret = pci_request_regions(priv->pdev, KBUILD_MODNAME); + if (ret < 0) + return -EBUSY; + + membase = pci_iomap(priv->pdev, 1, 0); + if (!membase) { + pci_release_regions(priv->pdev); + return -EBUSY; + } + priv->membase = port->membase = membase; + + return 0; +} + +static void pch_uart_config_port(struct uart_port *port, int type) +{ + struct eg20t_port *priv; + + priv = container_of(port, struct eg20t_port, port); + if (type & UART_CONFIG_TYPE) { + port->type = priv->port_type; + pch_uart_request_port(port); + } +} + +static int pch_uart_verify_port(struct uart_port *port, + struct serial_struct *serinfo) +{ + struct eg20t_port *priv; + + priv = container_of(port, struct eg20t_port, port); + if (serinfo->flags & UPF_LOW_LATENCY) { + pr_info("PCH UART : Use PIO Mode (without DMA)\n"); + priv->use_dma = 0; + serinfo->flags &= ~UPF_LOW_LATENCY; + } else { +#ifndef CONFIG_PCH_DMA + pr_err("%s : PCH DMA is not Loaded.\n", __func__); + return -EOPNOTSUPP; +#endif + priv->use_dma = 1; + priv->use_dma_flag = 1; + pr_info("PCH UART : Use DMA Mode\n"); + } + + return 0; +} + +static struct uart_ops pch_uart_ops = { + .tx_empty = pch_uart_tx_empty, + .set_mctrl = pch_uart_set_mctrl, + .get_mctrl = pch_uart_get_mctrl, + .stop_tx = pch_uart_stop_tx, + .start_tx = pch_uart_start_tx, + .stop_rx = pch_uart_stop_rx, + .enable_ms = pch_uart_enable_ms, + .break_ctl = pch_uart_break_ctl, + .startup = pch_uart_startup, + .shutdown = pch_uart_shutdown, + .set_termios = pch_uart_set_termios, +/* .pm = pch_uart_pm, Not supported yet */ +/* .set_wake = pch_uart_set_wake, Not supported yet */ + .type = pch_uart_type, + .release_port = pch_uart_release_port, + .request_port = pch_uart_request_port, + .config_port = pch_uart_config_port, + .verify_port = pch_uart_verify_port +}; + +static struct uart_driver pch_uart_driver = { + .owner = THIS_MODULE, + .driver_name = KBUILD_MODNAME, + .dev_name = PCH_UART_DRIVER_DEVICE, + .major = 0, + .minor = 0, + .nr = PCH_UART_NR, +}; + +static struct eg20t_port *pch_uart_init_port(struct pci_dev *pdev, + int port_type) +{ + struct eg20t_port *priv; + int ret; + unsigned int iobase; + unsigned int mapbase; + unsigned char *rxbuf; + int fifosize, base_baud; + static int num; + + priv = kzalloc(sizeof(struct eg20t_port), GFP_KERNEL); + if (priv == NULL) + goto init_port_alloc_err; + + rxbuf = (unsigned char *)__get_free_page(GFP_KERNEL); + if (!rxbuf) + goto init_port_free_txbuf; + + switch (port_type) { + case PORT_UNKNOWN: + fifosize = 256; /* UART0 */ + base_baud = 1843200; /* 1.8432MHz */ + break; + case PORT_8250: + fifosize = 64; /* UART1~3 */ + base_baud = 1843200; /* 1.8432MHz */ + break; + default: + dev_err(&pdev->dev, "Invalid Port Type(=%d)\n", port_type); + goto init_port_hal_free; + } + + iobase = pci_resource_start(pdev, 0); + mapbase = pci_resource_start(pdev, 1); + priv->mapbase = mapbase; + priv->iobase = iobase; + priv->pdev = pdev; + priv->tx_empty = 1; + priv->rxbuf.buf = rxbuf; + priv->rxbuf.size = PAGE_SIZE; + + priv->fifo_size = fifosize; + priv->base_baud = base_baud; + priv->port_type = PORT_MAX_8250 + port_type + 1; + priv->port.dev = &pdev->dev; + priv->port.iobase = iobase; + priv->port.membase = NULL; + priv->port.mapbase = mapbase; + priv->port.irq = pdev->irq; + priv->port.iotype = UPIO_PORT; + priv->port.ops = &pch_uart_ops; + priv->port.flags = UPF_BOOT_AUTOCONF; + priv->port.fifosize = fifosize; + priv->port.line = num++; + priv->trigger = PCH_UART_HAL_TRIGGER_M; + + pci_set_drvdata(pdev, priv); + pch_uart_hal_request(pdev, fifosize, base_baud); + ret = uart_add_one_port(&pch_uart_driver, &priv->port); + if (ret < 0) + goto init_port_hal_free; + + return priv; + +init_port_hal_free: + free_page((unsigned long)rxbuf); +init_port_free_txbuf: + kfree(priv); +init_port_alloc_err: + + return NULL; +} + +static void pch_uart_exit_port(struct eg20t_port *priv) +{ + uart_remove_one_port(&pch_uart_driver, &priv->port); + pci_set_drvdata(priv->pdev, NULL); + free_page((unsigned long)priv->rxbuf.buf); +} + +static void pch_uart_pci_remove(struct pci_dev *pdev) +{ + struct eg20t_port *priv; + + priv = (struct eg20t_port *)pci_get_drvdata(pdev); + pch_uart_exit_port(priv); + pci_disable_device(pdev); + kfree(priv); + return; +} +#ifdef CONFIG_PM +static int pch_uart_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct eg20t_port *priv = pci_get_drvdata(pdev); + + uart_suspend_port(&pch_uart_driver, &priv->port); + + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + return 0; +} + +static int pch_uart_pci_resume(struct pci_dev *pdev) +{ + struct eg20t_port *priv = pci_get_drvdata(pdev); + int ret; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s-pci_enable_device failed(ret=%d) ", __func__, ret); + return ret; + } + + uart_resume_port(&pch_uart_driver, &priv->port); + + return 0; +} +#else +#define pch_uart_pci_suspend NULL +#define pch_uart_pci_resume NULL +#endif + +static DEFINE_PCI_DEVICE_TABLE(pch_uart_pci_id) = { + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8811), + .driver_data = PCH_UART_8LINE}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8812), + .driver_data = PCH_UART_2LINE}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8813), + .driver_data = PCH_UART_2LINE}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8814), + .driver_data = PCH_UART_2LINE}, + {0,}, +}; + +static int __devinit pch_uart_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int ret; + struct eg20t_port *priv; + + ret = pci_enable_device(pdev); + if (ret < 0) + goto probe_error; + + priv = pch_uart_init_port(pdev, id->driver_data); + if (!priv) { + ret = -EBUSY; + goto probe_disable_device; + } + pci_set_drvdata(pdev, priv); + + return ret; + +probe_disable_device: + pci_disable_device(pdev); +probe_error: + return ret; +} + +static struct pci_driver pch_uart_pci_driver = { + .name = "pch_uart", + .id_table = pch_uart_pci_id, + .probe = pch_uart_pci_probe, + .remove = __devexit_p(pch_uart_pci_remove), + .suspend = pch_uart_pci_suspend, + .resume = pch_uart_pci_resume, +}; + +static int __init pch_uart_module_init(void) +{ + int ret; + + /* register as UART driver */ + ret = uart_register_driver(&pch_uart_driver); + if (ret < 0) + return ret; + + /* register as PCI driver */ + ret = pci_register_driver(&pch_uart_pci_driver); + if (ret < 0) + uart_unregister_driver(&pch_uart_driver); + + return ret; +} +module_init(pch_uart_module_init); + +static void __exit pch_uart_module_exit(void) +{ + pci_unregister_driver(&pch_uart_pci_driver); + uart_unregister_driver(&pch_uart_driver); +} +module_exit(pch_uart_module_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel EG20T PCH UART PCI Driver"); +module_param(default_baud, uint, S_IRUGO); diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c index 9c1243f..5b9cde7 100644 --- a/drivers/serial/pmac_zilog.c +++ b/drivers/serial/pmac_zilog.c @@ -54,7 +54,6 @@ #include <linux/delay.h> #include <linux/init.h> #include <linux/console.h> -#include <linux/slab.h> #include <linux/adb.h> #include <linux/pmu.h> #include <linux/bitops.h> @@ -63,11 +62,17 @@ #include <asm/sections.h> #include <asm/io.h> #include <asm/irq.h> + +#ifdef CONFIG_PPC_PMAC #include <asm/prom.h> #include <asm/machdep.h> #include <asm/pmac_feature.h> #include <asm/dbdma.h> #include <asm/macio.h> +#else +#include <linux/platform_device.h> +#define of_machine_is_compatible(x) (0) +#endif #if defined (CONFIG_SERIAL_PMACZILOG_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ @@ -83,11 +88,9 @@ static char version[] __initdata = "pmac_zilog: 0.6 (Benjamin Herrenschmidt <benh@kernel.crashing.org>)"; MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); -MODULE_DESCRIPTION("Driver for the PowerMac serial ports."); +MODULE_DESCRIPTION("Driver for the Mac and PowerMac serial ports."); MODULE_LICENSE("GPL"); -#define PWRDBG(fmt, arg...) printk(KERN_DEBUG fmt , ## arg) - #ifdef CONFIG_SERIAL_PMACZILOG_TTYS #define PMACZILOG_MAJOR TTY_MAJOR #define PMACZILOG_MINOR 64 @@ -153,8 +156,8 @@ static void pmz_load_zsregs(struct uart_pmac_port *uap, u8 *regs) write_zsreg(uap, R10, regs[R10]); /* Set TX/RX controls sans the enable bits. */ - write_zsreg(uap, R3, regs[R3] & ~RxENABLE); - write_zsreg(uap, R5, regs[R5] & ~TxENABLE); + write_zsreg(uap, R3, regs[R3] & ~RxENABLE); + write_zsreg(uap, R5, regs[R5] & ~TxENABLE); /* now set R7 "prime" on ESCC */ write_zsreg(uap, R15, regs[R15] | EN85C30); @@ -205,7 +208,7 @@ static void pmz_load_zsregs(struct uart_pmac_port *uap, u8 *regs) */ static void pmz_maybe_update_regs(struct uart_pmac_port *uap) { - if (!ZS_REGS_HELD(uap)) { + if (!ZS_REGS_HELD(uap)) { if (ZS_TX_ACTIVE(uap)) { uap->flags |= PMACZILOG_FLAG_REGS_HELD; } else { @@ -242,12 +245,12 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap) } /* Sanity check, make sure the old bug is no longer happening */ - if (uap->port.info == NULL || uap->port.info->port.tty == NULL) { + if (uap->port.state == NULL || uap->port.state->port.tty == NULL) { WARN_ON(1); (void)read_zsdata(uap); return NULL; } - tty = uap->port.info->port.tty; + tty = uap->port.state->port.tty; while (1) { error = 0; @@ -281,7 +284,7 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap) spin_lock(&uap->port.lock); if (swallow) goto next_char; - } + } #endif /* CONFIG_MAGIC_SYSRQ && CONFIG_SERIAL_CORE_CONSOLE */ /* A real serial line, record the character and status. */ @@ -317,7 +320,7 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap) if (uap->port.ignore_status_mask == 0xff || (r1 & uap->port.ignore_status_mask) == 0) { - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(tty, ch, flag); } if (r1 & Rx_OVR) tty_insert_flip_char(tty, 0, TTY_OVERRUN); @@ -341,7 +344,7 @@ static struct tty_struct *pmz_receive_chars(struct uart_pmac_port *uap) uap->curregs[R1] &= ~(EXT_INT_ENAB | TxINT_ENAB | RxINT_MASK); write_zsreg(uap, R1, uap->curregs[R1]); zssync(uap); - dev_err(&uap->dev->ofdev.dev, "pmz: rx irq flood !\n"); + pmz_error("pmz: rx irq flood !\n"); return tty; } @@ -369,7 +372,7 @@ static void pmz_status_handle(struct uart_pmac_port *uap) uart_handle_cts_change(&uap->port, !(status & CTS)); - wake_up_interruptible(&uap->port.info->delta_msr_wait); + wake_up_interruptible(&uap->port.state->port.delta_msr_wait); } if (status & BRK_ABRT) @@ -411,6 +414,17 @@ static void pmz_transmit_chars(struct uart_pmac_port *uap) goto ack_tx_int; } + /* Under some circumstances, we see interrupts reported for + * a closed channel. The interrupt mask in R1 is clear, but + * R3 still signals the interrupts and we see them when taking + * an interrupt for the other channel (this could be a qemu + * bug but since the ESCC doc doesn't specify precsiely whether + * R3 interrup status bits are masked by R1 interrupt enable + * bits, better safe than sorry). --BenH. + */ + if (!ZS_IS_OPEN(uap)) + goto ack_tx_int; + if (uap->port.x_char) { uap->flags |= PMACZILOG_FLAG_TX_ACTIVE; write_zsdata(uap, uap->port.x_char); @@ -420,9 +434,9 @@ static void pmz_transmit_chars(struct uart_pmac_port *uap) return; } - if (uap->port.info == NULL) + if (uap->port.state == NULL) goto ack_tx_int; - xmit = &uap->port.info->xmit; + xmit = &uap->port.state->xmit; if (uart_circ_empty(xmit)) { uart_write_wakeup(&uap->port); goto ack_tx_int; @@ -459,47 +473,47 @@ static irqreturn_t pmz_interrupt(int irq, void *dev_id) uap_a = pmz_get_port_A(uap); uap_b = uap_a->mate; - - spin_lock(&uap_a->port.lock); + + spin_lock(&uap_a->port.lock); r3 = read_zsreg(uap_a, R3); #ifdef DEBUG_HARD pmz_debug("irq, r3: %x\n", r3); #endif - /* Channel A */ + /* Channel A */ tty = NULL; - if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { + if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { write_zsreg(uap_a, R0, RES_H_IUS); zssync(uap_a); - if (r3 & CHAEXT) - pmz_status_handle(uap_a); + if (r3 & CHAEXT) + pmz_status_handle(uap_a); if (r3 & CHARxIP) tty = pmz_receive_chars(uap_a); - if (r3 & CHATxIP) - pmz_transmit_chars(uap_a); - rc = IRQ_HANDLED; - } - spin_unlock(&uap_a->port.lock); + if (r3 & CHATxIP) + pmz_transmit_chars(uap_a); + rc = IRQ_HANDLED; + } + spin_unlock(&uap_a->port.lock); if (tty != NULL) tty_flip_buffer_push(tty); if (uap_b->node == NULL) goto out; - spin_lock(&uap_b->port.lock); + spin_lock(&uap_b->port.lock); tty = NULL; if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { write_zsreg(uap_b, R0, RES_H_IUS); zssync(uap_b); - if (r3 & CHBEXT) - pmz_status_handle(uap_b); - if (r3 & CHBRxIP) - tty = pmz_receive_chars(uap_b); - if (r3 & CHBTxIP) - pmz_transmit_chars(uap_b); - rc = IRQ_HANDLED; - } - spin_unlock(&uap_b->port.lock); + if (r3 & CHBEXT) + pmz_status_handle(uap_b); + if (r3 & CHBRxIP) + tty = pmz_receive_chars(uap_b); + if (r3 & CHBTxIP) + pmz_transmit_chars(uap_b); + rc = IRQ_HANDLED; + } + spin_unlock(&uap_b->port.lock); if (tty != NULL) tty_flip_buffer_push(tty); @@ -655,7 +669,7 @@ static void pmz_start_tx(struct uart_port *port) port->icount.tx++; port->x_char = 0; } else { - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; write_zsdata(uap, xmit->buf[xmit->tail]); zssync(uap); @@ -707,7 +721,7 @@ static void pmz_enable_ms(struct uart_port *port) if (ZS_IS_ASLEEP(uap)) return; - /* NOTE: Not subject to 'transmitter active' rule. */ + /* NOTE: Not subject to 'transmitter active' rule. */ write_zsreg(uap, R15, uap->curregs[R15]); } } @@ -737,15 +751,19 @@ static void pmz_break_ctl(struct uart_port *port, int break_state) if (new_reg != uap->curregs[R5]) { uap->curregs[R5] = new_reg; - /* NOTE: Not subject to 'transmitter active' rule. */ - if (ZS_IS_ASLEEP(uap)) + /* NOTE: Not subject to 'transmitter active' rule. */ + if (ZS_IS_ASLEEP(uap)) { + spin_unlock_irqrestore(&port->lock, flags); return; + } write_zsreg(uap, R5, uap->curregs[R5]); } spin_unlock_irqrestore(&port->lock, flags); } +#ifdef CONFIG_PPC_PMAC + /* * Turn power on or off to the SCC and associated stuff * (port drivers, modem, IR port, etc.) @@ -781,6 +799,15 @@ static int pmz_set_scc_power(struct uart_pmac_port *uap, int state) return delay; } +#else + +static int pmz_set_scc_power(struct uart_pmac_port *uap, int state) +{ + return 0; +} + +#endif /* !CONFIG_PPC_PMAC */ + /* * FixZeroBug....Works around a bug in the SCC receving channel. * Inspired from Darwin code, 15 Sept. 2000 -DanM @@ -897,7 +924,6 @@ static int __pmz_startup(struct uart_pmac_port *uap) /* Remember status for DCD/CTS changes */ uap->prev_status = read_zsreg(uap, R0); - return pwr_delay; } @@ -944,9 +970,9 @@ static int pmz_startup(struct uart_port *port) } pmz_get_port_A(uap)->flags |= PMACZILOG_FLAG_IS_IRQ_ON; - if (request_irq(uap->port.irq, pmz_interrupt, IRQF_SHARED, "PowerMac Zilog", uap)) { - dev_err(&uap->dev->ofdev.dev, - "Unable to register zs interrupt handler.\n"); + if (request_irq(uap->port.irq, pmz_interrupt, IRQF_SHARED, + "SCC", uap)) { + pmz_error("Unable to register zs interrupt handler.\n"); pmz_set_scc_power(uap, 0); mutex_unlock(&pmz_irq_mutex); return -ENXIO; @@ -972,7 +998,7 @@ static int pmz_startup(struct uart_port *port) if (!ZS_IS_EXTCLK(uap)) uap->curregs[R1] |= EXT_INT_ENAB; write_zsreg(uap, R1, uap->curregs[R1]); - spin_unlock_irqrestore(&port->lock, flags); + spin_unlock_irqrestore(&port->lock, flags); pmz_debug("pmz: startup() done.\n"); @@ -992,7 +1018,7 @@ static void pmz_shutdown(struct uart_port *port) mutex_lock(&pmz_irq_mutex); /* Release interrupt handler */ - free_irq(uap->port.irq, uap); + free_irq(uap->port.irq, uap); spin_lock_irqsave(&port->lock, flags); @@ -1040,7 +1066,6 @@ static void pmz_convert_to_zs(struct uart_pmac_port *uap, unsigned int cflag, { int brg; - /* Switch to external clocking for IrDA high clock rates. That * code could be re-used for Midi interfaces with different * multipliers @@ -1187,7 +1212,7 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud) while ((read_zsreg(uap, R0) & Tx_BUF_EMP) == 0 || (read_zsreg(uap, R1) & ALL_SNT) == 0) { if (--t <= 0) { - dev_err(&uap->dev->ofdev.dev, "transmitter didn't drain\n"); + pmz_error("transmitter didn't drain\n"); return; } udelay(10); @@ -1203,7 +1228,7 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud) read_zsdata(uap); mdelay(10); if (--t <= 0) { - dev_err(&uap->dev->ofdev.dev, "receiver didn't drain\n"); + pmz_error("receiver didn't drain\n"); return; } } @@ -1212,20 +1237,19 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud) uap->curregs[R5] |= DTR; write_zsreg(uap, R5, uap->curregs[R5]); zssync(uap); - mdelay(1); + mdelay(1); /* Switch SCC to 19200 */ pmz_convert_to_zs(uap, CS8, 0, 19200); pmz_load_zsregs(uap, uap->curregs); - mdelay(1); + mdelay(1); /* Write get_version command byte */ write_zsdata(uap, 1); t = 5000; while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) { if (--t <= 0) { - dev_err(&uap->dev->ofdev.dev, - "irda_setup timed out on get_version byte\n"); + pmz_error("irda_setup timed out on get_version byte\n"); goto out; } udelay(10); @@ -1233,8 +1257,7 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud) version = read_zsdata(uap); if (version < 4) { - dev_info(&uap->dev->ofdev.dev, "IrDA: dongle version %d not supported\n", - version); + pmz_info("IrDA: dongle version %d not supported\n", version); goto out; } @@ -1243,18 +1266,16 @@ static void pmz_irda_setup(struct uart_pmac_port *uap, unsigned long *baud) t = 5000; while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0) { if (--t <= 0) { - dev_err(&uap->dev->ofdev.dev, - "irda_setup timed out on speed mode byte\n"); + pmz_error("irda_setup timed out on speed mode byte\n"); goto out; } udelay(10); } t = read_zsdata(uap); if (t != cmdbyte) - dev_err(&uap->dev->ofdev.dev, - "irda_setup speed mode byte = %x (%x)\n", t, cmdbyte); + pmz_error("irda_setup speed mode byte = %x (%x)\n", t, cmdbyte); - dev_info(&uap->dev->ofdev.dev, "IrDA setup for %ld bps, dongle version: %d\n", + pmz_info("IrDA setup for %ld bps, dongle version: %d\n", *baud, version); (void)read_zsdata(uap); @@ -1404,7 +1425,7 @@ static void pmz_poll_put_char(struct uart_port *port, unsigned char c) write_zsdata(uap, c); } -#endif +#endif /* CONFIG_CONSOLE_POLL */ static struct uart_ops pmz_pops = { .tx_empty = pmz_tx_empty, @@ -1429,6 +1450,8 @@ static struct uart_ops pmz_pops = { #endif }; +#ifdef CONFIG_PPC_PMAC + /* * Setup one port structure after probing, HW is down at this point, * Unlike sunzilog, we don't need to pre-init the spinlock as we don't @@ -1452,7 +1475,7 @@ static int __init pmz_init_port(struct uart_pmac_port *uap) return -ENODEV; uap->port.mapbase = r_ports.start; uap->port.membase = ioremap(uap->port.mapbase, 0x1000); - + uap->control_reg = uap->port.membase; uap->data_reg = uap->control_reg + 0x10; @@ -1579,7 +1602,7 @@ static void pmz_dispose_port(struct uart_pmac_port *uap) } /* - * Called upon match with an escc node in the devive-tree. + * Called upon match with an escc node in the device-tree. */ static int pmz_attach(struct macio_dev *mdev, const struct of_device_id *match) { @@ -1588,7 +1611,7 @@ static int pmz_attach(struct macio_dev *mdev, const struct of_device_id *match) /* Iterate the pmz_ports array to find a matching entry */ for (i = 0; i < MAX_ZS_PORTS; i++) - if (pmz_ports[i].node == mdev->ofdev.node) { + if (pmz_ports[i].node == mdev->ofdev.dev.of_node) { struct uart_pmac_port *uap = &pmz_ports[i]; uap->dev = mdev; @@ -1645,7 +1668,7 @@ static int pmz_suspend(struct macio_dev *mdev, pm_message_t pm_state) state = pmz_uart_reg.state + uap->port.line; mutex_lock(&pmz_irq_mutex); - mutex_lock(&state->mutex); + mutex_lock(&state->port.mutex); spin_lock_irqsave(&uap->port.lock, flags); @@ -1676,7 +1699,7 @@ static int pmz_suspend(struct macio_dev *mdev, pm_message_t pm_state) /* Shut the chip down */ pmz_set_scc_power(uap, 0); - mutex_unlock(&state->mutex); + mutex_unlock(&state->port.mutex); mutex_unlock(&pmz_irq_mutex); pmz_debug("suspend, switching complete\n"); @@ -1705,7 +1728,7 @@ static int pmz_resume(struct macio_dev *mdev) state = pmz_uart_reg.state + uap->port.line; mutex_lock(&pmz_irq_mutex); - mutex_lock(&state->mutex); + mutex_lock(&state->port.mutex); spin_lock_irqsave(&uap->port.lock, flags); if (!ZS_IS_OPEN(uap) && !ZS_IS_CONS(uap)) { @@ -1737,7 +1760,7 @@ static int pmz_resume(struct macio_dev *mdev) } bail: - mutex_unlock(&state->mutex); + mutex_unlock(&state->port.mutex); mutex_unlock(&pmz_irq_mutex); /* Right now, we deal with delay by blocking here, I'll be @@ -1801,7 +1824,7 @@ static int __init pmz_probe(void) pmz_ports[count].node = node_a; pmz_ports[count+1].node = node_b; pmz_ports[count].port.line = count; - pmz_ports[count+1].port.line = count+1; + pmz_ports[count+1].port.line = count+1; /* * Setup the ports for real @@ -1825,6 +1848,88 @@ next: return 0; } +#else + +extern struct platform_device scc_a_pdev, scc_b_pdev; + +static int __init pmz_init_port(struct uart_pmac_port *uap) +{ + struct resource *r_ports; + int irq; + + r_ports = platform_get_resource(uap->node, IORESOURCE_MEM, 0); + irq = platform_get_irq(uap->node, 0); + if (!r_ports || !irq) + return -ENODEV; + + uap->port.mapbase = r_ports->start; + uap->port.membase = (unsigned char __iomem *) r_ports->start; + uap->port.iotype = UPIO_MEM; + uap->port.irq = irq; + uap->port.uartclk = ZS_CLOCK; + uap->port.fifosize = 1; + uap->port.ops = &pmz_pops; + uap->port.type = PORT_PMAC_ZILOG; + uap->port.flags = 0; + + uap->control_reg = uap->port.membase; + uap->data_reg = uap->control_reg + 4; + uap->port_type = 0; + + pmz_convert_to_zs(uap, CS8, 0, 9600); + + return 0; +} + +static int __init pmz_probe(void) +{ + int err; + + pmz_ports_count = 0; + + pmz_ports[0].mate = &pmz_ports[1]; + pmz_ports[0].port.line = 0; + pmz_ports[0].flags = PMACZILOG_FLAG_IS_CHANNEL_A; + pmz_ports[0].node = &scc_a_pdev; + err = pmz_init_port(&pmz_ports[0]); + if (err) + return err; + pmz_ports_count++; + + pmz_ports[1].mate = &pmz_ports[0]; + pmz_ports[1].port.line = 1; + pmz_ports[1].flags = 0; + pmz_ports[1].node = &scc_b_pdev; + err = pmz_init_port(&pmz_ports[1]); + if (err) + return err; + pmz_ports_count++; + + return 0; +} + +static void pmz_dispose_port(struct uart_pmac_port *uap) +{ + memset(uap, 0, sizeof(struct uart_pmac_port)); +} + +static int __init pmz_attach(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < pmz_ports_count; i++) + if (pmz_ports[i].node == pdev) + return 0; + return -ENODEV; +} + +static int __exit pmz_detach(struct platform_device *pdev) +{ + return 0; +} + +#endif /* !CONFIG_PPC_PMAC */ + #ifdef CONFIG_SERIAL_PMACZILOG_CONSOLE static void pmz_console_write(struct console *con, const char *s, unsigned int count); @@ -1885,28 +1990,44 @@ err_out: return rc; } +#ifdef CONFIG_PPC_PMAC + static struct of_device_id pmz_match[] = { { - .name = "ch-a", + .name = "ch-a", }, { - .name = "ch-b", + .name = "ch-b", }, {}, }; MODULE_DEVICE_TABLE (of, pmz_match); -static struct macio_driver pmz_driver = -{ - .name = "pmac_zilog", - .match_table = pmz_match, +static struct macio_driver pmz_driver = { + .driver = { + .name = "pmac_zilog", + .owner = THIS_MODULE, + .of_match_table = pmz_match, + }, .probe = pmz_attach, .remove = pmz_detach, .suspend = pmz_suspend, - .resume = pmz_resume, + .resume = pmz_resume, +}; + +#else + +static struct platform_driver pmz_driver = { + .remove = __exit_p(pmz_detach), + .driver = { + .name = "scc", + .owner = THIS_MODULE, + }, }; +#endif /* !CONFIG_PPC_PMAC */ + static int __init init_pmz(void) { int rc, i; @@ -1941,19 +2062,27 @@ static int __init init_pmz(void) pmz_dispose_port(&pmz_ports[i]); return rc; } - + /* * Then we register the macio driver itself */ +#ifdef CONFIG_PPC_PMAC return macio_register_driver(&pmz_driver); +#else + return platform_driver_probe(&pmz_driver, pmz_attach); +#endif } static void __exit exit_pmz(void) { int i; +#ifdef CONFIG_PPC_PMAC /* Get rid of macio-driver (detach from macio) */ macio_unregister_driver(&pmz_driver); +#else + platform_driver_unregister(&pmz_driver); +#endif for (i = 0; i < pmz_ports_count; i++) { struct uart_pmac_port *uport = &pmz_ports[i]; @@ -2020,10 +2149,10 @@ static int __init pmz_console_setup(struct console *co, char *options) /* * XServe's default to 57600 bps */ - if (machine_is_compatible("RackMac1,1") - || machine_is_compatible("RackMac1,2") - || machine_is_compatible("MacRISC4")) - baud = 57600; + if (of_machine_is_compatible("RackMac1,1") + || of_machine_is_compatible("RackMac1,2") + || of_machine_is_compatible("MacRISC4")) + baud = 57600; /* * Check whether an invalid uart number has been specified, and diff --git a/drivers/serial/pmac_zilog.h b/drivers/serial/pmac_zilog.h index 570b0d9..cbc34fb 100644 --- a/drivers/serial/pmac_zilog.h +++ b/drivers/serial/pmac_zilog.h @@ -1,7 +1,15 @@ #ifndef __PMAC_ZILOG_H__ #define __PMAC_ZILOG_H__ -#define pmz_debug(fmt,arg...) dev_dbg(&uap->dev->ofdev.dev, fmt, ## arg) +#ifdef CONFIG_PPC_PMAC +#define pmz_debug(fmt, arg...) dev_dbg(&uap->dev->ofdev.dev, fmt, ## arg) +#define pmz_error(fmt, arg...) dev_err(&uap->dev->ofdev.dev, fmt, ## arg) +#define pmz_info(fmt, arg...) dev_info(&uap->dev->ofdev.dev, fmt, ## arg) +#else +#define pmz_debug(fmt, arg...) dev_dbg(&uap->node->dev, fmt, ## arg) +#define pmz_error(fmt, arg...) dev_err(&uap->node->dev, fmt, ## arg) +#define pmz_info(fmt, arg...) dev_info(&uap->node->dev, fmt, ## arg) +#endif /* * At most 2 ESCCs with 2 ports each @@ -17,6 +25,7 @@ struct uart_pmac_port { struct uart_port port; struct uart_pmac_port *mate; +#ifdef CONFIG_PPC_PMAC /* macio_dev for the escc holding this port (maybe be null on * early inited port) */ @@ -25,6 +34,9 @@ struct uart_pmac_port { * of "escc" node (ie. ch-a or ch-b) */ struct device_node *node; +#else + struct platform_device *node; +#endif /* Port type as obtained from device tree (IRDA, modem, ...) */ int port_type; @@ -55,10 +67,12 @@ struct uart_pmac_port { volatile u8 __iomem *control_reg; volatile u8 __iomem *data_reg; +#ifdef CONFIG_PPC_PMAC unsigned int tx_dma_irq; unsigned int rx_dma_irq; volatile struct dbdma_regs __iomem *tx_dma_regs; volatile struct dbdma_regs __iomem *rx_dma_regs; +#endif struct ktermios termios_cache; }; @@ -73,7 +87,7 @@ static inline struct uart_pmac_port *pmz_get_port_A(struct uart_pmac_port *uap) } /* - * Register acessors. Note that we don't need to enforce a recovery + * Register accessors. Note that we don't need to enforce a recovery * delay on PCI PowerMac hardware, it's dealt in HW by the MacIO chip, * though if we try to use this driver on older machines, we might have * to add it back @@ -113,7 +127,7 @@ static inline void zssync(struct uart_pmac_port *port) #define BRG_TO_BPS(brg, freq) ((freq) / 2 / ((brg) + 2)) #define BPS_TO_BRG(bps, freq) ((((freq) + (bps)) / (2 * (bps))) - 2) -#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */ +#define ZS_CLOCK 3686400 /* Z8530 RTxC input clock rate */ /* The Zilog register set */ @@ -171,7 +185,7 @@ static inline void zssync(struct uart_pmac_port *port) /* Write Register 3 */ -#define RxENABLE 0x1 /* Rx Enable */ +#define RxENABLE 0x1 /* Rx Enable */ #define SYNC_L_INH 0x2 /* Sync Character Load Inhibit */ #define ADD_SM 0x4 /* Address Search Mode (SDLC) */ #define RxCRC_ENAB 0x8 /* Rx CRC Enable */ @@ -185,7 +199,7 @@ static inline void zssync(struct uart_pmac_port *port) /* Write Register 4 */ -#define PAR_ENAB 0x1 /* Parity Enable */ +#define PAR_ENAB 0x1 /* Parity Enable */ #define PAR_EVEN 0x2 /* Parity Even/Odd* */ #define SYNC_ENAB 0 /* Sync Modes Enable */ @@ -210,7 +224,7 @@ static inline void zssync(struct uart_pmac_port *port) #define TxCRC_ENAB 0x1 /* Tx CRC Enable */ #define RTS 0x2 /* RTS */ #define SDLC_CRC 0x4 /* SDLC/CRC-16 */ -#define TxENABLE 0x8 /* Tx Enable */ +#define TxENABLE 0x8 /* Tx Enable */ #define SND_BRK 0x10 /* Send Break */ #define Tx5 0x0 /* Tx 5 bits (or less)/character */ #define Tx7 0x20 /* Tx 7 bits/character */ @@ -372,11 +386,11 @@ static inline void zssync(struct uart_pmac_port *port) #define ZS_TX_ACTIVE(UP) ((UP)->flags & PMACZILOG_FLAG_TX_ACTIVE) #define ZS_WANTS_MODEM_STATUS(UP) ((UP)->flags & PMACZILOG_FLAG_MODEM_STATUS) #define ZS_IS_IRDA(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRDA) -#define ZS_IS_INTMODEM(UP) ((UP)->flags & PMACZILOG_FLAG_IS_INTMODEM) +#define ZS_IS_INTMODEM(UP) ((UP)->flags & PMACZILOG_FLAG_IS_INTMODEM) #define ZS_HAS_DMA(UP) ((UP)->flags & PMACZILOG_FLAG_HAS_DMA) -#define ZS_IS_ASLEEP(UP) ((UP)->flags & PMACZILOG_FLAG_IS_ASLEEP) -#define ZS_IS_OPEN(UP) ((UP)->flags & PMACZILOG_FLAG_IS_OPEN) -#define ZS_IS_IRQ_ON(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRQ_ON) -#define ZS_IS_EXTCLK(UP) ((UP)->flags & PMACZILOG_FLAG_IS_EXTCLK) +#define ZS_IS_ASLEEP(UP) ((UP)->flags & PMACZILOG_FLAG_IS_ASLEEP) +#define ZS_IS_OPEN(UP) ((UP)->flags & PMACZILOG_FLAG_IS_OPEN) +#define ZS_IS_IRQ_ON(UP) ((UP)->flags & PMACZILOG_FLAG_IS_IRQ_ON) +#define ZS_IS_EXTCLK(UP) ((UP)->flags & PMACZILOG_FLAG_IS_EXTCLK) #endif /* __PMAC_ZILOG_H__ */ diff --git a/drivers/serial/pnx8xxx_uart.c b/drivers/serial/pnx8xxx_uart.c index 1bb8f1b..0aa75a9 100644 --- a/drivers/serial/pnx8xxx_uart.c +++ b/drivers/serial/pnx8xxx_uart.c @@ -100,7 +100,7 @@ static void pnx8xxx_mctrl_check(struct pnx8xxx_port *sport) if (changed & TIOCM_CTS) uart_handle_cts_change(&sport->port, status & TIOCM_CTS); - wake_up_interruptible(&sport->port.info->delta_msr_wait); + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); } /* @@ -112,7 +112,7 @@ static void pnx8xxx_timeout(unsigned long data) struct pnx8xxx_port *sport = (struct pnx8xxx_port *)data; unsigned long flags; - if (sport->port.info) { + if (sport->port.state) { spin_lock_irqsave(&sport->port.lock, flags); pnx8xxx_mctrl_check(sport); spin_unlock_irqrestore(&sport->port.lock, flags); @@ -181,7 +181,7 @@ static void pnx8xxx_enable_ms(struct uart_port *port) static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport) { - struct tty_struct *tty = sport->port.info->port.tty; + struct tty_struct *tty = sport->port.state->port.tty; unsigned int status, ch, flg; status = FIFO_TO_SM(serial_in(sport, PNX8XXX_FIFO)) | @@ -243,7 +243,7 @@ static void pnx8xxx_rx_chars(struct pnx8xxx_port *sport) static void pnx8xxx_tx_chars(struct pnx8xxx_port *sport) { - struct circ_buf *xmit = &sport->port.info->xmit; + struct circ_buf *xmit = &sport->port.state->xmit; if (sport->port.x_char) { serial_out(sport, PNX8XXX_FIFO, sport->port.x_char); diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c index a48a8a1..1102a39 100644 --- a/drivers/serial/pxa.c +++ b/drivers/serial/pxa.c @@ -44,6 +44,7 @@ #include <linux/serial_core.h> #include <linux/clk.h> #include <linux/io.h> +#include <linux/slab.h> struct uart_pxa_port { struct uart_port port; @@ -96,7 +97,7 @@ static void serial_pxa_stop_rx(struct uart_port *port) static inline void receive_chars(struct uart_pxa_port *up, int *status) { - struct tty_struct *tty = up->port.info->port.tty; + struct tty_struct *tty = up->port.state->port.tty; unsigned int ch, flag; int max_count = 256; @@ -161,7 +162,7 @@ static inline void receive_chars(struct uart_pxa_port *up, int *status) static void transmit_chars(struct uart_pxa_port *up) { - struct circ_buf *xmit = &up->port.info->xmit; + struct circ_buf *xmit = &up->port.state->xmit; int count; if (up->port.x_char) { @@ -220,7 +221,7 @@ static inline void check_modem_status(struct uart_pxa_port *up) if (status & UART_MSR_DCTS) uart_handle_cts_change(&up->port, status & UART_MSR_CTS); - wake_up_interruptible(&up->port.info->delta_msr_wait); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); } /* @@ -438,6 +439,7 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, unsigned char cval, fcr = 0; unsigned long flags; unsigned int baud, quot; + unsigned int dll; switch (termios->c_cflag & CSIZE) { case CS5: @@ -534,10 +536,18 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios, else up->mcr &= ~UART_MCR_AFE; - serial_out(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + serial_out(up, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ serial_out(up, UART_DLL, quot & 0xff); /* LS of divisor */ + + /* + * work around Errata #75 according to Intel(R) PXA27x Processor Family + * Specification Update (Nov 2005) + */ + dll = serial_in(up, UART_DLL); + WARN_ON(dll != (quot & 0xff)); + serial_out(up, UART_DLM, quot >> 8); /* MS of divisor */ - serial_out(up, UART_LCR, cval); /* reset DLAB */ + serial_out(up, UART_LCR, cval); /* reset DLAB */ up->lcr = cval; /* Save LCR */ serial_pxa_set_mctrl(&up->port, up->port.mctrl); serial_out(up, UART_FCR, fcr); @@ -726,9 +736,10 @@ static struct uart_driver serial_pxa_reg = { .cons = PXA_CONSOLE, }; -static int serial_pxa_suspend(struct platform_device *dev, pm_message_t state) +#ifdef CONFIG_PM +static int serial_pxa_suspend(struct device *dev) { - struct uart_pxa_port *sport = platform_get_drvdata(dev); + struct uart_pxa_port *sport = dev_get_drvdata(dev); if (sport) uart_suspend_port(&serial_pxa_reg, &sport->port); @@ -736,9 +747,9 @@ static int serial_pxa_suspend(struct platform_device *dev, pm_message_t state) return 0; } -static int serial_pxa_resume(struct platform_device *dev) +static int serial_pxa_resume(struct device *dev) { - struct uart_pxa_port *sport = platform_get_drvdata(dev); + struct uart_pxa_port *sport = dev_get_drvdata(dev); if (sport) uart_resume_port(&serial_pxa_reg, &sport->port); @@ -746,6 +757,12 @@ static int serial_pxa_resume(struct platform_device *dev) return 0; } +static const struct dev_pm_ops serial_pxa_pm_ops = { + .suspend = serial_pxa_suspend, + .resume = serial_pxa_resume, +}; +#endif + static int serial_pxa_probe(struct platform_device *dev) { struct uart_pxa_port *sport; @@ -825,11 +842,12 @@ static struct platform_driver serial_pxa_driver = { .probe = serial_pxa_probe, .remove = serial_pxa_remove, - .suspend = serial_pxa_suspend, - .resume = serial_pxa_resume, .driver = { .name = "pxa2xx-uart", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &serial_pxa_pm_ops, +#endif }, }; diff --git a/drivers/serial/s3c2400.c b/drivers/serial/s3c2400.c index fb00ed5..fed1a9a 100644 --- a/drivers/serial/s3c2400.c +++ b/drivers/serial/s3c2400.c @@ -76,7 +76,7 @@ static int s3c2400_serial_probe(struct platform_device *dev) return s3c24xx_serial_probe(dev, &s3c2400_uart_inf); } -static struct platform_driver s3c2400_serial_drv = { +static struct platform_driver s3c2400_serial_driver = { .probe = s3c2400_serial_probe, .remove = __devexit_p(s3c24xx_serial_remove), .driver = { @@ -85,16 +85,16 @@ static struct platform_driver s3c2400_serial_drv = { }, }; -s3c24xx_console_init(&s3c2400_serial_drv, &s3c2400_uart_inf); +s3c24xx_console_init(&s3c2400_serial_driver, &s3c2400_uart_inf); static inline int s3c2400_serial_init(void) { - return s3c24xx_serial_init(&s3c2400_serial_drv, &s3c2400_uart_inf); + return s3c24xx_serial_init(&s3c2400_serial_driver, &s3c2400_uart_inf); } static inline void s3c2400_serial_exit(void) { - platform_driver_unregister(&s3c2400_serial_drv); + platform_driver_unregister(&s3c2400_serial_driver); } module_init(s3c2400_serial_init); diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c index b5d7cbc..73f089d 100644 --- a/drivers/serial/s3c2410.c +++ b/drivers/serial/s3c2410.c @@ -2,7 +2,7 @@ * * Driver for Samsung S3C2410 SoC onboard UARTs. * - * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify @@ -88,7 +88,7 @@ static int s3c2410_serial_probe(struct platform_device *dev) return s3c24xx_serial_probe(dev, &s3c2410_uart_inf); } -static struct platform_driver s3c2410_serial_drv = { +static struct platform_driver s3c2410_serial_driver = { .probe = s3c2410_serial_probe, .remove = __devexit_p(s3c24xx_serial_remove), .driver = { @@ -97,16 +97,16 @@ static struct platform_driver s3c2410_serial_drv = { }, }; -s3c24xx_console_init(&s3c2410_serial_drv, &s3c2410_uart_inf); +s3c24xx_console_init(&s3c2410_serial_driver, &s3c2410_uart_inf); static int __init s3c2410_serial_init(void) { - return s3c24xx_serial_init(&s3c2410_serial_drv, &s3c2410_uart_inf); + return s3c24xx_serial_init(&s3c2410_serial_driver, &s3c2410_uart_inf); } static void __exit s3c2410_serial_exit(void) { - platform_driver_unregister(&s3c2410_serial_drv); + platform_driver_unregister(&s3c2410_serial_driver); } module_init(s3c2410_serial_init); diff --git a/drivers/serial/s3c2412.c b/drivers/serial/s3c2412.c index 11dcb90..1700b1a 100644 --- a/drivers/serial/s3c2412.c +++ b/drivers/serial/s3c2412.c @@ -2,7 +2,7 @@ * * Driver for Samsung S3C2412 and S3C2413 SoC onboard UARTs. * - * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify @@ -102,6 +102,7 @@ static struct s3c24xx_uart_info s3c2412_uart_inf = { .name = "Samsung S3C2412 UART", .type = PORT_S3C2412, .fifosize = 64, + .has_divslot = 1, .rx_fifomask = S3C2440_UFSTAT_RXMASK, .rx_fifoshift = S3C2440_UFSTAT_RXSHIFT, .rx_fifofull = S3C2440_UFSTAT_RXFULL, @@ -121,7 +122,7 @@ static int s3c2412_serial_probe(struct platform_device *dev) return s3c24xx_serial_probe(dev, &s3c2412_uart_inf); } -static struct platform_driver s3c2412_serial_drv = { +static struct platform_driver s3c2412_serial_driver = { .probe = s3c2412_serial_probe, .remove = __devexit_p(s3c24xx_serial_remove), .driver = { @@ -130,16 +131,16 @@ static struct platform_driver s3c2412_serial_drv = { }, }; -s3c24xx_console_init(&s3c2412_serial_drv, &s3c2412_uart_inf); +s3c24xx_console_init(&s3c2412_serial_driver, &s3c2412_uart_inf); static inline int s3c2412_serial_init(void) { - return s3c24xx_serial_init(&s3c2412_serial_drv, &s3c2412_uart_inf); + return s3c24xx_serial_init(&s3c2412_serial_driver, &s3c2412_uart_inf); } static inline void s3c2412_serial_exit(void) { - platform_driver_unregister(&s3c2412_serial_drv); + platform_driver_unregister(&s3c2412_serial_driver); } module_init(s3c2412_serial_init); diff --git a/drivers/serial/s3c2440.c b/drivers/serial/s3c2440.c index 06c5b0c..094cc39 100644 --- a/drivers/serial/s3c2440.c +++ b/drivers/serial/s3c2440.c @@ -2,7 +2,7 @@ * * Driver for Samsung S3C2440 and S3C2442 SoC onboard UARTs. * - * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify @@ -151,7 +151,7 @@ static int s3c2440_serial_probe(struct platform_device *dev) return s3c24xx_serial_probe(dev, &s3c2440_uart_inf); } -static struct platform_driver s3c2440_serial_drv = { +static struct platform_driver s3c2440_serial_driver = { .probe = s3c2440_serial_probe, .remove = __devexit_p(s3c24xx_serial_remove), .driver = { @@ -160,16 +160,16 @@ static struct platform_driver s3c2440_serial_drv = { }, }; -s3c24xx_console_init(&s3c2440_serial_drv, &s3c2440_uart_inf); +s3c24xx_console_init(&s3c2440_serial_driver, &s3c2440_uart_inf); static int __init s3c2440_serial_init(void) { - return s3c24xx_serial_init(&s3c2440_serial_drv, &s3c2440_uart_inf); + return s3c24xx_serial_init(&s3c2440_serial_driver, &s3c2440_uart_inf); } static void __exit s3c2440_serial_exit(void) { - platform_driver_unregister(&s3c2440_serial_drv); + platform_driver_unregister(&s3c2440_serial_driver); } module_init(s3c2440_serial_init); diff --git a/drivers/serial/s3c24a0.c b/drivers/serial/s3c24a0.c index 786a067..fad6083 100644 --- a/drivers/serial/s3c24a0.c +++ b/drivers/serial/s3c24a0.c @@ -6,7 +6,7 @@ * * Author: Sandeep Patil <sandeep.patil@azingo.com> * - * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify @@ -92,7 +92,7 @@ static int s3c24a0_serial_probe(struct platform_device *dev) return s3c24xx_serial_probe(dev, &s3c24a0_uart_inf); } -static struct platform_driver s3c24a0_serial_drv = { +static struct platform_driver s3c24a0_serial_driver = { .probe = s3c24a0_serial_probe, .remove = __devexit_p(s3c24xx_serial_remove), .driver = { @@ -101,16 +101,16 @@ static struct platform_driver s3c24a0_serial_drv = { }, }; -s3c24xx_console_init(&s3c24a0_serial_drv, &s3c24a0_uart_inf); +s3c24xx_console_init(&s3c24a0_serial_driver, &s3c24a0_uart_inf); static int __init s3c24a0_serial_init(void) { - return s3c24xx_serial_init(&s3c24a0_serial_drv, &s3c24a0_uart_inf); + return s3c24xx_serial_init(&s3c24a0_serial_driver, &s3c24a0_uart_inf); } static void __exit s3c24a0_serial_exit(void) { - platform_driver_unregister(&s3c24a0_serial_drv); + platform_driver_unregister(&s3c24a0_serial_driver); } module_init(s3c24a0_serial_init); diff --git a/drivers/serial/s3c6400.c b/drivers/serial/s3c6400.c index 48f1a37..4be92ab 100644 --- a/drivers/serial/s3c6400.c +++ b/drivers/serial/s3c6400.c @@ -122,7 +122,7 @@ static int s3c6400_serial_probe(struct platform_device *dev) return s3c24xx_serial_probe(dev, &s3c6400_uart_inf); } -static struct platform_driver s3c6400_serial_drv = { +static struct platform_driver s3c6400_serial_driver = { .probe = s3c6400_serial_probe, .remove = __devexit_p(s3c24xx_serial_remove), .driver = { @@ -131,16 +131,16 @@ static struct platform_driver s3c6400_serial_drv = { }, }; -s3c24xx_console_init(&s3c6400_serial_drv, &s3c6400_uart_inf); +s3c24xx_console_init(&s3c6400_serial_driver, &s3c6400_uart_inf); static int __init s3c6400_serial_init(void) { - return s3c24xx_serial_init(&s3c6400_serial_drv, &s3c6400_uart_inf); + return s3c24xx_serial_init(&s3c6400_serial_driver, &s3c6400_uart_inf); } static void __exit s3c6400_serial_exit(void) { - platform_driver_unregister(&s3c6400_serial_drv); + platform_driver_unregister(&s3c6400_serial_driver); } module_init(s3c6400_serial_init); diff --git a/drivers/serial/s5pv210.c b/drivers/serial/s5pv210.c new file mode 100644 index 0000000..6ebccd7 --- /dev/null +++ b/drivers/serial/s5pv210.c @@ -0,0 +1,162 @@ +/* linux/drivers/serial/s5pv210.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Based on drivers/serial/s3c6400.c + * + * Driver for Samsung S5PV210 SoC UARTs. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/module.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/serial_core.h> +#include <linux/serial.h> + +#include <asm/irq.h> +#include <mach/hardware.h> +#include <plat/regs-serial.h> +#include "samsung.h" + +static int s5pv210_serial_setsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + struct s3c2410_uartcfg *cfg = port->dev->platform_data; + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + if ((cfg->clocks_size) == 1) + return 0; + + if (strcmp(clk->name, "pclk") == 0) + ucon &= ~S5PV210_UCON_CLKMASK; + else if (strcmp(clk->name, "uclk1") == 0) + ucon |= S5PV210_UCON_CLKMASK; + else { + printk(KERN_ERR "unknown clock source %s\n", clk->name); + return -EINVAL; + } + + wr_regl(port, S3C2410_UCON, ucon); + return 0; +} + + +static int s5pv210_serial_getsource(struct uart_port *port, + struct s3c24xx_uart_clksrc *clk) +{ + struct s3c2410_uartcfg *cfg = port->dev->platform_data; + u32 ucon = rd_regl(port, S3C2410_UCON); + + clk->divisor = 1; + + if ((cfg->clocks_size) == 1) + return 0; + + switch (ucon & S5PV210_UCON_CLKMASK) { + case S5PV210_UCON_PCLK: + clk->name = "pclk"; + break; + case S5PV210_UCON_UCLK: + clk->name = "uclk1"; + break; + } + + return 0; +} + +static int s5pv210_serial_resetport(struct uart_port *port, + struct s3c2410_uartcfg *cfg) +{ + unsigned long ucon = rd_regl(port, S3C2410_UCON); + + ucon &= S5PV210_UCON_CLKMASK; + wr_regl(port, S3C2410_UCON, 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; +} + +#define S5PV210_UART_DEFAULT_INFO(fifo_size) \ + .name = "Samsung S5PV210 UART0", \ + .type = PORT_S3C6400, \ + .fifosize = fifo_size, \ + .has_divslot = 1, \ + .rx_fifomask = S5PV210_UFSTAT_RXMASK, \ + .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, \ + .rx_fifofull = S5PV210_UFSTAT_RXFULL, \ + .tx_fifofull = S5PV210_UFSTAT_TXFULL, \ + .tx_fifomask = S5PV210_UFSTAT_TXMASK, \ + .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT, \ + .get_clksrc = s5pv210_serial_getsource, \ + .set_clksrc = s5pv210_serial_setsource, \ + .reset_port = s5pv210_serial_resetport + +static struct s3c24xx_uart_info s5p_port_fifo256 = { + S5PV210_UART_DEFAULT_INFO(256), +}; + +static struct s3c24xx_uart_info s5p_port_fifo64 = { + S5PV210_UART_DEFAULT_INFO(64), +}; + +static struct s3c24xx_uart_info s5p_port_fifo16 = { + S5PV210_UART_DEFAULT_INFO(16), +}; + +static struct s3c24xx_uart_info *s5p_uart_inf[] = { + [0] = &s5p_port_fifo256, + [1] = &s5p_port_fifo64, + [2] = &s5p_port_fifo16, + [3] = &s5p_port_fifo16, +}; + +/* device management */ +static int s5p_serial_probe(struct platform_device *pdev) +{ + return s3c24xx_serial_probe(pdev, s5p_uart_inf[pdev->id]); +} + +static struct platform_driver s5p_serial_driver = { + .probe = s5p_serial_probe, + .remove = __devexit_p(s3c24xx_serial_remove), + .driver = { + .name = "s5pv210-uart", + .owner = THIS_MODULE, + }, +}; + +static int __init s5pv210_serial_console_init(void) +{ + return s3c24xx_serial_initconsole(&s5p_serial_driver, s5p_uart_inf); +} + +console_initcall(s5pv210_serial_console_init); + +static int __init s5p_serial_init(void) +{ + return s3c24xx_serial_init(&s5p_serial_driver, *s5p_uart_inf); +} + +static void __exit s5p_serial_exit(void) +{ + platform_driver_unregister(&s5p_serial_driver); +} + +module_init(s5p_serial_init); +module_exit(s5p_serial_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:s5pv210-uart"); +MODULE_DESCRIPTION("Samsung S5PV210 UART Driver support"); +MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>"); diff --git a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c index 94530f0..2199d81 100644 --- a/drivers/serial/sa1100.c +++ b/drivers/serial/sa1100.c @@ -117,7 +117,7 @@ static void sa1100_mctrl_check(struct sa1100_port *sport) if (changed & TIOCM_CTS) uart_handle_cts_change(&sport->port, status & TIOCM_CTS); - wake_up_interruptible(&sport->port.info->delta_msr_wait); + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); } /* @@ -129,7 +129,7 @@ static void sa1100_timeout(unsigned long data) struct sa1100_port *sport = (struct sa1100_port *)data; unsigned long flags; - if (sport->port.info) { + if (sport->port.state) { spin_lock_irqsave(&sport->port.lock, flags); sa1100_mctrl_check(sport); spin_unlock_irqrestore(&sport->port.lock, flags); @@ -189,7 +189,7 @@ static void sa1100_enable_ms(struct uart_port *port) static void sa1100_rx_chars(struct sa1100_port *sport) { - struct tty_struct *tty = sport->port.info->port.tty; + struct tty_struct *tty = sport->port.state->port.tty; unsigned int status, ch, flg; status = UTSR1_TO_SM(UART_GET_UTSR1(sport)) | @@ -239,7 +239,7 @@ sa1100_rx_chars(struct sa1100_port *sport) static void sa1100_tx_chars(struct sa1100_port *sport) { - struct circ_buf *xmit = &sport->port.info->xmit; + struct circ_buf *xmit = &sport->port.state->xmit; if (sport->port.x_char) { UART_PUT_CHAR(sport, sport->port.x_char); @@ -638,7 +638,7 @@ static void __init sa1100_init_ports(void) PPSR |= PPC_TXD1 | PPC_TXD3; } -void __init sa1100_register_uart_fns(struct sa1100_port_fns *fns) +void __devinit sa1100_register_uart_fns(struct sa1100_port_fns *fns) { if (fns->get_mctrl) sa1100_pops.get_mctrl = fns->get_mctrl; diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c index c8851a0..7ac2bf5 100644 --- a/drivers/serial/samsung.c +++ b/drivers/serial/samsung.c @@ -2,7 +2,7 @@ * * Driver core for Samsung SoC onboard UARTs. * - * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify @@ -196,7 +196,7 @@ s3c24xx_serial_rx_chars(int irq, void *dev_id) { struct s3c24xx_uart_port *ourport = dev_id; struct uart_port *port = &ourport->port; - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; unsigned int ufcon, ch, flag, ufstat, uerstat; int max_count = 64; @@ -281,7 +281,7 @@ static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id) { struct s3c24xx_uart_port *ourport = id; struct uart_port *port = &ourport->port; - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; int count = 256; if (port->x_char) { @@ -705,8 +705,13 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, if (ourport->info->has_divslot) { unsigned int div = ourport->baudclk_rate / baud; - udivslot = udivslot_table[div & 15]; - dbg("udivslot = %04x (div %d)\n", udivslot, div & 15); + if (cfg->has_fracval) { + udivslot = (div & 15); + dbg("fracval = %04x\n", udivslot); + } else { + udivslot = udivslot_table[div & 15]; + dbg("udivslot = %04x (div %d)\n", udivslot, div & 15); + } } switch (termios->c_cflag & CSIZE) { @@ -992,10 +997,10 @@ static int s3c24xx_serial_cpufreq_transition(struct notifier_block *nb, struct ktermios *termios; struct tty_struct *tty; - if (uport->info == NULL) + if (uport->state == NULL) goto exit; - tty = uport->info->port.tty; + tty = uport->state->port.tty; if (tty == NULL) goto exit; @@ -1096,7 +1101,7 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport, dbg("resource %p (%lx..%lx)\n", res, res->start, res->end); port->mapbase = res->start; - port->membase = S3C_VA_UART + res->start - (S3C_PA_UART & 0xfff00000); + port->membase = S3C_VA_UART + (res->start & 0xfffff); ret = platform_get_irq(platdev, 0); if (ret < 0) port->irq = 0; @@ -1271,7 +1276,7 @@ s3c24xx_serial_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... */ + /* fifo mode - check amount of data in fifo registers... */ ufstat = rd_regl(port, S3C2410_UFSTAT); return (ufstat & info->tx_fifofull) ? 0 : 1; @@ -1374,7 +1379,7 @@ s3c24xx_serial_get_options(struct uart_port *port, int *baud, * data. */ -static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info) +static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info **info) { struct s3c24xx_uart_port *ptr = s3c24xx_serial_ports; struct platform_device **platdev_ptr; @@ -1385,7 +1390,7 @@ static int s3c24xx_serial_init_ports(struct s3c24xx_uart_info *info) platdev_ptr = s3c24xx_uart_devs; for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++, ptr++, platdev_ptr++) { - s3c24xx_serial_init_port(ptr, info, *platdev_ptr); + s3c24xx_serial_init_port(ptr, info[i], *platdev_ptr); } return 0; @@ -1451,7 +1456,7 @@ static struct console s3c24xx_serial_console = { }; int s3c24xx_serial_initconsole(struct platform_driver *drv, - struct s3c24xx_uart_info *info) + struct s3c24xx_uart_info **info) { struct platform_device *dev = s3c24xx_uart_devs[0]; diff --git a/drivers/serial/samsung.h b/drivers/serial/samsung.h index d3fe315..0ac06a0 100644 --- a/drivers/serial/samsung.h +++ b/drivers/serial/samsung.h @@ -2,7 +2,7 @@ * * Driver for Samsung SoC onboard UARTs. * - * Ben Dooks, Copyright (c) 2003-2005,2008 Simtec Electronics + * Ben Dooks, Copyright (c) 2003-2008 Simtec Electronics * http://armlinux.simtec.co.uk/ * * This program is free software; you can redistribute it and/or modify @@ -75,19 +75,24 @@ extern int s3c24xx_serial_probe(struct platform_device *dev, extern int __devexit s3c24xx_serial_remove(struct platform_device *dev); extern int s3c24xx_serial_initconsole(struct platform_driver *drv, - struct s3c24xx_uart_info *uart); + struct s3c24xx_uart_info **uart); extern int s3c24xx_serial_init(struct platform_driver *drv, struct s3c24xx_uart_info *info); #ifdef CONFIG_SERIAL_SAMSUNG_CONSOLE -#define s3c24xx_console_init(__drv, __inf) \ -static int __init s3c_serial_console_init(void) \ -{ \ - return s3c24xx_serial_initconsole(__drv, __inf); \ -} \ - \ +#define s3c24xx_console_init(__drv, __inf) \ +static int __init s3c_serial_console_init(void) \ +{ \ + struct s3c24xx_uart_info *uinfo[CONFIG_SERIAL_SAMSUNG_UARTS]; \ + int i; \ + \ + for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++) \ + uinfo[i] = __inf; \ + return s3c24xx_serial_initconsole(__drv, uinfo); \ +} \ + \ console_initcall(s3c_serial_console_init) #else diff --git a/drivers/serial/sb1250-duart.c b/drivers/serial/sb1250-duart.c index 319e8b8..a2f2b32 100644 --- a/drivers/serial/sb1250-duart.c +++ b/drivers/serial/sb1250-duart.c @@ -384,13 +384,13 @@ static void sbd_receive_chars(struct sbd_port *sport) uart_insert_char(uport, status, M_DUART_OVRUN_ERR, ch, flag); } - tty_flip_buffer_push(uport->info->port.tty); + tty_flip_buffer_push(uport->state->port.tty); } static void sbd_transmit_chars(struct sbd_port *sport) { struct uart_port *uport = &sport->port; - struct circ_buf *xmit = &sport->port.info->xmit; + struct circ_buf *xmit = &sport->port.state->xmit; unsigned int mask; int stop_tx; @@ -440,7 +440,7 @@ static void sbd_status_handle(struct sbd_port *sport) if (delta & ((M_DUART_IN_PIN2_VAL | M_DUART_IN_PIN0_VAL) << S_DUART_IN_PIN_CHNG)) - wake_up_interruptible(&uport->info->delta_msr_wait); + wake_up_interruptible(&uport->state->port.delta_msr_wait); } static irqreturn_t sbd_interrupt(int irq, void *dev_id) diff --git a/drivers/serial/sc26xx.c b/drivers/serial/sc26xx.c index e0be11c..75038ad 100644 --- a/drivers/serial/sc26xx.c +++ b/drivers/serial/sc26xx.c @@ -140,8 +140,8 @@ static struct tty_struct *receive_chars(struct uart_port *port) char flag; u8 status; - if (port->info != NULL) /* Unopened serial console */ - tty = port->info->port.tty; + if (port->state != NULL) /* Unopened serial console */ + tty = port->state->port.tty; while (limit-- > 0) { status = READ_SC_PORT(port, SR); @@ -190,10 +190,10 @@ static void transmit_chars(struct uart_port *port) { struct circ_buf *xmit; - if (!port->info) + if (!port->state) return; - xmit = &port->info->xmit; + xmit = &port->state->xmit; if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { sc26xx_disable_irq(port, IMR_TXRDY); return; @@ -316,7 +316,7 @@ static void sc26xx_stop_tx(struct uart_port *port) /* port->lock held by caller. */ static void sc26xx_start_tx(struct uart_port *port) { - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; while (!uart_circ_empty(xmit)) { if (!(READ_SC_PORT(port, SR) & SR_TXRDY)) { diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index b0bb29d..460a72d 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -29,10 +29,9 @@ #include <linux/console.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> -#include <linux/serial_core.h> -#include <linux/smp_lock.h> #include <linux/device.h> #include <linux/serial.h> /* for serial_state and serial_icounter_struct */ +#include <linux/serial_core.h> #include <linux/delay.h> #include <linux/mutex.h> @@ -52,17 +51,15 @@ static struct lock_class_key port_lock_key; #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) -#define uart_users(state) ((state)->count + (state)->info.port.blocked_open) - #ifdef CONFIG_SERIAL_CORE_CONSOLE #define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line) #else #define uart_console(port) (0) #endif -static void uart_change_speed(struct uart_state *state, +static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, struct ktermios *old_termios); -static void uart_wait_until_sent(struct tty_struct *tty, int timeout); +static void __uart_wait_until_sent(struct uart_port *port, int timeout); static void uart_change_pm(struct uart_state *state, int pm_state); /* @@ -71,19 +68,19 @@ static void uart_change_pm(struct uart_state *state, int pm_state); */ void uart_write_wakeup(struct uart_port *port) { - struct uart_info *info = port->info; + struct uart_state *state = port->state; /* * This means you called this function _after_ the port was * closed. No cookie for you. */ - BUG_ON(!info); - tasklet_schedule(&info->tlet); + BUG_ON(!state); + tasklet_schedule(&state->tlet); } static void uart_stop(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; + struct uart_port *port = state->uart_port; unsigned long flags; spin_lock_irqsave(&port->lock, flags); @@ -94,9 +91,9 @@ static void uart_stop(struct tty_struct *tty) static void __uart_start(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; + struct uart_port *port = state->uart_port; - if (!uart_circ_empty(&state->info.xmit) && state->info.xmit.buf && + if (!uart_circ_empty(&state->xmit) && state->xmit.buf && !tty->stopped && !tty->hw_stopped) port->ops->start_tx(port); } @@ -104,7 +101,7 @@ static void __uart_start(struct tty_struct *tty) static void uart_start(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; + struct uart_port *port = state->uart_port; unsigned long flags; spin_lock_irqsave(&port->lock, flags); @@ -115,7 +112,7 @@ static void uart_start(struct tty_struct *tty) static void uart_tasklet_action(unsigned long data) { struct uart_state *state = (struct uart_state *)data; - tty_wakeup(state->info.port.tty); + tty_wakeup(state->port.tty); } static inline void @@ -139,14 +136,14 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) * Startup the port. This will be called once per open. All calls * will be serialised by the per-port mutex. */ -static int uart_startup(struct uart_state *state, int init_hw) +static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw) { - struct uart_info *info = &state->info; - struct uart_port *port = state->port; + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; unsigned long page; int retval = 0; - if (info->flags & UIF_INITIALIZED) + if (port->flags & ASYNC_INITIALIZED) return 0; /* @@ -154,51 +151,51 @@ static int uart_startup(struct uart_state *state, int init_hw) * once we have successfully opened the port. Also set * up the tty->alt_speed kludge */ - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + set_bit(TTY_IO_ERROR, &tty->flags); - if (port->type == PORT_UNKNOWN) + if (uport->type == PORT_UNKNOWN) return 0; /* * Initialise and allocate the transmit and temporary * buffer. */ - if (!info->xmit.buf) { + if (!state->xmit.buf) { /* This is protected by the per port mutex */ page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; - info->xmit.buf = (unsigned char *) page; - uart_circ_clear(&info->xmit); + state->xmit.buf = (unsigned char *) page; + uart_circ_clear(&state->xmit); } - retval = port->ops->startup(port); + retval = uport->ops->startup(uport); if (retval == 0) { if (init_hw) { /* * Initialise the hardware port settings. */ - uart_change_speed(state, NULL); + uart_change_speed(tty, state, NULL); /* * Setup the RTS and DTR signals once the * port is open and ready to respond. */ - if (info->port.tty->termios->c_cflag & CBAUD) - uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); + if (tty->termios->c_cflag & CBAUD) + uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR); } - if (info->flags & UIF_CTS_FLOW) { - spin_lock_irq(&port->lock); - if (!(port->ops->get_mctrl(port) & TIOCM_CTS)) - info->port.tty->hw_stopped = 1; - spin_unlock_irq(&port->lock); + if (port->flags & ASYNC_CTS_FLOW) { + spin_lock_irq(&uport->lock); + if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS)) + tty->hw_stopped = 1; + spin_unlock_irq(&uport->lock); } - info->flags |= UIF_INITIALIZED; + set_bit(ASYNCB_INITIALIZED, &port->flags); - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); } if (retval && capable(CAP_SYS_ADMIN)) @@ -212,11 +209,10 @@ static int uart_startup(struct uart_state *state, int init_hw) * DTR is dropped if the hangup on close termio flag is on. Calls to * uart_shutdown are serialised by the per-port semaphore. */ -static void uart_shutdown(struct uart_state *state) +static void uart_shutdown(struct tty_struct *tty, struct uart_state *state) { - struct uart_info *info = &state->info; - struct uart_port *port = state->port; - struct tty_struct *tty = info->port.tty; + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; /* * Set the TTY IO error marker @@ -224,14 +220,12 @@ static void uart_shutdown(struct uart_state *state) if (tty) set_bit(TTY_IO_ERROR, &tty->flags); - if (info->flags & UIF_INITIALIZED) { - info->flags &= ~UIF_INITIALIZED; - + if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) { /* * Turn off DTR and RTS early. */ if (!tty || (tty->termios->c_cflag & HUPCL)) - uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); + uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); /* * clear delta_msr_wait queue to avoid mem leaks: we may free @@ -240,30 +234,30 @@ static void uart_shutdown(struct uart_state *state) * any outstanding file descriptors should be pointing at * hung_up_tty_fops now. */ - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&port->delta_msr_wait); /* * Free the IRQ and disable the port. */ - port->ops->shutdown(port); + uport->ops->shutdown(uport); /* * Ensure that the IRQ handler isn't running on another CPU. */ - synchronize_irq(port->irq); + synchronize_irq(uport->irq); } /* * kill off our tasklet */ - tasklet_kill(&info->tlet); + tasklet_kill(&state->tlet); /* * Free the transmit buffer page. */ - if (info->xmit.buf) { - free_page((unsigned long)info->xmit.buf); - info->xmit.buf = NULL; + if (state->xmit.buf) { + free_page((unsigned long)state->xmit.buf); + state->xmit.buf = NULL; } } @@ -346,11 +340,11 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, if (flags == UPF_SPD_HI) altbaud = 57600; - if (flags == UPF_SPD_VHI) + else if (flags == UPF_SPD_VHI) altbaud = 115200; - if (flags == UPF_SPD_SHI) + else if (flags == UPF_SPD_SHI) altbaud = 230400; - if (flags == UPF_SPD_WARP) + else if (flags == UPF_SPD_WARP) altbaud = 460800; for (try = 0; try < 2; try++) { @@ -389,13 +383,20 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, } /* - * As a last resort, if the quotient is zero, - * default to 9600 bps + * As a last resort, if the range cannot be met then clip to + * the nearest chip supported rate. */ - if (!hung_up) - tty_termios_encode_baud_rate(termios, 9600, 9600); + if (!hung_up) { + if (baud <= min) + tty_termios_encode_baud_rate(termios, + min + 1, min + 1); + else + tty_termios_encode_baud_rate(termios, + max - 1, max - 1); + } } - + /* Should never happen */ + WARN_ON(1); return 0; } @@ -427,18 +428,18 @@ uart_get_divisor(struct uart_port *port, unsigned int baud) EXPORT_SYMBOL(uart_get_divisor); /* FIXME: Consistent locking policy */ -static void -uart_change_speed(struct uart_state *state, struct ktermios *old_termios) +static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, + struct ktermios *old_termios) { - struct tty_struct *tty = state->info.port.tty; - struct uart_port *port = state->port; + struct tty_port *port = &state->port; + struct uart_port *uport = state->uart_port; struct ktermios *termios; /* * If we have no tty, termios, or the port does not exist, * then we can't set the parameters for this port. */ - if (!tty || !tty->termios || port->type == PORT_UNKNOWN) + if (!tty || !tty->termios || uport->type == PORT_UNKNOWN) return; termios = tty->termios; @@ -447,20 +448,20 @@ uart_change_speed(struct uart_state *state, struct ktermios *old_termios) * Set flags based on termios cflag */ if (termios->c_cflag & CRTSCTS) - state->info.flags |= UIF_CTS_FLOW; + set_bit(ASYNCB_CTS_FLOW, &port->flags); else - state->info.flags &= ~UIF_CTS_FLOW; + clear_bit(ASYNCB_CTS_FLOW, &port->flags); if (termios->c_cflag & CLOCAL) - state->info.flags &= ~UIF_CHECK_CD; + clear_bit(ASYNCB_CHECK_CD, &port->flags); else - state->info.flags |= UIF_CHECK_CD; + set_bit(ASYNCB_CHECK_CD, &port->flags); - port->ops->set_termios(port, termios, old_termios); + uport->ops->set_termios(uport, termios, old_termios); } -static inline int -__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c) +static inline int __uart_put_char(struct uart_port *port, + struct circ_buf *circ, unsigned char c) { unsigned long flags; int ret = 0; @@ -482,7 +483,7 @@ static int uart_put_char(struct tty_struct *tty, unsigned char ch) { struct uart_state *state = tty->driver_data; - return __uart_put_char(state->port, &state->info.xmit, ch); + return __uart_put_char(state->uart_port, &state->xmit, ch); } static void uart_flush_chars(struct tty_struct *tty) @@ -490,8 +491,8 @@ static void uart_flush_chars(struct tty_struct *tty) uart_start(tty); } -static int -uart_write(struct tty_struct *tty, const unsigned char *buf, int count) +static int uart_write(struct tty_struct *tty, + const unsigned char *buf, int count) { struct uart_state *state = tty->driver_data; struct uart_port *port; @@ -508,8 +509,8 @@ uart_write(struct tty_struct *tty, const unsigned char *buf, int count) return -EL3HLT; } - port = state->port; - circ = &state->info.xmit; + port = state->uart_port; + circ = &state->xmit; if (!circ->buf) return 0; @@ -539,9 +540,9 @@ static int uart_write_room(struct tty_struct *tty) unsigned long flags; int ret; - spin_lock_irqsave(&state->port->lock, flags); - ret = uart_circ_chars_free(&state->info.xmit); - spin_unlock_irqrestore(&state->port->lock, flags); + spin_lock_irqsave(&state->uart_port->lock, flags); + ret = uart_circ_chars_free(&state->xmit); + spin_unlock_irqrestore(&state->uart_port->lock, flags); return ret; } @@ -551,9 +552,9 @@ static int uart_chars_in_buffer(struct tty_struct *tty) unsigned long flags; int ret; - spin_lock_irqsave(&state->port->lock, flags); - ret = uart_circ_chars_pending(&state->info.xmit); - spin_unlock_irqrestore(&state->port->lock, flags); + spin_lock_irqsave(&state->uart_port->lock, flags); + ret = uart_circ_chars_pending(&state->xmit); + spin_unlock_irqrestore(&state->uart_port->lock, flags); return ret; } @@ -572,11 +573,11 @@ static void uart_flush_buffer(struct tty_struct *tty) return; } - port = state->port; + port = state->uart_port; pr_debug("uart_flush_buffer(%d) called\n", tty->index); spin_lock_irqsave(&port->lock, flags); - uart_circ_clear(&state->info.xmit); + uart_circ_clear(&state->xmit); if (port->ops->flush_buffer) port->ops->flush_buffer(port); spin_unlock_irqrestore(&port->lock, flags); @@ -590,7 +591,7 @@ static void uart_flush_buffer(struct tty_struct *tty) static void uart_send_xchar(struct tty_struct *tty, char ch) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; + struct uart_port *port = state->uart_port; unsigned long flags; if (port->ops->send_xchar) @@ -613,13 +614,13 @@ static void uart_throttle(struct tty_struct *tty) uart_send_xchar(tty, STOP_CHAR(tty)); if (tty->termios->c_cflag & CRTSCTS) - uart_clear_mctrl(state->port, TIOCM_RTS); + uart_clear_mctrl(state->uart_port, TIOCM_RTS); } static void uart_unthrottle(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; + struct uart_port *port = state->uart_port; if (I_IXOFF(tty)) { if (port->x_char) @@ -635,46 +636,48 @@ static void uart_unthrottle(struct tty_struct *tty) static int uart_get_info(struct uart_state *state, struct serial_struct __user *retinfo) { - struct uart_port *port = state->port; + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; struct serial_struct tmp; memset(&tmp, 0, sizeof(tmp)); /* Ensure the state we copy is consistent and no hardware changes occur as we go */ - mutex_lock(&state->mutex); + mutex_lock(&port->mutex); - tmp.type = port->type; - tmp.line = port->line; - tmp.port = port->iobase; + tmp.type = uport->type; + tmp.line = uport->line; + tmp.port = uport->iobase; if (HIGH_BITS_OFFSET) - tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; - tmp.irq = port->irq; - tmp.flags = port->flags; - tmp.xmit_fifo_size = port->fifosize; - tmp.baud_base = port->uartclk / 16; - tmp.close_delay = state->close_delay / 10; - tmp.closing_wait = state->closing_wait == USF_CLOSING_WAIT_NONE ? + tmp.port_high = (long) uport->iobase >> HIGH_BITS_OFFSET; + tmp.irq = uport->irq; + tmp.flags = uport->flags; + tmp.xmit_fifo_size = uport->fifosize; + tmp.baud_base = uport->uartclk / 16; + tmp.close_delay = port->close_delay / 10; + tmp.closing_wait = port->closing_wait == ASYNC_CLOSING_WAIT_NONE ? ASYNC_CLOSING_WAIT_NONE : - state->closing_wait / 10; - tmp.custom_divisor = port->custom_divisor; - tmp.hub6 = port->hub6; - tmp.io_type = port->iotype; - tmp.iomem_reg_shift = port->regshift; - tmp.iomem_base = (void *)(unsigned long)port->mapbase; + port->closing_wait / 10; + tmp.custom_divisor = uport->custom_divisor; + tmp.hub6 = uport->hub6; + tmp.io_type = uport->iotype; + tmp.iomem_reg_shift = uport->regshift; + tmp.iomem_base = (void *)(unsigned long)uport->mapbase; - mutex_unlock(&state->mutex); + mutex_unlock(&port->mutex); if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; return 0; } -static int uart_set_info(struct uart_state *state, +static int uart_set_info(struct tty_struct *tty, struct uart_state *state, struct serial_struct __user *newinfo) { struct serial_struct new_serial; - struct uart_port *port = state->port; + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; unsigned long new_port; unsigned int change_irq, change_port, closing_wait; unsigned int old_custom_divisor, close_delay; @@ -691,58 +694,58 @@ static int uart_set_info(struct uart_state *state, new_serial.irq = irq_canonicalize(new_serial.irq); close_delay = new_serial.close_delay * 10; closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? - USF_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; + ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; /* - * This semaphore protects state->count. It is also + * This semaphore protects port->count. It is also * very useful to prevent opens. Also, take the * port configuration semaphore to make sure that a * module insertion/removal doesn't change anything * under us. */ - mutex_lock(&state->mutex); + mutex_lock(&port->mutex); - change_irq = !(port->flags & UPF_FIXED_PORT) - && new_serial.irq != port->irq; + change_irq = !(uport->flags & UPF_FIXED_PORT) + && new_serial.irq != uport->irq; /* * Since changing the 'type' of the port changes its resource * allocations, we should treat type changes the same as * IO port changes. */ - change_port = !(port->flags & UPF_FIXED_PORT) - && (new_port != port->iobase || - (unsigned long)new_serial.iomem_base != port->mapbase || - new_serial.hub6 != port->hub6 || - new_serial.io_type != port->iotype || - new_serial.iomem_reg_shift != port->regshift || - new_serial.type != port->type); - - old_flags = port->flags; + change_port = !(uport->flags & UPF_FIXED_PORT) + && (new_port != uport->iobase || + (unsigned long)new_serial.iomem_base != uport->mapbase || + new_serial.hub6 != uport->hub6 || + new_serial.io_type != uport->iotype || + new_serial.iomem_reg_shift != uport->regshift || + new_serial.type != uport->type); + + old_flags = uport->flags; new_flags = new_serial.flags; - old_custom_divisor = port->custom_divisor; + old_custom_divisor = uport->custom_divisor; if (!capable(CAP_SYS_ADMIN)) { retval = -EPERM; if (change_irq || change_port || - (new_serial.baud_base != port->uartclk / 16) || - (close_delay != state->close_delay) || - (closing_wait != state->closing_wait) || + (new_serial.baud_base != uport->uartclk / 16) || + (close_delay != port->close_delay) || + (closing_wait != port->closing_wait) || (new_serial.xmit_fifo_size && - new_serial.xmit_fifo_size != port->fifosize) || + new_serial.xmit_fifo_size != uport->fifosize) || (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0)) goto exit; - port->flags = ((port->flags & ~UPF_USR_MASK) | + uport->flags = ((uport->flags & ~UPF_USR_MASK) | (new_flags & UPF_USR_MASK)); - port->custom_divisor = new_serial.custom_divisor; + uport->custom_divisor = new_serial.custom_divisor; goto check_and_exit; } /* * Ask the low level driver to verify the settings. */ - if (port->ops->verify_port) - retval = port->ops->verify_port(port, &new_serial); + if (uport->ops->verify_port) + retval = uport->ops->verify_port(uport, &new_serial); if ((new_serial.irq >= nr_irqs) || (new_serial.irq < 0) || (new_serial.baud_base < 9600)) @@ -757,45 +760,45 @@ static int uart_set_info(struct uart_state *state, /* * Make sure that we are the sole user of this port. */ - if (uart_users(state) > 1) + if (tty_port_users(port) > 1) goto exit; /* * We need to shutdown the serial port at the old * port/type/irq combination. */ - uart_shutdown(state); + uart_shutdown(tty, state); } if (change_port) { unsigned long old_iobase, old_mapbase; unsigned int old_type, old_iotype, old_hub6, old_shift; - old_iobase = port->iobase; - old_mapbase = port->mapbase; - old_type = port->type; - old_hub6 = port->hub6; - old_iotype = port->iotype; - old_shift = port->regshift; + old_iobase = uport->iobase; + old_mapbase = uport->mapbase; + old_type = uport->type; + old_hub6 = uport->hub6; + old_iotype = uport->iotype; + old_shift = uport->regshift; /* * Free and release old regions */ if (old_type != PORT_UNKNOWN) - port->ops->release_port(port); + uport->ops->release_port(uport); - port->iobase = new_port; - port->type = new_serial.type; - port->hub6 = new_serial.hub6; - port->iotype = new_serial.io_type; - port->regshift = new_serial.iomem_reg_shift; - port->mapbase = (unsigned long)new_serial.iomem_base; + uport->iobase = new_port; + uport->type = new_serial.type; + uport->hub6 = new_serial.hub6; + uport->iotype = new_serial.io_type; + uport->regshift = new_serial.iomem_reg_shift; + uport->mapbase = (unsigned long)new_serial.iomem_base; /* * Claim and map the new regions */ - if (port->type != PORT_UNKNOWN) { - retval = port->ops->request_port(port); + if (uport->type != PORT_UNKNOWN) { + retval = uport->ops->request_port(uport); } else { /* Always success - Jean II */ retval = 0; @@ -806,19 +809,19 @@ static int uart_set_info(struct uart_state *state, * new port, try to restore the old settings. */ if (retval && old_type != PORT_UNKNOWN) { - port->iobase = old_iobase; - port->type = old_type; - port->hub6 = old_hub6; - port->iotype = old_iotype; - port->regshift = old_shift; - port->mapbase = old_mapbase; - retval = port->ops->request_port(port); + uport->iobase = old_iobase; + uport->type = old_type; + uport->hub6 = old_hub6; + uport->iotype = old_iotype; + uport->regshift = old_shift; + uport->mapbase = old_mapbase; + retval = uport->ops->request_port(uport); /* * If we failed to restore the old settings, * we fail like this. */ if (retval) - port->type = PORT_UNKNOWN; + uport->type = PORT_UNKNOWN; /* * We failed anyway. @@ -830,60 +833,63 @@ static int uart_set_info(struct uart_state *state, } if (change_irq) - port->irq = new_serial.irq; - if (!(port->flags & UPF_FIXED_PORT)) - port->uartclk = new_serial.baud_base * 16; - port->flags = (port->flags & ~UPF_CHANGE_MASK) | + uport->irq = new_serial.irq; + if (!(uport->flags & UPF_FIXED_PORT)) + uport->uartclk = new_serial.baud_base * 16; + uport->flags = (uport->flags & ~UPF_CHANGE_MASK) | (new_flags & UPF_CHANGE_MASK); - port->custom_divisor = new_serial.custom_divisor; - state->close_delay = close_delay; - state->closing_wait = closing_wait; + uport->custom_divisor = new_serial.custom_divisor; + port->close_delay = close_delay; + port->closing_wait = closing_wait; if (new_serial.xmit_fifo_size) - port->fifosize = new_serial.xmit_fifo_size; - if (state->info.port.tty) - state->info.port.tty->low_latency = - (port->flags & UPF_LOW_LATENCY) ? 1 : 0; + uport->fifosize = new_serial.xmit_fifo_size; + if (port->tty) + port->tty->low_latency = + (uport->flags & UPF_LOW_LATENCY) ? 1 : 0; check_and_exit: retval = 0; - if (port->type == PORT_UNKNOWN) + if (uport->type == PORT_UNKNOWN) goto exit; - if (state->info.flags & UIF_INITIALIZED) { - if (((old_flags ^ port->flags) & UPF_SPD_MASK) || - old_custom_divisor != port->custom_divisor) { + if (port->flags & ASYNC_INITIALIZED) { + if (((old_flags ^ uport->flags) & UPF_SPD_MASK) || + old_custom_divisor != uport->custom_divisor) { /* * If they're setting up a custom divisor or speed, * instead of clearing it, then bitch about it. No * need to rate-limit; it's CAP_SYS_ADMIN only. */ - if (port->flags & UPF_SPD_MASK) { + if (uport->flags & UPF_SPD_MASK) { char buf[64]; printk(KERN_NOTICE "%s sets custom speed on %s. This " "is deprecated.\n", current->comm, - tty_name(state->info.port.tty, buf)); + tty_name(port->tty, buf)); } - uart_change_speed(state, NULL); + uart_change_speed(tty, state, NULL); } } else - retval = uart_startup(state, 1); + retval = uart_startup(tty, state, 1); exit: - mutex_unlock(&state->mutex); + mutex_unlock(&port->mutex); return retval; } - -/* - * uart_get_lsr_info - get line status register info. - * Note: uart_ioctl protects us against hangups. +/** + * uart_get_lsr_info - get line status register info + * @tty: tty associated with the UART + * @state: UART being queried + * @value: returned modem value + * + * Note: uart_ioctl protects us against hangups. */ -static int uart_get_lsr_info(struct uart_state *state, - unsigned int __user *value) +static int uart_get_lsr_info(struct tty_struct *tty, + struct uart_state *state, unsigned int __user *value) { - struct uart_port *port = state->port; + struct uart_port *uport = state->uart_port; unsigned int result; - result = port->ops->tx_empty(port); + result = uport->ops->tx_empty(uport); /* * If we're about to load something into the transmit @@ -891,9 +897,9 @@ static int uart_get_lsr_info(struct uart_state *state, * avoid a race condition (depending on when the transmit * interrupt happens). */ - if (port->x_char || - ((uart_circ_chars_pending(&state->info.xmit) > 0) && - !state->info.port.tty->stopped && !state->info.port.tty->hw_stopped)) + if (uport->x_char || + ((uart_circ_chars_pending(&state->xmit) > 0) && + !tty->stopped && !tty->hw_stopped)) result &= ~TIOCSER_TEMT; return put_user(result, value); @@ -902,19 +908,20 @@ static int uart_get_lsr_info(struct uart_state *state, static int uart_tiocmget(struct tty_struct *tty, struct file *file) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; + struct tty_port *port = &state->port; + struct uart_port *uport = state->uart_port; int result = -EIO; - mutex_lock(&state->mutex); + mutex_lock(&port->mutex); if ((!file || !tty_hung_up_p(file)) && !(tty->flags & (1 << TTY_IO_ERROR))) { - result = port->mctrl; + result = uport->mctrl; - spin_lock_irq(&port->lock); - result |= port->ops->get_mctrl(port); - spin_unlock_irq(&port->lock); + spin_lock_irq(&uport->lock); + result |= uport->ops->get_mctrl(uport); + spin_unlock_irq(&uport->lock); } - mutex_unlock(&state->mutex); + mutex_unlock(&port->mutex); return result; } @@ -924,36 +931,39 @@ uart_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; int ret = -EIO; - mutex_lock(&state->mutex); + mutex_lock(&port->mutex); if ((!file || !tty_hung_up_p(file)) && !(tty->flags & (1 << TTY_IO_ERROR))) { - uart_update_mctrl(port, set, clear); + uart_update_mctrl(uport, set, clear); ret = 0; } - mutex_unlock(&state->mutex); + mutex_unlock(&port->mutex); return ret; } static int uart_break_ctl(struct tty_struct *tty, int break_state) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; + struct tty_port *port = &state->port; + struct uart_port *uport = state->uart_port; - mutex_lock(&state->mutex); + mutex_lock(&port->mutex); - if (port->type != PORT_UNKNOWN) - port->ops->break_ctl(port, break_state); + if (uport->type != PORT_UNKNOWN) + uport->ops->break_ctl(uport, break_state); - mutex_unlock(&state->mutex); + mutex_unlock(&port->mutex); return 0; } -static int uart_do_autoconfig(struct uart_state *state) +static int uart_do_autoconfig(struct tty_struct *tty,struct uart_state *state) { - struct uart_port *port = state->port; + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; int flags, ret; if (!capable(CAP_SYS_ADMIN)) @@ -964,33 +974,33 @@ static int uart_do_autoconfig(struct uart_state *state) * changing, and hence any extra opens of the port while * we're auto-configuring. */ - if (mutex_lock_interruptible(&state->mutex)) + if (mutex_lock_interruptible(&port->mutex)) return -ERESTARTSYS; ret = -EBUSY; - if (uart_users(state) == 1) { - uart_shutdown(state); + if (tty_port_users(port) == 1) { + uart_shutdown(tty, state); /* * If we already have a port type configured, * we must release its resources. */ - if (port->type != PORT_UNKNOWN) - port->ops->release_port(port); + if (uport->type != PORT_UNKNOWN) + uport->ops->release_port(uport); flags = UART_CONFIG_TYPE; - if (port->flags & UPF_AUTO_IRQ) + if (uport->flags & UPF_AUTO_IRQ) flags |= UART_CONFIG_IRQ; /* * This will claim the ports resources if * a port is found. */ - port->ops->config_port(port, flags); + uport->ops->config_port(uport, flags); - ret = uart_startup(state, 1); + ret = uart_startup(tty, state, 1); } - mutex_unlock(&state->mutex); + mutex_unlock(&port->mutex); return ret; } @@ -999,11 +1009,15 @@ static int uart_do_autoconfig(struct uart_state *state) * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was + * + * FIXME: This wants extracting into a common all driver implementation + * of TIOCMWAIT using tty_port. */ static int uart_wait_modem_status(struct uart_state *state, unsigned long arg) { - struct uart_port *port = state->port; + struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; DECLARE_WAITQUEUE(wait, current); struct uart_icount cprev, cnow; int ret; @@ -1011,20 +1025,20 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) /* * note the counters on entry */ - spin_lock_irq(&port->lock); - memcpy(&cprev, &port->icount, sizeof(struct uart_icount)); + spin_lock_irq(&uport->lock); + memcpy(&cprev, &uport->icount, sizeof(struct uart_icount)); /* * Force modem status interrupts on */ - port->ops->enable_ms(port); - spin_unlock_irq(&port->lock); + uport->ops->enable_ms(uport); + spin_unlock_irq(&uport->lock); - add_wait_queue(&state->info.delta_msr_wait, &wait); + add_wait_queue(&port->delta_msr_wait, &wait); for (;;) { - spin_lock_irq(&port->lock); - memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); - spin_unlock_irq(&port->lock); + spin_lock_irq(&uport->lock); + memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&uport->lock); set_current_state(TASK_INTERRUPTIBLE); @@ -1048,7 +1062,7 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) } current->state = TASK_RUNNING; - remove_wait_queue(&state->info.delta_msr_wait, &wait); + remove_wait_queue(&port->delta_msr_wait, &wait); return ret; } @@ -1059,30 +1073,30 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) * NB: both 1->0 and 0->1 transitions are counted except for * RI where only 0->1 is counted. */ -static int uart_get_count(struct uart_state *state, - struct serial_icounter_struct __user *icnt) +static int uart_get_icount(struct tty_struct *tty, + struct serial_icounter_struct *icount) { - struct serial_icounter_struct icount; + struct uart_state *state = tty->driver_data; struct uart_icount cnow; - struct uart_port *port = state->port; - - spin_lock_irq(&port->lock); - memcpy(&cnow, &port->icount, sizeof(struct uart_icount)); - spin_unlock_irq(&port->lock); + struct uart_port *uport = state->uart_port; + + spin_lock_irq(&uport->lock); + memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); + spin_unlock_irq(&uport->lock); + + icount->cts = cnow.cts; + icount->dsr = cnow.dsr; + icount->rng = cnow.rng; + icount->dcd = cnow.dcd; + icount->rx = cnow.rx; + icount->tx = cnow.tx; + icount->frame = cnow.frame; + icount->overrun = cnow.overrun; + icount->parity = cnow.parity; + icount->brk = cnow.brk; + icount->buf_overrun = cnow.buf_overrun; - icount.cts = cnow.cts; - icount.dsr = cnow.dsr; - icount.rng = cnow.rng; - icount.dcd = cnow.dcd; - icount.rx = cnow.rx; - icount.tx = cnow.tx; - icount.frame = cnow.frame; - icount.overrun = cnow.overrun; - icount.parity = cnow.parity; - icount.brk = cnow.brk; - icount.buf_overrun = cnow.buf_overrun; - - return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; + return 0; } /* @@ -1093,6 +1107,7 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg) { struct uart_state *state = tty->driver_data; + struct tty_port *port = &state->port; void __user *uarg = (void __user *)arg; int ret = -ENOIOCTLCMD; @@ -1106,11 +1121,11 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, break; case TIOCSSERIAL: - ret = uart_set_info(state, uarg); + ret = uart_set_info(tty, state, uarg); break; case TIOCSERCONFIG: - ret = uart_do_autoconfig(state); + ret = uart_do_autoconfig(tty, state); break; case TIOCSERGWILD: /* obsolete */ @@ -1134,16 +1149,12 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, case TIOCMIWAIT: ret = uart_wait_modem_status(state, arg); break; - - case TIOCGICOUNT: - ret = uart_get_count(state, uarg); - break; } if (ret != -ENOIOCTLCMD) goto out; - mutex_lock(&state->mutex); + mutex_lock(&port->mutex); if (tty_hung_up_p(filp)) { ret = -EIO; @@ -1156,18 +1167,18 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, */ switch (cmd) { case TIOCSERGETLSR: /* Get line status register */ - ret = uart_get_lsr_info(state, uarg); + ret = uart_get_lsr_info(tty, state, uarg); break; default: { - struct uart_port *port = state->port; - if (port->ops->ioctl) - ret = port->ops->ioctl(port, cmd, arg); + struct uart_port *uport = state->uart_port; + if (uport->ops->ioctl) + ret = uport->ops->ioctl(uport, cmd, arg); break; } } out_up: - mutex_unlock(&state->mutex); + mutex_unlock(&port->mutex); out: return ret; } @@ -1175,10 +1186,10 @@ out: static void uart_set_ldisc(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; + struct uart_port *uport = state->uart_port; - if (port->ops->set_ldisc) - port->ops->set_ldisc(port); + if (uport->ops->set_ldisc) + uport->ops->set_ldisc(uport, tty->termios->c_line); } static void uart_set_termios(struct tty_struct *tty, @@ -1203,37 +1214,35 @@ static void uart_set_termios(struct tty_struct *tty, return; } - uart_change_speed(state, old_termios); + uart_change_speed(tty, state, old_termios); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) - uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR); - + uart_clear_mctrl(state->uart_port, TIOCM_RTS | TIOCM_DTR); /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { + else if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { unsigned int mask = TIOCM_DTR; if (!(cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) mask |= TIOCM_RTS; - uart_set_mctrl(state->port, mask); + uart_set_mctrl(state->uart_port, mask); } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { - spin_lock_irqsave(&state->port->lock, flags); + spin_lock_irqsave(&state->uart_port->lock, flags); tty->hw_stopped = 0; __uart_start(tty); - spin_unlock_irqrestore(&state->port->lock, flags); + spin_unlock_irqrestore(&state->uart_port->lock, flags); } - /* Handle turning on CRTSCTS */ - if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { - spin_lock_irqsave(&state->port->lock, flags); - if (!(state->port->ops->get_mctrl(state->port) & TIOCM_CTS)) { + else if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { + spin_lock_irqsave(&state->uart_port->lock, flags); + if (!(state->uart_port->ops->get_mctrl(state->uart_port) & TIOCM_CTS)) { tty->hw_stopped = 1; - state->port->ops->stop_tx(state->port); + state->uart_port->ops->stop_tx(state->uart_port); } - spin_unlock_irqrestore(&state->port->lock, flags); + spin_unlock_irqrestore(&state->uart_port->lock, flags); } #if 0 /* @@ -1244,7 +1253,7 @@ static void uart_set_termios(struct tty_struct *tty, */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) - wake_up_interruptible(&info->port.open_wait); + wake_up_interruptible(&state->uart_port.open_wait); #endif } @@ -1256,41 +1265,49 @@ static void uart_set_termios(struct tty_struct *tty, static void uart_close(struct tty_struct *tty, struct file *filp) { struct uart_state *state = tty->driver_data; - struct uart_port *port; + struct tty_port *port; + struct uart_port *uport; + unsigned long flags; - BUG_ON(!kernel_locked()); + BUG_ON(!tty_locked()); - if (!state || !state->port) + if (!state) return; - port = state->port; + uport = state->uart_port; + port = &state->port; - pr_debug("uart_close(%d) called\n", port->line); + pr_debug("uart_close(%d) called\n", uport->line); - mutex_lock(&state->mutex); + mutex_lock(&port->mutex); + spin_lock_irqsave(&port->lock, flags); - if (tty_hung_up_p(filp)) + if (tty_hung_up_p(filp)) { + spin_unlock_irqrestore(&port->lock, flags); goto done; + } - if ((tty->count == 1) && (state->count != 1)) { + if ((tty->count == 1) && (port->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. state->count should always + * structure will be freed. port->count should always * be one in these conditions. If it's greater than * one, we've got real problems, since it means the * serial port won't be shutdown. */ printk(KERN_ERR "uart_close: bad serial port count; tty->count is 1, " - "state->count is %d\n", state->count); - state->count = 1; + "port->count is %d\n", port->count); + port->count = 1; } - if (--state->count < 0) { + if (--port->count < 0) { printk(KERN_ERR "uart_close: bad serial port count for %s: %d\n", - tty->name, state->count); - state->count = 0; + tty->name, port->count); + port->count = 0; } - if (state->count) + if (port->count) { + spin_unlock_irqrestore(&port->lock, flags); goto done; + } /* * Now we wait for the transmit buffer to clear; and we notify @@ -1298,63 +1315,74 @@ static void uart_close(struct tty_struct *tty, struct file *filp) * setting tty->closing. */ tty->closing = 1; + spin_unlock_irqrestore(&port->lock, flags); - if (state->closing_wait != USF_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, msecs_to_jiffies(state->closing_wait)); + if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE) { + /* + * hack: open-coded tty_wait_until_sent to avoid + * recursive tty_lock + */ + long timeout = msecs_to_jiffies(port->closing_wait); + if (wait_event_interruptible_timeout(tty->write_wait, + !tty_chars_in_buffer(tty), timeout) >= 0) + __uart_wait_until_sent(uport, timeout); + } /* * At this point, we stop accepting input. To do this, we * disable the receive line status interrupts. */ - if (state->info.flags & UIF_INITIALIZED) { + if (port->flags & ASYNC_INITIALIZED) { unsigned long flags; - spin_lock_irqsave(&port->lock, flags); - port->ops->stop_rx(port); - spin_unlock_irqrestore(&port->lock, flags); + spin_lock_irqsave(&uport->lock, flags); + uport->ops->stop_rx(uport); + spin_unlock_irqrestore(&uport->lock, flags); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ - uart_wait_until_sent(tty, port->timeout); + __uart_wait_until_sent(uport, uport->timeout); } - uart_shutdown(state); + uart_shutdown(tty, state); uart_flush_buffer(tty); tty_ldisc_flush(tty); + tty_port_tty_set(port, NULL); + spin_lock_irqsave(&port->lock, flags); tty->closing = 0; - state->info.port.tty = NULL; - if (state->info.port.blocked_open) { - if (state->close_delay) - msleep_interruptible(state->close_delay); - } else if (!uart_console(port)) { + if (port->blocked_open) { + spin_unlock_irqrestore(&port->lock, flags); + if (port->close_delay) + msleep_interruptible(port->close_delay); + spin_lock_irqsave(&port->lock, flags); + } else if (!uart_console(uport)) { + spin_unlock_irqrestore(&port->lock, flags); uart_change_pm(state, 3); + spin_lock_irqsave(&port->lock, flags); } /* * Wake up anyone trying to open this port. */ - state->info.flags &= ~UIF_NORMAL_ACTIVE; - wake_up_interruptible(&state->info.port.open_wait); + clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); + spin_unlock_irqrestore(&port->lock, flags); + wake_up_interruptible(&port->open_wait); - done: - mutex_unlock(&state->mutex); +done: + mutex_unlock(&port->mutex); } -static void uart_wait_until_sent(struct tty_struct *tty, int timeout) +static void __uart_wait_until_sent(struct uart_port *port, int timeout) { - struct uart_state *state = tty->driver_data; - struct uart_port *port = state->port; unsigned long char_time, expire; if (port->type == PORT_UNKNOWN || port->fifosize == 0) return; - lock_kernel(); - /* * Set the check interval to be 1/5 of the estimated time to * send a single character, and make it at least 1. The check @@ -1400,7 +1428,16 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) break; } set_current_state(TASK_RUNNING); /* might not be needed */ - unlock_kernel(); +} + +static void uart_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct uart_state *state = tty->driver_data; + struct uart_port *port = state->uart_port; + + tty_lock(); + __uart_wait_until_sent(port, timeout); + tty_unlock(); } /* @@ -1412,34 +1449,41 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout) static void uart_hangup(struct tty_struct *tty) { struct uart_state *state = tty->driver_data; - struct uart_info *info = &state->info; + struct tty_port *port = &state->port; + unsigned long flags; - BUG_ON(!kernel_locked()); - pr_debug("uart_hangup(%d)\n", state->port->line); + BUG_ON(!tty_locked()); + pr_debug("uart_hangup(%d)\n", state->uart_port->line); - mutex_lock(&state->mutex); - if (info->flags & UIF_NORMAL_ACTIVE) { + mutex_lock(&port->mutex); + if (port->flags & ASYNC_NORMAL_ACTIVE) { uart_flush_buffer(tty); - uart_shutdown(state); - state->count = 0; - info->flags &= ~UIF_NORMAL_ACTIVE; - info->port.tty = NULL; - wake_up_interruptible(&info->port.open_wait); - wake_up_interruptible(&info->delta_msr_wait); + uart_shutdown(tty, state); + spin_lock_irqsave(&port->lock, flags); + port->count = 0; + clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); + spin_unlock_irqrestore(&port->lock, flags); + tty_port_tty_set(port, NULL); + wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->delta_msr_wait); } - mutex_unlock(&state->mutex); + mutex_unlock(&port->mutex); } -/* - * Copy across the serial console cflag setting into the termios settings - * for the initial open of the port. This allows continuity between the - * kernel settings, and the settings init adopts when it opens the port - * for the first time. +/** + * uart_update_termios - update the terminal hw settings + * @tty: tty associated with UART + * @state: UART to update + * + * Copy across the serial console cflag setting into the termios settings + * for the initial open of the port. This allows continuity between the + * kernel settings, and the settings init adopts when it opens the port + * for the first time. */ -static void uart_update_termios(struct uart_state *state) +static void uart_update_termios(struct tty_struct *tty, + struct uart_state *state) { - struct tty_struct *tty = state->info.port.tty; - struct uart_port *port = state->port; + struct uart_port *port = state->uart_port; if (uart_console(port) && port->cons->cflag) { tty->termios->c_cflag = port->cons->cflag; @@ -1455,7 +1499,7 @@ static void uart_update_termios(struct uart_state *state) /* * Make termios settings take effect. */ - uart_change_speed(state, NULL); + uart_change_speed(tty, state, NULL); /* * And finally enable the RTS and DTR signals. @@ -1465,113 +1509,62 @@ static void uart_update_termios(struct uart_state *state) } } -/* - * Block the open until the port is ready. We must be called with - * the per-port semaphore held. - */ -static int -uart_block_til_ready(struct file *filp, struct uart_state *state) +static int uart_carrier_raised(struct tty_port *port) { - DECLARE_WAITQUEUE(wait, current); - struct uart_info *info = &state->info; - struct uart_port *port = state->port; - unsigned int mctrl; - - info->port.blocked_open++; - state->count--; - - add_wait_queue(&info->port.open_wait, &wait); - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - - /* - * If we have been hung up, tell userspace/restart open. - */ - if (tty_hung_up_p(filp) || info->port.tty == NULL) - break; - - /* - * If the port has been closed, tell userspace/restart open. - */ - if (!(info->flags & UIF_INITIALIZED)) - break; + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; + int mctrl; + spin_lock_irq(&uport->lock); + uport->ops->enable_ms(uport); + mctrl = uport->ops->get_mctrl(uport); + spin_unlock_irq(&uport->lock); + if (mctrl & TIOCM_CAR) + return 1; + return 0; +} - /* - * If non-blocking mode is set, or CLOCAL mode is set, - * we don't want to wait for the modem status lines to - * indicate that the port is ready. - * - * Also, if the port is not enabled/configured, we want - * to allow the open to succeed here. Note that we will - * have set TTY_IO_ERROR for a non-existant port. - */ - if ((filp->f_flags & O_NONBLOCK) || - (info->port.tty->termios->c_cflag & CLOCAL) || - (info->port.tty->flags & (1 << TTY_IO_ERROR))) - break; +static void uart_dtr_rts(struct tty_port *port, int onoff) +{ + struct uart_state *state = container_of(port, struct uart_state, port); + struct uart_port *uport = state->uart_port; - /* - * Set DTR to allow modem to know we're waiting. Do - * not set RTS here - we want to make sure we catch - * the data from the modem. - */ - if (info->port.tty->termios->c_cflag & CBAUD) - uart_set_mctrl(port, TIOCM_DTR); + if (onoff) { + uart_set_mctrl(uport, TIOCM_DTR | TIOCM_RTS); /* - * and wait for the carrier to indicate that the - * modem is ready for us. + * If this is the first open to succeed, + * adjust things to suit. */ - spin_lock_irq(&port->lock); - port->ops->enable_ms(port); - mctrl = port->ops->get_mctrl(port); - spin_unlock_irq(&port->lock); - if (mctrl & TIOCM_CAR) - break; - - mutex_unlock(&state->mutex); - schedule(); - mutex_lock(&state->mutex); - - if (signal_pending(current)) - break; + if (!test_and_set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags)) + uart_update_termios(port->tty, state); } - set_current_state(TASK_RUNNING); - remove_wait_queue(&info->port.open_wait, &wait); - - state->count++; - info->port.blocked_open--; - - if (signal_pending(current)) - return -ERESTARTSYS; - - if (!info->port.tty || tty_hung_up_p(filp)) - return -EAGAIN; - - return 0; + else + uart_clear_mctrl(uport, TIOCM_DTR | TIOCM_RTS); } static struct uart_state *uart_get(struct uart_driver *drv, int line) { struct uart_state *state; + struct tty_port *port; int ret = 0; state = drv->state + line; - if (mutex_lock_interruptible(&state->mutex)) { + port = &state->port; + if (mutex_lock_interruptible(&port->mutex)) { ret = -ERESTARTSYS; goto err; } - state->count++; - if (!state->port || state->port->flags & UPF_DEAD) { + port->count++; + if (!state->uart_port || state->uart_port->flags & UPF_DEAD) { ret = -ENXIO; goto err_unlock; } return state; err_unlock: - state->count--; - mutex_unlock(&state->mutex); + port->count--; + mutex_unlock(&port->mutex); err: return ERR_PTR(ret); } @@ -1590,9 +1583,10 @@ static int uart_open(struct tty_struct *tty, struct file *filp) { struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; struct uart_state *state; + struct tty_port *port; int retval, line = tty->index; - BUG_ON(!kernel_locked()); + BUG_ON(!tty_locked()); pr_debug("uart_open(%d) called\n", line); /* @@ -1606,16 +1600,18 @@ static int uart_open(struct tty_struct *tty, struct file *filp) /* * We take the semaphore inside uart_get to guarantee that we won't - * be re-entered while allocating the info structure, or while we + * be re-entered while allocating the state structure, or while we * request any IRQs that the driver may need. This also has the nice * side-effect that it delays the action of uart_hangup, so we can - * guarantee that info->port.tty will always contain something reasonable. + * guarantee that state->port.tty will always contain something + * reasonable. */ state = uart_get(drv, line); if (IS_ERR(state)) { retval = PTR_ERR(state); goto fail; } + port = &state->port; /* * Once we set tty->driver_data here, we are guaranteed that @@ -1623,49 +1619,40 @@ static int uart_open(struct tty_struct *tty, struct file *filp) * Any failures from here onwards should not touch the count. */ tty->driver_data = state; - state->port->info = &state->info; - tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; + state->uart_port->state = state; + tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty->alt_speed = 0; - state->info.port.tty = tty; + tty_port_tty_set(port, tty); /* * If the port is in the middle of closing, bail out now. */ if (tty_hung_up_p(filp)) { retval = -EAGAIN; - state->count--; - mutex_unlock(&state->mutex); + port->count--; + mutex_unlock(&port->mutex); goto fail; } /* * Make sure the device is in D0 state. */ - if (state->count == 1) + if (port->count == 1) uart_change_pm(state, 0); /* * Start up the serial port. */ - retval = uart_startup(state, 0); + retval = uart_startup(tty, state, 0); /* * If we succeeded, wait until the port is ready. */ + mutex_unlock(&port->mutex); if (retval == 0) - retval = uart_block_til_ready(filp, state); - mutex_unlock(&state->mutex); - - /* - * If this is the first open to succeed, adjust things to suit. - */ - if (retval == 0 && !(state->info.flags & UIF_NORMAL_ACTIVE)) { - state->info.flags |= UIF_NORMAL_ACTIVE; - - uart_update_termios(state); - } + retval = tty_port_block_til_ready(port, tty, filp); - fail: +fail: return retval; } @@ -1687,57 +1674,58 @@ static const char *uart_type(struct uart_port *port) static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) { struct uart_state *state = drv->state + i; + struct tty_port *port = &state->port; int pm_state; - struct uart_port *port = state->port; + struct uart_port *uport = state->uart_port; char stat_buf[32]; unsigned int status; int mmio; - if (!port) + if (!uport) return; - mmio = port->iotype >= UPIO_MEM; + mmio = uport->iotype >= UPIO_MEM; seq_printf(m, "%d: uart:%s %s%08llX irq:%d", - port->line, uart_type(port), + uport->line, uart_type(uport), mmio ? "mmio:0x" : "port:", - mmio ? (unsigned long long)port->mapbase - : (unsigned long long) port->iobase, - port->irq); + mmio ? (unsigned long long)uport->mapbase + : (unsigned long long)uport->iobase, + uport->irq); - if (port->type == PORT_UNKNOWN) { + if (uport->type == PORT_UNKNOWN) { seq_putc(m, '\n'); return; } if (capable(CAP_SYS_ADMIN)) { - mutex_lock(&state->mutex); + mutex_lock(&port->mutex); pm_state = state->pm_state; if (pm_state) uart_change_pm(state, 0); - spin_lock_irq(&port->lock); - status = port->ops->get_mctrl(port); - spin_unlock_irq(&port->lock); + spin_lock_irq(&uport->lock); + status = uport->ops->get_mctrl(uport); + spin_unlock_irq(&uport->lock); if (pm_state) uart_change_pm(state, pm_state); - mutex_unlock(&state->mutex); + mutex_unlock(&port->mutex); seq_printf(m, " tx:%d rx:%d", - port->icount.tx, port->icount.rx); - if (port->icount.frame) + uport->icount.tx, uport->icount.rx); + if (uport->icount.frame) seq_printf(m, " fe:%d", - port->icount.frame); - if (port->icount.parity) + uport->icount.frame); + if (uport->icount.parity) seq_printf(m, " pe:%d", - port->icount.parity); - if (port->icount.brk) + uport->icount.parity); + if (uport->icount.brk) seq_printf(m, " brk:%d", - port->icount.brk); - if (port->icount.overrun) + uport->icount.brk); + if (uport->icount.overrun) seq_printf(m, " oe:%d", - port->icount.overrun); + uport->icount.overrun); #define INFOBIT(bit, str) \ - if (port->mctrl & (bit)) \ + if (uport->mctrl & (bit)) \ strncat(stat_buf, (str), sizeof(stat_buf) - \ strlen(stat_buf) - 2) #define STATBIT(bit, str) \ @@ -1958,7 +1946,7 @@ EXPORT_SYMBOL_GPL(uart_set_options); static void uart_change_pm(struct uart_state *state, int pm_state) { - struct uart_port *port = state->port; + struct uart_port *port = state->uart_port; if (state->pm_state != pm_state) { if (port->ops->pm) @@ -1982,145 +1970,152 @@ static int serial_match_port(struct device *dev, void *data) return dev->devt == devt; /* Actually, only one tty per port */ } -int uart_suspend_port(struct uart_driver *drv, struct uart_port *port) +int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) { - struct uart_state *state = drv->state + port->line; + struct uart_state *state = drv->state + uport->line; + struct tty_port *port = &state->port; struct device *tty_dev; - struct uart_match match = {port, drv}; + struct uart_match match = {uport, drv}; + struct tty_struct *tty; - mutex_lock(&state->mutex); + mutex_lock(&port->mutex); - if (!console_suspend_enabled && uart_console(port)) { - /* we're going to avoid suspending serial console */ - mutex_unlock(&state->mutex); - return 0; - } + /* Must be inside the mutex lock until we convert to tty_port */ + tty = port->tty; - tty_dev = device_find_child(port->dev, &match, serial_match_port); + tty_dev = device_find_child(uport->dev, &match, serial_match_port); if (device_may_wakeup(tty_dev)) { - enable_irq_wake(port->irq); + if (!enable_irq_wake(uport->irq)) + uport->irq_wake = 1; put_device(tty_dev); - mutex_unlock(&state->mutex); + mutex_unlock(&port->mutex); return 0; } - port->suspended = 1; + if (console_suspend_enabled || !uart_console(uport)) + uport->suspended = 1; - if (state->info.flags & UIF_INITIALIZED) { - const struct uart_ops *ops = port->ops; + if (port->flags & ASYNC_INITIALIZED) { + const struct uart_ops *ops = uport->ops; int tries; - state->info.flags = (state->info.flags & ~UIF_INITIALIZED) - | UIF_SUSPENDED; + if (console_suspend_enabled || !uart_console(uport)) { + set_bit(ASYNCB_SUSPENDED, &port->flags); + clear_bit(ASYNCB_INITIALIZED, &port->flags); - spin_lock_irq(&port->lock); - ops->stop_tx(port); - ops->set_mctrl(port, 0); - ops->stop_rx(port); - spin_unlock_irq(&port->lock); + spin_lock_irq(&uport->lock); + ops->stop_tx(uport); + ops->set_mctrl(uport, 0); + ops->stop_rx(uport); + spin_unlock_irq(&uport->lock); + } /* * Wait for the transmitter to empty. */ - for (tries = 3; !ops->tx_empty(port) && tries; tries--) + for (tries = 3; !ops->tx_empty(uport) && tries; tries--) msleep(10); if (!tries) printk(KERN_ERR "%s%s%s%d: Unable to drain " "transmitter\n", - port->dev ? dev_name(port->dev) : "", - port->dev ? ": " : "", + uport->dev ? dev_name(uport->dev) : "", + uport->dev ? ": " : "", drv->dev_name, - drv->tty_driver->name_base + port->line); + drv->tty_driver->name_base + uport->line); - ops->shutdown(port); + if (console_suspend_enabled || !uart_console(uport)) + ops->shutdown(uport); } /* * Disable the console device before suspending. */ - if (uart_console(port)) - console_stop(port->cons); + if (console_suspend_enabled && uart_console(uport)) + console_stop(uport->cons); - uart_change_pm(state, 3); + if (console_suspend_enabled || !uart_console(uport)) + uart_change_pm(state, 3); - mutex_unlock(&state->mutex); + mutex_unlock(&port->mutex); return 0; } -int uart_resume_port(struct uart_driver *drv, struct uart_port *port) +int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) { - struct uart_state *state = drv->state + port->line; + struct uart_state *state = drv->state + uport->line; + struct tty_port *port = &state->port; struct device *tty_dev; - struct uart_match match = {port, drv}; - - mutex_lock(&state->mutex); + struct uart_match match = {uport, drv}; + struct ktermios termios; - if (!console_suspend_enabled && uart_console(port)) { - /* no need to resume serial console, it wasn't suspended */ - mutex_unlock(&state->mutex); - return 0; - } + mutex_lock(&port->mutex); - tty_dev = device_find_child(port->dev, &match, serial_match_port); - if (!port->suspended && device_may_wakeup(tty_dev)) { - disable_irq_wake(port->irq); - mutex_unlock(&state->mutex); + tty_dev = device_find_child(uport->dev, &match, serial_match_port); + if (!uport->suspended && device_may_wakeup(tty_dev)) { + if (uport->irq_wake) { + disable_irq_wake(uport->irq); + uport->irq_wake = 0; + } + mutex_unlock(&port->mutex); return 0; } - port->suspended = 0; + uport->suspended = 0; /* * Re-enable the console device after suspending. */ - if (uart_console(port)) { - struct ktermios termios; - + if (console_suspend_enabled && uart_console(uport)) { /* * First try to use the console cflag setting. */ memset(&termios, 0, sizeof(struct ktermios)); - termios.c_cflag = port->cons->cflag; + termios.c_cflag = uport->cons->cflag; /* * If that's unset, use the tty termios setting. */ - if (state->info.port.tty && termios.c_cflag == 0) - termios = *state->info.port.tty->termios; + if (port->tty && port->tty->termios && termios.c_cflag == 0) + termios = *(port->tty->termios); uart_change_pm(state, 0); - port->ops->set_termios(port, &termios, NULL); - console_start(port->cons); + uport->ops->set_termios(uport, &termios, NULL); + console_start(uport->cons); } - if (state->info.flags & UIF_SUSPENDED) { - const struct uart_ops *ops = port->ops; + if (port->flags & ASYNC_SUSPENDED) { + const struct uart_ops *ops = uport->ops; int ret; uart_change_pm(state, 0); - spin_lock_irq(&port->lock); - ops->set_mctrl(port, 0); - spin_unlock_irq(&port->lock); - ret = ops->startup(port); - if (ret == 0) { - uart_change_speed(state, NULL); - spin_lock_irq(&port->lock); - ops->set_mctrl(port, port->mctrl); - ops->start_tx(port); - spin_unlock_irq(&port->lock); - state->info.flags |= UIF_INITIALIZED; - } else { - /* - * Failed to resume - maybe hardware went away? - * Clear the "initialized" flag so we won't try - * to call the low level drivers shutdown method. - */ - uart_shutdown(state); + spin_lock_irq(&uport->lock); + ops->set_mctrl(uport, 0); + spin_unlock_irq(&uport->lock); + if (console_suspend_enabled || !uart_console(uport)) { + /* Protected by port mutex for now */ + struct tty_struct *tty = port->tty; + ret = ops->startup(uport); + if (ret == 0) { + if (tty) + uart_change_speed(tty, state, NULL); + spin_lock_irq(&uport->lock); + ops->set_mctrl(uport, uport->mctrl); + ops->start_tx(uport); + spin_unlock_irq(&uport->lock); + set_bit(ASYNCB_INITIALIZED, &port->flags); + } else { + /* + * Failed to resume - maybe hardware went away? + * Clear the "initialized" flag so we won't try + * to call the low level drivers shutdown method. + */ + uart_shutdown(tty, state); + } } - state->info.flags &= ~UIF_SUSPENDED; + clear_bit(ASYNCB_SUSPENDED, &port->flags); } - mutex_unlock(&state->mutex); + mutex_unlock(&port->mutex); return 0; } @@ -2143,6 +2138,7 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) case UPIO_AU: case UPIO_TSI: case UPIO_DWAPB: + case UPIO_DWAPB32: snprintf(address, sizeof(address), "MMIO 0x%llx", (unsigned long long)port->mapbase); break; @@ -2232,10 +2228,10 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options) int parity = 'n'; int flow = 'n'; - if (!state || !state->port) + if (!state || !state->uart_port) return -1; - port = state->port; + port = state->uart_port; if (!(port->ops->poll_get_char && port->ops->poll_put_char)) return -1; @@ -2253,10 +2249,10 @@ static int uart_poll_get_char(struct tty_driver *driver, int line) struct uart_state *state = drv->state + line; struct uart_port *port; - if (!state || !state->port) + if (!state || !state->uart_port) return -1; - port = state->port; + port = state->uart_port; return port->ops->poll_get_char(port); } @@ -2266,10 +2262,10 @@ static void uart_poll_put_char(struct tty_driver *driver, int line, char ch) struct uart_state *state = drv->state + line; struct uart_port *port; - if (!state || !state->port) + if (!state || !state->uart_port) return; - port = state->port; + port = state->uart_port; port->ops->poll_put_char(port, ch); } #endif @@ -2299,6 +2295,7 @@ static const struct tty_operations uart_ops = { #endif .tiocmget = uart_tiocmget, .tiocmset = uart_tiocmset, + .get_icount = uart_get_icount, #ifdef CONFIG_CONSOLE_POLL .poll_init = uart_poll_init, .poll_get_char = uart_poll_get_char, @@ -2306,6 +2303,11 @@ static const struct tty_operations uart_ops = { #endif }; +static const struct tty_port_operations uart_port_ops = { + .carrier_raised = uart_carrier_raised, + .dtr_rts = uart_dtr_rts, +}; + /** * uart_register_driver - register a driver with the uart core layer * @drv: low level driver structure @@ -2321,7 +2323,7 @@ static const struct tty_operations uart_ops = { */ int uart_register_driver(struct uart_driver *drv) { - struct tty_driver *normal = NULL; + struct tty_driver *normal; int i, retval; BUG_ON(drv->state); @@ -2331,13 +2333,12 @@ int uart_register_driver(struct uart_driver *drv) * we have a large number of ports to handle. */ drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL); - retval = -ENOMEM; if (!drv->state) goto out; - normal = alloc_tty_driver(drv->nr); + normal = alloc_tty_driver(drv->nr); if (!normal) - goto out; + goto out_kfree; drv->tty_driver = normal; @@ -2360,24 +2361,25 @@ int uart_register_driver(struct uart_driver *drv) */ for (i = 0; i < drv->nr; i++) { struct uart_state *state = drv->state + i; + struct tty_port *port = &state->port; - state->close_delay = 500; /* .5 seconds */ - state->closing_wait = 30000; /* 30 seconds */ - mutex_init(&state->mutex); - - tty_port_init(&state->info.port); - init_waitqueue_head(&state->info.delta_msr_wait); - tasklet_init(&state->info.tlet, uart_tasklet_action, + tty_port_init(port); + port->ops = &uart_port_ops; + port->close_delay = 500; /* .5 seconds */ + port->closing_wait = 30000; /* 30 seconds */ + tasklet_init(&state->tlet, uart_tasklet_action, (unsigned long)state); } retval = tty_register_driver(normal); - out: - if (retval < 0) { - put_tty_driver(normal); - kfree(drv->state); - } - return retval; + if (retval >= 0) + return retval; + + put_tty_driver(normal); +out_kfree: + kfree(drv->state); +out: + return -ENOMEM; } /** @@ -2408,69 +2410,71 @@ struct tty_driver *uart_console_device(struct console *co, int *index) /** * uart_add_one_port - attach a driver-defined port structure * @drv: pointer to the uart low level driver structure for this port - * @port: uart port structure to use for this port. + * @uport: uart port structure to use for this port. * * This allows the driver to register its own uart_port structure * with the core driver. The main purpose is to allow the low * level uart drivers to expand uart_port, rather than having yet * more levels of structures. */ -int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) +int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) { struct uart_state *state; + struct tty_port *port; int ret = 0; struct device *tty_dev; BUG_ON(in_interrupt()); - if (port->line >= drv->nr) + if (uport->line >= drv->nr) return -EINVAL; - state = drv->state + port->line; + state = drv->state + uport->line; + port = &state->port; mutex_lock(&port_mutex); - mutex_lock(&state->mutex); - if (state->port) { + mutex_lock(&port->mutex); + if (state->uart_port) { ret = -EINVAL; goto out; } - state->port = port; + state->uart_port = uport; state->pm_state = -1; - port->cons = drv->cons; - port->info = &state->info; + uport->cons = drv->cons; + uport->state = state; /* * If this port is a console, then the spinlock is already * initialised. */ - if (!(uart_console(port) && (port->cons->flags & CON_ENABLED))) { - spin_lock_init(&port->lock); - lockdep_set_class(&port->lock, &port_lock_key); + if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) { + spin_lock_init(&uport->lock); + lockdep_set_class(&uport->lock, &port_lock_key); } - uart_configure_port(drv, state, port); + uart_configure_port(drv, state, uport); /* * Register the port whether it's detected or not. This allows * setserial to be used to alter this ports parameters. */ - tty_dev = tty_register_device(drv->tty_driver, port->line, port->dev); + tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev); if (likely(!IS_ERR(tty_dev))) { device_init_wakeup(tty_dev, 1); device_set_wakeup_enable(tty_dev, 0); } else printk(KERN_ERR "Cannot register tty device on line %d\n", - port->line); + uport->line); /* * Ensure UPF_DEAD is not set. */ - port->flags &= ~UPF_DEAD; + uport->flags &= ~UPF_DEAD; out: - mutex_unlock(&state->mutex); + mutex_unlock(&port->mutex); mutex_unlock(&port_mutex); return ret; @@ -2479,22 +2483,22 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) /** * uart_remove_one_port - detach a driver defined port structure * @drv: pointer to the uart low level driver structure for this port - * @port: uart port structure for this port + * @uport: uart port structure for this port * * This unhooks (and hangs up) the specified port structure from the * core driver. No further calls will be made to the low-level code * for this port. */ -int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) +int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) { - struct uart_state *state = drv->state + port->line; - struct uart_info *info; + struct uart_state *state = drv->state + uport->line; + struct tty_port *port = &state->port; BUG_ON(in_interrupt()); - if (state->port != port) + if (state->uart_port != uport) printk(KERN_ALERT "Removing wrong port: %p != %p\n", - state->port, port); + state->uart_port, uport); mutex_lock(&port_mutex); @@ -2502,37 +2506,35 @@ int uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) * Mark the port "dead" - this prevents any opens from * succeeding while we shut down the port. */ - mutex_lock(&state->mutex); - port->flags |= UPF_DEAD; - mutex_unlock(&state->mutex); + mutex_lock(&port->mutex); + uport->flags |= UPF_DEAD; + mutex_unlock(&port->mutex); /* * Remove the devices from the tty layer */ - tty_unregister_device(drv->tty_driver, port->line); + tty_unregister_device(drv->tty_driver, uport->line); - info = &state->info; - if (info && info->port.tty) - tty_vhangup(info->port.tty); + if (port->tty) + tty_vhangup(port->tty); /* * Free the port IO and memory resources, if any. */ - if (port->type != PORT_UNKNOWN) - port->ops->release_port(port); + if (uport->type != PORT_UNKNOWN) + uport->ops->release_port(uport); /* * Indicate that there isn't a port here anymore. */ - port->type = PORT_UNKNOWN; + uport->type = PORT_UNKNOWN; /* * Kill the tasklet, and free resources. */ - if (info) - tasklet_kill(&info->tlet); + tasklet_kill(&state->tlet); - state->port = NULL; + state->uart_port = NULL; mutex_unlock(&port_mutex); return 0; @@ -2557,6 +2559,7 @@ int uart_match_port(struct uart_port *port1, struct uart_port *port2) case UPIO_AU: case UPIO_TSI: case UPIO_DWAPB: + case UPIO_DWAPB32: return (port1->mapbase == port2->mapbase); } return 0; diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index 79c9c5f..93760b2 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -45,8 +45,6 @@ #include <asm/io.h> #include <asm/system.h> -#include <pcmcia/cs_types.h> -#include <pcmcia/cs.h> #include <pcmcia/cistpl.h> #include <pcmcia/ciscode.h> #include <pcmcia/ds.h> @@ -54,14 +52,6 @@ #include "8250.h" -#ifdef PCMCIA_DEBUG -static int pc_debug = PCMCIA_DEBUG; -module_param(pc_debug, int, 0644); -#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) -static char *version = "serial_cs.c 1.134 2002/05/04 05:48:53 (David Hinds)"; -#else -#define DEBUG(n, args...) -#endif /*====================================================================*/ @@ -97,7 +87,6 @@ struct serial_info { int manfid; int prodid; int c950ctrl; - dev_node_t node[4]; int line[4]; const struct serial_quirk *quirk; }; @@ -113,6 +102,10 @@ struct serial_cfg_mem { * manfid 0x0160, 0x0104 * This card appears to have a 14.7456MHz clock. */ +/* Generic Modem: MD55x (GPRS/EDGE) have + * Elan VPU16551 UART with 14.7456MHz oscillator + * manfid 0x015D, 0x4C45 + */ static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_port *port) { port->uartclk = 14745600; @@ -120,25 +113,19 @@ static void quirk_setup_brainboxes_0104(struct pcmcia_device *link, struct uart_ static int quirk_post_ibm(struct pcmcia_device *link) { - conf_reg_t reg = { 0, CS_READ, 0x800, 0 }; - int last_ret, last_fn; + u8 val; + int ret; - last_ret = pcmcia_access_configuration_register(link, ®); - if (last_ret) { - last_fn = AccessConfigurationRegister; - goto cs_failed; - } - reg.Action = CS_WRITE; - reg.Value = reg.Value | 1; - last_ret = pcmcia_access_configuration_register(link, ®); - if (last_ret) { - last_fn = AccessConfigurationRegister; - goto cs_failed; - } + ret = pcmcia_read_config_byte(link, 0x800, &val); + if (ret) + goto failed; + + ret = pcmcia_write_config_byte(link, 0x800, val | 1); + if (ret) + goto failed; return 0; - cs_failed: - cs_error(link, last_fn, last_ret); + failed: return -ENODEV; } @@ -158,7 +145,8 @@ static void quirk_wakeup_oxsemi(struct pcmcia_device *link) { struct serial_info *info = link->priv; - outb(12, info->c950ctrl + 1); + if (info->c950ctrl) + outb(12, info->c950ctrl + 1); } /* request_region? oxsemi branch does no request_region too... */ @@ -194,10 +182,8 @@ static void quirk_config_socket(struct pcmcia_device *link) { struct serial_info *info = link->priv; - if (info->multi) { - link->conf.Present |= PRESENT_EXT_STATUS; - link->conf.ExtStatus = ESR_REQ_ATTN_ENA; - } + if (info->multi) + link->config_flags |= CONF_ENABLE_ESR; } static const struct serial_quirk quirks[] = { @@ -207,6 +193,11 @@ static const struct serial_quirk quirks[] = { .multi = -1, .setup = quirk_setup_brainboxes_0104, }, { + .manfid = 0x015D, + .prodid = 0x4C45, + .multi = -1, + .setup = quirk_setup_brainboxes_0104, + }, { .manfid = MANFID_IBM, .prodid = ~0, .multi = -1, @@ -271,19 +262,12 @@ static const struct serial_quirk quirks[] = { static int serial_config(struct pcmcia_device * link); -/*====================================================================== - - After a card is removed, serial_remove() will unregister - the serial device(s), and release the PCMCIA configuration. - -======================================================================*/ - static void serial_remove(struct pcmcia_device *link) { struct serial_info *info = link->priv; int i; - DEBUG(0, "serial_release(0x%p)\n", link); + dev_dbg(&link->dev, "serial_release\n"); /* * Recheck to see if the device is still configured. @@ -291,8 +275,6 @@ static void serial_remove(struct pcmcia_device *link) for (i = 0; i < info->ndev; i++) serial8250_unregister_port(info->line[i]); - info->p_dev->dev_node = NULL; - if (!info->slave) pcmcia_disable_device(link); } @@ -322,19 +304,11 @@ static int serial_resume(struct pcmcia_device *link) return 0; } -/*====================================================================== - - serial_attach() creates an "instance" of the driver, allocating - local data structures for one device. The device is registered - with Card Services. - -======================================================================*/ - static int serial_probe(struct pcmcia_device *link) { struct serial_info *info; - DEBUG(0, "serial_attach()\n"); + dev_dbg(&link->dev, "serial_attach()\n"); /* Create new serial device */ info = kzalloc(sizeof (*info), GFP_KERNEL); @@ -343,39 +317,18 @@ static int serial_probe(struct pcmcia_device *link) info->p_dev = link; link->priv = info; - link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; - link->io.NumPorts1 = 8; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING; - link->irq.IRQInfo1 = IRQ_LEVEL_ID; - link->conf.Attributes = CONF_ENABLE_IRQ; - if (do_sound) { - link->conf.Attributes |= CONF_ENABLE_SPKR; - link->conf.Status = CCSR_AUDIO_ENA; - } - link->conf.IntType = INT_MEMORY_AND_IO; + link->config_flags |= CONF_ENABLE_IRQ; + if (do_sound) + link->config_flags |= CONF_ENABLE_SPKR; return serial_config(link); } -/*====================================================================== - - This deletes a driver "instance". The device is de-registered - with Card Services. If it has been released, all local data - structures are freed. Otherwise, the structures will be freed - when the device is released. - -======================================================================*/ - static void serial_detach(struct pcmcia_device *link) { struct serial_info *info = link->priv; - DEBUG(0, "serial_detach(0x%p)\n", link); - - /* - * Ensure any outstanding scheduled tasks are completed. - */ - flush_scheduled_work(); + dev_dbg(&link->dev, "serial_detach\n"); /* * Ensure that the ports have been released. @@ -399,7 +352,7 @@ static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, port.irq = irq; port.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ; port.uartclk = 1843200; - port.dev = &handle_to_dev(handle); + port.dev = &handle->dev; if (buggy_uart) port.flags |= UPF_BUGGY_UART; @@ -414,11 +367,6 @@ static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, } info->line[info->ndev] = line; - sprintf(info->node[info->ndev].dev_name, "ttyS%d", line); - info->node[info->ndev].major = TTY_MAJOR; - info->node[info->ndev].minor = 0x40 + line; - if (info->ndev > 0) - info->node[info->ndev - 1].next = &info->node[info->ndev]; info->ndev++; return 0; @@ -426,61 +374,66 @@ static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, /*====================================================================*/ -static int -first_tuple(struct pcmcia_device *handle, tuple_t * tuple, cisparse_t * parse) +static int pfc_config(struct pcmcia_device *p_dev) { - int i; - i = pcmcia_get_first_tuple(handle, tuple); - if (i != 0) - return i; - i = pcmcia_get_tuple_data(handle, tuple); - if (i != 0) - return i; - return pcmcia_parse_tuple(tuple, parse); -} + unsigned int port = 0; + struct serial_info *info = p_dev->priv; + + if ((p_dev->resource[1]->end != 0) && + (resource_size(p_dev->resource[1]) == 8)) { + port = p_dev->resource[1]->start; + info->slave = 1; + } else if ((info->manfid == MANFID_OSITECH) && + (resource_size(p_dev->resource[0]) == 0x40)) { + port = p_dev->resource[0]->start + 0x28; + info->slave = 1; + } + if (info->slave) + return setup_serial(p_dev, info, port, p_dev->irq); -/*====================================================================*/ + dev_warn(&p_dev->dev, "no usable port range found, giving up\n"); + return -ENODEV; +} -static int simple_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - unsigned int vcc, - void *priv_data) +static int simple_config_check(struct pcmcia_device *p_dev, void *priv_data) { static const int size_table[2] = { 8, 16 }; int *try = priv_data; - if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) - p_dev->conf.Vpp = - cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; + if (p_dev->resource[0]->start == 0) + return -ENODEV; + + if ((*try & 0x1) == 0) + p_dev->io_lines = 16; - if ((cf->io.nwin > 0) && (cf->io.win[0].len == size_table[(*try >> 1)]) - && (cf->io.win[0].base != 0)) { - p_dev->io.BasePort1 = cf->io.win[0].base; - p_dev->io.IOAddrLines = ((*try & 0x1) == 0) ? - 16 : cf->io.flags & CISTPL_IO_LINES_MASK; - if (!pcmcia_request_io(p_dev, &p_dev->io)) - return 0; - } - return -EINVAL; + if (p_dev->resource[0]->end != size_table[(*try >> 1)]) + return -ENODEV; + + p_dev->resource[0]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + return pcmcia_request_io(p_dev); } static int simple_config_check_notpicky(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - unsigned int vcc, void *priv_data) { static const unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; int j; - if ((cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { - for (j = 0; j < 5; j++) { - p_dev->io.BasePort1 = base[j]; - p_dev->io.IOAddrLines = base[j] ? 16 : 3; - if (!pcmcia_request_io(p_dev, &p_dev->io)) - return 0; - } + if (p_dev->io_lines > 3) + return -ENODEV; + + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = 8; + + for (j = 0; j < 5; j++) { + p_dev->resource[0]->start = base[j]; + p_dev->io_lines = base[j] ? 16 : 3; + if (!pcmcia_request_io(p_dev)) + return 0; } return -ENODEV; } @@ -490,26 +443,9 @@ static int simple_config(struct pcmcia_device *link) struct serial_info *info = link->priv; int i = -ENODEV, try; - /* If the card is already configured, look up the port and irq */ - if (link->function_config) { - unsigned int port = 0; - if ((link->io.BasePort2 != 0) && - (link->io.NumPorts2 == 8)) { - port = link->io.BasePort2; - info->slave = 1; - } else if ((info->manfid == MANFID_OSITECH) && - (link->io.NumPorts1 == 0x40)) { - port = link->io.BasePort1 + 0x28; - info->slave = 1; - } - if (info->slave) { - return setup_serial(link, info, port, - link->irq.AssignedIRQ); - } - } - /* First pass: look for a config entry that looks normal. * Two tries: without IO aliases, then with aliases */ + link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_SET_IO; for (try = 0; try < 4; try++) if (!pcmcia_loop_config(link, simple_config_check, &try)) goto found_port; @@ -520,19 +456,12 @@ static int simple_config(struct pcmcia_device *link) if (!pcmcia_loop_config(link, simple_config_check_notpicky, NULL)) goto found_port; - printk(KERN_NOTICE - "serial_cs: no usable port range found, giving up\n"); - cs_error(link, RequestIO, i); + dev_warn(&link->dev, "no usable port range found, giving up\n"); return -1; found_port: - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) { - cs_error(link, RequestIRQ, i); - link->irq.AssignedIRQ = 0; - } if (info->multi && (info->manfid == MANFID_3COM)) - link->conf.ConfigIndex &= ~(0x08); + link->config_index &= ~(0x08); /* * Apply any configuration quirks. @@ -540,53 +469,50 @@ found_port: if (info->quirk && info->quirk->config) info->quirk->config(link); - i = pcmcia_request_configuration(link, &link->conf); - if (i != 0) { - cs_error(link, RequestConfiguration, i); + i = pcmcia_enable_device(link); + if (i != 0) return -1; - } - return setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ); + return setup_serial(link, info, link->resource[0]->start, link->irq); } -static int multi_config_check(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - unsigned int vcc, - void *priv_data) +static int multi_config_check(struct pcmcia_device *p_dev, void *priv_data) { - int *base2 = priv_data; + int *multi = priv_data; + + if (p_dev->resource[1]->end) + return -EINVAL; /* The quad port cards have bad CIS's, so just look for a window larger than 8 ports and assume it will be right */ - if ((cf->io.nwin == 1) && (cf->io.win[0].len > 8)) { - p_dev->io.BasePort1 = cf->io.win[0].base; - p_dev->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; - if (!pcmcia_request_io(p_dev, &p_dev->io)) { - *base2 = p_dev->io.BasePort1 + 8; - return 0; - } - } - return -ENODEV; + if (p_dev->resource[0]->end <= 8) + return -EINVAL; + + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = *multi * 8; + + if (pcmcia_request_io(p_dev)) + return -ENODEV; + return 0; } static int multi_config_check_notpicky(struct pcmcia_device *p_dev, - cistpl_cftable_entry_t *cf, - cistpl_cftable_entry_t *dflt, - unsigned int vcc, void *priv_data) { int *base2 = priv_data; - if (cf->io.nwin == 2) { - p_dev->io.BasePort1 = cf->io.win[0].base; - p_dev->io.BasePort2 = cf->io.win[1].base; - p_dev->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; - if (!pcmcia_request_io(p_dev, &p_dev->io)) { - *base2 = p_dev->io.BasePort2; - return 0; - } - } - return -ENODEV; + if (!p_dev->resource[0]->end || !p_dev->resource[1]->end) + return -ENODEV; + + p_dev->resource[0]->end = p_dev->resource[1]->end = 8; + p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + + if (pcmcia_request_io(p_dev)) + return -ENODEV; + + *base2 = p_dev->resource[0]->start + 8; + return 0; } static int multi_config(struct pcmcia_device *link) @@ -594,28 +520,23 @@ static int multi_config(struct pcmcia_device *link) struct serial_info *info = link->priv; int i, base2 = 0; + link->config_flags |= CONF_AUTO_SET_IO; /* First, look for a generic full-sized window */ - link->io.NumPorts1 = info->multi * 8; - if (pcmcia_loop_config(link, multi_config_check, &base2)) { + if (!pcmcia_loop_config(link, multi_config_check, &info->multi)) + base2 = link->resource[0]->start + 8; + else { /* If that didn't work, look for two windows */ - link->io.NumPorts1 = link->io.NumPorts2 = 8; info->multi = 2; if (pcmcia_loop_config(link, multi_config_check_notpicky, &base2)) { - printk(KERN_NOTICE "serial_cs: no usable port range" + dev_warn(&link->dev, "no usable port range " "found, giving up\n"); return -ENODEV; } } - i = pcmcia_request_irq(link, &link->irq); - if (i != 0) { - /* FIXME: comment does not fit, error handling does not fit */ - printk(KERN_NOTICE - "serial_cs: no usable port range found, giving up\n"); - cs_error(link, RequestIRQ, i); - link->irq.AssignedIRQ = 0; - } + if (!link->irq) + dev_warn(&link->dev, "no usable IRQ found, continuing...\n"); /* * Apply any configuration quirks. @@ -623,11 +544,9 @@ static int multi_config(struct pcmcia_device *link) if (info->quirk && info->quirk->config) info->quirk->config(link); - i = pcmcia_request_configuration(link, &link->conf); - if (i != 0) { - cs_error(link, RequestConfiguration, i); + i = pcmcia_enable_device(link); + if (i != 0) return -ENODEV; - } /* The Oxford Semiconductor OXCF950 cards are in fact single-port: * 8 registers are for the UART, the others are extra registers. @@ -637,14 +556,14 @@ static int multi_config(struct pcmcia_device *link) info->prodid == PRODID_POSSIO_GCC)) { int err; - if (link->conf.ConfigIndex == 1 || - link->conf.ConfigIndex == 3) { + if (link->config_index == 1 || + link->config_index == 3) { err = setup_serial(link, info, base2, - link->irq.AssignedIRQ); - base2 = link->io.BasePort1; + link->irq); + base2 = link->resource[0]->start; } else { - err = setup_serial(link, info, link->io.BasePort1, - link->irq.AssignedIRQ); + err = setup_serial(link, info, link->resource[0]->start, + link->irq); } info->c950ctrl = base2; @@ -658,64 +577,42 @@ static int multi_config(struct pcmcia_device *link) return 0; } - setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ); + setup_serial(link, info, link->resource[0]->start, link->irq); for (i = 0; i < info->multi - 1; i++) setup_serial(link, info, base2 + (8 * i), - link->irq.AssignedIRQ); + link->irq); return 0; } -/*====================================================================== +static int serial_check_for_multi(struct pcmcia_device *p_dev, void *priv_data) +{ + struct serial_info *info = p_dev->priv; - serial_config() is scheduled to run after a CARD_INSERTION event - is received, to configure the PCMCIA socket, and to make the - serial device available to the system. + if (!p_dev->resource[0]->end) + return -EINVAL; + + if ((!p_dev->resource[1]->end) && (p_dev->resource[0]->end % 8 == 0)) + info->multi = p_dev->resource[0]->end >> 3; + + if ((p_dev->resource[1]->end) && (p_dev->resource[0]->end == 8) + && (p_dev->resource[1]->end == 8)) + info->multi = 2; + + return 0; /* break */ +} -======================================================================*/ static int serial_config(struct pcmcia_device * link) { struct serial_info *info = link->priv; - struct serial_cfg_mem *cfg_mem; - tuple_t *tuple; - u_char *buf; - cisparse_t *parse; - cistpl_cftable_entry_t *cf; - int i, last_ret, last_fn; - - DEBUG(0, "serial_config(0x%p)\n", link); + int i; - cfg_mem = kmalloc(sizeof(struct serial_cfg_mem), GFP_KERNEL); - if (!cfg_mem) - goto failed; - - tuple = &cfg_mem->tuple; - parse = &cfg_mem->parse; - cf = &parse->cftable_entry; - buf = cfg_mem->buf; - - tuple->TupleData = (cisdata_t *) buf; - tuple->TupleOffset = 0; - tuple->TupleDataMax = 255; - tuple->Attributes = 0; - - /* Get configuration register information */ - tuple->DesiredTuple = CISTPL_CONFIG; - last_ret = first_tuple(link, tuple, parse); - if (last_ret != 0) { - last_fn = ParseTuple; - goto cs_failed; - } - link->conf.ConfigBase = parse->config.base; - link->conf.Present = parse->config.rmask[0]; + dev_dbg(&link->dev, "serial_config\n"); /* Is this a compliant multifunction card? */ - tuple->DesiredTuple = CISTPL_LONGLINK_MFC; - tuple->Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK; - info->multi = (first_tuple(link, tuple, parse) == 0); + info->multi = (link->socket->functions > 1); /* Is this a multiport card? */ - tuple->DesiredTuple = CISTPL_MANFID; info->manfid = link->manf_id; info->prodid = link->card_id; @@ -730,20 +627,12 @@ static int serial_config(struct pcmcia_device * link) /* Another check for dual-serial cards: look for either serial or multifunction cards that ask for appropriate IO port ranges */ - tuple->DesiredTuple = CISTPL_FUNCID; if ((info->multi == 0) && (link->has_func_id) && + (link->socket->pcmcia_pfc == 0) && ((link->func_id == CISTPL_FUNCID_MULTI) || - (link->func_id == CISTPL_FUNCID_SERIAL))) { - tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY; - if (first_tuple(link, tuple, parse) == 0) { - if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0)) - info->multi = cf->io.win[0].len >> 3; - if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) && - (cf->io.win[1].len == 8)) - info->multi = 2; - } - } + (link->func_id == CISTPL_FUNCID_SERIAL))) + pcmcia_loop_config(link, serial_check_for_multi, info); /* * Apply any multi-port quirk. @@ -751,12 +640,18 @@ static int serial_config(struct pcmcia_device * link) if (info->quirk && info->quirk->multi != -1) info->multi = info->quirk->multi; - if (info->multi > 1) - multi_config(link); + dev_info(&link->dev, + "trying to set up [0x%04x:0x%04x] (pfc: %d, multi: %d, quirk: %p)\n", + link->manf_id, link->card_id, + link->socket->pcmcia_pfc, info->multi, info->quirk); + if (link->socket->pcmcia_pfc) + i = pfc_config(link); + else if (info->multi > 1) + i = multi_config(link); else - simple_config(link); + i = simple_config(link); - if (info->ndev == 0) + if (i || info->ndev == 0) goto failed; /* @@ -767,15 +662,11 @@ static int serial_config(struct pcmcia_device * link) if (info->quirk->post(link)) goto failed; - link->dev_node = &info->node[0]; - kfree(cfg_mem); return 0; -cs_failed: - cs_error(link, last_fn, last_ret); failed: + dev_warn(&link->dev, "failed to initialize\n"); serial_remove(link); - kfree(cfg_mem); return -ENODEV; } @@ -783,6 +674,8 @@ static struct pcmcia_device_id serial_ids[] = { PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0057, 0x0021), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0089, 0x110a), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0104, 0x000a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0d0a), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0e0a), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0xea15), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0109, 0x0501), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0138, 0x110a), @@ -792,8 +685,6 @@ static struct pcmcia_device_id serial_ids[] = { PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x016c, 0x0081), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x021b, 0x0101), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x08a1, 0xc0ab), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0d0a), - PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0105, 0x0e0a), PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63), PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63), PCMCIA_PFC_DEVICE_PROD_ID123(1, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef), @@ -804,6 +695,7 @@ static struct pcmcia_device_id serial_ids[] = { PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "REM10", 0x2e3ee845, 0x76df1d29), PCMCIA_PFC_DEVICE_PROD_ID13(1, "Xircom", "XEM5600", 0x2e3ee845, 0xf1403719), PCMCIA_PFC_DEVICE_PROD_ID12(1, "AnyCom", "Fast Ethernet + 56K COMBO", 0x578ba6e7, 0xb0ac62c4), + PCMCIA_PFC_DEVICE_PROD_ID12(1, "ATKK", "LM33-PCM-T", 0xba9eb7e2, 0x077c174e), PCMCIA_PFC_DEVICE_PROD_ID12(1, "D-Link", "DME336T", 0x1a424a1c, 0xb23897ff), PCMCIA_PFC_DEVICE_PROD_ID12(1, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c), PCMCIA_PFC_DEVICE_PROD_ID12(1, "Grey Cell", "GCS3000", 0x2a151fac, 0x48b932ae), @@ -818,6 +710,7 @@ static struct pcmcia_device_id serial_ids[] = { PCMCIA_PFC_DEVICE_PROD_ID12(1, "PCMCIAs", "LanModem", 0xdcfe12d3, 0xc67c648f), PCMCIA_PFC_DEVICE_PROD_ID12(1, "TDK", "GlobalNetworker 3410/3412", 0x1eae9475, 0xd9a93bed), PCMCIA_PFC_DEVICE_PROD_ID12(1, "Xircom", "CreditCard Ethernet+Modem II", 0x2e3ee845, 0xeca401bf), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0e01), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0a05), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x1101), PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0104, 0x0070), @@ -834,17 +727,26 @@ static struct pcmcia_device_id serial_ids[] = { PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x0276), PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0039), PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0006), + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x0101), /* TDK DF2814 */ + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x100a), /* Xircom CM-56G */ + PCMCIA_DEVICE_MANF_CARD(0x0105, 0x3e0a), /* TDK DF5660 */ PCMCIA_DEVICE_MANF_CARD(0x0105, 0x410a), + PCMCIA_DEVICE_MANF_CARD(0x0107, 0x0002), /* USRobotics 14,400 */ PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d50), PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d51), PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d52), PCMCIA_DEVICE_MANF_CARD(0x010b, 0x0d53), PCMCIA_DEVICE_MANF_CARD(0x010b, 0xd180), + PCMCIA_DEVICE_MANF_CARD(0x0115, 0x3330), /* USRobotics/SUN 14,400 */ + PCMCIA_DEVICE_MANF_CARD(0x0124, 0x0100), /* Nokia DTP-2 ver II */ + PCMCIA_DEVICE_MANF_CARD(0x0134, 0x5600), /* LASAT COMMUNICATIONS A/S */ PCMCIA_DEVICE_MANF_CARD(0x0137, 0x000e), PCMCIA_DEVICE_MANF_CARD(0x0137, 0x001b), PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0025), PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0045), PCMCIA_DEVICE_MANF_CARD(0x0137, 0x0052), + PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0006), /* Psion 56K+Fax */ + PCMCIA_DEVICE_MANF_CARD(0x0200, 0x0001), /* MultiMobile */ PCMCIA_DEVICE_PROD_ID134("ADV", "TECH", "COMpad-32/85", 0x67459937, 0x916d02ba, 0x8fbe92ae), PCMCIA_DEVICE_PROD_ID124("GATEWAY2000", "CC3144", "PCMCIA MODEM", 0x506bccae, 0xcb3685f1, 0xbd6c43ef), PCMCIA_DEVICE_PROD_ID14("MEGAHERTZ", "PCMCIA MODEM", 0xf510db04, 0xbd6c43ef), @@ -858,36 +760,44 @@ static struct pcmcia_device_id serial_ids[] = { PCMCIA_DEVICE_PROD_ID12("COMPAQ", "PCMCIA 33600 FAX/DATA MODEM", 0xa3a3062c, 0x5a00ce95), PCMCIA_DEVICE_PROD_ID12("Computerboards, Inc.", "PCM-COM422", 0xd0b78f51, 0x7e2d49ed), PCMCIA_DEVICE_PROD_ID12("Dr. Neuhaus", "FURY CARD 14K4", 0x76942813, 0x8b96ce65), + PCMCIA_DEVICE_PROD_ID12("IBM", "ISDN/56K/GSM", 0xb569a6e5, 0xfee5297b), PCMCIA_DEVICE_PROD_ID12("Intelligent", "ANGIA FAX/MODEM", 0xb496e65e, 0xf31602a6), PCMCIA_DEVICE_PROD_ID12("Intel", "MODEM 2400+", 0x816cc815, 0x412729fb), + PCMCIA_DEVICE_PROD_ID12("Intertex", "IX34-PCMCIA", 0xf8a097e3, 0x97880447), PCMCIA_DEVICE_PROD_ID12("IOTech Inc ", "PCMCIA Dual RS-232 Serial Port Card", 0x3bd2d898, 0x92abc92f), PCMCIA_DEVICE_PROD_ID12("MACRONIX", "FAX/MODEM", 0x668388b3, 0x3f9bdf2f), PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT1432LT", 0x5f73be51, 0x0b3e2383), PCMCIA_DEVICE_PROD_ID12("Multi-Tech", "MT2834LT", 0x5f73be51, 0x4cd7c09e), PCMCIA_DEVICE_PROD_ID12("OEM ", "C288MX ", 0xb572d360, 0xd2385b7a), + PCMCIA_DEVICE_PROD_ID12("Option International", "V34bis GSM/PSTN Data/Fax Modem", 0x9d7cd6f5, 0x5cb8bf41), PCMCIA_DEVICE_PROD_ID12("PCMCIA ", "C336MX ", 0x99bcafe9, 0xaa25bcab), PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "PCMCIA Dual RS-232 Serial Port Card", 0xc4420b35, 0x92abc92f), PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "Dual RS-232 Serial Port PC Card", 0xc4420b35, 0x031a380d), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "PCMLM28.cis"), - PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "PCMLM28.cis"), - PCMCIA_MFC_DEVICE_CIS_PROD_ID12(1, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "DP83903.cis"), - PCMCIA_MFC_DEVICE_CIS_PROD_ID4(1, "NSC MF LAN/Modem", 0x58fc6056, "DP83903.cis"), + PCMCIA_DEVICE_PROD_ID12("Telia", "SurfinBird 560P/A+", 0xe2cdd5e, 0xc9314b38), + PCMCIA_DEVICE_PROD_ID1("Smart Serial Port", 0x2d8ce292), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet GSM", 0xf5f025c2, 0x4ae85d35, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "LINKSYS", "PCMLM28", 0xf7cb0b07, 0x66881874, "cis/PCMLM28.cis"), + PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "TOSHIBA", "Modem/LAN Card", 0xb4585a1a, 0x53f922f8, "cis/PCMLM28.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID12(1, "DAYNA COMMUNICATIONS", "LAN AND MODEM MULTIFUNCTION", 0x8fdf8f89, 0xdd5ed9e8, "cis/DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_PROD_ID4(1, "NSC MF LAN/Modem", 0x58fc6056, "cis/DP83903.cis"), PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0556, "cis/3CCFEM556.cis"), - PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0175, 0x0000, "DP83903.cis"), + PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0175, 0x0000, "cis/DP83903.cis"), PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x0035, "cis/3CXEM556.cis"), PCMCIA_MFC_DEVICE_CIS_MANF_CARD(1, 0x0101, 0x003d, "cis/3CXEM556.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC850", 0xd85f6206, 0x42a2c018, "SW_8xx_SER.cis"), /* Sierra Wireless AC850 3G Network Adapter R1 */ - PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0x0710, "SW_7xx_SER.cis"), /* Sierra Wireless AC710/AC750 GPRS Network Adapter R1 */ - PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0xa555, "SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- pre update */ - PCMCIA_DEVICE_CIS_MANF_CARD(0x013f, 0xa555, "SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- post update */ - PCMCIA_DEVICE_CIS_PROD_ID12("MultiTech", "PCMCIA 56K DataFax", 0x842047ee, 0xc2efcf03, "MT5634ZLX.cis"), - PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-4", 0x96913a85, 0xcec8f102, "COMpad4.cis"), - PCMCIA_DEVICE_CIS_PROD_ID123("ADVANTECH", "COMpad-32/85", "1.0", 0x96913a85, 0x8fbe92ae, 0x0877b627, "COMpad2.cis"), - PCMCIA_DEVICE_CIS_PROD_ID2("RS-COM 2P", 0xad20b156, "RS-COM-2P.cis"), - PCMCIA_DEVICE_CIS_MANF_CARD(0x0013, 0x0000, "GLOBETROTTER.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC850", 0xd85f6206, 0x42a2c018, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC850 3G Network Adapter R1 */ + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC860", 0xd85f6206, 0x698f93db, "cis/SW_8xx_SER.cis"), /* Sierra Wireless AC860 3G Network Adapter R1 */ + PCMCIA_DEVICE_CIS_PROD_ID12("Sierra Wireless", "AC710/AC750", 0xd85f6206, 0x761b11e0, "cis/SW_7xx_SER.cis"), /* Sierra Wireless AC710/AC750 GPRS Network Adapter R1 */ + PCMCIA_DEVICE_CIS_MANF_CARD(0x0192, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- pre update */ + PCMCIA_DEVICE_CIS_MANF_CARD(0x013f, 0xa555, "cis/SW_555_SER.cis"), /* Sierra Aircard 555 CDMA 1xrtt Modem -- post update */ + PCMCIA_DEVICE_CIS_PROD_ID12("MultiTech", "PCMCIA 56K DataFax", 0x842047ee, 0xc2efcf03, "cis/MT5634ZLX.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-2", 0x96913a85, 0x27ab5437, "cis/COMpad2.cis"), + PCMCIA_DEVICE_CIS_PROD_ID12("ADVANTECH", "COMpad-32/85B-4", 0x96913a85, 0xcec8f102, "cis/COMpad4.cis"), + PCMCIA_DEVICE_CIS_PROD_ID123("ADVANTECH", "COMpad-32/85", "1.0", 0x96913a85, 0x8fbe92ae, 0x0877b627, "cis/COMpad2.cis"), + PCMCIA_DEVICE_CIS_PROD_ID2("RS-COM 2P", 0xad20b156, "cis/RS-COM-2P.cis"), + PCMCIA_DEVICE_CIS_MANF_CARD(0x0013, 0x0000, "cis/GLOBETROTTER.cis"), PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100 1.00.",0x19ca78af,0xf964f42b), PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL100",0x19ca78af,0x71d98e83), PCMCIA_DEVICE_PROD_ID12("ELAN DIGITAL SYSTEMS LTD, c1997.","SERIAL CARD: SL232 1.00.",0x19ca78af,0x69fb7490), @@ -921,11 +831,21 @@ static struct pcmcia_device_id serial_ids[] = { }; MODULE_DEVICE_TABLE(pcmcia, serial_ids); +MODULE_FIRMWARE("cis/PCMLM28.cis"); +MODULE_FIRMWARE("cis/DP83903.cis"); +MODULE_FIRMWARE("cis/3CCFEM556.cis"); +MODULE_FIRMWARE("cis/3CXEM556.cis"); +MODULE_FIRMWARE("cis/SW_8xx_SER.cis"); +MODULE_FIRMWARE("cis/SW_7xx_SER.cis"); +MODULE_FIRMWARE("cis/SW_555_SER.cis"); +MODULE_FIRMWARE("cis/MT5634ZLX.cis"); +MODULE_FIRMWARE("cis/COMpad2.cis"); +MODULE_FIRMWARE("cis/COMpad4.cis"); +MODULE_FIRMWARE("cis/RS-COM-2P.cis"); + static struct pcmcia_driver serial_cs_driver = { .owner = THIS_MODULE, - .drv = { - .name = "serial_cs", - }, + .name = "serial_cs", .probe = serial_probe, .remove = serial_detach, .id_table = serial_ids, diff --git a/drivers/serial/serial_ks8695.c b/drivers/serial/serial_ks8695.c index 998e89d..b196202 100644 --- a/drivers/serial/serial_ks8695.c +++ b/drivers/serial/serial_ks8695.c @@ -110,7 +110,11 @@ static struct console ks8695_console; static void ks8695uart_stop_tx(struct uart_port *port) { if (tx_enabled(port)) { - disable_irq(KS8695_IRQ_UART_TX); + /* use disable_irq_nosync() and not disable_irq() to avoid self + * imposed deadlock by not waiting for irq handler to end, + * since this ks8695uart_stop_tx() is called from interrupt context. + */ + disable_irq_nosync(KS8695_IRQ_UART_TX); tx_enable(port, 0); } } @@ -150,7 +154,7 @@ static void ks8695uart_disable_ms(struct uart_port *port) static irqreturn_t ks8695uart_rx_chars(int irq, void *dev_id) { struct uart_port *port = dev_id; - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; unsigned int status, ch, lsr, flg, max_count = 256; status = UART_GET_LSR(port); /* clears pending LSR interrupts */ @@ -206,7 +210,7 @@ ignore_char: static irqreturn_t ks8695uart_tx_chars(int irq, void *dev_id) { struct uart_port *port = dev_id; - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; unsigned int count; if (port->x_char) { @@ -262,7 +266,7 @@ static irqreturn_t ks8695uart_modem_status(int irq, void *dev_id) if (status & URMS_URTERI) port->icount.rng++; - wake_up_interruptible(&port->info->delta_msr_wait); + wake_up_interruptible(&port->state->port.delta_msr_wait); return IRQ_HANDLED; } @@ -549,7 +553,7 @@ static struct uart_port ks8695uart_ports[SERIAL_KS8695_NR] = { .mapbase = KS8695_UART_VA, .iotype = SERIAL_IO_MEM, .irq = KS8695_IRQ_UART_TX, - .uartclk = CLOCK_TICK_RATE * 16, + .uartclk = KS8695_CLOCK_RATE * 16, .fifosize = 16, .ops = &ks8695uart_pops, .flags = ASYNC_BOOT_AUTOCONF, @@ -646,6 +650,7 @@ static struct console ks8695_console = { static int __init ks8695_console_init(void) { + add_preferred_console(SERIAL_KS8695_DEVNAME, 0, NULL); register_console(&ks8695_console); return 0; } diff --git a/drivers/serial/serial_lh7a40x.c b/drivers/serial/serial_lh7a40x.c index a7bf024..ea74470 100644 --- a/drivers/serial/serial_lh7a40x.c +++ b/drivers/serial/serial_lh7a40x.c @@ -138,7 +138,7 @@ static void lh7a40xuart_enable_ms (struct uart_port* port) static void lh7a40xuart_rx_chars (struct uart_port* port) { - struct tty_struct* tty = port->info->port.tty; + struct tty_struct* tty = port->state->port.tty; int cbRxMax = 256; /* (Gross) limit on receive */ unsigned int data; /* Received data and status */ unsigned int flag; @@ -184,7 +184,7 @@ static void lh7a40xuart_rx_chars (struct uart_port* port) static void lh7a40xuart_tx_chars (struct uart_port* port) { - struct circ_buf* xmit = &port->info->xmit; + struct circ_buf* xmit = &port->state->xmit; int cbTxMax = port->fifosize; if (port->x_char) { @@ -241,7 +241,7 @@ static void lh7a40xuart_modem_status (struct uart_port* port) if (delta & CTS) uart_handle_cts_change (port, status & CTS); - wake_up_interruptible (&port->info->delta_msr_wait); + wake_up_interruptible (&port->state->port.delta_msr_wait); } static irqreturn_t lh7a40xuart_int (int irq, void* dev_id) diff --git a/drivers/serial/serial_txx9.c b/drivers/serial/serial_txx9.c index 7313c2e..c50e9fb 100644 --- a/drivers/serial/serial_txx9.c +++ b/drivers/serial/serial_txx9.c @@ -221,21 +221,26 @@ sio_quot_set(struct uart_txx9_port *up, int quot) sio_out(up, TXX9_SIBGR, 0xff | TXX9_SIBGR_BCLK_T6); } +static struct uart_txx9_port *to_uart_txx9_port(struct uart_port *port) +{ + return container_of(port, struct uart_txx9_port, port); +} + static void serial_txx9_stop_tx(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_TIE); } static void serial_txx9_start_tx(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); sio_set(up, TXX9_SIDICR, TXX9_SIDICR_TIE); } static void serial_txx9_stop_rx(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); up->port.read_status_mask &= ~TXX9_SIDISR_RDIS; } @@ -246,7 +251,7 @@ static void serial_txx9_enable_ms(struct uart_port *port) static void serial_txx9_initialize(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned int tmout = 10000; sio_out(up, TXX9_SIFCR, TXX9_SIFCR_SWRST); @@ -272,7 +277,7 @@ static void serial_txx9_initialize(struct uart_port *port) static inline void receive_chars(struct uart_txx9_port *up, unsigned int *status) { - struct tty_struct *tty = up->port.info->port.tty; + struct tty_struct *tty = up->port.state->port.tty; unsigned char ch; unsigned int disr = *status; int max_count = 256; @@ -348,7 +353,7 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status) static inline void transmit_chars(struct uart_txx9_port *up) { - struct circ_buf *xmit = &up->port.info->xmit; + struct circ_buf *xmit = &up->port.state->xmit; int count; if (up->port.x_char) { @@ -414,7 +419,7 @@ static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id) static unsigned int serial_txx9_tx_empty(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned long flags; unsigned int ret; @@ -427,7 +432,7 @@ static unsigned int serial_txx9_tx_empty(struct uart_port *port) static unsigned int serial_txx9_get_mctrl(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned int ret; /* no modem control lines */ @@ -440,7 +445,7 @@ static unsigned int serial_txx9_get_mctrl(struct uart_port *port) static void serial_txx9_set_mctrl(struct uart_port *port, unsigned int mctrl) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); if (mctrl & TIOCM_RTS) sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC); @@ -450,7 +455,7 @@ static void serial_txx9_set_mctrl(struct uart_port *port, unsigned int mctrl) static void serial_txx9_break_ctl(struct uart_port *port, int break_state) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); @@ -461,9 +466,97 @@ static void serial_txx9_break_ctl(struct uart_port *port, int break_state) spin_unlock_irqrestore(&up->port.lock, flags); } +#if defined(CONFIG_SERIAL_TXX9_CONSOLE) || (CONFIG_CONSOLE_POLL) +/* + * Wait for transmitter & holding register to empty + */ +static void wait_for_xmitr(struct uart_txx9_port *up) +{ + unsigned int tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + while (--tmout && + !(sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS)) + udelay(1); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + (sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS)) + udelay(1); + } +} +#endif + +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +static int serial_txx9_get_poll_char(struct uart_port *port) +{ + unsigned int ier; + unsigned char c; + struct uart_txx9_port *up = to_uart_txx9_port(port); + + /* + * First save the IER then disable the interrupts + */ + ier = sio_in(up, TXX9_SIDICR); + sio_out(up, TXX9_SIDICR, 0); + + while (sio_in(up, TXX9_SIDISR) & TXX9_SIDISR_UVALID) + ; + + c = sio_in(up, TXX9_SIRFIFO); + + /* + * Finally, clear RX interrupt status + * and restore the IER + */ + sio_mask(up, TXX9_SIDISR, TXX9_SIDISR_RDIS); + sio_out(up, TXX9_SIDICR, ier); + return c; +} + + +static void serial_txx9_put_poll_char(struct uart_port *port, unsigned char c) +{ + unsigned int ier; + struct uart_txx9_port *up = to_uart_txx9_port(port); + + /* + * First save the IER then disable the interrupts + */ + ier = sio_in(up, TXX9_SIDICR); + sio_out(up, TXX9_SIDICR, 0); + + wait_for_xmitr(up); + /* + * Send the character out. + * If a LF, also do CR... + */ + sio_out(up, TXX9_SITFIFO, c); + if (c == 10) { + wait_for_xmitr(up); + sio_out(up, TXX9_SITFIFO, 13); + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + sio_out(up, TXX9_SIDICR, ier); +} + +#endif /* CONFIG_CONSOLE_POLL */ + static int serial_txx9_startup(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned long flags; int retval; @@ -508,7 +601,7 @@ static int serial_txx9_startup(struct uart_port *port) static void serial_txx9_shutdown(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned long flags; /* @@ -548,7 +641,7 @@ static void serial_txx9_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); unsigned int cval, fcr = 0; unsigned long flags; unsigned int baud, quot; @@ -726,19 +819,19 @@ static void serial_txx9_release_resource(struct uart_txx9_port *up) static void serial_txx9_release_port(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); serial_txx9_release_resource(up); } static int serial_txx9_request_port(struct uart_port *port) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); return serial_txx9_request_resource(up); } static void serial_txx9_config_port(struct uart_port *port, int uflags) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); int ret; /* @@ -781,6 +874,10 @@ static struct uart_ops serial_txx9_pops = { .release_port = serial_txx9_release_port, .request_port = serial_txx9_request_port, .config_port = serial_txx9_config_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = serial_txx9_get_poll_char, + .poll_put_char = serial_txx9_put_poll_char, +#endif }; static struct uart_txx9_port serial_txx9_ports[UART_NR]; @@ -803,30 +900,9 @@ static void __init serial_txx9_register_ports(struct uart_driver *drv, #ifdef CONFIG_SERIAL_TXX9_CONSOLE -/* - * Wait for transmitter & holding register to empty - */ -static inline void wait_for_xmitr(struct uart_txx9_port *up) -{ - unsigned int tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - while (--tmout && - !(sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS)) - udelay(1); - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; - while (--tmout && - (sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS)) - udelay(1); - } -} - static void serial_txx9_console_putchar(struct uart_port *port, int ch) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; + struct uart_txx9_port *up = to_uart_txx9_port(port); wait_for_xmitr(up); sio_out(up, TXX9_SITFIFO, ch); diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 403b01b..251c08c 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -3,7 +3,7 @@ * * SuperH on-chip serial module support. (SCI with no FIFO / with FIFO) * - * Copyright (C) 2002 - 2008 Paul Mundt + * Copyright (C) 2002 - 2011 Paul Mundt * Modified to support SH7720 SCIF. Markus Brunner, Mark Jonas (Jul 2007). * * based off of the old drivers/char/sh-sci.c by: @@ -48,9 +48,11 @@ #include <linux/ctype.h> #include <linux/err.h> #include <linux/list.h> +#include <linux/dmaengine.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> #ifdef CONFIG_SUPERH -#include <asm/clock.h> #include <asm/sh_bios.h> #endif @@ -85,22 +87,42 @@ struct sci_port { /* SCBRR calculation algo */ unsigned int scbrr_algo_id; -#ifdef CONFIG_HAVE_CLK /* Interface clock */ struct clk *iclk; - /* Data clock */ - struct clk *dclk; -#endif + /* Function clock */ + struct clk *fclk; + struct list_head node; + + struct dma_chan *chan_tx; + struct dma_chan *chan_rx; + +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct device *dma_dev; + unsigned int slave_tx; + unsigned int slave_rx; + struct dma_async_tx_descriptor *desc_tx; + struct dma_async_tx_descriptor *desc_rx[2]; + dma_cookie_t cookie_tx; + dma_cookie_t cookie_rx[2]; + dma_cookie_t active_rx; + struct scatterlist sg_tx; + unsigned int sg_len_tx; + struct scatterlist sg_rx[2]; + size_t buf_len_rx; + struct sh_dmae_slave param_tx; + struct sh_dmae_slave param_rx; + struct work_struct work_tx; + struct work_struct work_rx; + struct timer_list rx_timer; + unsigned int rx_timeout; +#endif }; struct sh_sci_priv { spinlock_t lock; struct list_head ports; - -#ifdef CONFIG_HAVE_CLK struct notifier_block clk_nb; -#endif }; /* Function prototypes */ @@ -137,7 +159,11 @@ static int sci_poll_get_char(struct uart_port *port) handle_error(port); continue; } - } while (!(status & SCxSR_RDxF(port))); + break; + } while (1); + + if (!(status & SCxSR_RDxF(port))) + return NO_POLL_CHAR; c = sci_in(port, SCxRDR); @@ -162,32 +188,6 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c) } #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ -#if defined(__H8300S__) -enum { sci_disable, sci_enable }; - -static void h8300_sci_config(struct uart_port *port, unsigned int ctrl) -{ - volatile unsigned char *mstpcrl = (volatile unsigned char *)MSTPCRL; - int ch = (port->mapbase - SMR0) >> 3; - unsigned char mask = 1 << (ch+1); - - if (ctrl == sci_disable) - *mstpcrl |= mask; - else - *mstpcrl &= ~mask; -} - -static void h8300_sci_enable(struct uart_port *port) -{ - h8300_sci_config(port, sci_enable); -} - -static void h8300_sci_disable(struct uart_port *port) -{ - h8300_sci_config(port, sci_disable); -} -#endif - #if defined(__H8300H__) || defined(__H8300S__) static void sci_init_pins(struct uart_port *port, unsigned int cflag) { @@ -259,9 +259,9 @@ static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) Set SCP6MD1,0 = {01} (output) */ __raw_writew((data & 0x0fcf) | 0x1000, SCPCR); - data = ctrl_inb(SCPDR); + data = __raw_readb(SCPDR); /* Set /RTS2 (bit6) = 0 */ - ctrl_outb(data & 0xbf, SCPDR); + __raw_writeb(data & 0xbf, SCPDR); } } #elif defined(CONFIG_CPU_SUBTYPE_SH7722) @@ -278,7 +278,8 @@ static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) __raw_writew(data, PSCR); } } -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \ +#elif defined(CONFIG_CPU_SUBTYPE_SH7757) || \ + defined(CONFIG_CPU_SUBTYPE_SH7763) || \ defined(CONFIG_CPU_SUBTYPE_SH7780) || \ defined(CONFIG_CPU_SUBTYPE_SH7785) || \ defined(CONFIG_CPU_SUBTYPE_SH7786) || \ @@ -305,29 +306,44 @@ static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) defined(CONFIG_CPU_SUBTYPE_SH7780) || \ defined(CONFIG_CPU_SUBTYPE_SH7785) || \ defined(CONFIG_CPU_SUBTYPE_SH7786) -static inline int scif_txroom(struct uart_port *port) +static int scif_txfill(struct uart_port *port) +{ + return sci_in(port, SCTFDR) & 0xff; +} + +static int scif_txroom(struct uart_port *port) { - return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0xff); + return SCIF_TXROOM_MAX - scif_txfill(port); } -static inline int scif_rxroom(struct uart_port *port) +static int scif_rxfill(struct uart_port *port) { return sci_in(port, SCRFDR) & 0xff; } #elif defined(CONFIG_CPU_SUBTYPE_SH7763) -static inline int scif_txroom(struct uart_port *port) +static int scif_txfill(struct uart_port *port) { - if ((port->mapbase == 0xffe00000) || - (port->mapbase == 0xffe08000)) { + if (port->mapbase == 0xffe00000 || + port->mapbase == 0xffe08000) /* SCIF0/1*/ - return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0xff); - } else { + return sci_in(port, SCTFDR) & 0xff; + else /* SCIF2 */ - return SCIF2_TXROOM_MAX - (sci_in(port, SCFDR) >> 8); - } + return sci_in(port, SCFDR) >> 8; } -static inline int scif_rxroom(struct uart_port *port) +static int scif_txroom(struct uart_port *port) +{ + if (port->mapbase == 0xffe00000 || + port->mapbase == 0xffe08000) + /* SCIF0/1*/ + return SCIF_TXROOM_MAX - scif_txfill(port); + else + /* SCIF2 */ + return SCIF2_TXROOM_MAX - scif_txfill(port); +} + +static int scif_rxfill(struct uart_port *port) { if ((port->mapbase == 0xffe00000) || (port->mapbase == 0xffe08000)) { @@ -338,24 +354,55 @@ static inline int scif_rxroom(struct uart_port *port) return sci_in(port, SCFDR) & SCIF2_RFDC_MASK; } } +#elif defined(CONFIG_ARCH_SH7372) +static int scif_txfill(struct uart_port *port) +{ + if (port->type == PORT_SCIFA) + return sci_in(port, SCFDR) >> 8; + else + return sci_in(port, SCTFDR); +} + +static int scif_txroom(struct uart_port *port) +{ + return port->fifosize - scif_txfill(port); +} + +static int scif_rxfill(struct uart_port *port) +{ + if (port->type == PORT_SCIFA) + return sci_in(port, SCFDR) & SCIF_RFDC_MASK; + else + return sci_in(port, SCRFDR); +} #else -static inline int scif_txroom(struct uart_port *port) +static int scif_txfill(struct uart_port *port) +{ + return sci_in(port, SCFDR) >> 8; +} + +static int scif_txroom(struct uart_port *port) { - return SCIF_TXROOM_MAX - (sci_in(port, SCFDR) >> 8); + return SCIF_TXROOM_MAX - scif_txfill(port); } -static inline int scif_rxroom(struct uart_port *port) +static int scif_rxfill(struct uart_port *port) { return sci_in(port, SCFDR) & SCIF_RFDC_MASK; } #endif -static inline int sci_txroom(struct uart_port *port) +static int sci_txfill(struct uart_port *port) { - return (sci_in(port, SCxSR) & SCI_TDRE) != 0; + return !(sci_in(port, SCxSR) & SCI_TDRE); } -static inline int sci_rxroom(struct uart_port *port) +static int sci_txroom(struct uart_port *port) +{ + return !sci_txfill(port); +} + +static int sci_rxfill(struct uart_port *port) { return (sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0; } @@ -366,7 +413,7 @@ static inline int sci_rxroom(struct uart_port *port) static void sci_transmit_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; unsigned int stopped = uart_tx_stopped(port); unsigned short status; unsigned short ctrl; @@ -431,7 +478,7 @@ static void sci_transmit_chars(struct uart_port *port) static inline void sci_receive_chars(struct uart_port *port) { struct sci_port *sci_port = to_sci_port(port); - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; int i, count, copied = 0; unsigned short status; unsigned char flag; @@ -442,9 +489,9 @@ static inline void sci_receive_chars(struct uart_port *port) while (1) { if (port->type == PORT_SCI) - count = sci_rxroom(port); + count = sci_rxfill(port); else - count = scif_rxroom(port); + count = scif_rxfill(port); /* Don't copy more bytes than there is room for in the buffer */ count = tty_buffer_request_room(tty, count); @@ -489,10 +536,10 @@ static inline void sci_receive_chars(struct uart_port *port) } /* Store data and status */ - if (status&SCxSR_FER(port)) { + if (status & SCxSR_FER(port)) { flag = TTY_FRAME; dev_notice(port->dev, "frame error\n"); - } else if (status&SCxSR_PER(port)) { + } else if (status & SCxSR_PER(port)) { flag = TTY_PARITY; dev_notice(port->dev, "parity error\n"); } else @@ -551,7 +598,7 @@ static inline int sci_handle_errors(struct uart_port *port) { int copied = 0; unsigned short status = sci_in(port, SCxSR); - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; if (status & SCxSR_ORER(port)) { /* overrun error */ @@ -605,7 +652,7 @@ static inline int sci_handle_errors(struct uart_port *port) static inline int sci_handle_fifo_overrun(struct uart_port *port) { - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; int copied = 0; if (port->type != PORT_SCIF) @@ -628,7 +675,7 @@ static inline int sci_handle_breaks(struct uart_port *port) { int copied = 0; unsigned short status = sci_in(port, SCxSR); - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; struct sci_port *s = to_sci_port(port); if (uart_handle_break(port)) @@ -654,13 +701,39 @@ static inline int sci_handle_breaks(struct uart_port *port) return copied; } -static irqreturn_t sci_rx_interrupt(int irq, void *port) +static irqreturn_t sci_rx_interrupt(int irq, void *ptr) { +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); + + if (s->chan_rx) { + u16 scr = sci_in(port, SCSCR); + u16 ssr = sci_in(port, SCxSR); + + /* Disable future Rx interrupts */ + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + disable_irq_nosync(irq); + scr |= 0x4000; + } else { + scr &= ~SCSCR_RIE; + } + sci_out(port, SCSCR, scr); + /* Clear current interrupt */ + sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); + dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", + jiffies, s->rx_timeout); + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); + + return IRQ_HANDLED; + } +#endif + /* I think sci_receive_chars has to be called irrespective * of whether the I_IXOFF is set, otherwise, how is the interrupt * to be disabled? */ - sci_receive_chars(port); + sci_receive_chars(ptr); return IRQ_HANDLED; } @@ -668,10 +741,11 @@ static irqreturn_t sci_rx_interrupt(int irq, void *port) static irqreturn_t sci_tx_interrupt(int irq, void *ptr) { struct uart_port *port = ptr; + unsigned long flags; - spin_lock_irq(&port->lock); + spin_lock_irqsave(&port->lock, flags); sci_transmit_chars(port); - spin_unlock_irq(&port->lock); + spin_unlock_irqrestore(&port->lock, flags); return IRQ_HANDLED; } @@ -711,32 +785,53 @@ static irqreturn_t sci_br_interrupt(int irq, void *ptr) return IRQ_HANDLED; } +static inline unsigned long port_rx_irq_mask(struct uart_port *port) +{ + /* + * Not all ports (such as SCIFA) will support REIE. Rather than + * special-casing the port type, we check the port initialization + * IRQ enable mask to see whether the IRQ is desired at all. If + * it's unset, it's logically inferred that there's no point in + * testing for it. + */ + return SCSCR_RIE | (to_sci_port(port)->scscr & SCSR_REIE); +} + static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) { - unsigned short ssr_status, scr_status; + unsigned short ssr_status, scr_status, err_enabled; struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); irqreturn_t ret = IRQ_NONE; ssr_status = sci_in(port, SCxSR); scr_status = sci_in(port, SCSCR); + err_enabled = scr_status & port_rx_irq_mask(port); /* Tx Interrupt */ - if ((ssr_status & 0x0020) && (scr_status & SCSCR_TIE)) + if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCSCR_TIE) && + !s->chan_tx) ret = sci_tx_interrupt(irq, ptr); - /* Rx Interrupt */ - if ((ssr_status & 0x0002) && (scr_status & SCSCR_RIE)) + + /* + * Rx Interrupt: if we're using DMA, the DMA controller clears RDF / + * DR flags + */ + if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) && + (scr_status & SCSCR_RIE)) ret = sci_rx_interrupt(irq, ptr); + /* Error Interrupt */ - if ((ssr_status & 0x0080) && (scr_status & SCSCR_REIE)) + if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled) ret = sci_er_interrupt(irq, ptr); + /* Break Interrupt */ - if ((ssr_status & 0x0010) && (scr_status & SCSCR_REIE)) + if ((ssr_status & SCxSR_BRK(port)) && err_enabled) ret = sci_br_interrupt(irq, ptr); return ret; } -#ifdef CONFIG_HAVE_CLK /* * Here we define a transistion notifier so that we can update all of our * ports' baud rate when the peripheral clock changes. @@ -753,8 +848,7 @@ static int sci_notifier(struct notifier_block *self, (phase == CPUFREQ_RESUMECHANGE)) { spin_lock_irqsave(&priv->lock, flags); list_for_each_entry(sci_port, &priv->ports, node) - sci_port->port.uartclk = clk_get_rate(sci_port->dclk); - + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); spin_unlock_irqrestore(&priv->lock, flags); } @@ -765,23 +859,18 @@ static void sci_clk_enable(struct uart_port *port) { struct sci_port *sci_port = to_sci_port(port); - clk_enable(sci_port->dclk); - sci_port->port.uartclk = clk_get_rate(sci_port->dclk); - - if (sci_port->iclk) - clk_enable(sci_port->iclk); + clk_enable(sci_port->iclk); + sci_port->port.uartclk = clk_get_rate(sci_port->iclk); + clk_enable(sci_port->fclk); } static void sci_clk_disable(struct uart_port *port) { struct sci_port *sci_port = to_sci_port(port); - if (sci_port->iclk) - clk_disable(sci_port->iclk); - - clk_disable(sci_port->dclk); + clk_disable(sci_port->fclk); + clk_disable(sci_port->iclk); } -#endif static int sci_request_irq(struct sci_port *port) { @@ -836,8 +925,10 @@ static void sci_free_irq(struct sci_port *port) static unsigned int sci_tx_empty(struct uart_port *port) { - /* Can't detect */ - return TIOCSER_TEMT; + unsigned short status = sci_in(port, SCxSR); + unsigned short in_tx_fifo = scif_txfill(port); + + return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0; } static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) @@ -849,20 +940,314 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) static unsigned int sci_get_mctrl(struct uart_port *port) { - /* This routine is used for geting signals of: DTR, DCD, DSR, RI, + /* This routine is used for getting signals of: DTR, DCD, DSR, RI, and CTS/RTS */ return TIOCM_DTR | TIOCM_RTS | TIOCM_DSR; } +#ifdef CONFIG_SERIAL_SH_SCI_DMA +static void sci_dma_tx_complete(void *arg) +{ + struct sci_port *s = arg; + struct uart_port *port = &s->port; + struct circ_buf *xmit = &port->state->xmit; + unsigned long flags; + + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + + spin_lock_irqsave(&port->lock, flags); + + xmit->tail += sg_dma_len(&s->sg_tx); + xmit->tail &= UART_XMIT_SIZE - 1; + + port->icount.tx += sg_dma_len(&s->sg_tx); + + async_tx_ack(s->desc_tx); + s->cookie_tx = -EINVAL; + s->desc_tx = NULL; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); + + if (!uart_circ_empty(xmit)) { + schedule_work(&s->work_tx); + } else if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + u16 ctrl = sci_in(port, SCSCR); + sci_out(port, SCSCR, ctrl & ~SCSCR_TIE); + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +/* Locking: called with port lock held */ +static int sci_dma_rx_push(struct sci_port *s, struct tty_struct *tty, + size_t count) +{ + struct uart_port *port = &s->port; + int i, active, room; + + room = tty_buffer_request_room(tty, count); + + if (s->active_rx == s->cookie_rx[0]) { + active = 0; + } else if (s->active_rx == s->cookie_rx[1]) { + active = 1; + } else { + dev_err(port->dev, "cookie %d not found!\n", s->active_rx); + return 0; + } + + if (room < count) + dev_warn(port->dev, "Rx overrun: dropping %u bytes\n", + count - room); + if (!room) + return room; + + for (i = 0; i < room; i++) + tty_insert_flip_char(tty, ((u8 *)sg_virt(&s->sg_rx[active]))[i], + TTY_NORMAL); + + port->icount.rx += room; + + return room; +} + +static void sci_dma_rx_complete(void *arg) +{ + struct sci_port *s = arg; + struct uart_port *port = &s->port; + struct tty_struct *tty = port->state->port.tty; + unsigned long flags; + int count; + + dev_dbg(port->dev, "%s(%d) active #%d\n", __func__, port->line, s->active_rx); + + spin_lock_irqsave(&port->lock, flags); + + count = sci_dma_rx_push(s, tty, s->buf_len_rx); + + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); + + spin_unlock_irqrestore(&port->lock, flags); + + if (count) + tty_flip_buffer_push(tty); + + schedule_work(&s->work_rx); +} + +static void sci_start_rx(struct uart_port *port); +static void sci_start_tx(struct uart_port *port); + +static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) +{ + struct dma_chan *chan = s->chan_rx; + struct uart_port *port = &s->port; + + s->chan_rx = NULL; + s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; + dma_release_channel(chan); + if (sg_dma_address(&s->sg_rx[0])) + dma_free_coherent(port->dev, s->buf_len_rx * 2, + sg_virt(&s->sg_rx[0]), sg_dma_address(&s->sg_rx[0])); + if (enable_pio) + sci_start_rx(port); +} + +static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) +{ + struct dma_chan *chan = s->chan_tx; + struct uart_port *port = &s->port; + + s->chan_tx = NULL; + s->cookie_tx = -EINVAL; + dma_release_channel(chan); + if (enable_pio) + sci_start_tx(port); +} + +static void sci_submit_rx(struct sci_port *s) +{ + struct dma_chan *chan = s->chan_rx; + int i; + + for (i = 0; i < 2; i++) { + struct scatterlist *sg = &s->sg_rx[i]; + struct dma_async_tx_descriptor *desc; + + desc = chan->device->device_prep_slave_sg(chan, + sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT); + + if (desc) { + s->desc_rx[i] = desc; + desc->callback = sci_dma_rx_complete; + desc->callback_param = s; + s->cookie_rx[i] = desc->tx_submit(desc); + } + + if (!desc || s->cookie_rx[i] < 0) { + if (i) { + async_tx_ack(s->desc_rx[0]); + s->cookie_rx[0] = -EINVAL; + } + if (desc) { + async_tx_ack(desc); + s->cookie_rx[i] = -EINVAL; + } + dev_warn(s->port.dev, + "failed to re-start DMA, using PIO\n"); + sci_rx_dma_release(s, true); + return; + } + dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__, + s->cookie_rx[i], i); + } + + s->active_rx = s->cookie_rx[0]; + + dma_async_issue_pending(chan); +} + +static void work_fn_rx(struct work_struct *work) +{ + struct sci_port *s = container_of(work, struct sci_port, work_rx); + struct uart_port *port = &s->port; + struct dma_async_tx_descriptor *desc; + int new; + + if (s->active_rx == s->cookie_rx[0]) { + new = 0; + } else if (s->active_rx == s->cookie_rx[1]) { + new = 1; + } else { + dev_err(port->dev, "cookie %d not found!\n", s->active_rx); + return; + } + desc = s->desc_rx[new]; + + if (dma_async_is_tx_complete(s->chan_rx, s->active_rx, NULL, NULL) != + DMA_SUCCESS) { + /* Handle incomplete DMA receive */ + struct tty_struct *tty = port->state->port.tty; + struct dma_chan *chan = s->chan_rx; + struct sh_desc *sh_desc = container_of(desc, struct sh_desc, + async_tx); + unsigned long flags; + int count; + + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0); + dev_dbg(port->dev, "Read %u bytes with cookie %d\n", + sh_desc->partial, sh_desc->cookie); + + spin_lock_irqsave(&port->lock, flags); + count = sci_dma_rx_push(s, tty, sh_desc->partial); + spin_unlock_irqrestore(&port->lock, flags); + + if (count) + tty_flip_buffer_push(tty); + + sci_submit_rx(s); + + return; + } + + s->cookie_rx[new] = desc->tx_submit(desc); + if (s->cookie_rx[new] < 0) { + dev_warn(port->dev, "Failed submitting Rx DMA descriptor\n"); + sci_rx_dma_release(s, true); + return; + } + + s->active_rx = s->cookie_rx[!new]; + + dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__, + s->cookie_rx[new], new, s->active_rx); +} + +static void work_fn_tx(struct work_struct *work) +{ + struct sci_port *s = container_of(work, struct sci_port, work_tx); + struct dma_async_tx_descriptor *desc; + struct dma_chan *chan = s->chan_tx; + struct uart_port *port = &s->port; + struct circ_buf *xmit = &port->state->xmit; + struct scatterlist *sg = &s->sg_tx; + + /* + * DMA is idle now. + * Port xmit buffer is already mapped, and it is one page... Just adjust + * offsets and lengths. Since it is a circular buffer, we have to + * transmit till the end, and then the rest. Take the port lock to get a + * consistent xmit buffer state. + */ + spin_lock_irq(&port->lock); + sg->offset = xmit->tail & (UART_XMIT_SIZE - 1); + sg_dma_address(sg) = (sg_dma_address(sg) & ~(UART_XMIT_SIZE - 1)) + + sg->offset; + sg_dma_len(sg) = min((int)CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE), + CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE)); + spin_unlock_irq(&port->lock); + + BUG_ON(!sg_dma_len(sg)); + + desc = chan->device->device_prep_slave_sg(chan, + sg, s->sg_len_tx, DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + /* switch to PIO */ + sci_tx_dma_release(s, true); + return; + } + + dma_sync_sg_for_device(port->dev, sg, 1, DMA_TO_DEVICE); + + spin_lock_irq(&port->lock); + s->desc_tx = desc; + desc->callback = sci_dma_tx_complete; + desc->callback_param = s; + spin_unlock_irq(&port->lock); + s->cookie_tx = desc->tx_submit(desc); + if (s->cookie_tx < 0) { + dev_warn(port->dev, "Failed submitting Tx DMA descriptor\n"); + /* switch to PIO */ + sci_tx_dma_release(s, true); + return; + } + + dev_dbg(port->dev, "%s: %p: %d...%d, cookie %d\n", __func__, + xmit->buf, xmit->tail, xmit->head, s->cookie_tx); + + dma_async_issue_pending(chan); +} +#endif + static void sci_start_tx(struct uart_port *port) { + struct sci_port *s = to_sci_port(port); unsigned short ctrl; - /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = sci_in(port, SCSCR); - ctrl |= SCSCR_TIE; - sci_out(port, SCSCR, ctrl); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + u16 new, scr = sci_in(port, SCSCR); + if (s->chan_tx) + new = scr | 0x8000; + else + new = scr & ~0x8000; + if (new != scr) + sci_out(port, SCSCR, new); + } + + if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && + s->cookie_tx < 0) + schedule_work(&s->work_tx); +#endif + + if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ + ctrl = sci_in(port, SCSCR); + sci_out(port, SCSCR, ctrl | SCSCR_TIE); + } } static void sci_stop_tx(struct uart_port *port) @@ -871,17 +1256,24 @@ static void sci_stop_tx(struct uart_port *port) /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); + + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~0x8000; + ctrl &= ~SCSCR_TIE; + sci_out(port, SCSCR, ctrl); } -static void sci_start_rx(struct uart_port *port, unsigned int tty_start) +static void sci_start_rx(struct uart_port *port) { unsigned short ctrl; - /* Set RIE (Receive Interrupt Enable) bit in SCSCR */ - ctrl = sci_in(port, SCSCR); - ctrl |= SCSCR_RIE | SCSCR_REIE; + ctrl = sci_in(port, SCSCR) | port_rx_irq_mask(port); + + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~0x4000; + sci_out(port, SCSCR, ctrl); } @@ -889,9 +1281,13 @@ static void sci_stop_rx(struct uart_port *port) { unsigned short ctrl; - /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ ctrl = sci_in(port, SCSCR); - ctrl &= ~(SCSCR_RIE | SCSCR_REIE); + + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~0x4000; + + ctrl &= ~port_rx_irq_mask(port); + sci_out(port, SCSCR, ctrl); } @@ -905,16 +1301,157 @@ static void sci_break_ctl(struct uart_port *port, int break_state) /* Nothing here yet .. */ } +#ifdef CONFIG_SERIAL_SH_SCI_DMA +static bool filter(struct dma_chan *chan, void *slave) +{ + struct sh_dmae_slave *param = slave; + + dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__, + param->slave_id); + + if (param->dma_dev == chan->device->dev) { + chan->private = param; + return true; + } else { + return false; + } +} + +static void rx_timer_fn(unsigned long arg) +{ + struct sci_port *s = (struct sci_port *)arg; + struct uart_port *port = &s->port; + u16 scr = sci_in(port, SCSCR); + + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + scr &= ~0x4000; + enable_irq(s->irqs[1]); + } + sci_out(port, SCSCR, scr | SCSCR_RIE); + dev_dbg(port->dev, "DMA Rx timed out\n"); + schedule_work(&s->work_rx); +} + +static void sci_request_dma(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + struct sh_dmae_slave *param; + struct dma_chan *chan; + dma_cap_mask_t mask; + int nent; + + dev_dbg(port->dev, "%s: port %d DMA %p\n", __func__, + port->line, s->dma_dev); + + if (!s->dma_dev) + return; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + param = &s->param_tx; + + /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */ + param->slave_id = s->slave_tx; + param->dma_dev = s->dma_dev; + + s->cookie_tx = -EINVAL; + chan = dma_request_channel(mask, filter, param); + dev_dbg(port->dev, "%s: TX: got channel %p\n", __func__, chan); + if (chan) { + s->chan_tx = chan; + sg_init_table(&s->sg_tx, 1); + /* UART circular tx buffer is an aligned page. */ + BUG_ON((int)port->state->xmit.buf & ~PAGE_MASK); + sg_set_page(&s->sg_tx, virt_to_page(port->state->xmit.buf), + UART_XMIT_SIZE, (int)port->state->xmit.buf & ~PAGE_MASK); + nent = dma_map_sg(port->dev, &s->sg_tx, 1, DMA_TO_DEVICE); + if (!nent) + sci_tx_dma_release(s, false); + else + dev_dbg(port->dev, "%s: mapped %d@%p to %x\n", __func__, + sg_dma_len(&s->sg_tx), + port->state->xmit.buf, sg_dma_address(&s->sg_tx)); + + s->sg_len_tx = nent; + + INIT_WORK(&s->work_tx, work_fn_tx); + } + + param = &s->param_rx; + + /* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */ + param->slave_id = s->slave_rx; + param->dma_dev = s->dma_dev; + + chan = dma_request_channel(mask, filter, param); + dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan); + if (chan) { + dma_addr_t dma[2]; + void *buf[2]; + int i; + + s->chan_rx = chan; + + s->buf_len_rx = 2 * max(16, (int)port->fifosize); + buf[0] = dma_alloc_coherent(port->dev, s->buf_len_rx * 2, + &dma[0], GFP_KERNEL); + + if (!buf[0]) { + dev_warn(port->dev, + "failed to allocate dma buffer, using PIO\n"); + sci_rx_dma_release(s, true); + return; + } + + buf[1] = buf[0] + s->buf_len_rx; + dma[1] = dma[0] + s->buf_len_rx; + + for (i = 0; i < 2; i++) { + struct scatterlist *sg = &s->sg_rx[i]; + + sg_init_table(sg, 1); + sg_set_page(sg, virt_to_page(buf[i]), s->buf_len_rx, + (int)buf[i] & ~PAGE_MASK); + sg_dma_address(sg) = dma[i]; + } + + INIT_WORK(&s->work_rx, work_fn_rx); + setup_timer(&s->rx_timer, rx_timer_fn, (unsigned long)s); + + sci_submit_rx(s); + } +} + +static void sci_free_dma(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + + if (!s->dma_dev) + return; + + if (s->chan_tx) + sci_tx_dma_release(s, false); + if (s->chan_rx) + sci_rx_dma_release(s, false); +} +#endif + static int sci_startup(struct uart_port *port) { struct sci_port *s = to_sci_port(port); + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + if (s->enable) s->enable(port); sci_request_irq(s); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + sci_request_dma(port); +#endif sci_start_tx(port); - sci_start_rx(port, 1); + sci_start_rx(port); return 0; } @@ -923,8 +1460,13 @@ static void sci_shutdown(struct uart_port *port) { struct sci_port *s = to_sci_port(port); + dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + sci_stop_rx(port); sci_stop_tx(port); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + sci_free_dma(port); +#endif sci_free_irq(s); if (s->disable) @@ -956,11 +1498,22 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, struct ktermios *old) { struct sci_port *s = to_sci_port(port); - unsigned int status, baud, smr_val; + unsigned int status, baud, smr_val, max_baud; int t = -1; + u16 scfcr = 0; + + /* + * earlyprintk comes here early on with port->uartclk set to zero. + * the clock framework is not up and running at this point so here + * we assume that 115200 is the maximum baud rate. please note that + * the baud rate is not programmed during earlyprintk - it is assumed + * that the previous boot loader has enabled required clocks and + * setup the baud rate generator hardware for us already. + */ + max_baud = port->uartclk ? port->uartclk / 16 : 115200; - baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); - if (likely(baud)) + baud = uart_get_baud_rate(port, termios, old, 0, max_baud); + if (likely(baud && port->uartclk)) t = sci_scbrr_calc(s->scbrr_algo_id, baud, port->uartclk); do { @@ -970,7 +1523,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ if (port->type != PORT_SCI) - sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); + sci_out(port, SCFCR, scfcr | SCFCR_RFRST | SCFCR_TFRST); smr_val = sci_in(port, SCSMR) & 3; if ((termios->c_cflag & CSIZE) == CS7) @@ -986,6 +1539,9 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_out(port, SCSMR, smr_val); + dev_dbg(port->dev, "%s: SMR %x, t %x, SCSCR %x\n", __func__, smr_val, t, + SCSCR_INIT(port)); + if (t > 0) { if (t >= 256) { sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1); @@ -998,12 +1554,34 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, } sci_init_pins(port, termios->c_cflag); - sci_out(port, SCFCR, (termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0); + sci_out(port, SCFCR, scfcr | ((termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0)); sci_out(port, SCSCR, s->scscr); +#ifdef CONFIG_SERIAL_SH_SCI_DMA + /* + * Calculate delay for 1.5 DMA buffers: see + * drivers/serial/serial_core.c::uart_update_timeout(). With 10 bits + * (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above function + * calculates 1 jiffie for the data plus 5 jiffies for the "slop(e)." + * Then below we calculate 3 jiffies (12ms) for 1.5 DMA buffers (3 FIFO + * sizes), but it has been found out experimentally, that this is not + * enough: the driver too often needlessly runs on a DMA timeout. 20ms + * as a minimum seem to work perfectly. + */ + if (s->chan_rx) { + s->rx_timeout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / + port->fifosize / 2; + dev_dbg(port->dev, + "DMA Rx t-out %ums, tty t-out %u jiffies\n", + s->rx_timeout * 1000 / HZ, port->timeout); + if (s->rx_timeout < msecs_to_jiffies(20)) + s->rx_timeout = msecs_to_jiffies(20); + } +#endif + if ((termios->c_cflag & CREAD) != 0) - sci_start_rx(port, 0); + sci_start_rx(port); } static const char *sci_type(struct uart_port *port) @@ -1017,6 +1595,8 @@ static const char *sci_type(struct uart_port *port) return "scif"; case PORT_SCIFA: return "scifa"; + case PORT_SCIFB: + return "scifb"; } return NULL; @@ -1093,45 +1673,79 @@ static struct uart_ops sci_uart_ops = { #endif }; -static void __devinit sci_init_single(struct platform_device *dev, - struct sci_port *sci_port, - unsigned int index, - struct plat_sci_port *p) +static int __devinit sci_init_single(struct platform_device *dev, + struct sci_port *sci_port, + unsigned int index, + struct plat_sci_port *p) { - sci_port->port.ops = &sci_uart_ops; - sci_port->port.iotype = UPIO_MEM; - sci_port->port.line = index; - sci_port->port.fifosize = 1; + struct uart_port *port = &sci_port->port; -#if defined(__H8300H__) || defined(__H8300S__) -#ifdef __H8300S__ - sci_port->enable = h8300_sci_enable; - sci_port->disable = h8300_sci_disable; -#endif - sci_port->port.uartclk = CONFIG_CPU_CLOCK; -#elif defined(CONFIG_HAVE_CLK) - sci_port->iclk = p->clk ? clk_get(&dev->dev, p->clk) : NULL; - sci_port->dclk = clk_get(&dev->dev, "peripheral_clk"); - sci_port->enable = sci_clk_enable; - sci_port->disable = sci_clk_disable; -#else -#error "Need a valid uartclk" -#endif + port->ops = &sci_uart_ops; + port->iotype = UPIO_MEM; + port->line = index; + + switch (p->type) { + case PORT_SCIFB: + port->fifosize = 256; + break; + case PORT_SCIFA: + port->fifosize = 64; + break; + case PORT_SCIF: + port->fifosize = 16; + break; + default: + port->fifosize = 1; + break; + } + + if (dev) { + sci_port->iclk = clk_get(&dev->dev, "sci_ick"); + if (IS_ERR(sci_port->iclk)) { + sci_port->iclk = clk_get(&dev->dev, "peripheral_clk"); + if (IS_ERR(sci_port->iclk)) { + dev_err(&dev->dev, "can't get iclk\n"); + return PTR_ERR(sci_port->iclk); + } + } + + /* + * The function clock is optional, ignore it if we can't + * find it. + */ + sci_port->fclk = clk_get(&dev->dev, "sci_fck"); + if (IS_ERR(sci_port->fclk)) + sci_port->fclk = NULL; + + sci_port->enable = sci_clk_enable; + sci_port->disable = sci_clk_disable; + port->dev = &dev->dev; + } sci_port->break_timer.data = (unsigned long)sci_port; sci_port->break_timer.function = sci_break_timer; init_timer(&sci_port->break_timer); - sci_port->port.mapbase = p->mapbase; - sci_port->port.membase = p->membase; + port->mapbase = p->mapbase; + port->membase = p->membase; + port->irq = p->irqs[SCIx_TXI_IRQ]; + port->flags = p->flags; + sci_port->type = port->type = p->type; sci_port->scscr = p->scscr; - sci_port->port.irq = p->irqs[SCIx_TXI_IRQ]; - sci_port->port.flags = p->flags; - sci_port->port.dev = &dev->dev; - sci_port->type = sci_port->port.type = p->type; + sci_port->scbrr_algo_id = p->scbrr_algo_id; + +#ifdef CONFIG_SERIAL_SH_SCI_DMA + sci_port->dma_dev = p->dma_dev; + sci_port->slave_tx = p->dma_slave_tx; + sci_port->slave_rx = p->dma_slave_rx; + + dev_dbg(port->dev, "%s: DMA device %p, tx %d, rx %d\n", __func__, + p->dma_dev, p->dma_slave_tx, p->dma_slave_rx); +#endif memcpy(&sci_port->irqs, &p->irqs, sizeof(p->irqs)); + return 0; } #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE @@ -1168,11 +1782,11 @@ static void serial_console_write(struct console *co, const char *s, while ((sci_in(port, SCxSR) & bits) != bits) cpu_relax(); - if (sci_port->disable); + if (sci_port->disable) sci_port->disable(port); } -static int __init serial_console_setup(struct console *co, char *options) +static int __devinit serial_console_setup(struct console *co, char *options) { struct sci_port *sci_port; struct uart_port *port; @@ -1190,9 +1804,14 @@ static int __init serial_console_setup(struct console *co, char *options) if (co->index >= SCI_NPORTS) co->index = 0; - sci_port = &sci_ports[co->index]; - port = &sci_port->port; - co->data = port; + if (co->data) { + port = co->data; + sci_port = to_sci_port(port); + } else { + sci_port = &sci_ports[co->index]; + port = &sci_port->port; + co->data = port; + } /* * Also need to check port->type, we don't actually have any @@ -1236,6 +1855,15 @@ static int __init sci_console_init(void) return 0; } console_initcall(sci_console_init); + +static struct sci_port early_serial_port; +static struct console early_serial_console = { + .name = "early_ttySC", + .write = serial_console_write, + .flags = CON_PRINTBUFFER, +}; +static char early_serial_buf[32]; + #endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */ #if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) @@ -1264,14 +1892,14 @@ static int sci_remove(struct platform_device *dev) struct sci_port *p; unsigned long flags; -#ifdef CONFIG_HAVE_CLK cpufreq_unregister_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); -#endif spin_lock_irqsave(&priv->lock, flags); - list_for_each_entry(p, &priv->ports, node) + list_for_each_entry(p, &priv->ports, node) { uart_remove_one_port(&sci_uart_driver, &p->port); - + clk_put(p->iclk); + clk_put(p->fclk); + } spin_unlock_irqrestore(&priv->lock, flags); kfree(priv); @@ -1297,7 +1925,9 @@ static int __devinit sci_probe_single(struct platform_device *dev, return 0; } - sci_init_single(dev, sciport, index, p); + ret = sci_init_single(dev, sciport, index, p); + if (ret) + return ret; ret = uart_add_one_port(&sci_uart_driver, &sciport->port); if (ret) @@ -1324,6 +1954,21 @@ static int __devinit sci_probe(struct platform_device *dev) struct sh_sci_priv *priv; int i, ret = -EINVAL; +#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE + if (is_early_platform_device(dev)) { + if (dev->id == -1) + return -ENOTSUPP; + early_serial_console.index = dev->id; + early_serial_console.data = &early_serial_port.port; + sci_init_single(NULL, &early_serial_port, dev->id, p); + serial_console_setup(&early_serial_console, early_serial_buf); + if (!strstr(early_serial_buf, "keep")) + early_serial_console.flags |= CON_BOOT; + register_console(&early_serial_console); + return 0; + } +#endif + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1332,10 +1977,8 @@ static int __devinit sci_probe(struct platform_device *dev) spin_lock_init(&priv->lock); platform_set_drvdata(dev, priv); -#ifdef CONFIG_HAVE_CLK priv->clk_nb.notifier_call = sci_notifier; cpufreq_register_notifier(&priv->clk_nb, CPUFREQ_TRANSITION_NOTIFIER); -#endif if (dev->id != -1) { ret = sci_probe_single(dev, dev->id, p, &sci_ports[dev->id]); @@ -1388,14 +2031,14 @@ static int sci_resume(struct device *dev) return 0; } -static struct dev_pm_ops sci_dev_pm_ops = { +static const struct dev_pm_ops sci_dev_pm_ops = { .suspend = sci_suspend, .resume = sci_resume, }; static struct platform_driver sci_driver = { .probe = sci_probe, - .remove = __devexit_p(sci_remove), + .remove = sci_remove, .driver = { .name = "sh-sci", .owner = THIS_MODULE, @@ -1425,6 +2068,10 @@ static void __exit sci_exit(void) uart_unregister_driver(&sci_uart_driver); } +#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE +early_platform_init_buffer("earlyprintk", &sci_driver, + early_serial_buf, ARRAY_SIZE(early_serial_buf)); +#endif module_init(sci_init); module_exit(sci_exit); diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h index 1b2ce15..b223d6c 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -1,5 +1,5 @@ #include <linux/serial_core.h> -#include <asm/io.h> +#include <linux/io.h> #include <linux/gpio.h> #if defined(CONFIG_H83007) || defined(CONFIG_H83068) @@ -21,7 +21,11 @@ # define SCPCR 0xA4000116 # define SCPDR 0xA4000136 #elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) # define PORT_PTCR 0xA405011EUL # define PORT_PVCR 0xA4050122UL # define SCIF_ORER 0x0200 /* overrun error bit */ @@ -83,6 +87,12 @@ # define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) #elif defined(CONFIG_H8S2678) # define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) +#elif defined(CONFIG_CPU_SUBTYPE_SH7757) +# define SCSPTR0 0xfe4b0020 +# define SCSPTR1 0xfe4b0020 +# define SCSPTR2 0xfe4b0020 +# define SCIF_ORER 0x0001 +# define SCIF_ONLY #elif defined(CONFIG_CPU_SUBTYPE_SH7763) # define SCSPTR0 0xffe00024 /* 16 bit SCIF */ # define SCSPTR1 0xffe08024 /* 16 bit SCIF */ @@ -159,7 +169,11 @@ #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) # define SCIF_ORER 0x0200 # define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER) # define SCIF_RFDC_MASK 0x007f @@ -192,7 +206,11 @@ #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) # define SCxSR_RDxF_CLEAR(port) (sci_in(port, SCxSR) & 0xfffc) # define SCxSR_ERROR_CLEAR(port) (sci_in(port, SCxSR) & 0xfd73) # define SCxSR_TDxE_CLEAR(port) (sci_in(port, SCxSR) & 0xffdf) @@ -228,7 +246,7 @@ #define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\ static inline unsigned int sci_##name##_in(struct uart_port *port) \ { \ - if (port->type == PORT_SCIF) { \ + if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ SCI_IN(scif_size, scif_offset) \ } else { /* PORT_SCI or PORT_SCIFA */ \ SCI_IN(sci_size, sci_offset); \ @@ -236,7 +254,7 @@ } \ static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ { \ - if (port->type == PORT_SCIF) { \ + if (port->type == PORT_SCIF || port->type == PORT_SCIFB) { \ SCI_OUT(scif_size, scif_offset, value) \ } else { /* PORT_SCI or PORT_SCIFA */ \ SCI_OUT(sci_size, sci_offset, value); \ @@ -275,7 +293,11 @@ SCI_OUT(sci_size, sci_offset, value); \ } -#ifdef CONFIG_CPU_SH3 +#if defined(CONFIG_CPU_SH3) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) || \ + defined(CONFIG_ARCH_SH7372) #if defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) #define SCIx_FNS(name, sh3_sci_offset, sh3_sci_size, sh4_sci_offset, sh4_sci_size, \ sh3_scif_offset, sh3_scif_size, sh4_scif_offset, sh4_scif_size, \ @@ -285,7 +307,15 @@ CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size) #elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) +#define SCIF_FNS(name, scif_offset, scif_size) \ + CPU_SCIF_FNS(name, scif_offset, scif_size) +#elif defined(CONFIG_ARCH_SH7372) +#define SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) \ + CPU_SCIx_FNS(name, sh4_scifa_offset, sh4_scifa_size, sh4_scifb_offset, sh4_scifb_size) #define SCIF_FNS(name, scif_offset, scif_size) \ CPU_SCIF_FNS(name, scif_offset, scif_size) #else @@ -320,7 +350,10 @@ #if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) + defined(CONFIG_CPU_SUBTYPE_SH7721) || \ + defined(CONFIG_ARCH_SH73A0) || \ + defined(CONFIG_ARCH_SH7367) || \ + defined(CONFIG_ARCH_SH7377) SCIF_FNS(SCSMR, 0x00, 16) SCIF_FNS(SCBRR, 0x04, 8) @@ -330,7 +363,21 @@ SCIF_FNS(SCFCR, 0x18, 16) SCIF_FNS(SCFDR, 0x1c, 16) SCIF_FNS(SCxTDR, 0x20, 8) SCIF_FNS(SCxRDR, 0x24, 8) -SCIF_FNS(SCLSR, 0x24, 16) +SCIF_FNS(SCLSR, 0x00, 0) +#elif defined(CONFIG_ARCH_SH7372) +SCIF_FNS(SCSMR, 0x00, 16) +SCIF_FNS(SCBRR, 0x04, 8) +SCIF_FNS(SCSCR, 0x08, 16) +SCIF_FNS(SCTDSR, 0x0c, 16) +SCIF_FNS(SCFER, 0x10, 16) +SCIF_FNS(SCxSR, 0x14, 16) +SCIF_FNS(SCFCR, 0x18, 16) +SCIF_FNS(SCFDR, 0x1c, 16) +SCIF_FNS(SCTFDR, 0x38, 16) +SCIF_FNS(SCRFDR, 0x3c, 16) +SCIx_FNS(SCxTDR, 0x20, 8, 0x40, 8) +SCIx_FNS(SCxRDR, 0x24, 8, 0x60, 8) +SCIF_FNS(SCLSR, 0x00, 0) #elif defined(CONFIG_CPU_SUBTYPE_SH7723) ||\ defined(CONFIG_CPU_SUBTYPE_SH7724) SCIx_FNS(SCSMR, 0x00, 16, 0x00, 16) @@ -431,35 +478,7 @@ static const struct __attribute__((packed)) { static inline int sci_rxd_in(struct uart_port *port) { if (port->mapbase == 0xfffffe80) - return ctrl_inb(SCPDR)&0x01 ? 1 : 0; /* SCI */ - if (port->mapbase == 0xa4000150) - return ctrl_inb(SCPDR)&0x10 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xa4000140) - return ctrl_inb(SCPDR)&0x04 ? 1 : 0; /* IRDA */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7705) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == SCIF0) - return ctrl_inb(SCPDR)&0x04 ? 1 : 0; /* IRDA */ - if (port->mapbase == SCIF2) - return ctrl_inb(SCPDR)&0x10 ? 1 : 0; /* SCIF */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7710) || defined(CONFIG_CPU_SUBTYPE_SH7712) -static inline int sci_rxd_in(struct uart_port *port) -{ - return sci_in(port,SCxSR)&0x0010 ? 1 : 0; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7720) || \ - defined(CONFIG_CPU_SUBTYPE_SH7721) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xa4430000) - return sci_in(port, SCxSR) & 0x0003 ? 1 : 0; - else if (port->mapbase == 0xa4438000) - return sci_in(port, SCxSR) & 0x0003 ? 1 : 0; + return __raw_readb(SCPDR)&0x01 ? 1 : 0; /* SCI */ return 1; } #elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \ @@ -471,198 +490,18 @@ static inline int sci_rxd_in(struct uart_port *port) static inline int sci_rxd_in(struct uart_port *port) { if (port->mapbase == 0xffe00000) - return ctrl_inb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */ - if (port->mapbase == 0xffe80000) - return ctrl_inw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH4_202) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xffe80000) - return ctrl_inw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7760) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xfe600000) - return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xfe610000) - return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xfe620000) - return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7343) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xffe00000) - return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffe10000) - return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffe20000) - return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffe30000) - return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7366) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xffe00000) - return ctrl_inb(SCPDR0) & 0x0001 ? 1 : 0; /* SCIF0 */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7722) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xffe00000) - return ctrl_inb(PSDR) & 0x02 ? 1 : 0; /* SCIF0 */ - if (port->mapbase == 0xffe10000) - return ctrl_inb(PADR) & 0x40 ? 1 : 0; /* SCIF1 */ - if (port->mapbase == 0xffe20000) - return ctrl_inb(PWDR) & 0x04 ? 1 : 0; /* SCIF2 */ - + return __raw_readb(SCSPTR1)&0x01 ? 1 : 0; /* SCI */ return 1; } -#elif defined(CONFIG_CPU_SUBTYPE_SH7723) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xffe00000) - return ctrl_inb(SCSPTR0) & 0x0008 ? 1 : 0; /* SCIF0 */ - if (port->mapbase == 0xffe10000) - return ctrl_inb(SCSPTR1) & 0x0020 ? 1 : 0; /* SCIF1 */ - if (port->mapbase == 0xffe20000) - return ctrl_inb(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF2 */ - if (port->mapbase == 0xa4e30000) - return ctrl_inb(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF3 */ - if (port->mapbase == 0xa4e40000) - return ctrl_inb(SCSPTR4) & 0x0001 ? 1 : 0; /* SCIF4 */ - if (port->mapbase == 0xa4e50000) - return ctrl_inb(SCSPTR5) & 0x0008 ? 1 : 0; /* SCIF5 */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7724) -# define SCFSR 0x0010 -# define SCASSR 0x0014 -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->type == PORT_SCIF) - return ctrl_inw((port->mapbase + SCFSR)) & SCIF_BRK ? 1 : 0; - if (port->type == PORT_SCIFA) - return ctrl_inw((port->mapbase + SCASSR)) & SCIF_BRK ? 1 : 0; - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103) -static inline int sci_rxd_in(struct uart_port *port) -{ - return sci_in(port, SCSPTR)&0x0001 ? 1 : 0; /* SCIF */ -} #elif defined(__H8300H__) || defined(__H8300S__) static inline int sci_rxd_in(struct uart_port *port) { int ch = (port->mapbase - SMR0) >> 3; return (H8300_SCI_DR(ch) & h8300_sci_pins[ch].rx) ? 1 : 0; } -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xffe00000) - return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffe08000) - return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffe10000) - return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF/IRDA */ - - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7770) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xff923000) - return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xff924000) - return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xff925000) - return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7780) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xffe00000) - return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffe10000) - return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7785) || \ - defined(CONFIG_CPU_SUBTYPE_SH7786) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xffea0000) - return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffeb0000) - return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffec0000) - return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffed0000) - return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffee0000) - return ctrl_inw(SCSPTR4) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffef0000) - return ctrl_inw(SCSPTR5) & 0x0001 ? 1 : 0; /* SCIF */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7201) || \ - defined(CONFIG_CPU_SUBTYPE_SH7203) || \ - defined(CONFIG_CPU_SUBTYPE_SH7206) || \ - defined(CONFIG_CPU_SUBTYPE_SH7263) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xfffe8000) - return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xfffe8800) - return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xfffe9000) - return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xfffe9800) - return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */ -#if defined(CONFIG_CPU_SUBTYPE_SH7201) - if (port->mapbase == 0xfffeA000) - return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xfffeA800) - return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xfffeB000) - return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xfffeB800) - return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */ -#endif - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SH7619) -static inline int sci_rxd_in(struct uart_port *port) -{ - if (port->mapbase == 0xf8400000) - return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xf8410000) - return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xf8420000) - return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */ - return 1; -} -#elif defined(CONFIG_CPU_SUBTYPE_SHX3) +#else /* default case for non-SCI processors */ static inline int sci_rxd_in(struct uart_port *port) { - if (port->mapbase == 0xffc30000) - return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffc40000) - return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffc50000) - return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */ - if (port->mapbase == 0xffc60000) - return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */ return 1; } #endif diff --git a/drivers/serial/sn_console.c b/drivers/serial/sn_console.c index d5276c0..cff9a30 100644 --- a/drivers/serial/sn_console.c +++ b/drivers/serial/sn_console.c @@ -469,9 +469,9 @@ sn_receive_chars(struct sn_cons_port *port, unsigned long flags) return; } - if (port->sc_port.info) { - /* The serial_core stuffs are initilized, use them */ - tty = port->sc_port.info->port.tty; + if (port->sc_port.state) { + /* The serial_core stuffs are initialized, use them */ + tty = port->sc_port.state->port.tty; } else { /* Not registered yet - can't pass to tty layer. */ @@ -492,7 +492,7 @@ sn_receive_chars(struct sn_cons_port *port, unsigned long flags) sysrq_requested = 0; if (ch && time_before(jiffies, sysrq_timeout)) { spin_unlock_irqrestore(&port->sc_port.lock, flags); - handle_sysrq(ch, NULL); + handle_sysrq(ch); spin_lock_irqsave(&port->sc_port.lock, flags); /* ignore actual sysrq command char */ continue; @@ -550,12 +550,12 @@ static void sn_transmit_chars(struct sn_cons_port *port, int raw) BUG_ON(!port->sc_is_asynch); - if (port->sc_port.info) { - /* We're initilized, using serial core infrastructure */ - xmit = &port->sc_port.info->xmit; + if (port->sc_port.state) { + /* We're initialized, using serial core infrastructure */ + xmit = &port->sc_port.state->xmit; } else { /* Probably sn_sal_switch_to_asynch has been run but serial core isn't - * initilized yet. Just return. Writes are going through + * initialized yet. Just return. Writes are going through * sn_sal_console_write (due to register_console) at this time. */ return; @@ -927,7 +927,7 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) /* We can't look at the xmit buffer if we're not registered with serial core * yet. So only do the fancy recovery after registering */ - if (!port->sc_port.info) { + if (!port->sc_port.state) { /* Not yet registered with serial core - simple case */ puts_raw_fixed(port->sc_ops->sal_puts_raw, s, count); return; @@ -936,8 +936,8 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) /* somebody really wants this output, might be an * oops, kdb, panic, etc. make sure they get it. */ if (spin_is_locked(&port->sc_port.lock)) { - int lhead = port->sc_port.info->xmit.head; - int ltail = port->sc_port.info->xmit.tail; + int lhead = port->sc_port.state->xmit.head; + int ltail = port->sc_port.state->xmit.tail; int counter, got_lock = 0; /* @@ -962,13 +962,13 @@ sn_sal_console_write(struct console *co, const char *s, unsigned count) break; } else { /* still locked */ - if ((lhead != port->sc_port.info->xmit.head) + if ((lhead != port->sc_port.state->xmit.head) || (ltail != - port->sc_port.info->xmit.tail)) { + port->sc_port.state->xmit.tail)) { lhead = - port->sc_port.info->xmit.head; + port->sc_port.state->xmit.head; ltail = - port->sc_port.info->xmit.tail; + port->sc_port.state->xmit.tail; counter = 0; } } diff --git a/drivers/serial/suncore.c b/drivers/serial/suncore.c index a2d4a19..6381a02 100644 --- a/drivers/serial/suncore.c +++ b/drivers/serial/suncore.c @@ -53,46 +53,53 @@ void sunserial_unregister_minors(struct uart_driver *drv, int count) EXPORT_SYMBOL(sunserial_unregister_minors); int sunserial_console_match(struct console *con, struct device_node *dp, - struct uart_driver *drv, int line) + struct uart_driver *drv, int line, bool ignore_line) { - int off; - - if (!con || of_console_device != dp) + if (!con) return 0; - off = 0; - if (of_console_options && - *of_console_options == 'b') - off = 1; + drv->cons = con; - if ((line & 1) != off) + if (of_console_device != dp) return 0; - con->index = line; - drv->cons = con; - add_preferred_console(con->name, line, NULL); + if (!ignore_line) { + int off = 0; + + if (of_console_options && + *of_console_options == 'b') + off = 1; + if ((line & 1) != off) + return 0; + } + + if (!console_set_on_cmdline) { + con->index = line; + add_preferred_console(con->name, line, NULL); + } return 1; } EXPORT_SYMBOL(sunserial_console_match); -void -sunserial_console_termios(struct console *con) +void sunserial_console_termios(struct console *con, struct device_node *uart_dp) { - struct device_node *dp; - const char *od, *mode, *s; + const char *mode, *s; char mode_prop[] = "ttyX-mode"; int baud, bits, stop, cflag; char parity; - dp = of_find_node_by_path("/options"); - od = of_get_property(dp, "output-device", NULL); - if (!strcmp(od, "rsc")) { - mode = of_get_property(of_console_device, + if (!strcmp(uart_dp->name, "rsc") || + !strcmp(uart_dp->name, "rsc-console") || + !strcmp(uart_dp->name, "rsc-control")) { + mode = of_get_property(uart_dp, "ssp-console-modes", NULL); if (!mode) mode = "115200,8,n,1,-"; + } else if (!strcmp(uart_dp->name, "lom-console")) { + mode = "9600,8,n,1,-"; } else { + struct device_node *dp; char c; c = 'a'; @@ -101,6 +108,7 @@ sunserial_console_termios(struct console *con) mode_prop[3] = c; + dp = of_find_node_by_path("/options"); mode = of_get_property(dp, mode_prop, NULL); if (!mode) mode = "9600,8,n,1,-"; diff --git a/drivers/serial/suncore.h b/drivers/serial/suncore.h index 042668a..db20579 100644 --- a/drivers/serial/suncore.h +++ b/drivers/serial/suncore.h @@ -26,7 +26,8 @@ extern int sunserial_register_minors(struct uart_driver *, int); extern void sunserial_unregister_minors(struct uart_driver *, int); extern int sunserial_console_match(struct console *, struct device_node *, - struct uart_driver *, int); -extern void sunserial_console_termios(struct console *); + struct uart_driver *, int, bool); +extern void sunserial_console_termios(struct console *, + struct device_node *); #endif /* !(_SERIAL_SUN_H) */ diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c index 1df5325..c901486 100644 --- a/drivers/serial/sunhv.c +++ b/drivers/serial/sunhv.c @@ -184,8 +184,8 @@ static struct tty_struct *receive_chars(struct uart_port *port) { struct tty_struct *tty = NULL; - if (port->info != NULL) /* Unopened serial console */ - tty = port->info->port.tty; + if (port->state != NULL) /* Unopened serial console */ + tty = port->state->port.tty; if (sunhv_ops->receive_chars(port, tty)) sun_do_break(); @@ -197,10 +197,10 @@ static void transmit_chars(struct uart_port *port) { struct circ_buf *xmit; - if (!port->info) + if (!port->state) return; - xmit = &port->info->xmit; + xmit = &port->state->xmit; if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return; @@ -519,13 +519,13 @@ static struct console sunhv_console = { .data = &sunhv_reg, }; -static int __devinit hv_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit hv_probe(struct platform_device *op, const struct of_device_id *match) { struct uart_port *port; unsigned long minor; int err; - if (op->irqs[0] == 0xffffffff) + if (op->archdata.irqs[0] == 0xffffffff) return -ENODEV; port = kzalloc(sizeof(struct uart_port), GFP_KERNEL); @@ -557,7 +557,7 @@ static int __devinit hv_probe(struct of_device *op, const struct of_device_id *m port->membase = (unsigned char __iomem *) __pa(port); - port->irq = op->irqs[0]; + port->irq = op->archdata.irqs[0]; port->dev = &op->dev; @@ -565,8 +565,8 @@ static int __devinit hv_probe(struct of_device *op, const struct of_device_id *m if (err) goto out_free_con_read_page; - sunserial_console_match(&sunhv_console, op->node, - &sunhv_reg, port->line); + sunserial_console_match(&sunhv_console, op->dev.of_node, + &sunhv_reg, port->line, false); err = uart_add_one_port(&sunhv_reg, port); if (err) @@ -598,7 +598,7 @@ out_free_port: return err; } -static int __devexit hv_remove(struct of_device *dev) +static int __devexit hv_remove(struct platform_device *dev) { struct uart_port *port = dev_get_drvdata(&dev->dev); @@ -630,8 +630,11 @@ static const struct of_device_id hv_match[] = { MODULE_DEVICE_TABLE(of, hv_match); static struct of_platform_driver hv_driver = { - .name = "hv", - .match_table = hv_match, + .driver = { + .name = "hv", + .owner = THIS_MODULE, + .of_match_table = hv_match, + }, .probe = hv_probe, .remove = __devexit_p(hv_remove), }; @@ -641,12 +644,12 @@ static int __init sunhv_init(void) if (tlb_type != hypervisor) return -ENODEV; - return of_register_driver(&hv_driver, &of_bus_type); + return of_register_platform_driver(&hv_driver); } static void __exit sunhv_exit(void) { - of_unregister_driver(&hv_driver); + of_unregister_platform_driver(&hv_driver); } module_init(sunhv_init); diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index 0355efe..5b246b1 100644 --- a/drivers/serial/sunsab.c +++ b/drivers/serial/sunsab.c @@ -117,8 +117,8 @@ receive_chars(struct uart_sunsab_port *up, int count = 0; int i; - if (up->port.info != NULL) /* Unopened serial console */ - tty = up->port.info->port.tty; + if (up->port.state != NULL) /* Unopened serial console */ + tty = up->port.state->port.tty; /* Read number of BYTES (Character + Status) available. */ if (stat->sreg.isr0 & SAB82532_ISR0_RPF) { @@ -229,7 +229,7 @@ static void sunsab_tx_idle(struct uart_sunsab_port *); static void transmit_chars(struct uart_sunsab_port *up, union sab82532_irq_status *stat) { - struct circ_buf *xmit = &up->port.info->xmit; + struct circ_buf *xmit = &up->port.state->xmit; int i; if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) { @@ -297,7 +297,7 @@ static void check_status(struct uart_sunsab_port *up, up->port.icount.dsr++; } - wake_up_interruptible(&up->port.info->delta_msr_wait); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); } static irqreturn_t sunsab_interrupt(int irq, void *dev_id) @@ -429,7 +429,7 @@ static void sunsab_tx_idle(struct uart_sunsab_port *up) static void sunsab_start_tx(struct uart_port *port) { struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - struct circ_buf *xmit = &up->port.info->xmit; + struct circ_buf *xmit = &up->port.state->xmit; int i; up->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS|SAB82532_IMR1_XPR); @@ -474,7 +474,7 @@ static void sunsab_stop_rx(struct uart_port *port) { struct uart_sunsab_port *up = (struct uart_sunsab_port *) port; - up->interrupt_mask0 |= SAB82532_ISR0_TCD; + up->interrupt_mask0 |= SAB82532_IMR0_TCD; writeb(up->interrupt_mask1, &up->regs->w.imr0); } @@ -883,7 +883,7 @@ static int sunsab_console_setup(struct console *con, char *options) printk("Console: ttyS%d (SAB82532)\n", (sunsab_reg.minor - 64) + con->index); - sunserial_console_termios(con); + sunserial_console_termios(con, up->port.dev->of_node); switch (con->cflag & CBAUD) { case B150: baud = 150; break; @@ -954,7 +954,7 @@ static inline struct console *SUNSAB_CONSOLE(void) #endif static int __devinit sunsab_init_one(struct uart_sunsab_port *up, - struct of_device *op, + struct platform_device *op, unsigned long offset, int line) { @@ -969,7 +969,7 @@ static int __devinit sunsab_init_one(struct uart_sunsab_port *up, return -ENOMEM; up->regs = (union sab82532_async_regs __iomem *) up->port.membase; - up->port.irq = op->irqs[0]; + up->port.irq = op->archdata.irqs[0]; up->port.fifosize = SAB82532_XMIT_FIFO_SIZE; up->port.iotype = UPIO_MEM; @@ -1006,7 +1006,7 @@ static int __devinit sunsab_init_one(struct uart_sunsab_port *up, return 0; } -static int __devinit sab_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit sab_probe(struct platform_device *op, const struct of_device_id *match) { static int inst; struct uart_sunsab_port *up; @@ -1026,11 +1026,13 @@ static int __devinit sab_probe(struct of_device *op, const struct of_device_id * if (err) goto out1; - sunserial_console_match(SUNSAB_CONSOLE(), op->node, - &sunsab_reg, up[0].port.line); + sunserial_console_match(SUNSAB_CONSOLE(), op->dev.of_node, + &sunsab_reg, up[0].port.line, + false); - sunserial_console_match(SUNSAB_CONSOLE(), op->node, - &sunsab_reg, up[1].port.line); + sunserial_console_match(SUNSAB_CONSOLE(), op->dev.of_node, + &sunsab_reg, up[1].port.line, + false); err = uart_add_one_port(&sunsab_reg, &up[0].port); if (err) @@ -1060,7 +1062,7 @@ out: return err; } -static int __devexit sab_remove(struct of_device *op) +static int __devexit sab_remove(struct platform_device *op) { struct uart_sunsab_port *up = dev_get_drvdata(&op->dev); @@ -1091,8 +1093,11 @@ static const struct of_device_id sab_match[] = { MODULE_DEVICE_TABLE(of, sab_match); static struct of_platform_driver sab_driver = { - .name = "sab", - .match_table = sab_match, + .driver = { + .name = "sab", + .owner = THIS_MODULE, + .of_match_table = sab_match, + }, .probe = sab_probe, .remove = __devexit_p(sab_remove), }; @@ -1116,7 +1121,6 @@ static int __init sunsab_init(void) if (!sunsab_ports) return -ENOMEM; - sunsab_reg.cons = SUNSAB_CONSOLE(); err = sunserial_register_minors(&sunsab_reg, num_channels); if (err) { kfree(sunsab_ports); @@ -1126,12 +1130,12 @@ static int __init sunsab_init(void) } } - return of_register_driver(&sab_driver, &of_bus_type); + return of_register_platform_driver(&sab_driver); } static void __exit sunsab_exit(void) { - of_unregister_driver(&sab_driver); + of_unregister_platform_driver(&sab_driver); if (sunsab_reg.nr) { sunserial_unregister_minors(&sunsab_reg, sunsab_reg.nr); } diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c index 47c6837..551ebfe 100644 --- a/drivers/serial/sunsu.c +++ b/drivers/serial/sunsu.c @@ -29,6 +29,7 @@ #include <linux/serial.h> #include <linux/sysrq.h> #include <linux/console.h> +#include <linux/slab.h> #ifdef CONFIG_SERIO #include <linux/serio.h> #endif @@ -311,7 +312,7 @@ static void sunsu_enable_ms(struct uart_port *port) static struct tty_struct * receive_chars(struct uart_sunsu_port *up, unsigned char *status) { - struct tty_struct *tty = up->port.info->port.tty; + struct tty_struct *tty = up->port.state->port.tty; unsigned char ch, flag; int max_count = 256; int saw_console_brk = 0; @@ -389,7 +390,7 @@ receive_chars(struct uart_sunsu_port *up, unsigned char *status) static void transmit_chars(struct uart_sunsu_port *up) { - struct circ_buf *xmit = &up->port.info->xmit; + struct circ_buf *xmit = &up->port.state->xmit; int count; if (up->port.x_char) { @@ -441,7 +442,7 @@ static void check_modem_status(struct uart_sunsu_port *up) if (status & UART_MSR_DCTS) uart_handle_cts_change(&up->port, status & UART_MSR_CTS); - wake_up_interruptible(&up->port.info->delta_msr_wait); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); } static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id) @@ -1199,7 +1200,7 @@ static int __devinit sunsu_kbd_ms_init(struct uart_sunsu_port *up) return -ENODEV; printk("%s: %s port at %llx, irq %u\n", - to_of_device(up->port.dev)->node->full_name, + up->port.dev->of_node->full_name, (up->su_type == SU_PORT_KBD) ? "Keyboard" : "Mouse", (unsigned long long) up->port.mapbase, up->port.irq); @@ -1329,11 +1330,9 @@ static void sunsu_console_write(struct console *co, const char *s, */ static int __init sunsu_console_setup(struct console *co, char *options) { + static struct ktermios dummy; + struct ktermios termios; struct uart_port *port; - int baud = 9600; - int bits = 8; - int parity = 'n'; - int flow = 'n'; printk("Console: ttyS%d (SU)\n", (sunsu_reg.minor - 64) + co->index); @@ -1352,10 +1351,15 @@ static int __init sunsu_console_setup(struct console *co, char *options) */ spin_lock_init(&port->lock); - if (options) - uart_parse_options(options, &baud, &parity, &bits, &flow); + /* Get firmware console settings. */ + sunserial_console_termios(co, port->dev->of_node); - return uart_set_options(port, co, baud, parity, bits, flow); + memset(&termios, 0, sizeof(struct ktermios)); + termios.c_cflag = co->cflag; + port->mctrl |= TIOCM_DTR; + port->ops->set_termios(port, &termios, &dummy); + + return 0; } static struct console sunsu_console = { @@ -1402,13 +1406,14 @@ static enum su_type __devinit su_get_type(struct device_node *dp) return SU_PORT_PORT; } -static int __devinit su_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit su_probe(struct platform_device *op, const struct of_device_id *match) { static int inst; - struct device_node *dp = op->node; + struct device_node *dp = op->dev.of_node; struct uart_sunsu_port *up; struct resource *rp; enum su_type type; + bool ignore_line; int err; type = su_get_type(dp); @@ -1438,7 +1443,7 @@ static int __devinit su_probe(struct of_device *op, const struct of_device_id *m return -ENOMEM; } - up->port.irq = op->irqs[0]; + up->port.irq = op->archdata.irqs[0]; up->port.dev = &op->dev; @@ -1449,8 +1454,10 @@ static int __devinit su_probe(struct of_device *op, const struct of_device_id *m if (up->su_type == SU_PORT_KBD || up->su_type == SU_PORT_MS) { err = sunsu_kbd_ms_init(up); if (err) { + of_iounmap(&op->resource[0], + up->port.membase, up->reg_size); kfree(up); - goto out_unmap; + return err; } dev_set_drvdata(&op->dev, up); @@ -1467,8 +1474,14 @@ static int __devinit su_probe(struct of_device *op, const struct of_device_id *m up->port.ops = &sunsu_pops; + ignore_line = false; + if (!strcmp(dp->name, "rsc-console") || + !strcmp(dp->name, "lom-console")) + ignore_line = true; + sunserial_console_match(SUNSU_CONSOLE(), dp, - &sunsu_reg, up->port.line); + &sunsu_reg, up->port.line, + ignore_line); err = uart_add_one_port(&sunsu_reg, &up->port); if (err) goto out_unmap; @@ -1484,23 +1497,28 @@ out_unmap: return err; } -static int __devexit su_remove(struct of_device *op) +static int __devexit su_remove(struct platform_device *op) { struct uart_sunsu_port *up = dev_get_drvdata(&op->dev); + bool kbdms = false; if (up->su_type == SU_PORT_MS || - up->su_type == SU_PORT_KBD) { + up->su_type == SU_PORT_KBD) + kbdms = true; + + if (kbdms) { #ifdef CONFIG_SERIO serio_unregister_port(&up->serio); #endif - kfree(up); - } else if (up->port.type != PORT_UNKNOWN) { + } else if (up->port.type != PORT_UNKNOWN) uart_remove_one_port(&sunsu_reg, &up->port); - } if (up->port.membase) of_iounmap(&op->resource[0], up->port.membase, up->reg_size); + if (kbdms) + kfree(up); + dev_set_drvdata(&op->dev, NULL); return 0; @@ -1517,13 +1535,20 @@ static const struct of_device_id su_match[] = { .name = "serial", .compatible = "su", }, + { + .type = "serial", + .compatible = "su", + }, {}, }; MODULE_DEVICE_TABLE(of, su_match); static struct of_platform_driver su_driver = { - .name = "su", - .match_table = su_match, + .driver = { + .name = "su", + .owner = THIS_MODULE, + .of_match_table = su_match, + }, .probe = su_probe, .remove = __devexit_p(su_remove), }; @@ -1548,6 +1573,12 @@ static int __init sunsu_init(void) num_uart++; } } + for_each_node_by_type(dp, "serial") { + if (of_device_is_compatible(dp, "su")) { + if (su_get_type(dp) == SU_PORT_PORT) + num_uart++; + } + } if (num_uart) { err = sunserial_register_minors(&sunsu_reg, num_uart); @@ -1555,7 +1586,7 @@ static int __init sunsu_init(void) return err; } - err = of_register_driver(&su_driver, &of_bus_type); + err = of_register_platform_driver(&su_driver); if (err && num_uart) sunserial_unregister_minors(&sunsu_reg, num_uart); diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c index e09d3ce..c1967ac 100644 --- a/drivers/serial/sunzilog.c +++ b/drivers/serial/sunzilog.c @@ -102,6 +102,8 @@ struct uart_sunzilog_port { #endif }; +static void sunzilog_putchar(struct uart_port *port, int ch); + #define ZILOG_CHANNEL_FROM_PORT(PORT) ((struct zilog_channel __iomem *)((PORT)->membase)) #define UART_ZILOG(PORT) ((struct uart_sunzilog_port *)(PORT)) @@ -328,9 +330,9 @@ sunzilog_receive_chars(struct uart_sunzilog_port *up, unsigned char ch, r1, flag; tty = NULL; - if (up->port.info != NULL && /* Unopened serial console */ - up->port.info->port.tty != NULL) /* Keyboard || mouse */ - tty = up->port.info->port.tty; + if (up->port.state != NULL && /* Unopened serial console */ + up->port.state->port.tty != NULL) /* Keyboard || mouse */ + tty = up->port.state->port.tty; for (;;) { @@ -451,7 +453,7 @@ static void sunzilog_status_handle(struct uart_sunzilog_port *up, uart_handle_cts_change(&up->port, (status & CTS)); - wake_up_interruptible(&up->port.info->delta_msr_wait); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); } up->prev_status = status; @@ -501,9 +503,9 @@ static void sunzilog_transmit_chars(struct uart_sunzilog_port *up, return; } - if (up->port.info == NULL) + if (up->port.state == NULL) goto ack_tx_int; - xmit = &up->port.info->xmit; + xmit = &up->port.state->xmit; if (uart_circ_empty(xmit)) goto ack_tx_int; @@ -705,7 +707,7 @@ static void sunzilog_start_tx(struct uart_port *port) port->icount.tx++; port->x_char = 0; } else { - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; writeb(xmit->buf[xmit->tail], &channel->data); ZSDELAY(); @@ -996,6 +998,50 @@ static int sunzilog_verify_port(struct uart_port *port, struct serial_struct *se return -EINVAL; } +#ifdef CONFIG_CONSOLE_POLL +static int sunzilog_get_poll_char(struct uart_port *port) +{ + unsigned char ch, r1; + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *) port; + struct zilog_channel __iomem *channel + = ZILOG_CHANNEL_FROM_PORT(&up->port); + + + r1 = read_zsreg(channel, R1); + if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) { + writeb(ERR_RES, &channel->control); + ZSDELAY(); + ZS_WSYNC(channel); + } + + ch = readb(&channel->control); + ZSDELAY(); + + /* This funny hack depends upon BRK_ABRT not interfering + * with the other bits we care about in R1. + */ + if (ch & BRK_ABRT) + r1 |= BRK_ABRT; + + if (!(ch & Rx_CH_AV)) + return NO_POLL_CHAR; + + ch = readb(&channel->data); + ZSDELAY(); + + ch &= up->parity_mask; + return ch; +} + +static void sunzilog_put_poll_char(struct uart_port *port, + unsigned char ch) +{ + struct uart_sunzilog_port *up = (struct uart_sunzilog_port *)port; + + sunzilog_putchar(&up->port, ch); +} +#endif /* CONFIG_CONSOLE_POLL */ + static struct uart_ops sunzilog_pops = { .tx_empty = sunzilog_tx_empty, .set_mctrl = sunzilog_set_mctrl, @@ -1013,6 +1059,10 @@ static struct uart_ops sunzilog_pops = { .request_port = sunzilog_request_port, .config_port = sunzilog_config_port, .verify_port = sunzilog_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = sunzilog_get_poll_char, + .poll_put_char = sunzilog_put_poll_char, +#endif }; static int uart_chip_count; @@ -1180,7 +1230,7 @@ static int __init sunzilog_console_setup(struct console *con, char *options) (sunzilog_reg.minor - 64) + con->index, con->index); /* Get firmware console settings. */ - sunserial_console_termios(con); + sunserial_console_termios(con, up->port.dev->of_node); /* Firmware console speed is limited to 150-->38400 baud so * this hackish cflag thing is OK. @@ -1349,7 +1399,7 @@ static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up) static int zilog_irq = -1; -static int __devinit zs_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit zs_probe(struct platform_device *op, const struct of_device_id *match) { static int kbm_inst, uart_inst; int inst; @@ -1358,7 +1408,7 @@ static int __devinit zs_probe(struct of_device *op, const struct of_device_id *m int keyboard_mouse = 0; int err; - if (of_find_property(op->node, "keyboard", NULL)) + if (of_find_property(op->dev.of_node, "keyboard", NULL)) keyboard_mouse = 1; /* uarts must come before keyboards/mice */ @@ -1376,7 +1426,7 @@ static int __devinit zs_probe(struct of_device *op, const struct of_device_id *m rp = sunzilog_chip_regs[inst]; if (zilog_irq == -1) - zilog_irq = op->irqs[0]; + zilog_irq = op->archdata.irqs[0]; up = &sunzilog_port_table[inst * 2]; @@ -1384,7 +1434,7 @@ static int __devinit zs_probe(struct of_device *op, const struct of_device_id *m up[0].port.mapbase = op->resource[0].start + 0x00; up[0].port.membase = (void __iomem *) &rp->channelA; up[0].port.iotype = UPIO_MEM; - up[0].port.irq = op->irqs[0]; + up[0].port.irq = op->archdata.irqs[0]; up[0].port.uartclk = ZS_CLOCK; up[0].port.fifosize = 1; up[0].port.ops = &sunzilog_pops; @@ -1401,7 +1451,7 @@ static int __devinit zs_probe(struct of_device *op, const struct of_device_id *m up[1].port.mapbase = op->resource[0].start + 0x04; up[1].port.membase = (void __iomem *) &rp->channelB; up[1].port.iotype = UPIO_MEM; - up[1].port.irq = op->irqs[0]; + up[1].port.irq = op->archdata.irqs[0]; up[1].port.uartclk = ZS_CLOCK; up[1].port.fifosize = 1; up[1].port.ops = &sunzilog_pops; @@ -1415,8 +1465,9 @@ static int __devinit zs_probe(struct of_device *op, const struct of_device_id *m sunzilog_init_hw(&up[1]); if (!keyboard_mouse) { - if (sunserial_console_match(SUNZILOG_CONSOLE(), op->node, - &sunzilog_reg, up[0].port.line)) + if (sunserial_console_match(SUNZILOG_CONSOLE(), op->dev.of_node, + &sunzilog_reg, up[0].port.line, + false)) up->flags |= SUNZILOG_FLAG_IS_CONS; err = uart_add_one_port(&sunzilog_reg, &up[0].port); if (err) { @@ -1424,8 +1475,9 @@ static int __devinit zs_probe(struct of_device *op, const struct of_device_id *m rp, sizeof(struct zilog_layout)); return err; } - if (sunserial_console_match(SUNZILOG_CONSOLE(), op->node, - &sunzilog_reg, up[1].port.line)) + if (sunserial_console_match(SUNZILOG_CONSOLE(), op->dev.of_node, + &sunzilog_reg, up[1].port.line, + false)) up->flags |= SUNZILOG_FLAG_IS_CONS; err = uart_add_one_port(&sunzilog_reg, &up[1].port); if (err) { @@ -1440,12 +1492,12 @@ static int __devinit zs_probe(struct of_device *op, const struct of_device_id *m "is a %s\n", dev_name(&op->dev), (unsigned long long) up[0].port.mapbase, - op->irqs[0], sunzilog_type(&up[0].port)); + op->archdata.irqs[0], sunzilog_type(&up[0].port)); printk(KERN_INFO "%s: Mouse at MMIO 0x%llx (irq = %d) " "is a %s\n", dev_name(&op->dev), (unsigned long long) up[1].port.mapbase, - op->irqs[0], sunzilog_type(&up[1].port)); + op->archdata.irqs[0], sunzilog_type(&up[1].port)); kbm_inst++; } @@ -1464,7 +1516,7 @@ static void __devexit zs_remove_one(struct uart_sunzilog_port *up) uart_remove_one_port(&sunzilog_reg, &up->port); } -static int __devexit zs_remove(struct of_device *op) +static int __devexit zs_remove(struct platform_device *op) { struct uart_sunzilog_port *up = dev_get_drvdata(&op->dev); struct zilog_layout __iomem *regs; @@ -1489,8 +1541,11 @@ static const struct of_device_id zs_match[] = { MODULE_DEVICE_TABLE(of, zs_match); static struct of_platform_driver zs_driver = { - .name = "zs", - .match_table = zs_match, + .driver = { + .name = "zs", + .owner = THIS_MODULE, + .of_match_table = zs_match, + }, .probe = zs_probe, .remove = __devexit_p(zs_remove), }; @@ -1521,7 +1576,7 @@ static int __init sunzilog_init(void) goto out_free_tables; } - err = of_register_driver(&zs_driver, &of_bus_type); + err = of_register_platform_driver(&zs_driver); if (err) goto out_unregister_uart; @@ -1549,7 +1604,7 @@ out: return err; out_unregister_driver: - of_unregister_driver(&zs_driver); + of_unregister_platform_driver(&zs_driver); out_unregister_uart: if (num_sunzilog) { @@ -1564,7 +1619,7 @@ out_free_tables: static void __exit sunzilog_exit(void) { - of_unregister_driver(&zs_driver); + of_unregister_platform_driver(&zs_driver); if (zilog_irq != -1) { struct uart_sunzilog_port *up = sunzilog_irq_chain; diff --git a/drivers/serial/timbuart.c b/drivers/serial/timbuart.c index 063a313..1f36b7e 100644 --- a/drivers/serial/timbuart.c +++ b/drivers/serial/timbuart.c @@ -26,6 +26,7 @@ #include <linux/kernel.h> #include <linux/platform_device.h> #include <linux/ioport.h> +#include <linux/slab.h> #include "timbuart.h" @@ -67,17 +68,27 @@ static void timbuart_start_tx(struct uart_port *port) tasklet_schedule(&uart->tasklet); } +static unsigned int timbuart_tx_empty(struct uart_port *port) +{ + u32 isr = ioread32(port->membase + TIMBUART_ISR); + + return (isr & TXBE) ? TIOCSER_TEMT : 0; +} + static void timbuart_flush_buffer(struct uart_port *port) { - u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | TIMBUART_CTRL_FLSHTX; + if (!timbuart_tx_empty(port)) { + u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | + TIMBUART_CTRL_FLSHTX; - iowrite8(ctl, port->membase + TIMBUART_CTRL); - iowrite32(TXBF, port->membase + TIMBUART_ISR); + iowrite8(ctl, port->membase + TIMBUART_CTRL); + iowrite32(TXBF, port->membase + TIMBUART_ISR); + } } static void timbuart_rx_chars(struct uart_port *port) { - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; while (ioread32(port->membase + TIMBUART_ISR) & RXDP) { u8 ch = ioread8(port->membase + TIMBUART_RXFIFO); @@ -86,7 +97,7 @@ static void timbuart_rx_chars(struct uart_port *port) } spin_unlock(&port->lock); - tty_flip_buffer_push(port->info->port.tty); + tty_flip_buffer_push(port->state->port.tty); spin_lock(&port->lock); dev_dbg(port->dev, "%s - total read %d bytes\n", @@ -95,7 +106,7 @@ static void timbuart_rx_chars(struct uart_port *port) static void timbuart_tx_chars(struct uart_port *port) { - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; while (!(ioread32(port->membase + TIMBUART_ISR) & TXBF) && !uart_circ_empty(xmit)) { @@ -118,7 +129,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier) { struct timbuart_port *uart = container_of(port, struct timbuart_port, port); - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return; @@ -194,13 +205,6 @@ void timbuart_tasklet(unsigned long arg) dev_dbg(uart->port.dev, "%s leaving\n", __func__); } -static unsigned int timbuart_tx_empty(struct uart_port *port) -{ - u32 isr = ioread32(port->membase + TIMBUART_ISR); - - return (isr & TXBE) ? TIOCSER_TEMT : 0; -} - static unsigned int timbuart_get_mctrl(struct uart_port *port) { u8 cts = ioread8(port->membase + TIMBUART_CTRL); @@ -219,7 +223,7 @@ static void timbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) if (mctrl & TIOCM_RTS) iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); else - iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); + iowrite8(0, port->membase + TIMBUART_CTRL); } static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier) @@ -231,7 +235,7 @@ static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier) iowrite32(CTS_DELTA, port->membase + TIMBUART_ISR); cts = timbuart_get_mctrl(port); uart_handle_cts_change(port, cts & TIOCM_CTS); - wake_up_interruptible(&port->info->delta_msr_wait); + wake_up_interruptible(&port->state->port.delta_msr_wait); } *ier |= CTS_DELTA; @@ -419,9 +423,9 @@ static struct uart_driver timbuart_driver = { .nr = 1 }; -static int timbuart_probe(struct platform_device *dev) +static int __devinit timbuart_probe(struct platform_device *dev) { - int err; + int err, irq; struct timbuart_port *uart; struct resource *iomem; @@ -453,11 +457,12 @@ static int timbuart_probe(struct platform_device *dev) uart->port.mapbase = iomem->start; uart->port.membase = NULL; - uart->port.irq = platform_get_irq(dev, 0); - if (uart->port.irq < 0) { + irq = platform_get_irq(dev, 0); + if (irq < 0) { err = -EINVAL; goto err_register; } + uart->port.irq = irq; tasklet_init(&uart->tasklet, timbuart_tasklet, (unsigned long)uart); @@ -484,7 +489,7 @@ err_mem: return err; } -static int timbuart_remove(struct platform_device *dev) +static int __devexit timbuart_remove(struct platform_device *dev) { struct timbuart_port *uart = platform_get_drvdata(dev); @@ -502,7 +507,7 @@ static struct platform_driver timbuart_platform_driver = { .owner = THIS_MODULE, }, .probe = timbuart_probe, - .remove = timbuart_remove, + .remove = __devexit_p(timbuart_remove), }; /*--------------------------------------------------------------------------*/ diff --git a/drivers/serial/uartlite.c b/drivers/serial/uartlite.c index 3317148..d2fce86 100644 --- a/drivers/serial/uartlite.c +++ b/drivers/serial/uartlite.c @@ -19,8 +19,9 @@ #include <linux/interrupt.h> #include <linux/init.h> #include <asm/io.h> -#if defined(CONFIG_OF) +#if defined(CONFIG_OF) && (defined(CONFIG_PPC32) || defined(CONFIG_MICROBLAZE)) #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_device.h> #include <linux/of_platform.h> @@ -43,7 +44,7 @@ MODULE_DEVICE_TABLE(of, ulite_of_match); * Register definitions * * For register details see datasheet: - * http://www.xilinx.com/bvdocs/ipcenter/data_sheet/opb_uartlite.pdf + * http://www.xilinx.com/support/documentation/ip_documentation/opb_uartlite.pdf */ #define ULITE_RX 0x00 @@ -75,7 +76,7 @@ static struct uart_port ulite_ports[ULITE_NR_UARTS]; static int ulite_receive(struct uart_port *port, int stat) { - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; unsigned char ch = 0; char flag = TTY_NORMAL; @@ -86,7 +87,7 @@ static int ulite_receive(struct uart_port *port, int stat) /* stats */ if (stat & ULITE_STATUS_RXVALID) { port->icount.rx++; - ch = readb(port->membase + ULITE_RX); + ch = ioread32be(port->membase + ULITE_RX); if (stat & ULITE_STATUS_PARITY) port->icount.parity++; @@ -125,13 +126,13 @@ static int ulite_receive(struct uart_port *port, int stat) static int ulite_transmit(struct uart_port *port, int stat) { - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; if (stat & ULITE_STATUS_TXFULL) return 0; if (port->x_char) { - writeb(port->x_char, port->membase + ULITE_TX); + iowrite32be(port->x_char, port->membase + ULITE_TX); port->x_char = 0; port->icount.tx++; return 1; @@ -140,7 +141,7 @@ static int ulite_transmit(struct uart_port *port, int stat) if (uart_circ_empty(xmit) || uart_tx_stopped(port)) return 0; - writeb(xmit->buf[xmit->tail], port->membase + ULITE_TX); + iowrite32be(xmit->buf[xmit->tail], port->membase + ULITE_TX); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); port->icount.tx++; @@ -154,17 +155,22 @@ static int ulite_transmit(struct uart_port *port, int stat) static irqreturn_t ulite_isr(int irq, void *dev_id) { struct uart_port *port = dev_id; - int busy; + int busy, n = 0; do { - int stat = readb(port->membase + ULITE_STATUS); + int stat = ioread32be(port->membase + ULITE_STATUS); busy = ulite_receive(port, stat); busy |= ulite_transmit(port, stat); + n++; } while (busy); - tty_flip_buffer_push(port->info->port.tty); - - return IRQ_HANDLED; + /* work done? */ + if (n > 1) { + tty_flip_buffer_push(port->state->port.tty); + return IRQ_HANDLED; + } else { + return IRQ_NONE; + } } static unsigned int ulite_tx_empty(struct uart_port *port) @@ -173,7 +179,7 @@ static unsigned int ulite_tx_empty(struct uart_port *port) unsigned int ret; spin_lock_irqsave(&port->lock, flags); - ret = readb(port->membase + ULITE_STATUS); + ret = ioread32be(port->membase + ULITE_STATUS); spin_unlock_irqrestore(&port->lock, flags); return ret & ULITE_STATUS_TXEMPTY ? TIOCSER_TEMT : 0; @@ -196,7 +202,7 @@ static void ulite_stop_tx(struct uart_port *port) static void ulite_start_tx(struct uart_port *port) { - ulite_transmit(port, readb(port->membase + ULITE_STATUS)); + ulite_transmit(port, ioread32be(port->membase + ULITE_STATUS)); } static void ulite_stop_rx(struct uart_port *port) @@ -221,21 +227,21 @@ static int ulite_startup(struct uart_port *port) int ret; ret = request_irq(port->irq, ulite_isr, - IRQF_DISABLED | IRQF_SAMPLE_RANDOM, "uartlite", port); + IRQF_SHARED | IRQF_SAMPLE_RANDOM, "uartlite", port); if (ret) return ret; - writeb(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, + iowrite32be(ULITE_CONTROL_RST_RX | ULITE_CONTROL_RST_TX, port->membase + ULITE_CONTROL); - writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); return 0; } static void ulite_shutdown(struct uart_port *port) { - writeb(0, port->membase + ULITE_CONTROL); - readb(port->membase + ULITE_CONTROL); /* dummy */ + iowrite32be(0, port->membase + ULITE_CONTROL); + ioread32be(port->membase + ULITE_CONTROL); /* dummy */ free_irq(port->irq, port); } @@ -316,6 +322,26 @@ static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser) return -EINVAL; } +#ifdef CONFIG_CONSOLE_POLL +static int ulite_get_poll_char(struct uart_port *port) +{ + if (!(ioread32be(port->membase + ULITE_STATUS) + & ULITE_STATUS_RXVALID)) + return NO_POLL_CHAR; + + return ioread32be(port->membase + ULITE_RX); +} + +static void ulite_put_poll_char(struct uart_port *port, unsigned char ch) +{ + while (ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_TXFULL) + cpu_relax(); + + /* write char to device */ + iowrite32be(ch, port->membase + ULITE_TX); +} +#endif + static struct uart_ops ulite_ops = { .tx_empty = ulite_tx_empty, .set_mctrl = ulite_set_mctrl, @@ -332,7 +358,11 @@ static struct uart_ops ulite_ops = { .release_port = ulite_release_port, .request_port = ulite_request_port, .config_port = ulite_config_port, - .verify_port = ulite_verify_port + .verify_port = ulite_verify_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = ulite_get_poll_char, + .poll_put_char = ulite_put_poll_char, +#endif }; /* --------------------------------------------------------------------- @@ -347,7 +377,7 @@ static void ulite_console_wait_tx(struct uart_port *port) /* Spin waiting for TX fifo to have space available */ for (i = 0; i < 100000; i++) { - val = readb(port->membase + ULITE_STATUS); + val = ioread32be(port->membase + ULITE_STATUS); if ((val & ULITE_STATUS_TXFULL) == 0) break; cpu_relax(); @@ -357,7 +387,7 @@ static void ulite_console_wait_tx(struct uart_port *port) static void ulite_console_putchar(struct uart_port *port, int ch) { ulite_console_wait_tx(port); - writeb(ch, port->membase + ULITE_TX); + iowrite32be(ch, port->membase + ULITE_TX); } static void ulite_console_write(struct console *co, const char *s, @@ -374,8 +404,8 @@ static void ulite_console_write(struct console *co, const char *s, spin_lock_irqsave(&port->lock, flags); /* save and disable interrupt */ - ier = readb(port->membase + ULITE_STATUS) & ULITE_STATUS_IE; - writeb(0, port->membase + ULITE_CONTROL); + ier = ioread32be(port->membase + ULITE_STATUS) & ULITE_STATUS_IE; + iowrite32be(0, port->membase + ULITE_CONTROL); uart_console_write(port, s, count, ulite_console_putchar); @@ -383,13 +413,13 @@ static void ulite_console_write(struct console *co, const char *s, /* restore interrupt state */ if (ier) - writeb(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); + iowrite32be(ULITE_CONTROL_IE, port->membase + ULITE_CONTROL); if (locked) spin_unlock_irqrestore(&port->lock, flags); } -static int __init ulite_console_setup(struct console *co, char *options) +static int __devinit ulite_console_setup(struct console *co, char *options) { struct uart_port *port; int baud = 9600; @@ -576,9 +606,9 @@ static struct platform_driver ulite_platform_driver = { /* --------------------------------------------------------------------- * OF bus bindings */ -#if defined(CONFIG_OF) +#if defined(CONFIG_OF) && (defined(CONFIG_PPC32) || defined(CONFIG_MICROBLAZE)) static int __devinit -ulite_of_probe(struct of_device *op, const struct of_device_id *match) +ulite_of_probe(struct platform_device *op, const struct of_device_id *match) { struct resource res; const unsigned int *id; @@ -586,32 +616,31 @@ ulite_of_probe(struct of_device *op, const struct of_device_id *match) dev_dbg(&op->dev, "%s(%p, %p)\n", __func__, op, match); - rc = of_address_to_resource(op->node, 0, &res); + rc = of_address_to_resource(op->dev.of_node, 0, &res); if (rc) { dev_err(&op->dev, "invalid address\n"); return rc; } - irq = irq_of_parse_and_map(op->node, 0); + irq = irq_of_parse_and_map(op->dev.of_node, 0); - id = of_get_property(op->node, "port-number", NULL); + id = of_get_property(op->dev.of_node, "port-number", NULL); - return ulite_assign(&op->dev, id ? *id : -1, res.start+3, irq); + return ulite_assign(&op->dev, id ? *id : -1, res.start, irq); } -static int __devexit ulite_of_remove(struct of_device *op) +static int __devexit ulite_of_remove(struct platform_device *op) { return ulite_release(&op->dev); } static struct of_platform_driver ulite_of_driver = { - .owner = THIS_MODULE, - .name = "uartlite", - .match_table = ulite_of_match, .probe = ulite_of_probe, .remove = __devexit_p(ulite_of_remove), .driver = { .name = "uartlite", + .owner = THIS_MODULE, + .of_match_table = ulite_of_match, }, }; @@ -626,11 +655,11 @@ static inline void __exit ulite_of_unregister(void) { of_unregister_platform_driver(&ulite_of_driver); } -#else /* CONFIG_OF */ -/* CONFIG_OF not enabled; do nothing helpers */ +#else /* CONFIG_OF && (CONFIG_PPC32 || CONFIG_MICROBLAZE) */ +/* Appropriate config not enabled; do nothing helpers */ static inline int __init ulite_of_register(void) { return 0; } static inline void __exit ulite_of_unregister(void) { } -#endif /* CONFIG_OF */ +#endif /* CONFIG_OF && (CONFIG_PPC32 || CONFIG_MICROBLAZE) */ /* --------------------------------------------------------------------- * Module setup/teardown diff --git a/drivers/serial/ucc_uart.c b/drivers/serial/ucc_uart.c index e945e78..3f4848e 100644 --- a/drivers/serial/ucc_uart.c +++ b/drivers/serial/ucc_uart.c @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/serial.h> +#include <linux/slab.h> #include <linux/serial_core.h> #include <linux/io.h> #include <linux/of_platform.h> @@ -313,7 +314,7 @@ static void qe_uart_stop_tx(struct uart_port *port) * This function will attempt to stuff of all the characters from the * kernel's transmit buffer into TX BDs. * - * A return value of non-zero indicates that it sucessfully stuffed all + * A return value of non-zero indicates that it successfully stuffed all * characters from the kernel buffer. * * A return value of zero indicates that there are still characters in the @@ -327,7 +328,7 @@ static int qe_uart_tx_pump(struct uart_qe_port *qe_port) unsigned char *p; unsigned int count; struct uart_port *port = &qe_port->port; - struct circ_buf *xmit = &port->info->xmit; + struct circ_buf *xmit = &port->state->xmit; bdp = qe_port->rx_cur; @@ -466,7 +467,7 @@ static void qe_uart_int_rx(struct uart_qe_port *qe_port) int i; unsigned char ch, *cp; struct uart_port *port = &qe_port->port; - struct tty_struct *tty = port->info->port.tty; + struct tty_struct *tty = port->state->port.tty; struct qe_bd *bdp; u16 status; unsigned int flg; @@ -1179,22 +1180,24 @@ static void uart_firmware_cont(const struct firmware *fw, void *context) if (firmware->header.length != fw->size) { dev_err(dev, "invalid firmware\n"); - return; + goto out; } ret = qe_upload_firmware(firmware); if (ret) { dev_err(dev, "could not load firmware\n"); - return; + goto out; } firmware_loaded = 1; + out: + release_firmware(fw); } -static int ucc_uart_probe(struct of_device *ofdev, +static int ucc_uart_probe(struct platform_device *ofdev, const struct of_device_id *match) { - struct device_node *np = ofdev->node; + struct device_node *np = ofdev->dev.of_node; const unsigned int *iprop; /* Integer OF properties */ const char *sprop; /* String OF properties */ struct uart_qe_port *qe_port = NULL; @@ -1247,7 +1250,7 @@ static int ucc_uart_probe(struct of_device *ofdev, */ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, filename, &ofdev->dev, - &ofdev->dev, uart_firmware_cont); + GFP_KERNEL, &ofdev->dev, uart_firmware_cont); if (ret) { dev_err(&ofdev->dev, "could not load firmware %s\n", @@ -1459,7 +1462,7 @@ static int ucc_uart_probe(struct of_device *ofdev, return 0; } -static int ucc_uart_remove(struct of_device *ofdev) +static int ucc_uart_remove(struct platform_device *ofdev) { struct uart_qe_port *qe_port = dev_get_drvdata(&ofdev->dev); @@ -1483,9 +1486,11 @@ static struct of_device_id ucc_uart_match[] = { MODULE_DEVICE_TABLE(of, ucc_uart_match); static struct of_platform_driver ucc_uart_of_driver = { - .owner = THIS_MODULE, - .name = "ucc_uart", - .match_table = ucc_uart_match, + .driver = { + .name = "ucc_uart", + .owner = THIS_MODULE, + .of_match_table = ucc_uart_match, + }, .probe = ucc_uart_probe, .remove = ucc_uart_remove, }; diff --git a/drivers/serial/vr41xx_siu.c b/drivers/serial/vr41xx_siu.c index 0573f3b..3beb6ab 100644 --- a/drivers/serial/vr41xx_siu.c +++ b/drivers/serial/vr41xx_siu.c @@ -1,7 +1,7 @@ /* * Driver for NEC VR4100 series Serial Interface Unit. * - * Copyright (C) 2004-2008 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp> + * Copyright (C) 2004-2008 Yoichi Yuasa <yuasa@linux-mips.org> * * Based on drivers/serial/8250.c, by Russell King. * @@ -318,7 +318,7 @@ static inline void receive_chars(struct uart_port *port, uint8_t *status) char flag; int max_count = RX_MAX_COUNT; - tty = port->info->port.tty; + tty = port->state->port.tty; lsr = *status; do { @@ -386,7 +386,7 @@ static inline void check_modem_status(struct uart_port *port) if (msr & UART_MSR_DCTS) uart_handle_cts_change(port, msr & UART_MSR_CTS); - wake_up_interruptible(&port->info->delta_msr_wait); + wake_up_interruptible(&port->state->port.delta_msr_wait); } static inline void transmit_chars(struct uart_port *port) @@ -394,7 +394,7 @@ static inline void transmit_chars(struct uart_port *port) struct circ_buf *xmit; int max_count = TX_MAX_COUNT; - xmit = &port->info->xmit; + xmit = &port->state->xmit; if (port->x_char) { siu_write(port, UART_TX, port->x_char); diff --git a/drivers/serial/vt8500_serial.c b/drivers/serial/vt8500_serial.c new file mode 100644 index 0000000..322bf56 --- /dev/null +++ b/drivers/serial/vt8500_serial.c @@ -0,0 +1,648 @@ +/* + * drivers/serial/vt8500_serial.c + * + * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> + * + * Based on msm_serial.c, which is: + * Copyright (C) 2007 Google, Inc. + * Author: Robert Love <rlove@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#if defined(CONFIG_SERIAL_VT8500_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +# define SUPPORT_SYSRQ +#endif + +#include <linux/hrtimer.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/irq.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/platform_device.h> + +/* + * UART Register offsets + */ + +#define VT8500_URTDR 0x0000 /* Transmit data */ +#define VT8500_URRDR 0x0004 /* Receive data */ +#define VT8500_URDIV 0x0008 /* Clock/Baud rate divisor */ +#define VT8500_URLCR 0x000C /* Line control */ +#define VT8500_URICR 0x0010 /* IrDA control */ +#define VT8500_URIER 0x0014 /* Interrupt enable */ +#define VT8500_URISR 0x0018 /* Interrupt status */ +#define VT8500_URUSR 0x001c /* UART status */ +#define VT8500_URFCR 0x0020 /* FIFO control */ +#define VT8500_URFIDX 0x0024 /* FIFO index */ +#define VT8500_URBKR 0x0028 /* Break signal count */ +#define VT8500_URTOD 0x002c /* Time out divisor */ +#define VT8500_TXFIFO 0x1000 /* Transmit FIFO (16x8) */ +#define VT8500_RXFIFO 0x1020 /* Receive FIFO (16x10) */ + +/* + * Interrupt enable and status bits + */ + +#define TXDE (1 << 0) /* Tx Data empty */ +#define RXDF (1 << 1) /* Rx Data full */ +#define TXFAE (1 << 2) /* Tx FIFO almost empty */ +#define TXFE (1 << 3) /* Tx FIFO empty */ +#define RXFAF (1 << 4) /* Rx FIFO almost full */ +#define RXFF (1 << 5) /* Rx FIFO full */ +#define TXUDR (1 << 6) /* Tx underrun */ +#define RXOVER (1 << 7) /* Rx overrun */ +#define PER (1 << 8) /* Parity error */ +#define FER (1 << 9) /* Frame error */ +#define TCTS (1 << 10) /* Toggle of CTS */ +#define RXTOUT (1 << 11) /* Rx timeout */ +#define BKDONE (1 << 12) /* Break signal done */ +#define ERR (1 << 13) /* AHB error response */ + +#define RX_FIFO_INTS (RXFAF | RXFF | RXOVER | PER | FER | RXTOUT) +#define TX_FIFO_INTS (TXFAE | TXFE | TXUDR) + +struct vt8500_port { + struct uart_port uart; + char name[16]; + struct clk *clk; + unsigned int ier; +}; + +static inline void vt8500_write(struct uart_port *port, unsigned int val, + unsigned int off) +{ + writel(val, port->membase + off); +} + +static inline unsigned int vt8500_read(struct uart_port *port, unsigned int off) +{ + return readl(port->membase + off); +} + +static void vt8500_stop_tx(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = container_of(port, + struct vt8500_port, + uart); + + vt8500_port->ier &= ~TX_FIFO_INTS; + vt8500_write(port, vt8500_port->ier, VT8500_URIER); +} + +static void vt8500_stop_rx(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = container_of(port, + struct vt8500_port, + uart); + + vt8500_port->ier &= ~RX_FIFO_INTS; + vt8500_write(port, vt8500_port->ier, VT8500_URIER); +} + +static void vt8500_enable_ms(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = container_of(port, + struct vt8500_port, + uart); + + vt8500_port->ier |= TCTS; + vt8500_write(port, vt8500_port->ier, VT8500_URIER); +} + +static void handle_rx(struct uart_port *port) +{ + struct tty_struct *tty = tty_port_tty_get(&port->state->port); + if (!tty) { + /* Discard data: no tty available */ + int count = (vt8500_read(port, VT8500_URFIDX) & 0x1f00) >> 8; + u16 ch; + while (count--) + ch = readw(port->membase + VT8500_RXFIFO); + return; + } + + /* + * Handle overrun + */ + if ((vt8500_read(port, VT8500_URISR) & RXOVER)) { + port->icount.overrun++; + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + + /* and now the main RX loop */ + while (vt8500_read(port, VT8500_URFIDX) & 0x1f00) { + unsigned int c; + char flag = TTY_NORMAL; + + c = readw(port->membase + VT8500_RXFIFO) & 0x3ff; + + /* Mask conditions we're ignorning. */ + c &= ~port->read_status_mask; + + if (c & FER) { + port->icount.frame++; + flag = TTY_FRAME; + } else if (c & PER) { + port->icount.parity++; + flag = TTY_PARITY; + } + port->icount.rx++; + + if (!uart_handle_sysrq_char(port, c)) + tty_insert_flip_char(tty, c, flag); + } + + tty_flip_buffer_push(tty); + tty_kref_put(tty); +} + +static void handle_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + + if (port->x_char) { + writeb(port->x_char, port->membase + VT8500_TXFIFO); + port->icount.tx++; + port->x_char = 0; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { + vt8500_stop_tx(port); + return; + } + + while ((vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16) { + if (uart_circ_empty(xmit)) + break; + + writeb(xmit->buf[xmit->tail], port->membase + VT8500_TXFIFO); + + 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)) + vt8500_stop_tx(port); +} + +static void vt8500_start_tx(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = container_of(port, + struct vt8500_port, + uart); + + vt8500_port->ier &= ~TX_FIFO_INTS; + vt8500_write(port, vt8500_port->ier, VT8500_URIER); + handle_tx(port); + vt8500_port->ier |= TX_FIFO_INTS; + vt8500_write(port, vt8500_port->ier, VT8500_URIER); +} + +static void handle_delta_cts(struct uart_port *port) +{ + port->icount.cts++; + wake_up_interruptible(&port->state->port.delta_msr_wait); +} + +static irqreturn_t vt8500_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + unsigned long isr; + + spin_lock(&port->lock); + isr = vt8500_read(port, VT8500_URISR); + + /* Acknowledge active status bits */ + vt8500_write(port, isr, VT8500_URISR); + + if (isr & RX_FIFO_INTS) + handle_rx(port); + if (isr & TX_FIFO_INTS) + handle_tx(port); + if (isr & TCTS) + handle_delta_cts(port); + + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static unsigned int vt8500_tx_empty(struct uart_port *port) +{ + return (vt8500_read(port, VT8500_URFIDX) & 0x1f) < 16 ? + TIOCSER_TEMT : 0; +} + +static unsigned int vt8500_get_mctrl(struct uart_port *port) +{ + unsigned int usr; + + usr = vt8500_read(port, VT8500_URUSR); + if (usr & (1 << 4)) + return TIOCM_CTS; + else + return 0; +} + +static void vt8500_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static void vt8500_break_ctl(struct uart_port *port, int break_ctl) +{ + if (break_ctl) + vt8500_write(port, vt8500_read(port, VT8500_URLCR) | (1 << 9), + VT8500_URLCR); +} + +static int vt8500_set_baud_rate(struct uart_port *port, unsigned int baud) +{ + unsigned long div; + unsigned int loops = 1000; + + div = vt8500_read(port, VT8500_URDIV) & ~(0x3ff); + + if (unlikely((baud < 900) || (baud > 921600))) + div |= 7; + else + div |= (921600 / baud) - 1; + + while ((vt8500_read(port, VT8500_URUSR) & (1 << 5)) && --loops) + cpu_relax(); + vt8500_write(port, div, VT8500_URDIV); + + return baud; +} + +static int vt8500_startup(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = + container_of(port, struct vt8500_port, uart); + int ret; + + snprintf(vt8500_port->name, sizeof(vt8500_port->name), + "vt8500_serial%d", port->line); + + ret = request_irq(port->irq, vt8500_irq, IRQF_TRIGGER_HIGH, + vt8500_port->name, port); + if (unlikely(ret)) + return ret; + + vt8500_write(port, 0x03, VT8500_URLCR); /* enable TX & RX */ + + return 0; +} + +static void vt8500_shutdown(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = + container_of(port, struct vt8500_port, uart); + + vt8500_port->ier = 0; + + /* disable interrupts and FIFOs */ + vt8500_write(&vt8500_port->uart, 0, VT8500_URIER); + vt8500_write(&vt8500_port->uart, 0x880, VT8500_URFCR); + free_irq(port->irq, port); +} + +static void vt8500_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct vt8500_port *vt8500_port = + container_of(port, struct vt8500_port, uart); + unsigned long flags; + unsigned int baud, lcr; + unsigned int loops = 1000; + + spin_lock_irqsave(&port->lock, flags); + + /* calculate and set baud rate */ + baud = uart_get_baud_rate(port, termios, old, 900, 921600); + baud = vt8500_set_baud_rate(port, baud); + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + + /* calculate parity */ + lcr = vt8500_read(&vt8500_port->uart, VT8500_URLCR); + lcr &= ~((1 << 5) | (1 << 4)); + if (termios->c_cflag & PARENB) { + lcr |= (1 << 4); + termios->c_cflag &= ~CMSPAR; + if (termios->c_cflag & PARODD) + lcr |= (1 << 5); + } + + /* calculate bits per char */ + lcr &= ~(1 << 2); + switch (termios->c_cflag & CSIZE) { + case CS7: + break; + case CS8: + default: + lcr |= (1 << 2); + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + break; + } + + /* calculate stop bits */ + lcr &= ~(1 << 3); + if (termios->c_cflag & CSTOPB) + lcr |= (1 << 3); + + /* set parity, bits per char, and stop bit */ + vt8500_write(&vt8500_port->uart, lcr, VT8500_URLCR); + + /* Configure status bits to ignore based on termio flags. */ + port->read_status_mask = 0; + if (termios->c_iflag & IGNPAR) + port->read_status_mask = FER | PER; + + uart_update_timeout(port, termios->c_cflag, baud); + + /* Reset FIFOs */ + vt8500_write(&vt8500_port->uart, 0x88c, VT8500_URFCR); + while ((vt8500_read(&vt8500_port->uart, VT8500_URFCR) & 0xc) + && --loops) + cpu_relax(); + + /* Every possible FIFO-related interrupt */ + vt8500_port->ier = RX_FIFO_INTS | TX_FIFO_INTS; + + /* + * CTS flow control + */ + if (UART_ENABLE_MS(&vt8500_port->uart, termios->c_cflag)) + vt8500_port->ier |= TCTS; + + vt8500_write(&vt8500_port->uart, 0x881, VT8500_URFCR); + vt8500_write(&vt8500_port->uart, vt8500_port->ier, VT8500_URIER); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *vt8500_type(struct uart_port *port) +{ + struct vt8500_port *vt8500_port = + container_of(port, struct vt8500_port, uart); + return vt8500_port->name; +} + +static void vt8500_release_port(struct uart_port *port) +{ +} + +static int vt8500_request_port(struct uart_port *port) +{ + return 0; +} + +static void vt8500_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_VT8500; +} + +static int vt8500_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_VT8500)) + return -EINVAL; + if (unlikely(port->irq != ser->irq)) + return -EINVAL; + return 0; +} + +static struct vt8500_port *vt8500_uart_ports[4]; +static struct uart_driver vt8500_uart_driver; + +#ifdef CONFIG_SERIAL_VT8500_CONSOLE + +static inline void wait_for_xmitr(struct uart_port *port) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = vt8500_read(port, VT8500_URFIDX); + + if (--tmout == 0) + break; + udelay(1); + } while (status & 0x10); +} + +static void vt8500_console_putchar(struct uart_port *port, int c) +{ + wait_for_xmitr(port); + writeb(c, port->membase + VT8500_TXFIFO); +} + +static void vt8500_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct vt8500_port *vt8500_port = vt8500_uart_ports[co->index]; + unsigned long ier; + + BUG_ON(co->index < 0 || co->index >= vt8500_uart_driver.nr); + + ier = vt8500_read(&vt8500_port->uart, VT8500_URIER); + vt8500_write(&vt8500_port->uart, VT8500_URIER, 0); + + uart_console_write(&vt8500_port->uart, s, count, + vt8500_console_putchar); + + /* + * Finally, wait for transmitter to become empty + * and switch back to FIFO + */ + wait_for_xmitr(&vt8500_port->uart); + vt8500_write(&vt8500_port->uart, VT8500_URIER, ier); +} + +static int __init vt8500_console_setup(struct console *co, char *options) +{ + struct vt8500_port *vt8500_port; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + if (unlikely(co->index >= vt8500_uart_driver.nr || co->index < 0)) + return -ENXIO; + + vt8500_port = vt8500_uart_ports[co->index]; + + if (!vt8500_port) + return -ENODEV; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(&vt8500_port->uart, + co, baud, parity, bits, flow); +} + +static struct console vt8500_console = { + .name = "ttyWMT", + .write = vt8500_console_write, + .device = uart_console_device, + .setup = vt8500_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &vt8500_uart_driver, +}; + +#define VT8500_CONSOLE (&vt8500_console) + +#else +#define VT8500_CONSOLE NULL +#endif + +static struct uart_ops vt8500_uart_pops = { + .tx_empty = vt8500_tx_empty, + .set_mctrl = vt8500_set_mctrl, + .get_mctrl = vt8500_get_mctrl, + .stop_tx = vt8500_stop_tx, + .start_tx = vt8500_start_tx, + .stop_rx = vt8500_stop_rx, + .enable_ms = vt8500_enable_ms, + .break_ctl = vt8500_break_ctl, + .startup = vt8500_startup, + .shutdown = vt8500_shutdown, + .set_termios = vt8500_set_termios, + .type = vt8500_type, + .release_port = vt8500_release_port, + .request_port = vt8500_request_port, + .config_port = vt8500_config_port, + .verify_port = vt8500_verify_port, +}; + +static struct uart_driver vt8500_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "vt8500_serial", + .dev_name = "ttyWMT", + .nr = 6, + .cons = VT8500_CONSOLE, +}; + +static int __init vt8500_serial_probe(struct platform_device *pdev) +{ + struct vt8500_port *vt8500_port; + struct resource *mmres, *irqres; + int ret; + + mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!mmres || !irqres) + return -ENODEV; + + vt8500_port = kzalloc(sizeof(struct vt8500_port), GFP_KERNEL); + if (!vt8500_port) + return -ENOMEM; + + vt8500_port->uart.type = PORT_VT8500; + vt8500_port->uart.iotype = UPIO_MEM; + vt8500_port->uart.mapbase = mmres->start; + vt8500_port->uart.irq = irqres->start; + vt8500_port->uart.fifosize = 16; + vt8500_port->uart.ops = &vt8500_uart_pops; + vt8500_port->uart.line = pdev->id; + vt8500_port->uart.dev = &pdev->dev; + vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; + vt8500_port->uart.uartclk = 24000000; + + snprintf(vt8500_port->name, sizeof(vt8500_port->name), + "VT8500 UART%d", pdev->id); + + vt8500_port->uart.membase = ioremap(mmres->start, + mmres->end - mmres->start + 1); + if (!vt8500_port->uart.membase) { + ret = -ENOMEM; + goto err; + } + + vt8500_uart_ports[pdev->id] = vt8500_port; + + uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart); + + platform_set_drvdata(pdev, vt8500_port); + + return 0; + +err: + kfree(vt8500_port); + return ret; +} + +static int __devexit vt8500_serial_remove(struct platform_device *pdev) +{ + struct vt8500_port *vt8500_port = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + uart_remove_one_port(&vt8500_uart_driver, &vt8500_port->uart); + kfree(vt8500_port); + + return 0; +} + +static struct platform_driver vt8500_platform_driver = { + .probe = vt8500_serial_probe, + .remove = vt8500_serial_remove, + .driver = { + .name = "vt8500_serial", + .owner = THIS_MODULE, + }, +}; + +static int __init vt8500_serial_init(void) +{ + int ret; + + ret = uart_register_driver(&vt8500_uart_driver); + if (unlikely(ret)) + return ret; + + ret = platform_driver_register(&vt8500_platform_driver); + + if (unlikely(ret)) + uart_unregister_driver(&vt8500_uart_driver); + + return ret; +} + +static void __exit vt8500_serial_exit(void) +{ +#ifdef CONFIG_SERIAL_VT8500_CONSOLE + unregister_console(&vt8500_console); +#endif + platform_driver_unregister(&vt8500_platform_driver); + uart_unregister_driver(&vt8500_uart_driver); +} + +module_init(vt8500_serial_init); +module_exit(vt8500_serial_exit); + +MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>"); +MODULE_DESCRIPTION("Driver for vt8500 serial device"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/zs.c b/drivers/serial/zs.c index d8c2809..1a7fd3e 100644 --- a/drivers/serial/zs.c +++ b/drivers/serial/zs.c @@ -602,12 +602,12 @@ static void zs_receive_chars(struct zs_port *zport) uart_insert_char(uport, status, Rx_OVR, ch, flag); } - tty_flip_buffer_push(uport->info->port.tty); + tty_flip_buffer_push(uport->state->port.tty); } static void zs_raw_transmit_chars(struct zs_port *zport) { - struct circ_buf *xmit = &zport->port.info->xmit; + struct circ_buf *xmit = &zport->port.state->xmit; /* XON/XOFF chars. */ if (zport->port.x_char) { @@ -686,7 +686,7 @@ static void zs_status_handle(struct zs_port *zport, struct zs_port *zport_a) uport->icount.rng++; if (delta) - wake_up_interruptible(&uport->info->delta_msr_wait); + wake_up_interruptible(&uport->state->port.delta_msr_wait); spin_lock(&scc->zlock); } |