Mercurial > flash_v2
comparison packages/devs/serial/sh/scif/current/src/sh_scif_serial.c @ 130:eb9fd8c04db3 ecos-sw-2000-10-23
Merge from eCos master repository on 2000-10-23-17:01:37-BST
| author | jlarmour |
|---|---|
| date | Mon, 23 Oct 2000 17:10:57 +0000 |
| parents | 0c2b7be0d798 |
| children | e2d866e32dac |
comparison
equal
deleted
inserted
replaced
| 129:605a0fc6f385 | 130:eb9fd8c04db3 |
|---|---|
| 50 | 50 |
| 51 #include <cyg/io/io.h> | 51 #include <cyg/io/io.h> |
| 52 #include <cyg/hal/hal_intr.h> | 52 #include <cyg/hal/hal_intr.h> |
| 53 #include <cyg/io/devtab.h> | 53 #include <cyg/io/devtab.h> |
| 54 #include <cyg/infra/diag.h> | 54 #include <cyg/infra/diag.h> |
| 55 #include <cyg/infra/cyg_ass.h> | |
| 55 #include <cyg/io/serial.h> | 56 #include <cyg/io/serial.h> |
| 56 | 57 |
| 57 #include <cyg/hal/sh_regs.h> | 58 #include <cyg/hal/sh_regs.h> |
| 58 #include <cyg/hal/hal_cache.h> | 59 #include <cyg/hal/hal_cache.h> |
| 59 | 60 |
| 132 serial_rx_interrupt_handle, | 133 serial_rx_interrupt_handle, |
| 133 serial_tx_interrupt_handle; | 134 serial_tx_interrupt_handle; |
| 134 | 135 |
| 135 volatile bool tx_enabled; // expect tx _serial_ interrupts | 136 volatile bool tx_enabled; // expect tx _serial_ interrupts |
| 136 | 137 |
| 138 #if (CYGINT_IO_SERIAL_SH_SCIF_DMA > 0) | |
| 139 cyg_bool dma_enable; // Set if DMA mode | |
| 140 cyg_uint32 dma_xmt_cr_flags; // CR flags for DMA mode | |
| 141 CYG_WORD dma_xmt_int_num; // DMA xmt completion interrupt | |
| 142 CYG_ADDRWORD dma_xmt_base; // Base address of DMA channel | |
| 143 int dma_xmt_len; // length transferred by DMA | |
| 144 cyg_interrupt dma_xmt_interrupt; | |
| 145 cyg_handle_t dma_xmt_interrupt_handle; | |
| 146 volatile cyg_bool dma_xmt_running; // expect tx _dma_ interrupts | |
| 147 #endif | |
| 137 } sh3_scif_info; | 148 } sh3_scif_info; |
| 138 | 149 |
| 139 static bool sh3_scif_init(struct cyg_devtab_entry *tab); | 150 static bool sh3_scif_init(struct cyg_devtab_entry *tab); |
| 140 static bool sh3_scif_putc(serial_channel *chan, unsigned char c); | 151 static bool sh3_scif_putc(serial_channel *chan, unsigned char c); |
| 141 static Cyg_ErrNo sh3_scif_lookup(struct cyg_devtab_entry **tab, | 152 static Cyg_ErrNo sh3_scif_lookup(struct cyg_devtab_entry **tab, |
| 154 static void sh3_scif_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, | 165 static void sh3_scif_rx_DSR(cyg_vector_t vector, cyg_ucount32 count, |
| 155 cyg_addrword_t data); | 166 cyg_addrword_t data); |
| 156 static cyg_uint32 sh3_scif_er_ISR(cyg_vector_t vector, cyg_addrword_t data); | 167 static cyg_uint32 sh3_scif_er_ISR(cyg_vector_t vector, cyg_addrword_t data); |
| 157 static void sh3_scif_er_DSR(cyg_vector_t vector, cyg_ucount32 count, | 168 static void sh3_scif_er_DSR(cyg_vector_t vector, cyg_ucount32 count, |
| 158 cyg_addrword_t data); | 169 cyg_addrword_t data); |
| 170 | |
| 171 #if (CYGINT_IO_SERIAL_SH_SCIF_DMA > 0) | |
| 172 static cyg_uint32 sh3_dma_xmt_ISR(cyg_vector_t vector, cyg_addrword_t data); | |
| 173 static void sh3_dma_xmt_DSR(cyg_vector_t vector, cyg_ucount32 count, | |
| 174 cyg_addrword_t data); | |
| 175 #endif | |
| 159 | 176 |
| 160 static SERIAL_FUNS(sh3_scif_funs, | 177 static SERIAL_FUNS(sh3_scif_funs, |
| 161 sh3_scif_putc, | 178 sh3_scif_putc, |
| 162 sh3_scif_getc, | 179 sh3_scif_getc, |
| 163 sh3_scif_set_config, | 180 sh3_scif_set_config, |
| 280 &sh_chan->serial_er_interrupt); | 297 &sh_chan->serial_er_interrupt); |
| 281 cyg_drv_interrupt_attach(sh_chan->serial_er_interrupt_handle); | 298 cyg_drv_interrupt_attach(sh_chan->serial_er_interrupt_handle); |
| 282 // This unmasks both interrupt sources. | 299 // This unmasks both interrupt sources. |
| 283 cyg_drv_interrupt_unmask(sh_chan->rx_int_num); | 300 cyg_drv_interrupt_unmask(sh_chan->rx_int_num); |
| 284 } | 301 } |
| 302 | |
| 303 #if (CYGINT_IO_SERIAL_SH_SCIF_DMA > 0) | |
| 304 // Assign DMA channel and interrupt if requested | |
| 305 if (sh_chan->dma_enable) { | |
| 306 // FIXME: Need a cleaner way to assign DMA channels | |
| 307 static int dma_channel = 0; | |
| 308 sh_chan->dma_xmt_int_num = dma_channel+CYGNUM_HAL_INTERRUPT_DMAC_DEI0; | |
| 309 sh_chan->dma_xmt_base = (dma_channel*0x10)+CYGARC_REG_SAR0; | |
| 310 dma_channel++; | |
| 311 | |
| 312 // Enable the DMA engines. | |
| 313 HAL_WRITE_UINT16(CYGARC_REG_DMAOR, CYGARC_REG_DMAOR_DME); | |
| 314 | |
| 315 cyg_drv_interrupt_create(sh_chan->dma_xmt_int_num, | |
| 316 3, | |
| 317 (cyg_addrword_t)chan, // Data item passed to interrupt handler | |
| 318 sh3_dma_xmt_ISR, | |
| 319 sh3_dma_xmt_DSR, | |
| 320 &sh_chan->dma_xmt_interrupt_handle, | |
| 321 &sh_chan->dma_xmt_interrupt); | |
| 322 cyg_drv_interrupt_attach(sh_chan->dma_xmt_interrupt_handle); | |
| 323 cyg_drv_interrupt_unmask(sh_chan->dma_xmt_int_num); | |
| 324 } | |
| 325 sh_chan->dma_xmt_running = false; | |
| 326 #endif // CYGINT_IO_SERIAL_SH_SCIF_DMA | |
| 327 | |
| 285 sh3_scif_config_port(chan, &chan->config, true); | 328 sh3_scif_config_port(chan, &chan->config, true); |
| 286 return true; | 329 return true; |
| 287 } | 330 } |
| 288 | 331 |
| 289 // This routine is called when the device is "looked" up (i.e. attached) | 332 // This routine is called when the device is "looked" up (i.e. attached) |
| 363 } | 406 } |
| 364 break; | 407 break; |
| 365 #ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW | 408 #ifdef CYGOPT_IO_SERIAL_FLOW_CONTROL_HW |
| 366 case CYG_IO_SET_CONFIG_SERIAL_HW_RX_FLOW_THROTTLE: | 409 case CYG_IO_SET_CONFIG_SERIAL_HW_RX_FLOW_THROTTLE: |
| 367 { | 410 { |
| 368 cyg_uint8 _scfcr; | 411 // Control RX flow control by disabling/enabling RX interrupt. |
| 412 // When disabled, FIFO will fill up and clear RTS. | |
| 413 cyg_uint8 _scscr, mask = 0; | |
| 369 sh3_scif_info *ser_chan = (sh3_scif_info *)chan->dev_priv; | 414 sh3_scif_info *ser_chan = (sh3_scif_info *)chan->dev_priv; |
| 370 cyg_addrword_t base = ser_chan->ctrl_base; | 415 cyg_addrword_t base = ser_chan->ctrl_base; |
| 371 cyg_uint8 *f = (cyg_uint8 *)xbuf; | 416 cyg_uint8 *f = (cyg_uint8 *)xbuf; |
| 372 unsigned char mask=0; | |
| 373 if ( *len < *f ) | 417 if ( *len < *f ) |
| 374 return -EINVAL; | 418 return -EINVAL; |
| 375 | 419 |
| 376 if ( chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_RX ) | 420 if ( chan->config.flags & CYGNUM_SERIAL_FLOW_RTSCTS_RX ) |
| 377 mask |= CYGARC_REG_SCFCR1_MCE; | 421 mask |= CYGARC_REG_SCSCR2_RIE; |
| 378 HAL_READ_UINT8(base+SCIF_SCFCR, _scfcr); | 422 HAL_READ_UINT8(base+SCIF_SCSCR, _scscr); |
| 379 if (*f) // we should throttle | 423 if (*f) // we should throttle |
| 380 _scfcr &= ~mask; | 424 _scscr &= ~mask; |
| 381 else // we should no longer throttle | 425 else // we should no longer throttle |
| 382 _scfcr |= mask; | 426 _scscr |= mask; |
| 383 HAL_WRITE_UINT8(base+SCIF_SCFCR, _scfcr); | 427 HAL_WRITE_UINT8(base+SCIF_SCSCR, _scscr); |
| 384 } | 428 } |
| 385 break; | 429 break; |
| 386 case CYG_IO_SET_CONFIG_SERIAL_HW_FLOW_CONFIG: | 430 case CYG_IO_SET_CONFIG_SERIAL_HW_FLOW_CONFIG: |
| 387 { | 431 { |
| 388 // Clear DSR/DTR flag as it's not supported. | 432 // Handle CTS/RTS flag |
| 389 if (chan->config.flags & | 433 if ( chan->config.flags & |
| 390 (CYGNUM_SERIAL_FLOW_DSRDTR_RX|CYGNUM_SERIAL_FLOW_DSRDTR_TX)) { | 434 (CYGNUM_SERIAL_FLOW_RTSCTS_RX | CYGNUM_SERIAL_FLOW_RTSCTS_TX )){ |
| 391 chan->config.flags &= ~(CYGNUM_SERIAL_FLOW_DSRDTR_RX| | 435 cyg_uint8 _scfcr; |
| 392 CYGNUM_SERIAL_FLOW_DSRDTR_TX); | 436 sh3_scif_info *ser_chan = (sh3_scif_info *)chan->dev_priv; |
| 393 return -EINVAL; | 437 cyg_addrword_t base = ser_chan->ctrl_base; |
| 394 } | 438 cyg_uint8 *f = (cyg_uint8 *)xbuf; |
| 439 | |
| 440 HAL_READ_UINT8(base+SCIF_SCFCR, _scfcr); | |
| 441 if (*f) // enable RTS/CTS flow control | |
| 442 _scfcr |= CYGARC_REG_SCFCR1_MCE; | |
| 443 else // disable RTS/CTS flow control | |
| 444 _scfcr &= ~CYGARC_REG_SCFCR1_MCE; | |
| 445 HAL_WRITE_UINT8(base+SCIF_SCFCR, _scfcr); | |
| 446 } | |
| 447 // Clear DSR/DTR flag as it's not supported. | |
| 448 if (chan->config.flags & | |
| 449 (CYGNUM_SERIAL_FLOW_DSRDTR_RX|CYGNUM_SERIAL_FLOW_DSRDTR_TX)) { | |
| 450 chan->config.flags &= ~(CYGNUM_SERIAL_FLOW_DSRDTR_RX| | |
| 451 CYGNUM_SERIAL_FLOW_DSRDTR_TX); | |
| 452 return -EINVAL; | |
| 453 } | |
| 395 } | 454 } |
| 396 break; | 455 break; |
| 397 #endif | 456 #endif |
| 398 default: | 457 default: |
| 399 return -EINVAL; | 458 return -EINVAL; |
| 400 } | 459 } |
| 401 return ENOERR; | 460 return ENOERR; |
| 402 } | 461 } |
| 403 | 462 |
| 404 | 463 |
| 464 #if (CYGINT_IO_SERIAL_SH_SCIF_DMA > 0) | |
| 465 | |
| 466 // Must be called with serial interrupts disabled | |
| 467 static xmt_req_reply_t | |
| 468 sh3_scif_start_dma_xmt(serial_channel *chan) | |
| 469 { | |
| 470 int chars_avail; | |
| 471 unsigned char* chars; | |
| 472 xmt_req_reply_t res; | |
| 473 | |
| 474 // We can transfer the full buffer - ask how much to transfer | |
| 475 res = (chan->callbacks->data_xmt_req)(chan, chan->out_cbuf.len, | |
| 476 &chars_avail, &chars); | |
| 477 if (CYG_XMT_OK == res) { | |
| 478 sh3_scif_info *sh_chan = (sh3_scif_info *)chan->dev_priv; | |
| 479 cyg_uint32 dma_base = sh_chan->dma_xmt_base; | |
| 480 cyg_uint32 scr; | |
| 481 | |
| 482 // Save the length so it can be used in the DMA DSR | |
| 483 sh_chan->dma_xmt_len = chars_avail; | |
| 484 | |
| 485 // Flush cache for the area | |
| 486 HAL_DCACHE_FLUSH((cyg_haladdress)chars, chars_avail); | |
| 487 | |
| 488 // Program DMA | |
| 489 HAL_WRITE_UINT32(dma_base+CYGARC_REG_CHCR, 0); // disable and clear | |
| 490 HAL_WRITE_UINT32(dma_base+CYGARC_REG_SAR, (cyg_uint32)chars); | |
| 491 HAL_WRITE_UINT32(dma_base+CYGARC_REG_DAR, | |
| 492 (sh_chan->ctrl_base+SCIF_SCFTDR) & 0x0fffffff); | |
| 493 HAL_WRITE_UINT32(dma_base+CYGARC_REG_DMATCR, chars_avail); | |
| 494 // Source increments, dest static, byte transfer, enable | |
| 495 // interrupt on completion. | |
| 496 HAL_WRITE_UINT32(dma_base+CYGARC_REG_CHCR, | |
| 497 sh_chan->dma_xmt_cr_flags | CYGARC_REG_CHCR_SM0 \ | |
| 498 | CYGARC_REG_CHCR_IE | CYGARC_REG_CHCR_DE); | |
| 499 | |
| 500 // Enable serial interrupts | |
| 501 HAL_READ_UINT8(sh_chan->ctrl_base+SCIF_SCSCR, scr); | |
| 502 scr |= CYGARC_REG_SCSCR2_TIE; | |
| 503 HAL_WRITE_UINT8(sh_chan->ctrl_base+SCIF_SCSCR, scr); | |
| 504 } | |
| 505 | |
| 506 return res; | |
| 507 } | |
| 508 | |
| 509 // must be called with serial interrupts masked | |
| 510 static void | |
| 511 sh3_scif_stop_dma_xmt(serial_channel *chan) | |
| 512 { | |
| 513 sh3_scif_info *sh_chan = (sh3_scif_info *)chan->dev_priv; | |
| 514 cyg_uint32 dma_base = sh_chan->dma_xmt_base; | |
| 515 cyg_uint32 cr; | |
| 516 | |
| 517 // Disable DMA engine and interrupt enable flag. Should be safe | |
| 518 // to do since it's triggered by the serial interrupt which has | |
| 519 // already been disabled. | |
| 520 HAL_READ_UINT32(dma_base+CYGARC_REG_CHCR, cr); | |
| 521 cr &= ~(CYGARC_REG_CHCR_IE | CYGARC_REG_CHCR_DE); | |
| 522 HAL_WRITE_UINT32(dma_base+CYGARC_REG_CHCR, cr); | |
| 523 | |
| 524 // Did transfer complete? | |
| 525 HAL_READ_UINT32(dma_base+CYGARC_REG_CHCR, cr); | |
| 526 if (0 == (cr & CYGARC_REG_CHCR_TE)) { | |
| 527 // Transfer incomplete. Report actually transferred amount of data | |
| 528 // back to the serial driver. | |
| 529 int chars_left; | |
| 530 HAL_READ_UINT32(dma_base+CYGARC_REG_DMATCR, chars_left); | |
| 531 CYG_ASSERT(chars_left > 0, "DMA incomplete, but no data left"); | |
| 532 CYG_ASSERT(chars_left <= sh_chan->dma_xmt_len, | |
| 533 "More data remaining than was attempted transferred"); | |
| 534 | |
| 535 (chan->callbacks->data_xmt_done)(chan, | |
| 536 sh_chan->dma_xmt_len - chars_left); | |
| 537 } | |
| 538 | |
| 539 #ifdef CYGDBG_USE_ASSERTS | |
| 540 { | |
| 541 cyg_uint32 dmaor; | |
| 542 HAL_READ_UINT32(CYGARC_REG_DMAOR, dmaor); | |
| 543 CYG_ASSERT(0== (dmaor & (CYGARC_REG_DMAOR_AE | CYGARC_REG_DMAOR_NMIF)), | |
| 544 "DMA error"); | |
| 545 } | |
| 546 #endif | |
| 547 | |
| 548 // The DMA engine is free again. | |
| 549 sh_chan->dma_xmt_running = false; | |
| 550 } | |
| 551 | |
| 552 // Serial xmt DMA completion interrupt handler (ISR) | |
| 553 static cyg_uint32 | |
| 554 sh3_dma_xmt_ISR(cyg_vector_t vector, cyg_addrword_t data) | |
| 555 { | |
| 556 serial_channel *chan = (serial_channel *)data; | |
| 557 sh3_scif_info *sh_chan = (sh3_scif_info *)chan->dev_priv; | |
| 558 cyg_uint32 _cr; | |
| 559 | |
| 560 // mask serial interrupt | |
| 561 HAL_READ_UINT8(sh_chan->ctrl_base+SCIF_SCSCR, _cr); | |
| 562 _cr &= ~CYGARC_REG_SCSCR2_TIE; // Disable xmit interrupt | |
| 563 HAL_WRITE_UINT8(sh_chan->ctrl_base+SCIF_SCSCR, _cr); | |
| 564 | |
| 565 // mask DMA interrupt and disable engine | |
| 566 HAL_READ_UINT32(sh_chan->dma_xmt_base+CYGARC_REG_CHCR, _cr); | |
| 567 _cr &= ~(CYGARC_REG_CHCR_IE | CYGARC_REG_CHCR_DE); | |
| 568 HAL_WRITE_UINT32(sh_chan->dma_xmt_base+CYGARC_REG_CHCR, _cr); | |
| 569 | |
| 570 return CYG_ISR_CALL_DSR; // Cause DSR to be run | |
| 571 } | |
| 572 | |
| 573 // Serial xmt DMA completion interrupt handler (DSR) | |
| 574 static void | |
| 575 sh3_dma_xmt_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) | |
| 576 { | |
| 577 serial_channel *chan = (serial_channel *)data; | |
| 578 sh3_scif_info *sh_chan = (sh3_scif_info *)chan->dev_priv; | |
| 579 | |
| 580 (chan->callbacks->data_xmt_done)(chan, sh_chan->dma_xmt_len); | |
| 581 | |
| 582 // Try to load the engine again. | |
| 583 sh_chan->dma_xmt_running = | |
| 584 (CYG_XMT_OK == sh3_scif_start_dma_xmt(chan)) ? true : false; | |
| 585 } | |
| 586 #endif // CYGINT_IO_SERIAL_SH_SCIF_DMA | |
| 587 | |
| 405 | 588 |
| 406 // Enable the transmitter on the device | 589 // Enable the transmitter on the device |
| 407 static void | 590 static void |
| 408 sh3_scif_start_xmit(serial_channel *chan) | 591 sh3_scif_start_xmit(serial_channel *chan) |
| 409 { | 592 { |
| 411 sh3_scif_info *sh_chan = (sh3_scif_info *)chan->dev_priv; | 594 sh3_scif_info *sh_chan = (sh3_scif_info *)chan->dev_priv; |
| 412 xmt_req_reply_t _block_status = CYG_XMT_DISABLED; | 595 xmt_req_reply_t _block_status = CYG_XMT_DISABLED; |
| 413 | 596 |
| 414 if (sh_chan->tx_enabled) | 597 if (sh_chan->tx_enabled) |
| 415 return; | 598 return; |
| 599 | |
| 600 #if (CYGINT_IO_SERIAL_SH_SCIF_DMA > 0) | |
| 601 // Check if the engine is already running. If so, return. Note | |
| 602 // that there will never be a race on this flag - the caller of | |
| 603 // this function is respecting a per-channel lock. | |
| 604 if (sh_chan->dma_xmt_running) | |
| 605 return; | |
| 606 // If the channel uses DMA, try to start a DMA job for this - | |
| 607 // but handle the case where the job doesn't start by falling | |
| 608 // back to the FIFO/interrupt based code. | |
| 609 if (sh_chan->dma_enable) { | |
| 610 _block_status = sh3_scif_start_dma_xmt(chan); | |
| 611 CYG_ASSERT(_block_status != CYG_XMT_EMPTY, | |
| 612 "start_xmit called with empty buffers!"); | |
| 613 sh_chan->dma_xmt_running = | |
| 614 (CYG_XMT_OK == _block_status) ? true : false; | |
| 615 } | |
| 616 #endif // CYGINT_IO_SERIAL_SH_SCIF_DMA | |
| 416 | 617 |
| 417 if (CYG_XMT_DISABLED == _block_status) { | 618 if (CYG_XMT_DISABLED == _block_status) { |
| 418 // Mask interrupts while changing the CR since a rx | 619 // Mask interrupts while changing the CR since a rx |
| 419 // interrupt or another thread doing the same in the | 620 // interrupt or another thread doing the same in the |
| 420 // middle of this would result in a bad CR state. | 621 // middle of this would result in a bad CR state. |
| 445 _scr &= ~CYGARC_REG_SCSCR2_TIE; // Disable xmit interrupt | 646 _scr &= ~CYGARC_REG_SCSCR2_TIE; // Disable xmit interrupt |
| 446 HAL_WRITE_UINT8(sh_chan->ctrl_base+SCIF_SCSCR, _scr); | 647 HAL_WRITE_UINT8(sh_chan->ctrl_base+SCIF_SCSCR, _scr); |
| 447 } | 648 } |
| 448 cyg_drv_isr_unlock(); | 649 cyg_drv_isr_unlock(); |
| 449 | 650 |
| 651 #if (CYGINT_IO_SERIAL_SH_SCIF_DMA > 0) | |
| 652 // If the channel uses DMA, stop the DMA engine. | |
| 653 if (sh_chan->dma_xmt_running) | |
| 654 sh3_scif_stop_dma_xmt(chan); | |
| 655 else // dangling else! | |
| 656 #endif // CYGINT_IO_SERIAL_SH_SCIF_DMA | |
| 450 sh_chan->tx_enabled = false; | 657 sh_chan->tx_enabled = false; |
| 451 } | 658 } |
| 452 | 659 |
| 453 // Serial I/O - low level tx interrupt handler (ISR) | 660 // Serial I/O - low level tx interrupt handler (ISR) |
| 454 static cyg_uint32 | 661 static cyg_uint32 |
