fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / serial / mcfserial.c
index 7f1b9ee..0843096 100644 (file)
 #include <linux/serialP.h>
 #include <linux/console.h>
 #include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/system.h>
-#include <asm/segment.h>
 #include <asm/semaphore.h>
-#include <asm/bitops.h>
 #include <asm/delay.h>
 #include <asm/coldfire.h>
 #include <asm/mcfsim.h>
@@ -57,7 +57,15 @@ struct timer_list mcfrs_timer_struct;
  *     keep going.  Perhaps one day the cflag settings for the
  *     console can be used instead.
  */
-#if defined(CONFIG_ARNEWSH) || defined(CONFIG_MOTOROLA) || defined(CONFIG_senTec)
+#if defined(CONFIG_HW_FEITH)
+#define        CONSOLE_BAUD_RATE       38400
+#define        DEFAULT_CBAUD           B38400
+#elif defined(CONFIG_MOD5272) || defined(CONFIG_M5208EVB) || \
+      defined(CONFIG_M5329EVB) || defined(CONFIG_GILBARCO)
+#define CONSOLE_BAUD_RATE      115200
+#define DEFAULT_CBAUD          B115200
+#elif defined(CONFIG_ARNEWSH) || defined(CONFIG_FREESCALE) || \
+      defined(CONFIG_senTec) || defined(CONFIG_SNEHA) || defined(CONFIG_AVNET)
 #define        CONSOLE_BAUD_RATE       19200
 #define        DEFAULT_CBAUD           B19200
 #endif
@@ -85,8 +93,9 @@ static struct tty_driver *mcfrs_serial_driver;
 #undef SERIAL_DEBUG_OPEN
 #undef SERIAL_DEBUG_FLOW
 
-#ifdef CONFIG_M5282
-#define        IRQBASE 77
+#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
+    defined(CONFIG_M520x) || defined(CONFIG_M532x)
+#define        IRQBASE (MCFINT_VECBASE+MCFINT_UART0)
 #else
 #define        IRQBASE 73
 #endif
@@ -101,12 +110,30 @@ static struct mcf_serial mcfrs_table[] = {
                .irq = IRQBASE,
                .flags = ASYNC_BOOT_AUTOCONF,
        },
+#ifdef MCFUART_BASE2
        {  /* ttyS1 */
                .magic = 0,
                .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE2),
                .irq = IRQBASE+1,
                .flags = ASYNC_BOOT_AUTOCONF,
        },
+#endif
+#ifdef MCFUART_BASE3
+       {  /* ttyS2 */
+               .magic = 0,
+               .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE3),
+               .irq = IRQBASE+2,
+               .flags = ASYNC_BOOT_AUTOCONF,
+       },
+#endif
+#ifdef MCFUART_BASE4
+       {  /* ttyS3 */
+               .magic = 0,
+               .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE4),
+               .irq = IRQBASE+3,
+               .flags = ASYNC_BOOT_AUTOCONF,
+       },
+#endif
 };
 
 
@@ -131,18 +158,6 @@ extern int magic_sysrq_key(int ch);
 #endif
 
 
-/*
- * tmp_buf is used as a temporary buffer by serial_write.  We need to
- * lock it in case the copy_from_user blocks while swapping in a page,
- * and some other program tries to do a serial write at the same time.
- * Since the lock will only come under contention when the system is
- * swapping and available memory is low, it makes sense to share one
- * buffer across all the serial ports, since it significantly saves
- * memory if large numbers of serial ports are open.
- */
-static unsigned char mcfrs_tmp_buf[4096]; /* This is cheating */
-static DECLARE_MUTEX(mcfrs_tmp_buf_sem);
-
 /*
  *     Forware declarations...
  */
