SCC2691 UART , writing device drivers , debug help

From: Rushi Lala (rushi.lala_at_gmail.com)
Date: 10/29/04


Date: 29 Oct 2004 04:53:45 -0700

uart_irq, termios help

I am porting Vxworks custom drivers for SCC2691 UART to linux. When i
try to open and write from simple user application driver goes in to
open and then close but it never goes to write function.

I am wondering why ?? any ideas ??

I still have to implement set_cflag, but right now i am just asking
UART to run on fix baud 19200.

Do i need to implement console drivers for rs422 communication with
custom hardware ?

Why user application is not calling write function at all ?

Any help is appreciated .

Rushi

/*
 * 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 Library 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.
 */
 

//Written by Rushi Lala <rushi@iee.org>, 2004

 /* linux/drivers/char/scc2691_tty.c */

//****************************************************************************

#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/console.h>

#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/irq.h>

//****************************************************************************

#define DRIVER_VERSION "Debug V1.1"
#define DRIVER_DESC "scc2691_drivers"

#define base_addr 0x8620
# define preset 0xF8CC

#define TRUE 1
#define FALSE 0

//********************************************************************************

#define IRQ_DUART 15

#define MSB(x) (((x) >> 8) & 0xff) /* most signif byte of 2-byte
integer*/
#define LSB(x) ((x) & 0xff) /* least signif byte of 2-byte
integer*/

#define BASE_ADDR 0x8620
#define PRESET 0xF8CC

#define SERIAL_2691_NAME "ttyS2691"
#define SERIAL_2691_MAJOR 4
#define SERIAL_2691_MINOR 255

#define KDEBUG 1

//********************************************************************************

//****************************************************************************
// SCC2691 UART Register Map
// Spacings are multiplied by 2 since address line A0 is not used

#define SCC2691_MR (2 * 0x00) // Mode Register (uses Aux
pointer)
#define SCC2691_SR (2 * 0x01) // Status Register (read only)
#define SCC2691_CSR (2 * 0x01) // Clock Select Register
(write only)

#define SCC2691_CR (2 * 0x02) // Command Register (write
only)
#define SCC2691_RHR (2 * 0x03) // Rx Holding Register (read
only)
#define SCC2691_THR (2 * 0x03) // Tx Holding Register (write
only)
#define SCC2691_ACR (2 * 0x04) // Auxiliary Control Register
(write only)

#define SCC2691_ISR (2 * 0x05) // Interrupt Status Register
(read only)
#define SCC2691_IMR (2 * 0x05) // Interrupt Mask Register
(write only)
#define SCC2691_CTU (2 * 0x06) // Counter/Timer Upper Output
Register (read only)
#define SCC2691_CTUR (2 * 0x06) // Counter/Timer Upper Preset
Register (write only)
#define SCC2691_CTL (2 * 0x07) // Counter/Timer Lower Output
Register (read only)
#define SCC2691_CTLR (2 * 0x07) // Counter/Timer Lower Preset
Register (write only)

#define SCC2691_ERR_MASK 0xF0

#define SCC2691_ERR_OE 0x10 // Overrun error
#define SCC2691_ERR_PE 0x20 // Parity Error
#define SCC2691_ERR_FE 0x40 // Framing Error
#define SCC2691_ERR_BI 0x80 // Break Interrupt

#define SCC2691_RX_RDY 0x01
#define SCC2691_FFULL 0x02
#define SCC2691_TX_RDY 0x04
#define SCC2691_TX_EMT 0x08

//****************************************************************************

//****************************************************************************
static struct tty_driver scc2691_driver ;
        
static int scc2691_refcount;

static struct tty_struct *scc2691_tty;
        
static int scc2691_use_count;

static struct tty_struct *scc2691_table[1];

static struct termios *scc2691_termios[1];
        
static struct termios *scc2691_termios_locked[1];

static char wbuf[1000], *putp = wbuf, *getp = wbuf, x_char;

        

//****************************************************************************

int isr_count = 0;
int err_count = 0;
int rx_count = 0;
int tx_count = 0;
int max_ix = 0;
int tx_int_on = 0 ;
int repeat_count = 0;
int repeat_errors;
char repeat_char = 0;

//*****************init
UART*******************************************************

