From: A whole bunch of fixes for the ColdFire serial driver: . remove unused CONFIG_LEDMAN code . reformat port definitions to new style structure init . change "addr" field type to reduce casting in ColdFire serial driver . cleanup locking problems in mcfrs_write(). . implement fraction baud rate clock support for hardware that supports it (namely the ColdFire 5272) . implement wait_until_sent, some ColdFire parts of hardware support for this (again the 5272). . correctly use return values from put_user(), get_user() and copy_to_user() Many of these originaly from kernel janitors. --- 25-akpm/drivers/serial/mcfserial.c | 226 ++++++++++++++++++++++++++----------- 1 files changed, 160 insertions(+), 66 deletions(-) diff -puN drivers/serial/mcfserial.c~fixes-to-the-coldfire-serial-driver drivers/serial/mcfserial.c --- 25/drivers/serial/mcfserial.c~fixes-to-the-coldfire-serial-driver Mon Apr 12 13:42:08 2004 +++ 25-akpm/drivers/serial/mcfserial.c Mon Apr 12 13:42:08 2004 @@ -10,6 +10,11 @@ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1998 TSHG * Copyright (c) 1999 Rt-Control Inc. + * + * Changes: + * 08/07/2003 Daniele Bellucci + * some cleanups in mcfrs_write. + * */ #include @@ -27,11 +32,7 @@ #include #include #include -#ifdef CONFIG_LEDMAN -#include -#endif #include -#include #include #include @@ -84,8 +85,6 @@ static struct tty_driver *mcfrs_serial_d #undef SERIAL_DEBUG_OPEN #undef SERIAL_DEBUG_FLOW -#define _INLINE_ inline - #ifdef CONFIG_M5282 #define IRQBASE 77 #else @@ -96,8 +95,18 @@ static struct tty_driver *mcfrs_serial_d * Configuration table, UARTs to look for at startup. */ static struct mcf_serial mcfrs_table[] = { - { 0, (MCF_MBAR+MCFUART_BASE1), IRQBASE, ASYNC_BOOT_AUTOCONF }, /* ttyS0 */ - { 0, (MCF_MBAR+MCFUART_BASE2), IRQBASE+1, ASYNC_BOOT_AUTOCONF }, /* ttyS1 */ + { /* ttyS0 */ + .magic = 0, + .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE1), + .irq = IRQBASE, + .flags = ASYNC_BOOT_AUTOCONF, + }, + { /* ttyS1 */ + .magic = 0, + .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE2), + .irq = IRQBASE+1, + .flags = ASYNC_BOOT_AUTOCONF, + }, }; @@ -138,15 +147,16 @@ static DECLARE_MUTEX(mcfrs_tmp_buf_sem); * Forware declarations... */ static void mcfrs_change_speed(struct mcf_serial *info); +static void mcfrs_wait_until_sent(struct tty_struct *tty, int timeout); static inline int serial_paranoia_check(struct mcf_serial *info, char *name, const char *routine) { #ifdef SERIAL_PARANOIA_CHECK - static const char *badmagic = + static const char badmagic[] = "MCFRS(warning): bad magic number for serial struct %s in %s\n"; - static const char *badinfo = + static const char badinfo[] = "MCFRS(warning): null mcf_serial for %s in %s\n"; if (!info) { @@ -184,7 +194,7 @@ static void mcfrs_setsignals(struct mcf_ #endif } if (rts >= 0) { - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; if (rts) { info->sigs |= TIOCM_RTS; uartp[MCFUART_UOP1] = MCFUART_UOP_RTS; @@ -214,7 +224,7 @@ static int mcfrs_getsignals(struct mcf_s #endif local_irq_save(flags); - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; sigs = (uartp[MCFUART_UIPR] & MCFUART_UIPR_CTS) ? 0 : TIOCM_CTS; sigs |= (info->sigs & TIOCM_RTS); @@ -254,7 +264,7 @@ static void mcfrs_stop(struct tty_struct return; local_irq_save(flags); - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; info->imr &= ~MCFUART_UIR_TXREADY; uartp[MCFUART_UIMR] = info->imr; local_irq_restore(flags); @@ -271,7 +281,7 @@ static void mcfrs_start(struct tty_struc local_irq_save(flags); if (info->xmit_cnt && info->xmit_buf) { - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; info->imr |= MCFUART_UIR_TXREADY; uartp[MCFUART_UIMR] = info->imr; } @@ -299,7 +309,7 @@ static void mcfrs_start(struct tty_struc * ----------------------------------------------------------------------- */ -static _INLINE_ void receive_chars(struct mcf_serial *info, struct pt_regs *regs, unsigned short rx) +static inline void receive_chars(struct mcf_serial *info) { volatile unsigned char *uartp; struct tty_struct *tty = info->tty; @@ -308,11 +318,7 @@ static _INLINE_ void receive_chars(struc if (!tty) return; -#if defined(CONFIG_LEDMAN) - ledman_cmd(LEDMAN_CMD_SET, info->line ? LEDMAN_COM2_RX : LEDMAN_COM1_RX); -#endif - - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; while ((status = uartp[MCFUART_USR]) & MCFUART_USR_RXREADY) { @@ -354,15 +360,11 @@ static _INLINE_ void receive_chars(struc return; } -static _INLINE_ void transmit_chars(struct mcf_serial *info) +static inline void transmit_chars(struct mcf_serial *info) { volatile unsigned char *uartp; -#if defined(CONFIG_LEDMAN) - ledman_cmd(LEDMAN_CMD_SET, info->line ? LEDMAN_COM2_TX : LEDMAN_COM1_TX); -#endif - - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; if (info->x_char) { /* Send special char - probably flow control */ @@ -399,10 +401,10 @@ irqreturn_t mcfrs_interrupt(int irq, voi unsigned char isr; info = &mcfrs_table[(irq - IRQBASE)]; - isr = (((volatile unsigned char *)info->addr)[MCFUART_UISR]) & info->imr; + isr = info->addr[MCFUART_UISR] & info->imr; if (isr & MCFUART_UIR_RXREADY) - receive_chars(info, regs, isr); + receive_chars(info); if (isr & MCFUART_UIR_TXREADY) transmit_chars(info); return IRQ_HANDLED; @@ -525,7 +527,7 @@ static int startup(struct mcf_serial * i /* * Reset UART, get it into known state... */ - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */ uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */ mcfrs_setsignals(info, 1, 1); @@ -571,7 +573,7 @@ static void shutdown(struct mcf_serial * local_irq_save(flags); - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; uartp[MCFUART_UIMR] = 0; /* mask all interrupts */ uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */ uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */ @@ -603,6 +605,9 @@ static void mcfrs_change_speed(struct mc unsigned long flags; unsigned char mr1, mr2; int i; +#ifdef CONFIG_M5272 + unsigned int fraction; +#endif if (!info->tty || !info->tty->termios) return; @@ -626,7 +631,20 @@ static void mcfrs_change_speed(struct mc mcfrs_setsignals(info, 0, -1); return; } + + /* compute the baudrate clock */ +#ifdef CONFIG_M5272 + /* + * For the MCF5272, also compute the baudrate fraction. + */ + baudclk = (MCF_BUSCLK / mcfrs_baud_table[i]) / 32; + fraction = MCF_BUSCLK - (baudclk * 32 * mcfrs_baud_table[i]); + fraction *= 16; + fraction /= (32 * mcfrs_baud_table[i]); +#else baudclk = ((MCF_BUSCLK / mcfrs_baud_table[i]) + 16) / 32; +#endif + info->baud = mcfrs_baud_table[i]; mr1 = MCFUART_MR1_RXIRQRDY | MCFUART_MR1_RXERRCHAR; @@ -671,7 +689,7 @@ static void mcfrs_change_speed(struct mc else info->flags |= ASYNC_CHECK_CD; - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; local_irq_save(flags); #if 0 @@ -690,6 +708,9 @@ static void mcfrs_change_speed(struct mc uartp[MCFUART_UMR] = mr2; uartp[MCFUART_UBG1] = (baudclk & 0xff00) >> 8; /* set msb byte */ uartp[MCFUART_UBG2] = (baudclk & 0xff); /* set lsb byte */ +#ifdef CONFIG_M5272 + uartp[MCFUART_UFPD] = (fraction & 0xf); /* set fraction */ +#endif uartp[MCFUART_UCSR] = MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER; uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE; mcfrs_setsignals(info, 1, -1); @@ -712,7 +733,7 @@ static void mcfrs_flush_chars(struct tty /* Enable transmitter */ local_irq_save(flags); - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; info->imr |= MCFUART_UIR_TXREADY; uartp[MCFUART_UIMR] = info->imr; local_irq_restore(flags); @@ -742,24 +763,26 @@ static int mcfrs_write(struct tty_struct local_irq_disable(); c = min(count, (int) min(((int)SERIAL_XMIT_SIZE) - info->xmit_cnt - 1, ((int)SERIAL_XMIT_SIZE) - info->xmit_head)); + local_irq_restore(flags); - if (c <= 0) { - local_irq_restore(flags); + if (c <= 0) break; - } if (from_user) { down(&mcfrs_tmp_buf_sem); - copy_from_user(mcfrs_tmp_buf, buf, c); - local_irq_restore(flags); + if (copy_from_user(mcfrs_tmp_buf, buf, c)) + return -EFAULT; + local_irq_disable(); c = min(c, (int) min(((int)SERIAL_XMIT_SIZE) - info->xmit_cnt - 1, ((int)SERIAL_XMIT_SIZE) - info->xmit_head)); + local_irq_restore(flags); memcpy(info->xmit_buf + info->xmit_head, mcfrs_tmp_buf, c); up(&mcfrs_tmp_buf_sem); } else memcpy(info->xmit_buf + info->xmit_head, buf, c); + local_irq_disable(); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); info->xmit_cnt += c; local_irq_restore(flags); @@ -770,7 +793,7 @@ static int mcfrs_write(struct tty_struct } local_irq_disable(); - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; info->imr |= MCFUART_UIR_TXREADY; uartp[MCFUART_UIMR] = info->imr; local_irq_restore(flags); @@ -884,15 +907,14 @@ static int get_serial_info(struct mcf_se memset(&tmp, 0, sizeof(tmp)); tmp.type = info->type; tmp.line = info->line; - tmp.port = info->addr; + tmp.port = (unsigned int) info->addr; tmp.irq = info->irq; tmp.flags = info->flags; tmp.baud_base = info->baud_base; 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)); - return 0; + return copy_to_user(retinfo,&tmp,sizeof(*retinfo)) ? -EFAULT : 0; } static int set_serial_info(struct mcf_serial * info, @@ -904,7 +926,8 @@ static int set_serial_info(struct mcf_se 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)) { @@ -957,12 +980,11 @@ static int get_lsr_info(struct mcf_seria unsigned char status; local_irq_save(flags); - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; status = (uartp[MCFUART_USR] & MCFUART_USR_TXEMPTY) ? TIOCSER_TEMT : 0; local_irq_restore(flags); - put_user(status,value); - return 0; + return put_user(status,value); } /* @@ -976,7 +998,7 @@ static void send_break( struct mcf_seria if (!info->addr) return; current->state = TASK_INTERRUPTIBLE; - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; local_irq_save(flags); uartp[MCFUART_UCR] = MCFUART_UCR_CMDBREAKSTART; @@ -1026,9 +1048,6 @@ static int mcfrs_ioctl(struct tty_struct unsigned int cmd, unsigned long arg) { struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; -#ifdef TIOCSET422 - unsigned int val; -#endif int retval, error; if (serial_paranoia_check(info, tty->name, "mcfrs_ioctl")) @@ -1058,11 +1077,10 @@ static int mcfrs_ioctl(struct tty_struct send_break(info, arg ? arg*(HZ/10) : HZ/4); return 0; case TIOCGSOFTCAR: - error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long)); + error = put_user(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); if (error) return error; - put_user(C_CLOCAL(tty) ? 1 : 0, - (unsigned long *) arg); return 0; case TIOCSSOFTCAR: get_user(arg, (unsigned long *) arg); @@ -1089,23 +1107,25 @@ static int mcfrs_ioctl(struct tty_struct return get_lsr_info(info, (unsigned int *) arg); case TIOCSERGSTRUCT: - error = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct mcf_serial)); - if (error) - return error; - copy_to_user((struct mcf_serial *) arg, + error = copy_to_user((struct mcf_serial *) arg, info, sizeof(struct mcf_serial)); + if (error) + return -EFAULT; return 0; #ifdef TIOCSET422 - case TIOCSET422: + case TIOCSET422: { + unsigned int val; get_user(val, (unsigned int *) arg); mcf_setpa(MCFPP_PA11, (val ? 0 : MCFPP_PA11)); break; - case TIOCGET422: + } + case TIOCGET422: { + unsigned int val; val = (mcf_getpa() & MCFPP_PA11) ? 0 : 1; put_user(val, (unsigned int *) arg); break; + } #endif default: @@ -1200,7 +1220,7 @@ static void mcfrs_close(struct tty_struc * line status register. */ info->imr &= ~MCFUART_UIR_RXREADY; - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; uartp[MCFUART_UIMR] = info->imr; #if 0 @@ -1238,6 +1258,76 @@ static void mcfrs_close(struct tty_struc } /* + * mcfrs_wait_until_sent() --- wait until the transmitter is empty + */ +static void +mcfrs_wait_until_sent(struct tty_struct *tty, int timeout) +{ +#ifdef CONFIG_M5272 +#define MCF5272_FIFO_SIZE 25 /* fifo size + shift reg */ + + struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; + volatile unsigned char *uartp; + unsigned long orig_jiffies, fifo_time, char_time, fifo_cnt; + + if (serial_paranoia_check(info, tty->name, "mcfrs_wait_until_sent")) + return; + + orig_jiffies = jiffies; + + /* + * Set the check interval to be 1/5 of the approximate time + * to send the entire fifo, and make it at least 1. The check + * interval should also be less than the timeout. + * + * Note: we have to use pretty tight timings here to satisfy + * the NIST-PCTS. + */ + fifo_time = (MCF5272_FIFO_SIZE * HZ * 10) / info->baud; + char_time = fifo_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + + /* + * Clamp the timeout period at 2 * the time to empty the + * fifo. Just to be safe, set the minimum at .5 seconds. + */ + fifo_time *= 2; + if (fifo_time < (HZ/2)) + fifo_time = HZ/2; + if (!timeout || timeout > fifo_time) + timeout = fifo_time; + + /* + * Account for the number of bytes in the UART + * transmitter FIFO plus any byte being shifted out. + */ + uartp = (volatile unsigned char *) info->addr; + for (;;) { + fifo_cnt = (uartp[MCFUART_UTF] & MCFUART_UTF_TXB); + if ((uartp[MCFUART_USR] & (MCFUART_USR_TXREADY| + MCFUART_USR_TXEMPTY)) == + MCFUART_USR_TXREADY) + fifo_cnt++; + if (fifo_cnt == 0) + break; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } +#else + /* + * For the other coldfire models, assume all data has been sent + */ +#endif +} + +/* * mcfrs_hangup() --- called by tty_hangup() when a hangup is signaled. */ void mcfrs_hangup(struct tty_struct *tty) @@ -1413,7 +1503,7 @@ static void mcfrs_irqinit(struct mcf_ser volatile unsigned long *portp; volatile unsigned char *uartp; - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR2); switch (info->line) { @@ -1438,7 +1528,7 @@ static void mcfrs_irqinit(struct mcf_ser volatile unsigned char *icrp, *uartp; volatile unsigned long *imrp; - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; icrp = (volatile unsigned char *) (MCF_MBAR + MCFICM_INTC0 + MCFINTC_ICR0 + MCFINT_UART0 + info->line); @@ -1469,7 +1559,7 @@ static void mcfrs_irqinit(struct mcf_ser return; } - uartp = (volatile unsigned char *) info->addr; + uartp = info->addr; uartp[MCFUART_UIVR] = info->irq; #endif @@ -1503,7 +1593,7 @@ int mcfrs_readproc(char *page, char **st for (i = 0; (i < NR_PORTS); i++) { info = &mcfrs_table[i]; len += sprintf((page + len), "%d: port:%x irq=%d baud:%d ", - i, info->addr, info->irq, info->baud); + i, (unsigned int) info->addr, info->irq, info->baud); if (info->stats.rx || info->stats.tx) len += sprintf((page + len), "tx:%d rx:%d ", info->stats.tx, info->stats.rx); @@ -1562,7 +1652,8 @@ static struct tty_operations mcfrs_ops = .start = mcfrs_start, .hangup = mcfrs_hangup, .read_proc = mcfrs_readproc, - .tiocmget = mcfrs_tiocmget, + .wait_until_sent = mcfrs_wait_until_sent, + .tiocmget = mcfrs_tiocmget, .tiocmset = mcfrs_tiocmset, }; @@ -1590,7 +1681,9 @@ mcfrs_init(void) show_serial_version(); /* Initialize the tty_driver structure */ + mcfrs_serial_driver->owner = THIS_MODULE; mcfrs_serial_driver->name = "ttyS"; + mcfrs_serial_driver->devfs_name = "ttys/"; mcfrs_serial_driver->driver_name = "serial"; mcfrs_serial_driver->major = TTY_MAJOR; mcfrs_serial_driver->minor_start = 64; @@ -1635,7 +1728,8 @@ mcfrs_init(void) mcfrs_setsignals(info, 0, 0); mcfrs_irqinit(info); - printk("ttyS%d at 0x%04x (irq = %d)", info->line, info->addr, info->irq); + printk("ttyS%d at 0x%04x (irq = %d)", info->line, + (unsigned int) info->addr, info->irq); printk(" is a builtin ColdFire UART\n"); } _