@@ -313,7 +328,7 @@ static inline void receive_chars(struct mcf_serial *info)
 {
        volatile unsigned char  *uartp;
        struct tty_struct       *tty = info->tty;
-       unsigned char           status, ch;
+       unsigned char           status, ch, flag;
 
        if (!tty)
                return;
@@ -321,10 +336,6 @@ static inline void receive_chars(struct mcf_serial *info)
        uartp = info->addr;
 
        while ((status = uartp[MCFUART_USR]) & MCFUART_USR_RXREADY) {
-
-               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-                       break;
-
                ch = uartp[MCFUART_URB];
                info->stats.rx++;
 
@@ -335,28 +346,26 @@ static inline void receive_chars(struct mcf_serial *info)
                }
 #endif
 
-               tty->flip.count++;
-               if (status & MCFUART_USR_RXERR)
+               flag = TTY_NORMAL;
+               if (status & MCFUART_USR_RXERR) {
                        uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETERR;
-               if (status & MCFUART_USR_RXBREAK) {
-                       info->stats.rxbreak++;
-                       *tty->flip.flag_buf_ptr++ = TTY_BREAK;
-               } else if (status & MCFUART_USR_RXPARITY) {
-                       info->stats.rxparity++;
-                       *tty->flip.flag_buf_ptr++ = TTY_PARITY;
-               } else if (status & MCFUART_USR_RXOVERRUN) {
-                       info->stats.rxoverrun++;
-                       *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
-               } else if (status & MCFUART_USR_RXFRAMING) {
-                       info->stats.rxframing++;
-                       *tty->flip.flag_buf_ptr++ = TTY_FRAME;
-               } else {
-                       *tty->flip.flag_buf_ptr++ = 0;
+                       if (status & MCFUART_USR_RXBREAK) {
+                               info->stats.rxbreak++;
+                               flag = TTY_BREAK;
+                       } else if (status & MCFUART_USR_RXPARITY) {
+                               info->stats.rxparity++;
+                               flag = TTY_PARITY;
+                       } else if (status & MCFUART_USR_RXOVERRUN) {
+                               info->stats.rxoverrun++;
+                               flag = TTY_OVERRUN;
+                       } else if (status & MCFUART_USR_RXFRAMING) {
+                               info->stats.rxframing++;
+                               flag = TTY_FRAME;
+                       }
                }
-               *tty->flip.char_buf_ptr++ = ch;
+               tty_insert_flip_char(tty, ch, flag);
        }
-
-       schedule_work(&tty->flip.work);
+       tty_schedule_flip(tty);
        return;
 }
 
@@ -395,7 +404,7 @@ static inline void transmit_chars(struct mcf_serial *info)
 /*
  * This is the serial driver's generic interrupt routine
  */
-irqreturn_t mcfrs_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+irqreturn_t mcfrs_interrupt(int irq, void *dev_id)
 {
        struct mcf_serial       *info;
        unsigned char           isr;
@@ -723,19 +732,31 @@ static void mcfrs_flush_chars(struct tty_struct *tty)
        if (serial_paranoia_check(info, tty->name, "mcfrs_flush_chars"))
                return;
 
+       uartp = (volatile unsigned char *) info->addr;
+
+       /*
+        * re-enable receiver interrupt
+        */
+       local_irq_save(flags);
+       if ((!(info->imr & MCFUART_UIR_RXREADY)) &&
+           (info->flags & ASYNC_INITIALIZED) ) {
+               info->imr |= MCFUART_UIR_RXREADY;
+               uartp[MCFUART_UIMR] = info->imr;
+       }
+       local_irq_restore(flags);
+
        if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
            !info->xmit_buf)
                return;
 
        /* Enable transmitter */
        local_irq_save(flags);
-       uartp = info->addr;
        info->imr |= MCFUART_UIR_TXREADY;
        uartp[MCFUART_UIMR] = info->imr;
        local_irq_restore(flags);
 }
 
