LOGIN   :::   RECOVER PASS   :::   GET ACCOUNT    
Browse
  • Projects
  • Code (CVS)
  • Forums
  • News
  • Articles
  • Polls
  •  
    OpenCores
  • FAQ
  • CVS HowTo
  • Mission
  • Media
  • Tools
  • Advertise
  • Mirrors
  • Logos
  • Contact us
  • Find Resources
  • Job Opportunity
  •  
    Tools
  • Search
      
  • Download Cores (CVSGet)
  •  
    More
  • Wishbone
  • Perlilog
  • EDA tools
  • OpenTech CD
  •  
    Navigation: All forums > Openrisc > Message List > Message Post

    Message

    Reply | Reply all
    Date Prev | Date Next | Thread Prev | Thread Next Date Index | Thread Index

    From: György 'nog' Jeney<nog@s...>
    Date: Sun May 29 17:53:25 CEST 2005
    Subject: [openrisc] [or1ksim #101] UART: Move interrupt handling out of uart_clock16 [3/6]
    Top
    Hi,

    Changelog says it all.

    ChangeLog:
    * Move interrupt handling out of uart_clock16.

    nog.
    -------------- next part --------------
    --- ../or1ksim-patch/peripheral/16450.c 2005-05-29 15:25:02.000000000 +0200
    +++ peripheral/16450.c 2005-05-29 15:36:46.000000000 +0200
    @@ -58,6 +58,7 @@ void uart_check_vapi(void *dat);
    void uart_check_char(void *dat);
    static void uart_sched_recv_check(struct dev_16450 *uart);
    static void uart_vapi_cmd(void *dat);
    +static void uart_clear_int(struct dev_16450 *uart, int intr);
    void uart_tx_send(void *dat);

    /* Number of clock cycles (one clock cycle is one call to the uart_clock())
    @@ -84,6 +85,181 @@ static unsigned long char_clks(int dll,
    return (char_clks * bauds_per_char) >> 1;
    }

    +/*---------------------------------------------------[ Interrupt handling ]---*/
    +/* Signals the specified interrupt. If a higher priority interrupt is already
    + * pending, do nothing */
    +static void uart_int_msi(void *dat)
    +{
    + struct dev_16450 *uart = dat;
    +
    + uart->istat.ints |= 1 << UART_IIR_MSI;
    +
    + if(!(uart->regs.ier & UART_IER_MSI))
    + return;
    +
    + if(uart->regs.iir & UART_IIR_NO_INT) {
    + TRACE("Raiseing modem status interrupt\n");
    + uart_clear_int(uart, uart->regs.iir);
    +
    + uart->regs.iir = UART_IIR_MSI;
    + SCHED_ADD(uart_int_msi, dat, UART_CLOCK_DIVIDER);
    + report_interrupt(uart->irq);
    + }
    +}
    +
    +static void uart_int_thri(void *dat)
    +{
    + struct dev_16450 *uart = dat;
    +
    + uart->istat.ints |= 1 << UART_IIR_THRI;
    +
    + if(!(uart->regs.ier & UART_IER_THRI))
    + return;
    +
    + if((uart->regs.iir & UART_IIR_NO_INT) || (uart->regs.iir == UART_IIR_MSI)) {
    + TRACE("Raiseing transmitter holding register interrupt\n");
    + uart_clear_int(uart, uart->regs.iir);
    +
    + uart->regs.iir = UART_IIR_THRI;
    + SCHED_ADD(uart_int_thri, dat, UART_CLOCK_DIVIDER);
    + report_interrupt(uart->irq);
    + }
    +}
    +
    +static void uart_int_cti(void *dat)
    +{
    + struct dev_16450 *uart = dat;
    +
    + uart->istat.ints |= 1 << UART_IIR_CTI;
    +
    + if(!(uart->regs.ier & UART_IER_RDI))
    + return;
    +
    + if((uart->regs.iir != UART_IIR_RLSI) && (uart->regs.iir != UART_IIR_RDI)) {
    + TRACE("Raiseing character timeout interrupt\n");
    + uart_clear_int(uart, uart->regs.iir);
    +
    + uart->regs.iir = UART_IIR_CTI;
    + SCHED_ADD(uart_int_cti, dat, UART_CLOCK_DIVIDER);
    + report_interrupt(uart->irq);
    + }
    +}
    +
    +static void uart_int_rdi(void *dat)
    +{
    + struct dev_16450 *uart = dat;
    +
    + uart->istat.ints |= 1 << UART_IIR_RDI;
    +
    + if(!(uart->regs.ier & UART_IER_RDI))
    + return;
    +
    + if(uart->regs.iir != UART_IIR_RLSI) {
    + TRACE("Raiseing receiver data interrupt\n");
    + uart_clear_int(uart, uart->regs.iir);
    +
    + uart->regs.iir = UART_IIR_RDI;
    + SCHED_ADD(uart_int_rdi, dat, UART_CLOCK_DIVIDER);
    + report_interrupt(uart->irq);
    + }
    +}
    +
    +static void uart_int_rlsi(void *dat)
    +{
    + struct dev_16450 *uart = dat; + + uart->istat.ints |= 1 << UART_IIR_RLSI; + + if(!(uart->regs.ier & UART_IER_RLSI)) + return; + + uart_clear_int(uart, uart->regs.iir); + + TRACE("Raiseing receiver line status interrupt\n"); + + /* Highest priority interrupt */ + uart->regs.iir = UART_IIR_RLSI; + SCHED_ADD(uart_int_rlsi, dat, UART_CLOCK_DIVIDER); + report_interrupt(uart->irq); +} + +/* Checks to see if an RLSI interrupt is due and schedules one if need be */ +static void uart_check_rlsi(void *dat) +{ + struct dev_16450 *uart = dat; + + if(uart->regs.lsr & (UART_LSR_OVRRUN | UART_LSR_PARITY | UART_LSR_FRAME | + UART_LSR_BREAK)) + uart_int_rlsi(uart); +} + +/* Checks to see if an RDI interrupt is due and schedules one if need be */ +static void uart_check_rdi(void *dat) +{ + struct dev_16450 *uart = dat; + + if(uart->istat.rxbuf_full >= UART_FIFO_TRIGGER(uart->regs.fcr >> 6)) + uart_int_rdi(uart); +} + +/* Raises the next highest priority interrupt */ +static void uart_next_int(void *dat) +{ + struct dev_16450 *uart = dat; + + /* Interrupt detection in proper priority order. */ + if(uart->istat.ints & (1 << UART_IIR_RLSI)) + uart_int_rlsi(uart); + else if(uart->istat.ints & (1 << UART_IIR_RDI)) + uart_int_rdi(uart); + else if(uart->istat.ints & (1 << UART_IIR_CTI)) + uart_int_cti(uart); + else if(uart->istat.ints & (1 << UART_IIR_THRI)) + uart_int_thri(uart); + else if(uart->istat.ints & (1 << UART_IIR_MSI)) + uart_int_msi(uart); +} + +/* Clears potentially pending interrupts */ +static void uart_clear_int(struct dev_16450 *uart, int intr) +{ + uart->istat.ints &= ~(1 << intr); + + /* Short-circuit most likely case */ + if(uart->regs.iir == UART_IIR_NO_INT) + return; + + if(intr != uart->regs.iir) + return; + + TRACE("Clearing interrupt 0x%x\n", intr); + + uart->regs.iir = UART_IIR_NO_INT; + + switch(intr) { + case UART_IIR_RLSI: + SCHED_FIND_REMOVE(uart_int_rlsi, uart); + break; + case UART_IIR_RDI: + SCHED_FIND_REMOVE(uart_int_rdi, uart); + break; + case UART_IIR_CTI: + SCHED_FIND_REMOVE(uart_int_cti, uart); + break; + case UART_IIR_THRI: + SCHED_FIND_REMOVE(uart_int_thri, uart); + break; + case UART_IIR_MSI: + SCHED_FIND_REMOVE(uart_int_msi, uart); + break; + } + + /* Schedule this job as there is no rush to send the next interrupt (the or. + * code is probably still running with interrupts disabled and this function + * is called from the uart_{read,write}_byte functions. */ + SCHED_ADD(uart_next_int, uart, 0); +} + /*----------------------------------------------------[ Transmitter logic ]---*/ /* Sends the data in the shift register to the outside world */ static void send_char (struct dev_16450 *uart, int bits_send) @@ -222,7 +398,7 @@ void uart_tx_send(void *dat) */ if (!uart->istat.txbuf_full) { uart->regs.lsr |= UART_LSR_TXBUFE; - uart->istat.thre_int = 1; + uart_int_thri(uart); } } @@ -231,18 +407,24 @@ void uart_tx_send(void *dat) static void uart_add_char (struct dev_16450 *uart, int ch) { uart->regs.lsr |= UART_LSR_RDRDY; - uart->istat.timeout_count = 0; + uart_clear_int(uart, UART_IIR_CTI); + SCHED_FIND_REMOVE(uart_int_cti, uart); + SCHED_ADD(uart_int_cti, uart, + uart->char_clks * UART_CHAR_TIMEOUT * UART_CLOCK_DIVIDER); if (uart->istat.rxbuf_full + 1 > uart->fifo_len) { uart->regs.lsr |= UART_LSR_OVRRUN | UART_LSR_RXERR; + uart_int_rlsi(uart); } else { TRACE("add %02x\n", ch); uart->regs.rxbuf[uart->istat.rxbuf_head] = ch; uart->istat.rxbuf_head = (uart->istat.rxbuf_head + 1) % uart->fifo_len; if(!uart->istat.rxbuf_full++) { uart->regs.lsr |= ch >> 8; + uart_check_rlsi(uart); } } + uart_check_rdi(uart); } /* Called when a break sequence is about to start. It stops receiveing @@ -371,8 +553,7 @@ void uart_write_byte(oraddr_t addr, uint } else uart->regs.txbuf[uart->istat.txbuf_head] = value; - if (uart->regs.iir & UART_IIR_THRI) - uart->istat.thre_int = 0; + uart_clear_int(uart, UART_IIR_THRI); break; case UART_FCR: uart->regs.fcr = value & UART_VALID_FCR; @@ -385,8 +566,10 @@ void uart_write_byte(oraddr_t addr, uint uart->istat.txbuf_full = 0; uart->regs.lsr |= UART_LSR_TXBUFE; - // For FIFO-mode only, THRE interrupt is set when THR and FIFO are empty - uart->istat.thre_int = (uart->fifo_len == 16); + /* For FIFO-mode only, THRE interrupt is set when THR and FIFO are empty + */ + if(uart->fifo_len == 16) + SCHED_ADD(uart_int_thri, uart, 0); SCHED_FIND_REMOVE(uart_tx_send, uart); } @@ -394,10 +577,13 @@ void uart_write_byte(oraddr_t addr, uint uart->istat.rxbuf_head = uart->istat.rxbuf_tail = 0; uart->istat.rxbuf_full = 0; uart->regs.lsr &= ~UART_LSR_RDRDY; + uart_clear_int(uart, UART_IIR_RDI); + uart_clear_int(uart, UART_IIR_CTI); } break; case UART_IER: uart->regs.ier = value & UART_VALID_IER; + SCHED_ADD(uart_next_int, uart, 0); break; case UART_LCR: if((uart->regs.lcr & UART_LCR_SBC) != (value & UART_LCR_SBC)) { @@ -465,21 +651,32 @@ uint8_t uart_read_byte(oraddr_t addr, vo TRACE("Reading %"PRIx8" out of RX FIFO\n", value); } else TRACE("Trying to read out of RX FIFO but it's empty!\n"); - - if(uart->istat.rxbuf_full) + + uart_clear_int(uart, UART_IIR_RDI); + uart_clear_int(uart, UART_IIR_CTI); + SCHED_FIND_REMOVE(uart_int_cti, uart); + + if(uart->istat.rxbuf_full) { uart->regs.lsr |= UART_LSR_RDRDY | uart->regs.rxbuf[uart->istat.rxbuf_tail] >> 8; - else + SCHED_ADD(uart_int_cti, uart, + uart->char_clks * UART_CHAR_TIMEOUT * UART_CLOCK_DIVIDER); + /* Since we're not allowed to raise interrupts from read/write functions + * schedule them to run just after we have executed this read + * instruction. */ + SCHED_ADD(uart_check_rlsi, uart, 0); + SCHED_ADD(uart_check_rdi, uart, 0); + } else { uart->regs.lsr &= ~UART_LSR_RDRDY; - - uart->istat.timeout_count = 0; + } break; case UART_IER: value = uart->regs.ier & UART_VALID_IER; break; case UART_IIR: value = (uart->regs.iir & UART_VALID_IIR) | 0xc0; - if (uart->regs.iir & UART_IIR_THRI) - uart->istat.thre_int = 0; + /* Only clear the thri interrupt if it is the one we are repporting */ + if(uart->regs.iir == UART_IIR_THRI) + uart_clear_int(uart, UART_IIR_THRI); break; case UART_LCR: value = uart->regs.lcr & UART_VALID_LCR; @@ -492,10 +689,13 @@ uint8_t uart_read_byte(oraddr_t addr, vo uart->regs.lsr &= ~(UART_LSR_OVRRUN | UART_LSR_BREAK | UART_LSR_PARITY | UART_LSR_FRAME | UART_LSR_RXERR); + /* Clear potentially pending RLSI interrupt */ + uart_clear_int(uart, UART_IIR_RLSI); break; case UART_MSR: value = uart->regs.msr & UART_VALID_MSR; uart->regs.msr = 0; + uart_clear_int(uart, UART_IIR_MSI); break; case UART_SCR: value = uart->regs.scr; @@ -647,36 +847,6 @@ void uart_clock16 (void *dat) uart->regs.msr |= ((uart->regs.mcr & UART_MCR_RTS) << 3); uart->regs.msr |= ((uart->regs.mcr & UART_MCR_DTR) << 5); } - - if (uart->regs.lsr & UART_LSR_RDRDY) - uart->istat.timeout_count++; - - /* Interrupt detection in proper priority order. */ - uart->regs.iir = UART_IIR_NO_INT; - if (uart->regs.ier & UART_IER_RLSI && /* Receiver LS */ - uart->regs.lsr & (UART_LSR_OVRRUN | UART_LSR_PARITY - | UART_LSR_FRAME | UART_LSR_BREAK)) { - uart->regs.iir = UART_IIR_RLSI; - } else if ((uart->regs.ier & UART_IER_RDI) /* RD available */ - && (uart->istat.rxbuf_full >= UART_FIFO_TRIGGER(uart->regs.fcr >> 6)) - && (uart->regs.lsr & UART_LSR_RDRDY)) { - uart->regs.iir = UART_IIR_RDI; - } else if ((uart->regs.ier & UART_IER_RDI) /* timeout */ - && (uart->istat.timeout_count >= UART_CHAR_TIMEOUT * uart->char_clks) - && (uart->istat.rxbuf_head != uart->istat.rxbuf_tail)) { - uart->regs.iir = UART_IIR_CTI; - } else if (uart->regs.ier & UART_IER_THRI && /* Transm. empty */ - uart->istat.thre_int == 1) { - uart->regs.iir = UART_IIR_THRI; - } else if (uart->regs.ier & UART_IER_MSI && /* Modem status */ - uart->regs.msr & (UART_MSR_DCTS | UART_MSR_DDSR - | UART_MSR_TERI | UART_MSR_DDCD)) { - uart->regs.iir = UART_IIR_MSI; - } - if (!(uart->regs.iir & UART_IIR_NO_INT)) { - TRACE("\tuart->regs.iir = %i\t", uart->regs.iir); - report_interrupt(uart->irq); - } } /* Reset. It initializes all registers of all UART devices to zero values, @@ -712,12 +882,6 @@ void uart_reset(void *dat) uart->istat.txbuf_full = uart->istat.rxbuf_full = 0; - uart->istat.thre_int = 0; - uart->istat.timeout_count = 0; - - // For FIFO-mode only, THRE interrupt is set when both THR and FIFO are empty - uart->istat.thre_int = (uart->fifo_len == 16); - uart->char_clks = 0; uart->iregs.txser = 0; @@ -725,6 +889,7 @@ void uart_reset(void *dat) uart->iregs.loopback = 0; uart->istat.receiveing = 0; uart->istat.recv_break = 0; + uart->istat.ints = 0; memset(uart->regs.txbuf, 0, sizeof(uart->regs.txbuf)); memset(uart->regs.rxbuf, 0, sizeof(uart->regs.rxbuf)); @@ -732,7 +897,7 @@ void uart_reset(void *dat) uart->regs.dll = 0; uart->regs.dlh = 0; uart->regs.ier = 0; - uart->regs.iir = 0; + uart->regs.iir = UART_IIR_NO_INT; uart->regs.fcr = 0; uart->regs.lcr = UART_LCR_RESET; uart->regs.mcr = 0; --- ../or1ksim-patch/peripheral/16450.h 2005-05-29 15:25:02.000000000 +0200 +++ peripheral/16450.h 2005-05-29 15:27:12.000000000 +0200 @@ -59,10 +59,9 @@ struct dev_16450 { int rxbuf_tail; unsigned int txbuf_full; unsigned int rxbuf_full; - unsigned thre_int; - unsigned timeout_count; int receiveing; /* Receiveing a char */ int recv_break; /* Receiveing a break */ + int ints; /* Which interrupts are pending */ } istat; /* Internal status */ /* Clocks per char */

     
    Copyright (c) 1999 OPENCORES.ORG. All rights reserved.