void init_uart(void)

 {
         outw(0x30,BASE_ADDR + SCC2691_CR); // Reset Tx
     outw(0x20,BASE_ADDR + SCC2691_CR); // Reset Rx
     outw(0x00,BASE_ADDR + SCC2691_IMR); // Disable interrupts - get
enabled later
     outw(0x1A,BASE_ADDR + SCC2691_CR); // Reset MR pointer, disable
Tx and Rx
     outw(0x13,BASE_ADDR + SCC2691_MR); // 8-bits, no parity
     outw(0x07,BASE_ADDR + SCC2691_MR); // 1 stop bit
     outw(0xA0,BASE_ADDR + SCC2691_CR); // Assert MPO low - enables
422 driver
    #ifdef KDEBUG
        printk("DUART: init scc2691- \n");
    #endif
    outw(MSB(preset),BASE_ADDR + SCC2691_ACR); //speed
    outw (LSB(preset),BASE_ADDR + SCC2691_CSR);
        #ifdef KDEBUG
        printk("DUART: set the spped - \n");
    #endif
         
  }
//****************************************************************************

static int scc2691_write_room(struct tty_struct *tty)
{
    #ifdef KDEBUG
                printk("DUART: scc2691_write_room - \n");
         #endif
        return putp >= getp ? (sizeof(wbuf) - (long) putp + (long) getp) :
((long) getp - (long) putp - 1);
}

//****************************************************************************

static void scc2691_send_xchar(struct tty_struct *tty, char ch)
{

    #ifdef KDEBUG
                printk("DUART: scc2691_send_xchar - \n");
         #endif
        x_char = ch;
        enable_irq(IRQ_DUART);
}

//****************************************************************************

static void scc2691_throttle(struct tty_struct *tty)
{
            #ifdef KDEBUG
                printk("DUART: scc2691_throttle - \n ");
         #endif
   
   if (I_IXOFF(tty))
                scc2691_send_xchar(tty, STOP_CHAR(tty));
}

//****************************************************************************

//****************************************************************************

static void scc2691_unthrottle(struct tty_struct *tty)
{
        
  #ifdef KDEBUG
                printk("DUART: scc2691_unthrottle - \n");
         #endif

   if (I_IXOFF(tty)) {
                if (x_char)
                        x_char = 0;
                else
                        scc2691_send_xchar(tty, START_CHAR(tty));
        }
}

//****************************************************************************

//****************************************************************************
  
  static int scc2691_chars_in_buffer(struct tty_struct *tty)
{

    #ifdef KDEBUG
                printk("DUART: scc2691_chars_in_buffer - \n");
         #endif
        return sizeof(wbuf) - scc2691_write_room(tty);
}

//****************************************************************************
  
static void scc2691_flush_buffer(struct tty_struct *tty)
{

    #ifdef KDEBUG
                printk("DUART: scc2691_flush_buffer - \n ");
         #endif
        disable_irq(IRQ_DUART);
        putp = getp = wbuf;
        if (x_char)
                enable_irq(IRQ_DUART);
}
  
  
//****************************************************************************
static inline int scc2691_xmit(int ch)
{
        #ifdef KDEBUG
                printk("DUART: scc2691_xmit ** %c ** - \n",ch);
         #endif

   if (putp + 1 == getp || (putp + 1 == wbuf + sizeof(wbuf) && getp ==
wbuf))
                return 0;
        *putp = ch;
        if (++putp >= wbuf + sizeof(wbuf))
                putp = wbuf;
        
        outw(0x05,BASE_ADDR + SCC2691_IMR); // IMR
        tx_int_on = TRUE;
        enable_irq(IRQ_DUART);
        return 1;
}

//****************************************************************************

//****************************************************************************

static int scc2691_write(struct tty_struct *tty, int from_user,
                       const u_char * buf, int count)
{
        int i;
    
    #ifdef KDEBUG
                printk("DUART: scc2691_write -\n ");
         #endif

        if (from_user && verify_area(VERIFY_READ, buf, count))
                {
                #ifdef KDEBUG
                printk("DUART: not from user space return -\n ");
                 #endif
                return -EINVAL;
        }
        for (i = 0; i < count; i++) {
                char ch;
                if (from_user)
                { __get_user(ch, buf + i);
                #ifdef KDEBUG
                printk("DUART: scc2691_write FROM USER %c -\n", ch);
           #endif
                }
                else
                        ch = buf[i];
                if (!scc2691_xmit(ch))
                        break;
        }
        return i;
}

//****************************************************************************
  
//****************************************************************************
  
  static void scc2691_put_char(struct tty_struct *tty, u_char ch)
{
        #ifdef KDEBUG
                printk("DUART: scc2691_put_char - \n");
         #endif
    scc2691_xmit(ch);
}

//****************************************************************************
  
                  
  
