* [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).