public inbox for ecos-discuss@sourceware.org
 help / color / mirror / Atom feed
* [ECOS] new at91 serial driver source
@ 2002-04-03 15:18 Recker, John
  2002-04-03 22:25 ` Jonathan Larmour
  0 siblings, 1 reply; 2+ messages in thread
From: Recker, John @ 2002-04-03 15:18 UTC (permalink / raw)
  To: 'ecos-discuss@sources.redhat.com'

[-- Attachment #1: Type: text/plain, Size: 2 bytes --]




[-- Attachment #2: serial.txt --]
[-- Type: text/plain, Size: 27530 bytes --]


A newer serial port driver for the AT91
atmel processor.

Note that I have only tested this on a
custom card, so your mileage might vary.
I also don't think that this driver is
perfect: I still drop occasional chars
when communicating with a PC (~1 every
200k chars @ 38400 baud). But that is
dramatically better than I get with the
default driver, which drops characters
regularily at that baud rate.

Note that this driver requires that the
following be added to the serial .cdl
file:

    implements CYGINT_IO_SERIAL_BLOCK_TRANSFER

for each of the supported devices (eg 
CYGPKG_IO_SERIAL_ARM_MY_DEV_SERIAL0
CYGPKG_IO_SERIAL_ARM_MY_DEV_SERIAL1)

Next, you need to add a few defines to
hal/arm/at91/current/include/plf_io.h  I
can't remember what I added, so the following
is the relevant section from my version of the
file:

#define AT91_US_CR  0x00  // Control register
#define AT91_US_CR_RxRESET (1<<2)
#define AT91_US_CR_TxRESET (1<<3)
#define AT91_US_CR_RxENAB  (1<<4)
#define AT91_US_CR_RxDISAB (1<<5)
#define AT91_US_CR_TxENAB  (1<<6)
#define AT91_US_CR_TxDISAB (1<<7)
#define AT91_US_CR_RSTATUS (1<<8)
#define AT91_US_MR  0x04  // Mode register
#define AT91_US_MR_CLOCK   4
#define AT91_US_MR_CLOCK_MCK  (0<<AT91_US_MR_CLOCK)
#define AT91_US_MR_CLOCK_MCK8 (1<<AT91_US_MR_CLOCK)
#define AT91_US_MR_CLOCK_SCK  (2<<AT91_US_MR_CLOCK)
#define AT91_US_MR_LENGTH  6
#define AT91_US_MR_LENGTH_5   (0<<AT91_US_MR_LENGTH)
#define AT91_US_MR_LENGTH_6   (1<<AT91_US_MR_LENGTH)
#define AT91_US_MR_LENGTH_7   (2<<AT91_US_MR_LENGTH)
#define AT91_US_MR_LENGTH_8   (3<<AT91_US_MR_LENGTH)
#define AT91_US_MR_SYNC    8
#define AT91_US_MR_SYNC_ASYNC (0<<AT91_US_MR_SYNC)
#define AT91_US_MR_SYNC_SYNC  (1<<AT91_US_MR_SYNC)
#define AT91_US_MR_PARITY  9
#define AT91_US_MR_PARITY_EVEN  (0<<AT91_US_MR_PARITY)
#define AT91_US_MR_PARITY_ODD   (1<<AT91_US_MR_PARITY)
#define AT91_US_MR_PARITY_SPACE (2<<AT91_US_MR_PARITY)
#define AT91_US_MR_PARITY_MARK  (3<<AT91_US_MR_PARITY)
#define AT91_US_MR_PARITY_NONE  (4<<AT91_US_MR_PARITY)
#define AT91_US_MR_PARITY_MULTI (6<<AT91_US_MR_PARITY)
#define AT91_US_MR_STOP   12
#define AT91_US_MR_STOP_1       (0<<AT91_US_MR_STOP)
#define AT91_US_MR_STOP_1_5     (1<<AT91_US_MR_STOP)
#define AT91_US_MR_STOP_2       (2<<AT91_US_MR_STOP)
#define AT91_US_MR_MODE   14
#define AT91_US_MR_MODE_NORMAL  (0<<AT91_US_MR_MODE)
#define AT91_US_MR_MODE_ECHO    (1<<AT91_US_MR_MODE)
#define AT91_US_MR_MODE_LOCAL   (2<<AT91_US_MR_MODE)
#define AT91_US_MR_MODE_REMOTE  (3<<AT91_US_MR_MODE)
#define AT91_US_MR_MODE9  17
#define AT91_US_MR_CLKO   18
#define AT91_US_IER 0x08  // Interrupt enable register
#define AT91_US_IER_RxRDY   (1<<0)  // Receive data ready
#define AT91_US_IER_TxRDY   (1<<1)  // Transmitter ready
#define AT91_US_IER_RxBRK   (1<<2)  // Break received
#define AT91_US_IER_ENDRX   (1<<3)  // Rx end
#define AT91_US_IER_ENDTX   (1<<4)  // Tx end
#define AT91_US_IER_OVRE    (1<<5)  // Rx overflow
#define AT91_US_IER_FRAME   (1<<6)  // Rx framing error
#define AT91_US_IER_PARITY  (1<<7)  // Rx parity
#define AT91_US_IER_TIMEOUT (1<<8)  // Rx timeout
#define AT91_US_IER_TxEMPTY (1<<9)  // Tx empty
#define AT91_US_IDR 0x0C  // Interrupt disable register
#define AT91_US_IMR 0x10  // Interrupt mask register
#define AT91_US_CSR 0x14  // Channel status register
#define AT91_US_CSR_RxRDY 0x01 // Receive data ready
#define AT91_US_CSR_TxRDY 0x02 // Transmit ready
#define AT91_US_RHR 0x18  // Receive holding register
#define AT91_US_THR 0x1C  // Transmit holding register
#define AT91_US_BRG 0x20  // Baud rate generator
#define AT91_US_RTO 0x24  // Receive time out
#define AT91_US_TTG 0x28  // Transmit timer guard

lastly, to use the below serial.c file, substitute:

	my_dev -> dev_name (eg at91)
	MY_DEV -> DEV_NAME (eg AT91)

This version is heavily based on the version
posted by Paul Sheer some time ago.

Happy hacking...

jr

=============================================================
=============================================================
=============================================================

//==========================================================================
//
//      io/serial/arm/my_dev/my_dev_serial.c
//
//      Atmel MY_DEV/EB40 Serial I/O Interface Module (interrupt driven)
//
//==========================================================================
//####COPYRIGHTBEGIN####
//                                                                          
// -------------------------------------------                              
// The contents of this file are subject to the Red Hat eCos Public License 
// Version 1.1 (the "License"); you may not use this file except in         
// compliance with the License.  You may obtain a copy of the License at    
// http://www.redhat.com/                                                   
//                                                                          
// Software distributed under the License is distributed on an "AS IS"      
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
// License for the specific language governing rights and limitations under 
// the License.                                                             
//                                                                          
// The Original Code is eCos - Embedded Configurable Operating System,      
// released September 30, 1998.                                             
//                                                                          
// The Initial Developer of the Original Code is Red Hat.                   
// Portions created by Red Hat are                                          
// Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc.                             
// All Rights Reserved.                                                     
// -------------------------------------------                              
//                                                                          
//####COPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):     gthomas, Paul Sheer, John Recker
// Contributors:  gthomas
// Date:          2001-07-24
// Purpose:       Atmel MY_DEV/EB40 Serial I/O module (interrupt driven version)
// Description: 
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <pkgconf/system.h>
#include <pkgconf/io_serial.h>
#include <pkgconf/io.h>
#include <pkgconf/kernel.h>

#include <cyg/io/io.h>
#include <cyg/hal/hal_io.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/io/devtab.h>
#include <cyg/io/serial.h>
#include <cyg/infra/diag.h>

#include <cyg/kernel/kapi.h>

#include <stdio.h>

#ifdef CYGPKG_IO_SERIAL_ARM_MY_DEV

#include "my_dev_serial.h"

#define MY_DEV_UART_BUF_SIZE            256
#define MY_DEV_UART_RECV_BUF_COUNT       16
#define MY_DEV_UART_RECV_BUF_MASK        15
#define MY_DEV_RECV_UART_CHAR_TIMEOUT    20

typedef struct flip_buffer  {
    cyg_uint8      buf[MY_DEV_UART_BUF_SIZE];
    cyg_uint8      *outp;
    int            count;
} flip_buffer;

typedef struct my_dev_pdc_serial_info {
    CYG_ADDRWORD   base;
    CYG_WORD       int_num;
    cyg_interrupt  serial_interrupt;
    cyg_handle_t   serial_interrupt_handle;
    cyg_interrupt  line_interrupt;
    cyg_handle_t   line_interrupt_handle;
    cyg_mutex_t    DSR_mutex;
    cyg_sem_t	   flip_semaphore;
    int            send_buf_which;
    flip_buffer    send_buf[2];
    int            recv_buf_in_index;
    int            recv_buf_out_index;
    flip_buffer    recv_buf[MY_DEV_UART_RECV_BUF_COUNT];
    int            tx_enabled;
    cyg_uint32     stat;
    cyg_serial_info_t config;
} my_dev_pdc_serial_info;

static bool my_dev_pdc_serial_init(struct cyg_devtab_entry *tab);
static bool my_dev_pdc_serial_putc(serial_channel *chan, unsigned char c);
static Cyg_ErrNo my_dev_pdc_serial_lookup(struct cyg_devtab_entry **tab, 
                                    struct cyg_devtab_entry *sub_tab,
                                    const char *name);
static unsigned char my_dev_pdc_serial_getc(serial_channel *chan);
static Cyg_ErrNo my_dev_pdc_serial_set_config(serial_channel *chan, cyg_uint32 key,
                                        const void *xbuf, cyg_uint32 *len);
static void my_dev_pdc_serial_start_xmit(serial_channel *chan);
static void my_dev_pdc_serial_stop_xmit(serial_channel *chan);

static cyg_uint32 my_dev_pdc_serial_ISR(cyg_vector_t vector, cyg_addrword_t data);
static void       my_dev_pdc_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);

static SERIAL_FUNS(my_dev_pdc_serial_funs, 
                   my_dev_pdc_serial_putc, 
                   my_dev_pdc_serial_getc,
                   my_dev_pdc_serial_set_config,
                   my_dev_pdc_serial_start_xmit,
                   my_dev_pdc_serial_stop_xmit
    );

#ifdef CYGPKG_IO_SERIAL_ARM_MY_DEV_SERIAL0
static my_dev_pdc_serial_info my_dev_pdc_serial_info0 = {(CYG_ADDRWORD)MY_DEV_USART0,
                                             CYGNUM_HAL_INTERRUPT_USART0};
#if CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL0_BUFSIZE > 0
static unsigned char my_dev_pdc_serial_out_buf0[CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL0_BUFSIZE];
static unsigned char my_dev_pdc_serial_in_buf0[CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL0_BUFSIZE];

static SERIAL_CHANNEL_USING_INTERRUPTS(my_dev_pdc_serial_channel0,
                                       my_dev_pdc_serial_funs, 
                                       my_dev_pdc_serial_info0,
                                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL0_BAUD),
                                       CYG_SERIAL_STOP_DEFAULT,
                                       CYG_SERIAL_PARITY_DEFAULT,
                                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
                                       CYG_SERIAL_FLAGS_DEFAULT,
                                       &my_dev_pdc_serial_out_buf0[0], sizeof(my_dev_pdc_serial_out_buf0),
                                       &my_dev_pdc_serial_in_buf0[0], sizeof(my_dev_pdc_serial_in_buf0)
    );
#else
static SERIAL_CHANNEL(my_dev_pdc_serial_channel0,
                      my_dev_pdc_serial_funs, 
                      my_dev_pdc_serial_info0,
                      CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL0_BAUD),
                      CYG_SERIAL_STOP_DEFAULT,
                      CYG_SERIAL_PARITY_DEFAULT,
                      CYG_SERIAL_WORD_LENGTH_DEFAULT,
                      CYG_SERIAL_FLAGS_DEFAULT
    );
#endif

DEVTAB_ENTRY(my_dev_pdc_serial_io0, 
             CYGDAT_IO_SERIAL_ARM_MY_DEV_SERIAL0_NAME,
             0,                     // Does not depend on a lower level interface
             &cyg_io_serial_devio, 
             my_dev_pdc_serial_init, 
             my_dev_pdc_serial_lookup,     // Serial driver may need initializing
             &my_dev_pdc_serial_channel0
    );
#endif //  CYGPKG_IO_SERIAL_ARM_MY_DEV_SERIAL1

#ifdef CYGPKG_IO_SERIAL_ARM_MY_DEV_SERIAL1
static my_dev_pdc_serial_info my_dev_pdc_serial_info1 = {(CYG_ADDRWORD)MY_DEV_USART1,
                                              CYGNUM_HAL_INTERRUPT_USART1};
#if CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL1_BUFSIZE > 0
static unsigned char my_dev_pdc_serial_out_buf1[CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL1_BUFSIZE];
static unsigned char my_dev_pdc_serial_in_buf1[CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL1_BUFSIZE];

static SERIAL_CHANNEL_USING_INTERRUPTS(my_dev_pdc_serial_channel1,
                                       my_dev_pdc_serial_funs, 
                                       my_dev_pdc_serial_info1,
                                       CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL1_BAUD),
                                       CYG_SERIAL_STOP_DEFAULT,
                                       CYG_SERIAL_PARITY_DEFAULT,
                                       CYG_SERIAL_WORD_LENGTH_DEFAULT,
                                       CYG_SERIAL_FLAGS_DEFAULT,
                                       &my_dev_pdc_serial_out_buf1[0], sizeof(my_dev_pdc_serial_out_buf1),
                                       &my_dev_pdc_serial_in_buf1[0], sizeof(my_dev_pdc_serial_in_buf1)
    );
#else
static SERIAL_CHANNEL(my_dev_pdc_serial_channel1,
                      my_dev_pdc_serial_funs, 
                      my_dev_pdc_serial_info1,
                      CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL1_BAUD),
                      CYG_SERIAL_STOP_DEFAULT,
                      CYG_SERIAL_PARITY_DEFAULT,
                      CYG_SERIAL_WORD_LENGTH_DEFAULT,
                      CYG_SERIAL_FLAGS_DEFAULT
    );
#endif

DEVTAB_ENTRY(my_dev_pdc_serial_io1, 
             CYGDAT_IO_SERIAL_ARM_MY_DEV_SERIAL1_NAME,
             0,                     // Does not depend on a lower level interface
             &cyg_io_serial_devio, 
             my_dev_pdc_serial_init, 
             my_dev_pdc_serial_lookup,     // Serial driver may need initializing
             &my_dev_pdc_serial_channel1
    );
#endif //  CYGPKG_IO_SERIAL_ARM_MY_DEV_SERIAL1

// Internal function to actually configure the hardware to desired baud rate, etc.
static bool
my_dev_pdc_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init)
{
    my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *)chan->dev_priv;
    CYG_ADDRWORD base = my_dev_pdc_chan->base;
    cyg_uint32 parity = select_parity[new_config->parity];
    cyg_uint32 word_length = select_word_length[new_config->word_length-CYGNUM_SERIAL_WORD_LENGTH_5];
    cyg_uint32 stop_bits = select_stop_bits[new_config->stop];

    if ((word_length == 0xFF) ||
        (parity == 0xFF) ||
        (stop_bits == 0xFF)) {
        return false;  // Unsupported configuration
    }

    if (init) {
	int i;

	memset (&my_dev_pdc_chan->config, 0, sizeof (my_dev_pdc_chan->config));
	my_dev_pdc_chan->send_buf_which = 0;
	my_dev_pdc_chan->send_buf[0].count = my_dev_pdc_chan->send_buf[1].count = 0;
	my_dev_pdc_chan->tx_enabled = 0;

	my_dev_pdc_chan->recv_buf_in_index = 0;
	my_dev_pdc_chan->recv_buf_out_index = 0;
	for (i = 0; i < MY_DEV_UART_RECV_BUF_COUNT; i++)
		my_dev_pdc_chan->recv_buf[i].count = 0;

    }

    if (new_config->word_length != my_dev_pdc_chan->config.word_length ||
	    new_config->parity != my_dev_pdc_chan->config.parity ||
	    new_config->stop != my_dev_pdc_chan->config.stop ||
	    new_config->baud != my_dev_pdc_chan->config.baud) {
	// Reset device
	HAL_WRITE_UINT32(base+MY_DEV_US_CR, MY_DEV_US_CR_RxRESET | MY_DEV_US_CR_TxRESET);

	// Configuration
	HAL_WRITE_UINT32(base+MY_DEV_US_MR, parity | word_length | stop_bits);

	// Baud rate
	HAL_WRITE_UINT32(base+MY_DEV_US_BRG, MY_DEV_US_BAUD(select_baud[new_config->baud]));

	// Enable RX and TX
	HAL_WRITE_UINT32(base+MY_DEV_US_CR, MY_DEV_US_CR_RxENAB | MY_DEV_US_CR_TxENAB);
	my_dev_pdc_chan->config = *new_config;
    }

    if (init) {
	// Disable all interrupts
	HAL_WRITE_UINT32(base+MY_DEV_US_IDR, 0xFFFFFFFF);

	// Initialize hardware buffers
	HAL_WRITE_UINT32 (base + MY_DEV_US_RPR, (cyg_uint32) my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_in_index].buf);
	HAL_WRITE_UINT32 (base + MY_DEV_US_RCR, (cyg_uint32) MY_DEV_UART_BUF_SIZE);
	HAL_WRITE_UINT32 (base + MY_DEV_US_TCR, (cyg_uint32) 0);
	HAL_WRITE_UINT32 (base + MY_DEV_US_TPR, (cyg_uint32) my_dev_pdc_chan->send_buf[my_dev_pdc_chan->send_buf_which].buf);

	// Enable buffer full and timeout interrupts
	HAL_WRITE_UINT32(base+MY_DEV_US_IER, MY_DEV_US_IER_TIMEOUT | MY_DEV_US_IER_ENDRX);
    }

    // Start timeout
    HAL_WRITE_UINT32(base+MY_DEV_US_RTO, MY_DEV_RECV_UART_CHAR_TIMEOUT);
    HAL_WRITE_UINT32(base+MY_DEV_US_CR, MY_DEV_US_CR_STTTO);

    return true;
}

// Function to initialize the device.  Called at bootstrap time.
static bool 
my_dev_pdc_serial_init(struct cyg_devtab_entry *tab)
{
    serial_channel *chan = (serial_channel *)tab->priv;
    my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *)chan->dev_priv;
    int res;

#ifdef CYGDBG_IO_INIT
    diag_printf("MY_DEV SERIAL init - dev: %x.%d\n", my_dev_pdc_chan->base, my_dev_pdc_chan->int_num);
#endif

    cyg_mutex_init(&my_dev_pdc_chan->DSR_mutex);
    cyg_semaphore_init(&my_dev_pdc_chan->flip_semaphore, 1);

    (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
    if (chan->out_cbuf.len != 0) {
        cyg_drv_interrupt_create(my_dev_pdc_chan->int_num,
                                 4,                      // Priority
                                 (cyg_addrword_t)chan,   // Data item passed to interrupt handler
                                 my_dev_pdc_serial_ISR,
                                 my_dev_pdc_serial_DSR,
                                 &my_dev_pdc_chan->serial_interrupt_handle,
                                 &my_dev_pdc_chan->serial_interrupt);
        cyg_drv_interrupt_attach(my_dev_pdc_chan->serial_interrupt_handle);
        cyg_drv_interrupt_unmask(my_dev_pdc_chan->int_num);
    }
    res = my_dev_pdc_serial_config_port(chan, &chan->config, true);
    return res;
}

// This routine is called when the device is "looked" up (i.e. attached)
static Cyg_ErrNo 
my_dev_pdc_serial_lookup(struct cyg_devtab_entry **tab, 
                  struct cyg_devtab_entry *sub_tab,
                  const char *name)
{
    serial_channel *chan = (serial_channel *)(*tab)->priv;

    (chan->callbacks->serial_init)(chan);  // Really only required for interrupt driven devices
    return ENOERR;
}

static void
my_dev_pdc_serial_flip_send_buffer (my_dev_pdc_serial_info * my_dev_pdc_chan)
{
    CYG_ADDRWORD base = my_dev_pdc_chan->base;
    cyg_uint32 count;
    int other;

    HAL_READ_UINT32 (base + MY_DEV_US_TCR, count);
    if (count > 0) {

	// transmitter still busy
	HAL_WRITE_UINT32 (base + MY_DEV_US_IER, MY_DEV_US_IER_ENDTX);

    } else {

	// transmitter done with old buf
	// Avoid conflicts w/ putc, DSR...
	if (!cyg_semaphore_trywait(&my_dev_pdc_chan->flip_semaphore)) {
	    // If someone else is messing w/ send buffers, let them re-enable interrupts
	    HAL_WRITE_UINT32 (base + MY_DEV_US_IDR, MY_DEV_US_IER_ENDTX);
	    return;
	}

	// empty buffer -
	my_dev_pdc_chan->send_buf[my_dev_pdc_chan->send_buf_which].count = 0;

	// flip -
	other = 1 - my_dev_pdc_chan->send_buf_which;
	if (my_dev_pdc_chan->send_buf[other].count > 0) {
	    my_dev_pdc_chan->send_buf_which = other;
	    HAL_WRITE_UINT32 (base + MY_DEV_US_TPR, (cyg_uint32) my_dev_pdc_chan->send_buf[other].buf);
	    HAL_WRITE_UINT32 (base + MY_DEV_US_TCR,
			      (cyg_uint32) my_dev_pdc_chan->send_buf[other].count);

	    // enable completion interrupt -
	    HAL_WRITE_UINT32 (base + MY_DEV_US_IER, MY_DEV_US_IER_ENDTX);
	} else {
	    // Nothing to do, disable interrupts
	    HAL_WRITE_UINT32 (base + MY_DEV_US_IDR, MY_DEV_US_IER_ENDTX);
	}

	// release semaphore
	cyg_semaphore_post(&my_dev_pdc_chan->flip_semaphore);
    }
}

// Send a character to the device output buffer.
// Return 'true' if character is sent to device
static bool
my_dev_pdc_serial_putc (serial_channel * chan, unsigned char c)
{
    my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *) chan->dev_priv;
    int other;

    // avoid re-entrancy problems
    cyg_semaphore_wait(&my_dev_pdc_chan->flip_semaphore);

    other = 1 - my_dev_pdc_chan->send_buf_which;
    if (my_dev_pdc_chan->send_buf[other].count >= MY_DEV_UART_BUF_SIZE)
	return false;

    // append buffer with char
    my_dev_pdc_chan->send_buf[other].buf[my_dev_pdc_chan->send_buf[other].count] = (cyg_uint8) c;
    my_dev_pdc_chan->send_buf[other].count++;

    // release semaphore
    cyg_semaphore_post(&my_dev_pdc_chan->flip_semaphore);

    // flip if needed:
    my_dev_pdc_serial_flip_send_buffer (my_dev_pdc_chan);

    return true;
}

// we don't support this - its only for bufferless operation: see serial.c
static unsigned char 
my_dev_pdc_serial_getc(serial_channel *chan)
{
    return 0;
}

// Set up the device characteristics; baud rate, etc.
static Cyg_ErrNo
my_dev_pdc_serial_set_config(serial_channel *chan, cyg_uint32 key,
                         const void *xbuf, cyg_uint32 *len)
{
    switch (key) {
    case CYG_IO_SET_CONFIG_SERIAL_INFO:
      {
        cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf;
        if ( *len < sizeof(cyg_serial_info_t) ) {
            return -EINVAL;
        }
        *len = sizeof(cyg_serial_info_t);
        if ( true != my_dev_pdc_serial_config_port(chan, config, false) )
            return -EINVAL;
      }
      break;
    default:
        return -EINVAL;
    }
    return ENOERR;
}

// Enable the transmitter on the device
static void
my_dev_pdc_serial_start_xmit (serial_channel * chan)
{
    my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *) chan->dev_priv;
    CYG_ADDRWORD base = my_dev_pdc_chan->base;
    cyg_uint32 count;

    HAL_READ_UINT32 (base + MY_DEV_US_TCR, count);
    if (count > 0)
	HAL_WRITE_UINT32 (base + MY_DEV_US_IER, MY_DEV_US_IER_ENDTX);
    my_dev_pdc_chan->tx_enabled = 1;

    (chan->callbacks->xmt_char) (chan);
}

// Disable the transmitter on the device
static void 
my_dev_pdc_serial_stop_xmit(serial_channel *chan)
{
    my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *)chan->dev_priv;
    my_dev_pdc_chan->tx_enabled = 0;
}

// Serial I/O - low level interrupt handler (ISR)
static cyg_uint32 
my_dev_pdc_serial_ISR (cyg_vector_t vector, cyg_addrword_t data)
{
    serial_channel *chan = (serial_channel *) data;
    my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *) chan->dev_priv;
    CYG_ADDRWORD base = my_dev_pdc_chan->base;

    cyg_uint32 ipr, imr, p;
    cyg_uint32 count;

    cyg_drv_interrupt_mask (vector);

    HAL_READ_UINT32 (base + MY_DEV_US_CSR, ipr);
    HAL_READ_UINT32 (base + MY_DEV_US_IMR, imr);

    if (ipr & (MY_DEV_US_CSR_OVRE		// Rx overflow
               | MY_DEV_US_CSR_FRAME)) {	// Rx framing error 
 
	    HAL_WRITE_UINT32 (base + MY_DEV_US_CR, MY_DEV_US_CR_RSTATUS);
    }

    if (ipr & imr & (MY_DEV_US_IER_ENDRX | MY_DEV_US_IER_TIMEOUT)) {

	struct flip_buffer *recv = &my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_in_index];

        HAL_READ_UINT32 (base + MY_DEV_US_RPR, p);
        count = p - (cyg_uint32) recv->buf;

        if (count > 0) {	// PDC has done work

	    /* flip the buffer */
	    recv->count = count;
	    recv->outp = recv->buf;
	    my_dev_pdc_chan->recv_buf_in_index = ++my_dev_pdc_chan->recv_buf_in_index
						      & MY_DEV_UART_RECV_BUF_MASK;

	    HAL_WRITE_UINT32 (base + MY_DEV_US_RPR,
                              (cyg_uint32) my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_in_index].buf);
	    HAL_WRITE_UINT32 (base + MY_DEV_US_RCR, (cyg_uint32) MY_DEV_UART_BUF_SIZE);

        }

        HAL_WRITE_UINT32 (base + MY_DEV_US_RTO, MY_DEV_RECV_UART_CHAR_TIMEOUT);
        HAL_WRITE_UINT32 (base + MY_DEV_US_CR, MY_DEV_US_CR_STTTO);
    }

    if ((ipr & imr & MY_DEV_US_IER_ENDTX)) {
	my_dev_pdc_serial_flip_send_buffer (my_dev_pdc_chan);
    }

    cyg_drv_interrupt_acknowledge (vector);
    cyg_drv_interrupt_unmask (vector);

    return CYG_ISR_CALL_DSR | CYG_ISR_HANDLED;
}

// Serial I/O - high level interrupt handler (DSR)
static void       
my_dev_pdc_serial_DSR (cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    serial_channel *chan = (serial_channel *) data;
    my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *) chan->dev_priv;

    // prevent re-entrancy problems....
    if (!cyg_mutex_lock(&my_dev_pdc_chan->DSR_mutex))
	return;

    // Check status
    if (my_dev_pdc_chan->tx_enabled) {

	int count, other;
	unsigned char *p;

	// Avoid conflicts w/ putc, ISR...
	if (cyg_semaphore_trywait(&my_dev_pdc_chan->flip_semaphore))  {

	    other = 1 - my_dev_pdc_chan->send_buf_which;
	    if (CYG_XMT_OK == (chan->callbacks->data_xmt_req) (chan,
                                                 MY_DEV_UART_BUF_SIZE - my_dev_pdc_chan->send_buf[other].count,
						 &count, &p)) {
		do {
		    if (count <= 0)
			break;
		    memcpy (my_dev_pdc_chan->send_buf[other].buf + my_dev_pdc_chan->send_buf[other].count,
                            p, count);
		    my_dev_pdc_chan->send_buf[other].count += count;

		    (chan->callbacks->data_xmt_done) (chan, count);
		    if (!(MY_DEV_UART_BUF_SIZE - my_dev_pdc_chan->send_buf[other].count))
			break;
		} while ( ((my_dev_pdc_chan->send_buf[other].count - MY_DEV_UART_BUF_SIZE) > 0)
			  && (CYG_RCV_OK == (chan->callbacks->data_xmt_req) (chan,
									MY_DEV_UART_BUF_SIZE -
									my_dev_pdc_chan->send_buf[other].count,
									&count, &p)) );
	    } else {
		(chan->callbacks->xmt_char) (chan);
	    }

	    // release semaphore
	    cyg_semaphore_post(&my_dev_pdc_chan->flip_semaphore);

	    // flip if needed:
	    my_dev_pdc_serial_flip_send_buffer (my_dev_pdc_chan);
	}
    }

    if (my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_out_index].count > 0) {

	// chars waiting from ISR
	struct flip_buffer *recv = &my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_out_index];
	int avail_count = recv->count;
	int count;
	unsigned char *p;

	if (CYG_RCV_OK == (chan->callbacks->data_rcv_req) (chan,
                                                           avail_count,
                                                           &count, &p)) {
	   do {
		// block transfer acceptable to upper layer:
		memcpy (p, recv->outp, count);
		recv->outp += count;
		recv->count -= count;
		(chan->callbacks->data_rcv_done) (chan, count);
		if (recv->count <= 0) {
	          my_dev_pdc_chan->recv_buf_out_index = ++my_dev_pdc_chan->recv_buf_out_index
							      & MY_DEV_UART_RECV_BUF_MASK;
		  recv = &my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_out_index];
	          if (my_dev_pdc_chan->recv_buf_out_index == my_dev_pdc_chan->recv_buf_in_index) {
		    break;
		  }
		}
		avail_count = recv->count;
	   } while (CYG_RCV_OK == (chan->callbacks->data_rcv_req) (chan,
                                                                   avail_count,
                                                                   &count, &p));
	}

	else { 
		// block transfer not acceptable to upper layer:
		while (1) {

			(chan->callbacks->rcv_char) (chan,
			                             (unsigned char)*recv->outp++);
			if (--recv->count <= 0) {
		          my_dev_pdc_chan->recv_buf_out_index = ++my_dev_pdc_chan->recv_buf_out_index
								      & MY_DEV_UART_RECV_BUF_MASK;
		          recv = &my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_out_index];
		          if (my_dev_pdc_chan->recv_buf_out_index == my_dev_pdc_chan->recv_buf_in_index)
			    break;
			}
		}
	}
    }

    cyg_mutex_unlock(&my_dev_pdc_chan->DSR_mutex);
}

#endif






[-- Attachment #3: Type: text/plain, Size: 146 bytes --]

-- 
Before posting, please read the FAQ: http://sources.redhat.com/fom/ecos
and search the list archive: http://sources.redhat.com/ml/ecos-discuss

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [ECOS] new at91 serial driver source
  2002-04-03 15:18 [ECOS] new at91 serial driver source Recker, John
@ 2002-04-03 22:25 ` Jonathan Larmour
  0 siblings, 0 replies; 2+ messages in thread
From: Jonathan Larmour @ 2002-04-03 22:25 UTC (permalink / raw)
  To: Recker, John; +Cc: 'ecos-discuss@sources.redhat.com'

"Recker, John" wrote:
> 
>   ---------------------------------------------------------------------------
>                  Name: serial.txt
>    serial.txt    Type: Plain Text (text/plain)

Cool! I suggest incorporating this alongside the existing driver rather
than replacing it. But before we do that we need a copyright assignment to
allow the code to be put in eCos at all as per
http://sources.redhat.com/fom/ecos?file=47 

If you could fill out the assignment form (and disclaimer from HP) at
http://sources.redhat.com/ecos/assign.html and mail it to us, that would be
brill.

Thanks,

Jifl
-- 
Red Hat, Rustat House, Clifton Road, Cambridge, UK. Tel: +44 (1223) 271062
Maybe this world is another planet's Hell -Aldous Huxley || Opinions==mine

-- 
Before posting, please read the FAQ: http://sources.redhat.com/fom/ecos
and search the list archive: http://sources.redhat.com/ml/ecos-discuss

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2002-04-04  6:25 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-04-03 15:18 [ECOS] new at91 serial driver source Recker, John
2002-04-03 22:25 ` Jonathan Larmour

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).