//****************************************************************************
  
  
static int scc2691_open(struct tty_struct *tty, struct file *filp)
{
  
       MOD_INC_USE_COUNT;

      #ifdef KDEBUG
                printk("DUART: scc2691_open - \n");
     #endif
        
           tty->driver_data = NULL;
                        
                if (!scc2691_tty)
                                {
                                scc2691_tty = tty;
                                 #ifdef KDEBUG
                                 printk("DUART: scc2691_tty scc2691_tty = tty - \n");
                                 #endif
                                }

                
        
         scc2691_use_count++;
                        
         init_uart(); // reset the port again
         outw(0x40,BASE_ADDR + SCC2691_CR ); // Clear any outstanding
errors
     outw(0x05,BASE_ADDR + SCC2691_CR ); // Enable Rx and Tx
     outw(0x04,BASE_ADDR + SCC2691_IMR ); // Enable interrupts
 
        enable_irq(IRQ_DUART);
        #ifdef KDEBUG
         printk("DUART: enable tx, rx - \n");
        #endif
                        
        return 0;
                        
}

//****************************************************************************
static void scc2691_wait_until_sent(struct tty_struct *tty, int
timeout)
{
        #ifdef KDEBUG
                printk("DUART: scc2691_wait_until_sent - \n");
         #endif

   int orig_jiffies = jiffies;
        // Wait till TxEMT in SR
        while (SCC2691_SR & 0x08) {
                current->state = TASK_INTERRUPTIBLE;
                schedule_timeout(1);
                if (signal_pending(current))
                        break;
                if (timeout && time_after(jiffies, orig_jiffies + timeout))
                        break;
        }
        current->state = TASK_RUNNING;
        
}

static void scc2691_close(struct tty_struct *tty, struct file *filp)
{

    #ifdef KDEBUG
                printk("DUART: scc2691_close - \n");
         #endif
        if (!--scc2691_use_count)
                                {
                                        
                //scc2691_wait_until_sent(tty, 0);
                disable_irq(IRQ_DUART);
                scc2691_tty = NULL;
                }

        MOD_DEC_USE_COUNT;
        
}
  
static void duart_irq(int irq, void *dev_id, struct pt_regs *regs)
        
{

   #ifdef KDEBUG
                printk("DUART: duart_irq - \n");
         #endif

        int interruptID,flag = 0;
    char ch;

    if (!scc2691_tty) {
                disable_irq(IRQ_DUART);
                return;
        }

    interruptID = inw(BASE_ADDR + SCC2691_SR); // Get 8 bit Status
Register

        if (interruptID & SCC2691_ERR_MASK) // Mask last 4 bits
for with Error flags so get error flags
        {
                        // if error
            err_count ++;
            outw(0x40,BASE_ADDR + SCC2691_CR); // Reset errors
            outw(0x20, BASE_ADDR + SCC2691_CR ); // Reset (and
disable) Rx
            outw(0x01, BASE_ADDR + SCC2691_CR ); // Re-enable Rx
           interruptID = inw(BASE_ADDR + SCC2691_SR); // Get 8 bit
Status Register
                
        }
        
    while (tx_int_on && (interruptID & SCC2691_TX_RDY)) {
                if (x_char) {
                    #ifdef KDEBUG
                    printk("CHAR FROM USER %c - \n",ch);
                  #endif
                        outw(ch,BASE_ADDR + SCC2691_THR); // put the data in Tx holding
Register
                        x_char = 0;
                        continue;
                }
                if (putp == getp) {
                        disable_irq(IRQ_DUART);
                        break;
                }
                outw(0x04,BASE_ADDR + SCC2691_IMR); // set IMR to 100 rx full and
there is set tx_int_on false
            outw(*getp,BASE_ADDR + SCC2691_THR); // put the data in Tx
holding Register
                tx_int_on = FALSE;
                if (++getp >= wbuf + sizeof(wbuf))
                        getp = wbuf;
        }
        
    if (scc2691_tty)
                wake_up_interruptible(&scc2691_tty->write_wait);

         while (interruptID & SCC2691_RX_RDY) // Last bit of CR is RX Enable
so if Data Ready
        {
            rx_count ++; // count rx
            ch = inw(BASE_ADDR + SCC2691_RHR); // read holding
register

            if (ch == repeat_char)
            {
                repeat_count++;
                if (repeat_count > 10) // If there are more then 10
repeat_char then reset the UART
                {
                    repeat_errors++;
                    repeat_count = 0;

                    outw(0x40,BASE_ADDR + SCC2691_CR); // Reset
errors
                    outw(0x20,BASE_ADDR + SCC2691_CR); // Reset
(and disable) Rx
                    outw(0x01,BASE_ADDR + SCC2691_CR); // Re-enable
Rx

                                        interruptID = inw(BASE_ADDR + SCC2691_SR); // Get 8 bit Status
Register
                }
            }
            
                else
            {
                repeat_char = ch;
                repeat_count = 0;
            }

            tty_insert_flip_char(scc2691_tty, ch, flag);
                        //User space
        }
        
                tty_flip_buffer_push(scc2691_tty);
       
 }

