sh_scif_serial.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,094 行 · 第 1/3 页
C
1,094 行
chan->config = *new_config;
}
return true;
}
// Function to initialize the device. Called at bootstrap time.
static bool
sh_scif_init(struct cyg_devtab_entry *tab)
{
serial_channel *chan = (serial_channel *)tab->priv;
sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv;
#ifdef CYGDBG_IO_INIT
diag_printf("SH SERIAL init - dev: %x.%d\n",
sh_chan->ctrl_base, sh_chan->rx_int_num);
#endif
// Really only required for interrupt driven devices
(chan->callbacks->serial_init)(chan);
if (chan->out_cbuf.len != 0) {
cyg_drv_interrupt_create(sh_chan->tx_int_num,
3,
(cyg_addrword_t)chan, // Data item passed to interrupt handler
sh_scif_tx_ISR,
sh_scif_tx_DSR,
&sh_chan->serial_tx_interrupt_handle,
&sh_chan->serial_tx_interrupt);
cyg_drv_interrupt_attach(sh_chan->serial_tx_interrupt_handle);
cyg_drv_interrupt_unmask(sh_chan->tx_int_num);
sh_chan->tx_enabled = false;
}
if (chan->in_cbuf.len != 0) {
// Receive interrupt
cyg_drv_interrupt_create(sh_chan->rx_int_num,
3,
(cyg_addrword_t)chan, // Data item passed to interrupt handler
sh_scif_rx_ISR,
sh_scif_rx_DSR,
&sh_chan->serial_rx_interrupt_handle,
&sh_chan->serial_rx_interrupt);
cyg_drv_interrupt_attach(sh_chan->serial_rx_interrupt_handle);
// Receive error interrupt
cyg_drv_interrupt_create(sh_chan->er_int_num,
3,
(cyg_addrword_t)chan, // Data item passed to interrupt handler
sh_scif_er_ISR,
sh_scif_er_DSR,
&sh_chan->serial_er_interrupt_handle,
&sh_chan->serial_er_interrupt);
cyg_drv_interrupt_attach(sh_chan->serial_er_interrupt_handle);
#ifdef CYGINT_IO_SERIAL_SH_SCIF_BR_INTERRUPT
// Break error interrupt
cyg_drv_interrupt_create(sh_chan->br_int_num,
3,
(cyg_addrword_t)chan, // Data item passed to interrupt handler
sh_scif_er_ISR,
sh_scif_er_DSR,
&sh_chan->serial_br_interrupt_handle,
&sh_chan->serial_br_interrupt);
cyg_drv_interrupt_attach(sh_chan->serial_br_interrupt_handle);
#endif
// This unmasks all interrupt sources.
cyg_drv_interrupt_unmask(sh_chan->rx_int_num);
}
#ifdef CYGINT_IO_SERIAL_SH_SCIF_DMA
// Assign DMA channel and interrupt if requested
if (sh_chan->dma_enable) {
// FIXME: Need a cleaner way to assign DMA channels
static int dma_channel = 0;
#if defined(CYGPKG_HAL_SH_SH2)
sh_chan->dma_xmt_int_num = dma_channel+CYGNUM_HAL_INTERRUPT_DMAC0_TE;
#elif defined(CYGPKG_HAL_SH_SH3)
sh_chan->dma_xmt_int_num = dma_channel+CYGNUM_HAL_INTERRUPT_DMAC_DEI0;
#else
# error "No interrupt defined for variant"
#endif
sh_chan->dma_xmt_base = (dma_channel*0x10)+CYGARC_REG_SAR0;
dma_channel++;
// Enable the DMA engines.
HAL_WRITE_UINT16(CYGARC_REG_DMAOR, CYGARC_REG_DMAOR_DME);
cyg_drv_interrupt_create(sh_chan->dma_xmt_int_num,
3,
(cyg_addrword_t)chan, // Data item passed to interrupt handler
sh_dma_xmt_ISR,
sh_dma_xmt_DSR,
&sh_chan->dma_xmt_interrupt_handle,
&sh_chan->dma_xmt_interrupt);
cyg_drv_interrupt_attach(sh_chan->dma_xmt_interrupt_handle);
cyg_drv_interrupt_unmask(sh_chan->dma_xmt_int_num);
}
sh_chan->dma_xmt_running = false;
#endif // CYGINT_IO_SERIAL_SH_SCIF_DMA
sh_scif_config_port(chan, &chan->config, true);
return true;
}
// This routine is called when the device is "looked" up (i.e. attached)
static Cyg_ErrNo
sh_scif_lookup(struct cyg_devtab_entry **tab,
struct cyg_devtab_entry *sub_tab,
const char *name)
{
serial_channel *chan = (serial_channel *)(*tab)->priv;
// Really only required for interrupt driven devices
(chan->callbacks->serial_init)(chan);
return ENOERR;
}
// Send a character to the device output buffer.
// Return 'true' if character is sent to device
static bool
sh_scif_putc(serial_channel *chan, unsigned char c)
{
cyg_uint16 _fdr, _sr;
sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv;
HAL_READ_UINT16(sh_chan->ctrl_base+SCIF_SCFDR, _fdr);
if (((_fdr & CYGARC_REG_SCIF_SCFDR_TCOUNT_MASK) >> CYGARC_REG_SCIF_SCFDR_TCOUNT_shift) < 15) {
// Transmit FIFO has room for another char
HAL_WRITE_UINT8(sh_chan->ctrl_base+SCIF_SCFTDR, c);
// Clear FIFO-empty/transmit end flags (read back sr first)
HAL_READ_UINT16(sh_chan->ctrl_base+SCIF_SCSSR, _sr);
HAL_WRITE_UINT16(sh_chan->ctrl_base+SCIF_SCSSR,
CYGARC_REG_SCIF_SCSSR_CLEARMASK & ~CYGARC_REG_SCIF_SCSSR_TDFE);
return true;
} else {
// No space
return false;
}
}
// Fetch a character from the device input buffer, waiting if necessary
// Note: Input is running wo FIFO enabled, so the counter is not checked here.
static unsigned char
sh_scif_getc(serial_channel *chan)
{
sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv;
unsigned char c;
cyg_uint16 _sr;
do {
HAL_READ_UINT16(sh_chan->ctrl_base+SCIF_SCSSR, _sr);
} while ((_sr & CYGARC_REG_SCIF_SCSSR_RDF) == 0);
HAL_READ_UINT8(sh_chan->ctrl_base+SCIF_SCFRDR, c);
// Clear buffer full flag (read back first)
HAL_READ_UINT16(sh_chan->ctrl_base+SCIF_SCSSR, _sr);
HAL_WRITE_UINT16(sh_chan->ctrl_base+SCIF_SCSSR,
CYGARC_REG_SCIF_SCSSR_CLEARMASK & ~CYGARC_REG_SCIF_SCSSR_RDF);
return c;
}
// Set up the device characteristics; baud rate, etc.
static Cyg_ErrNo
sh_scif_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 != sh_scif_config_port(chan, config, false) )
return -EINVAL;
}
break;
#ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW
case CYG_IO_SET_CONFIG_SERIAL_HW_RX_FLOW_THROTTLE:
{
sh_scif_info *ser_chan = (sh_scif_info *)chan->dev_priv;
cyg_addrword_t base = ser_chan->ctrl_base;
cyg_uint32 *f = (cyg_uint32 *)xbuf;
if ( *len < *f )
return -EINVAL;
if ( chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_RX ) {
// Control RX RTC/CTS flow control by disabling/enabling
// RX interrupt. When disabled, FIFO will fill up and
// clear RTS.
cyg_uint8 _scscr;
HAL_READ(base+SCIF_SCSCR, _scscr);
if (*f) // we should throttle
_scscr &= ~CYGARC_REG_SCIF_SCSCR_RIE;
else // we should no longer throttle
_scscr |= CYGARC_REG_SCIF_SCSCR_RIE;
HAL_WRITE(base+SCIF_SCSCR, _scscr);
}
#ifdef CYGHWR_SH_SCIF_FLOW_DSRDTR
if ( chan->config.flags & CYGNUM_SERIAL_FLOW_DSRDTR_RX ) {
// Control RX DSR/DTR flow control via platform specific macro
CYGHWR_SH_SCIF_FLOW_DSRDTR_RX(chan, *f);
}
#endif
}
break;
case CYG_IO_SET_CONFIG_SERIAL_HW_FLOW_CONFIG:
{
// Handle CTS/RTS flag
if ( chan->config.flags &
(CYGNUM_SERIAL_FLOW_RTSCTS_RX | CYGNUM_SERIAL_FLOW_RTSCTS_TX )){
cyg_uint8 _scfcr;
sh_scif_info *ser_chan = (sh_scif_info *)chan->dev_priv;
cyg_addrword_t base = ser_chan->ctrl_base;
cyg_uint8 *f = (cyg_uint8 *)xbuf;
HAL_READ(base+SCIF_SCFCR, _scfcr);
if (*f) // enable RTS/CTS flow control
_scfcr |= CYGARC_REG_SCIF_SCFCR_MCE;
else // disable RTS/CTS flow control
_scfcr &= ~CYGARC_REG_SCIF_SCFCR_MCE;
HAL_WRITE(base+SCIF_SCFCR, _scfcr);
}
#ifndef CYGHWR_SH_SCIF_FLOW_DSRDTR
// Clear DSR/DTR flag as it's not supported.
if (chan->config.flags &
(CYGNUM_SERIAL_FLOW_DSRDTR_RX|CYGNUM_SERIAL_FLOW_DSRDTR_TX)) {
chan->config.flags &= ~(CYGNUM_SERIAL_FLOW_DSRDTR_RX|
CYGNUM_SERIAL_FLOW_DSRDTR_TX);
return -EINVAL;
}
#else
return CYGHWR_SH_SCIF_FLOW_DSRDTR_CONFIG(chan);
#endif
}
break;
#endif
CYGPRI_DEVS_SH_SCIF_SET_CONFIG_PLF
default:
return -EINVAL;
}
return ENOERR;
}
#ifdef CYGINT_IO_SERIAL_SH_SCIF_DMA
// Must be called with serial interrupts disabled
static xmt_req_reply_t
sh_scif_start_dma_xmt(serial_channel *chan)
{
int chars_avail;
unsigned char* chars;
xmt_req_reply_t res;
// We can transfer the full buffer - ask how much to transfer
res = (chan->callbacks->data_xmt_req)(chan, chan->out_cbuf.len,
&chars_avail, &chars);
if (CYG_XMT_OK == res) {
sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv;
cyg_uint32 dma_base = sh_chan->dma_xmt_base;
cyg_uint32 scr;
// Save the length so it can be used in the DMA DSR
sh_chan->dma_xmt_len = chars_avail;
// Flush cache for the area
HAL_DCACHE_FLUSH((cyg_haladdress)chars, chars_avail);
// Program DMA
HAL_WRITE_UINT32(dma_base+CYGARC_REG_CHCR, 0); // disable and clear
HAL_WRITE_UINT32(dma_base+CYGARC_REG_SAR, (cyg_uint32)chars);
HAL_WRITE_UINT32(dma_base+CYGARC_REG_DAR,
(sh_chan->ctrl_base+SCIF_SCFTDR) & 0x0fffffff);
HAL_WRITE_UINT32(dma_base+CYGARC_REG_DMATCR, chars_avail);
// Source increments, dest static, byte transfer, enable
// interrupt on completion.
HAL_WRITE_UINT32(dma_base+CYGARC_REG_CHCR,
sh_chan->dma_xmt_cr_flags | CYGARC_REG_CHCR_SM0 \
| CYGARC_REG_CHCR_IE | CYGARC_REG_CHCR_DE);
// Enable serial interrupts
HAL_READ(sh_chan->ctrl_base+SCIF_SCSCR, scr);
scr |= CYGARC_REG_SCIF_SCSCR_TIE;
HAL_WRITE(sh_chan->ctrl_base+SCIF_SCSCR, scr);
}
return res;
}
// must be called with serial interrupts masked
static void
sh_scif_stop_dma_xmt(serial_channel *chan)
{
sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv;
cyg_uint32 dma_base = sh_chan->dma_xmt_base;
cyg_uint32 cr;
// Disable DMA engine and interrupt enable flag. Should be safe
// to do since it's triggered by the serial interrupt which has
// already been disabled.
HAL_READ_UINT32(dma_base+CYGARC_REG_CHCR, cr);
cr &= ~(CYGARC_REG_CHCR_IE | CYGARC_REG_CHCR_DE);
HAL_WRITE_UINT32(dma_base+CYGARC_REG_CHCR, cr);
// Did transfer complete?
HAL_READ_UINT32(dma_base+CYGARC_REG_CHCR, cr);
if (0 == (cr & CYGARC_REG_CHCR_TE)) {
// Transfer incomplete. Report actually transferred amount of data
// back to the serial driver.
int chars_left;
HAL_READ_UINT32(dma_base+CYGARC_REG_DMATCR, chars_left);
CYG_ASSERT(chars_left > 0, "DMA incomplete, but no data left");
CYG_ASSERT(chars_left <= sh_chan->dma_xmt_len,
"More data remaining than was attempted transferred");
(chan->callbacks->data_xmt_done)(chan,
sh_chan->dma_xmt_len - chars_left);
}
#ifdef CYGDBG_USE_ASSERTS
{
cyg_uint32 dmaor;
HAL_READ_UINT32(CYGARC_REG_DMAOR, dmaor);
CYG_ASSERT(0== (dmaor & (CYGARC_REG_DMAOR_AE | CYGARC_REG_DMAOR_NMIF)),
"DMA error");
}
#endif
// The DMA engine is free again.
sh_chan->dma_xmt_running = false;
}
// Serial xmt DMA completion interrupt handler (ISR)
static cyg_uint32
sh_dma_xmt_ISR(cyg_vector_t vector, cyg_addrword_t data)
{
serial_channel *chan = (serial_channel *)data;
sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv;
cyg_uint32 _cr;
// mask serial interrupt
HAL_READ(sh_chan->ctrl_base+SCIF_SCSCR, _cr);
_cr &= ~CYGARC_REG_SCIF_SCSCR_TIE; // Disable xmit interrupt
HAL_WRITE(sh_chan->ctrl_base+SCIF_SCSCR, _cr);
// mask DMA interrupt and disable engine
HAL_READ_UINT32(sh_chan->dma_xmt_base+CYGARC_REG_CHCR, _cr);
_cr &= ~(CYGARC_REG_CHCR_IE | CYGARC_REG_CHCR_DE);
HAL_WRITE_UINT32(sh_chan->dma_xmt_base+CYGARC_REG_CHCR, _cr);
return CYG_ISR_CALL_DSR; // Cause DSR to be run
}
// Serial xmt DMA completion interrupt handler (DSR)
static void
sh_dma_xmt_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
serial_channel *chan = (serial_channel *)data;
sh_scif_info *sh_chan = (sh_scif_info *)chan->dev_priv;
(chan->callbacks->data_xmt_done)(chan, sh_chan->dma_xmt_len);
// Try to load the engine again.
sh_chan->dma_xmt_running =
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?