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 + -
显示快捷键?