static void scc2691_stop(struct tty_struct *tty)
{
        #ifdef KDEBUG
                printk("DUART: scc2691_stop - \n ");
         #endif
   disable_irq(IRQ_DUART);
}

static void scc2691_start(struct tty_struct *tty)
{

    #ifdef KDEBUGksdb.sh
                printk("DUART: scc2691_start - \n");
         #endif
        enable_irq(IRQ_DUART);
}

//****************************************************************************

int __init scc2691_init(void)
{
         
        memset(&scc2691_driver, 0, sizeof(scc2691_driver));
        scc2691_driver.magic = TTY_DRIVER_MAGIC;
        scc2691_driver.driver_name = "SCC2691";
        scc2691_driver.name = SERIAL_2691_NAME;
    scc2691_driver.major = SERIAL_2691_MAJOR;
        scc2691_driver.minor_start = SERIAL_2691_MINOR;
        scc2691_driver.num = 1;
        scc2691_driver.type = TTY_DRIVER_TYPE_SERIAL;
        scc2691_driver.subtype = SERIAL_TYPE_NORMAL;
        scc2691_driver.init_termios = tty_std_termios;
        scc2691_driver.init_termios.c_cflag = B19200 | CS8 | CREAD | HUPCL |
CLOCAL;
        scc2691_driver.flags = TTY_DRIVER_REAL_RAW;
        scc2691_driver.refcount = &scc2691_refcount;
        scc2691_driver.table = scc2691_table;
        scc2691_driver.termios = scc2691_termios;
        scc2691_driver.termios_locked = scc2691_termios_locked;
        scc2691_driver.open = scc2691_open;
        scc2691_driver.close = scc2691_close;
        
        scc2691_driver.stop = scc2691_stop;
        scc2691_driver.start = scc2691_start;
        scc2691_driver.stop = scc2691_write;
        scc2691_driver.put_char = scc2691_put_char;
        scc2691_driver.start = scc2691_xmit;
        scc2691_driver.write_room = scc2691_write_room;
        scc2691_driver.chars_in_buffer = scc2691_chars_in_buffer;
        scc2691_driver.flush_buffer = scc2691_flush_buffer;
        scc2691_driver.throttle = scc2691_throttle;
        scc2691_driver.unthrottle = scc2691_unthrottle;
        scc2691_driver.send_xchar = scc2691_send_xchar;
        
        scc2691_driver.hangup = NULL;
        scc2691_driver.break_ctl = NULL;
        scc2691_driver.read_proc = NULL;
                
                
        if (tty_register_driver(&scc2691_driver)) {
            printk(KERN_ERR "Couldn't register scc2691 serial driver\n");
                  return -EAGAIN;
        }

         if (request_irq(IRQ_DUART, duart_irq, SA_SHIRQ, "scc2691",
&scc2691_tty)) {
                printk(KERN_ERR "Couldn't register for scc2691 irqs\n");
                  return -EAGAIN;
                  }
   
        printk(DRIVER_DESC ":"DRIVER_VERSION "Rushi Lala" );
                 
        init_uart(); // initialise the UART
                 
        return 0;
}

module_init(scc2691_init);

MODULE_AUTHOR("Rushi Lala");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");

EXPORT_NO_SYMBOLS;



Relevant Pages

  • Re: [PATCH] 2.6.6 synclinkmp.c
    ... Therefore it must not be unregistered after failure ... > to register. ... -} - -static void __exit synclinkmp_exit+static void synclinkmp_cleanup{unsigned long flags; int rc; ...
    (Linux-Kernel)
  • Re: Can this conversion code be simplified?
    ... Now I need each byte as an int. ... I wrote the following test program: ...
    (comp.lang.c)
  • Request for help
    ... double sasum(int n,double * sx,int incx); ... int isamax(int n, double *sx, int incx); ... void saxpy(int n,double sa, double *sx,int incx,double * sy,int incy); ... register double s; ...
    (comp.lang.c)
  • [PATCH] epca.c: reformat comments and coding style improvements
    ... static int nbdevs, num_cards, liloconfig; ... This structure is NOT used to overlay the cards physical channel ... static void memwinoff ... static struct channel *verifyChannel ...
    (Linux-Kernel)
  • [PATCH 2/7] dlm: communication
    ... +static struct task_struct *recv_task; ... +static int nodeid_to_addr(int nodeid, struct sockaddr *retaddr) ... +static void lowcomms_data_ready ...
    (Linux-Kernel)