-static int mcfrs_write(struct tty_struct * tty, int from_user,
+static int mcfrs_write(struct tty_struct * tty,
                    const unsigned char *buf, int count)
 {
        volatile unsigned char  *uartp;
@@ -744,8 +765,8 @@ static int mcfrs_write(struct tty_struct * tty, int from_user,
        int                     c, total = 0;
 
 #if 0
-       printk("%s(%d): mcfrs_write(tty=%x,from_user=%d,buf=%x,count=%d)\n",
-               __FILE__, __LINE__, (int)tty, from_user, (int)buf, count);
+       printk("%s(%d): mcfrs_write(tty=%x,buf=%x,count=%d)\n",
+               __FILE__, __LINE__, (int)tty, (int)buf, count);
 #endif
 
        if (serial_paranoia_check(info, tty->name, "mcfrs_write"))
@@ -764,19 +785,7 @@ static int mcfrs_write(struct tty_struct * tty, int from_user,
                if (c <= 0)
                        break;
 
-               if (from_user) {
-                       down(&mcfrs_tmp_buf_sem);
-                       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);
+               memcpy(info->xmit_buf + info->xmit_head, buf, c);
 
                local_irq_disable();
                info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
@@ -990,12 +999,12 @@ static void send_break(   struct mcf_serial * info, int duration)
 
        if (!info->addr)
                return;
-       current->state = TASK_INTERRUPTIBLE;
+       set_current_state(TASK_INTERRUPTIBLE);
        uartp = info->addr;
 
        local_irq_save(flags);
        uartp[MCFUART_UCR] = MCFUART_UCR_CMDBREAKSTART;
-       schedule_timeout(jiffies + duration);
+       schedule_timeout(duration);
        uartp[MCFUART_UCR] = MCFUART_UCR_CMDBREAKSTOP;
        local_irq_restore(flags);
 }
@@ -1082,23 +1091,19 @@ static int mcfrs_ioctl(struct tty_struct *tty, struct file * file,
                                 (arg ? CLOCAL : 0));
                        return 0;
                case TIOCGSERIAL:
-                       error = verify_area(VERIFY_WRITE, (void *) arg,
-                                               sizeof(struct serial_struct));
-                       if (error)
-                               return error;
-                       return get_serial_info(info,
+                       if (access_ok(VERIFY_WRITE, (void *) arg,
+                                               sizeof(struct serial_struct)))
+                               return get_serial_info(info,
                                               (struct serial_struct *) arg);
+                       return -EFAULT;
                case TIOCSSERIAL:
                        return set_serial_info(info,
                                               (struct serial_struct *) arg);
                case TIOCSERGETLSR: /* Get line status register */
-                       error = verify_area(VERIFY_WRITE, (void *) arg,
-                               sizeof(unsigned int));
-                       if (error)
-                               return error;
-                       else
-                           return get_lsr_info(info, (unsigned int *) arg);
-
+                       if (access_ok(VERIFY_WRITE, (void *) arg,
+                                               sizeof(unsigned int)))
+                               return get_lsr_info(info, (unsigned int *) arg);
+                       return -EFAULT;
                case TIOCSERGSTRUCT:
                        error = copy_to_user((struct mcf_serial *) arg,
                                    info, sizeof(struct mcf_serial));
@@ -1127,7 +1132,7 @@ static int mcfrs_ioctl(struct tty_struct *tty, struct file * file,
        return 0;
 }
 
-static void mcfrs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+static void mcfrs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
 {
        struct mcf_serial *info = (struct mcf_serial *)tty->driver_data;
 
@@ -1242,8 +1247,7 @@ static void mcfrs_close(struct tty_struct *tty, struct file * filp)
 #endif 
        if (info->blocked_open) {
                if (info->close_delay) {
-                       current->state = TASK_INTERRUPTIBLE;
-                       schedule_timeout(info->close_delay);
+                       msleep_interruptible(jiffies_to_msecs(info->close_delay));
                }
                wake_up_interruptible(&info->open_wait);
        }
@@ -1308,8 +1312,7 @@ mcfrs_wait_until_sent(struct tty_struct *tty, int timeout)
                        fifo_cnt++;
                if (fifo_cnt == 0)
                        break;
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule_timeout(char_time);
+               msleep_interruptible(jiffies_to_msecs(char_time));
                if (signal_pending(current))
                        break;
                if (timeout && time_after(jiffies, orig_jiffies + timeout))
@@ -1519,7 +1522,7 @@ static void mcfrs_irqinit(struct mcf_serial *info)
        *portp = (*portp & ~0x000000ff) | 0x00000055;
        portp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_PDCNT);
        *portp = (*portp & ~0x000003fc) | 0x000002a8;
-#elif defined(CONFIG_M5282)
+#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x)
        volatile unsigned char *icrp, *uartp;
        volatile unsigned long *imrp;
 
@@ -1527,11 +1530,78 @@ static void mcfrs_irqinit(struct mcf_serial *info)
 
        icrp = (volatile unsigned char *) (MCF_MBAR + MCFICM_INTC0 +
                MCFINTC_ICR0 + MCFINT_UART0 + info->line);
-       *icrp = 0x33; /* UART0 with level 6, priority 3 */
+       *icrp = 0x30 + info->line; /* level 6, line based priority */
 
        imrp = (volatile unsigned long *) (MCF_MBAR + MCFICM_INTC0 +
                MCFINTC_IMRL);
-       *imrp &= ~((1 << (info->irq - 64)) | 1);
+       *imrp &= ~((1 << (info->irq - MCFINT_VECBASE)) | 1);
+#if defined(CONFIG_M527x)
+       {
+               /*
+                * External Pin Mask Setting & Enable External Pin for Interface
+                * mrcbis@aliceposta.it
+                */
+               unsigned short *serpin_enable_mask;
+               serpin_enable_mask = (MCF_IPSBAR + MCF_GPIO_PAR_UART);
+               if (info->line == 0)
+                       *serpin_enable_mask |= UART0_ENABLE_MASK;
+               else if (info->line == 1)
+                       *serpin_enable_mask |= UART1_ENABLE_MASK;
+               else if (info->line == 2)
+                       *serpin_enable_mask |= UART2_ENABLE_MASK;
+       }
+#endif
+#elif defined(CONFIG_M520x)
+       volatile unsigned char *icrp, *uartp;
+       volatile unsigned long *imrp;
+
+       uartp = info->addr;
+
+       icrp = (volatile unsigned char *) (MCF_MBAR + MCFICM_INTC0 +
+               MCFINTC_ICR0 + MCFINT_UART0 + info->line);
+       *icrp = 0x03;
+
+       imrp = (volatile unsigned long *) (MCF_MBAR + MCFICM_INTC0 +
+               MCFINTC_IMRL);
+       *imrp &= ~((1 << (info->irq - MCFINT_VECBASE)) | 1);
+       if (info->line < 2) {
+               unsigned short *uart_par;
+               uart_par = (unsigned short *)(MCF_IPSBAR + MCF_GPIO_PAR_UART);
+               if (info->line == 0)
+                       *uart_par |=  MCF_GPIO_PAR_UART_PAR_UTXD0
+                                 | MCF_GPIO_PAR_UART_PAR_URXD0;
+               else if (info->line == 1)
+                       *uart_par |=  MCF_GPIO_PAR_UART_PAR_UTXD1
+                                 | MCF_GPIO_PAR_UART_PAR_URXD1;
+               } else if (info->line == 2) {
+                       unsigned char *feci2c_par;
+                       feci2c_par = (unsigned char *)(MCF_IPSBAR +  MCF_GPIO_PAR_FECI2C);
+                       *feci2c_par &= ~0x0F;
+                       *feci2c_par |=  MCF_GPIO_PAR_FECI2C_PAR_SCL_UTXD2
+                                   | MCF_GPIO_PAR_FECI2C_PAR_SDA_URXD2;
+               }
+#elif defined(CONFIG_M532x)
+       volatile unsigned char *uartp;
+       uartp = info->addr;
+       switch (info->line) {
+       case 0:
+               MCF_INTC0_ICR26 = 0x3;
+               MCF_INTC0_CIMR = 26;
+               /* GPIO initialization */
+               MCF_GPIO_PAR_UART |= 0x000F;
+               break;
+       case 1:
+               MCF_INTC0_ICR27 = 0x3;
+               MCF_INTC0_CIMR = 27;
+               /* GPIO initialization */
+               MCF_GPIO_PAR_UART |= 0x0FF0;
+               break;
+       case 2:
+               MCF_INTC0_ICR28 = 0x3;
+               MCF_INTC0_CIMR = 28;
+               /* GPIOs also must be initalized, depends on board */
+               break;
+       }
 #else
        volatile unsigned char  *icrp, *uartp;
 
@@ -1561,7 +1631,7 @@ static void mcfrs_irqinit(struct mcf_serial *info)
        /* Clear mask, so no surprise interrupts. */
        uartp[MCFUART_UIMR] = 0;
 
-       if (request_irq(info->irq, mcfrs_interrupt, SA_INTERRUPT,
+       if (request_irq(info->irq, mcfrs_interrupt, IRQF_DISABLED,
            "ColdFire UART", NULL)) {
                printk("MCFRS: Unable to attach ColdFire UART %d interrupt "
                        "vector=%d\n", info->line, info->irq);
@@ -1631,7 +1701,7 @@ static void show_serial_version(void)
        printk(mcfrs_drivername);
 }
 
-static struct tty_operations mcfrs_ops = {
+static const struct tty_operations mcfrs_ops = {
        .open = mcfrs_open,
        .close = mcfrs_close,
        .write = mcfrs_write,
@@ -1678,8 +1748,7 @@ mcfrs_init(void)
        /* 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->driver_name = "mcfserial";
        mcfrs_serial_driver->major = TTY_MAJOR;
        mcfrs_serial_driver->minor_start = 64;
        mcfrs_serial_driver->type = TTY_DRIVER_TYPE_SERIAL;
@@ -1763,10 +1832,23 @@ void mcfrs_init_console(void)
        uartp[MCFUART_UMR] = MCFUART_MR1_PARITYNONE | MCFUART_MR1_CS8;
        uartp[MCFUART_UMR] = MCFUART_MR2_STOP1;
 
+#ifdef CONFIG_M5272
+{
+       /*
+        * For the MCF5272, also compute the baudrate fraction.
+        */
+       int fraction = MCF_BUSCLK - (clk * 32 * mcfrs_console_baud);
+       fraction *= 16;
+       fraction /= (32 * mcfrs_console_baud);
+       uartp[MCFUART_UFPD] = (fraction & 0xf);         /* set fraction */
+       clk = (MCF_BUSCLK / mcfrs_console_baud) / 32;
+}
+#else
        clk = ((MCF_BUSCLK / mcfrs_console_baud) + 16) / 32; /* set baud */
+#endif
+
        uartp[MCFUART_UBG1] = (clk & 0xff00) >> 8;  /* set msb baud */
        uartp[MCFUART_UBG2] = (clk & 0xff);  /* set lsb baud */
-
        uartp[MCFUART_UCSR] = MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER;
        uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE;