Line 50... |
Line 50... |
|
|
DEFAULT_DEBUG_CHANNEL(uart);
|
DEFAULT_DEBUG_CHANNEL(uart);
|
|
|
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
|
|
|
void uart_tx_send(void *dat);
|
|
|
/* Number of clock cycles (one clock cycle is one call to the uart_clock())
|
/* Number of clock cycles (one clock cycle is one call to the uart_clock())
|
before a single character is transmitted or received. */
|
before a single character is transmitted or received. */
|
static unsigned long char_clks(int dll, int dlh, int lcr)
|
static unsigned long char_clks(int dll, int dlh, int lcr)
|
{
|
{
|
unsigned int bauds_per_char = 2;
|
unsigned int bauds_per_char = 2;
|
Line 74... |
Line 76... |
bauds_per_char += 10 + ((lcr & 0x3) << 1);
|
bauds_per_char += 10 + ((lcr & 0x3) << 1);
|
|
|
return (char_clks * bauds_per_char) >> 1;
|
return (char_clks * bauds_per_char) >> 1;
|
}
|
}
|
|
|
|
/*----------------------------------------------------[ Transmitter logic ]---*/
|
|
/* Sends the data in the shift register to the outside world */
|
|
static void send_char (struct dev_16450 *uart, int bits_send)
|
|
{
|
|
PRINTF ("%c", (char)uart->iregs.txser);
|
|
TRACE("TX \'%c\' via UART at %"PRIxADDR"\n", (char)uart->iregs.txser,
|
|
uart->baseaddr);
|
|
if (uart->regs.mcr & UART_MCR_LOOP)
|
|
uart->iregs.loopback = uart->iregs.txser;
|
|
else {
|
|
/* Send to either VAPI or to file */
|
|
if (uart->vapi_id) {
|
|
int par, pe, fe, nbits;
|
|
int j, data;
|
|
unsigned long packet = 0;
|
|
|
|
nbits = MIN (bits_send, (uart->regs.lcr & UART_LCR_WLEN8) + 5);
|
|
/* Encode a packet */
|
|
packet = uart->iregs.txser & ((1 << nbits) - 1);
|
|
|
|
/* Calculate parity */
|
|
for (j = 0, par = 0; j < nbits; j++)
|
|
par ^= (packet >> j) & 1;
|
|
|
|
if (uart->regs.lcr & UART_LCR_PARITY) {
|
|
if (uart->regs.lcr & UART_LCR_SPAR) {
|
|
packet |= 1 << nbits;
|
|
} else {
|
|
if (uart->regs.lcr & UART_LCR_EPAR)
|
|
packet |= par << nbits;
|
|
else
|
|
packet |= (par ^ 1) << nbits;
|
|
}
|
|
nbits++;
|
|
}
|
|
packet |= 1 << (nbits++);
|
|
if (uart->regs.lcr & UART_LCR_STOP)
|
|
packet |= 1 << (nbits++);
|
|
|
|
/* Decode a packet */
|
|
nbits = (uart->vapi.lcr & UART_LCR_WLEN8) + 5;
|
|
data = packet & ((1 << nbits) - 1);
|
|
|
|
/* Calculate parity, including parity bit */
|
|
for (j = 0, par = 0; j < nbits + 1; j++)
|
|
par ^= (packet >> j) & 1;
|
|
|
|
if (uart->vapi.lcr & UART_LCR_PARITY) {
|
|
if (uart->vapi.lcr & UART_LCR_SPAR) {
|
|
pe = !((packet >> nbits) & 1);
|
|
} else {
|
|
if (uart->vapi.lcr & UART_LCR_EPAR)
|
|
pe = par != 0;
|
|
else
|
|
pe = par != 1;
|
|
}
|
|
nbits++;
|
|
} else
|
|
pe = 0;
|
|
|
|
fe = ((packet >> (nbits++)) & 1) ^ 1;
|
|
if (uart->vapi.lcr & UART_LCR_STOP)
|
|
fe |= ((packet >> (nbits++)) & 1) ^ 1;
|
|
|
|
TRACE ("lcr vapi %02x, uart %02x\n", uart->vapi.lcr, uart->regs.lcr);
|
|
data |= (uart->vapi.lcr << 8) | (pe << 16) | (fe << 17) | (uart->vapi.lcr << 8);
|
|
TRACE ("vapi_send (%08lx, %08x)\n", uart->vapi_id, data);
|
|
vapi_send (uart->vapi_id, data);
|
|
} else {
|
|
char buffer[1] = { uart->iregs.txser & 0xFF };
|
|
channel_write(uart->channel, buffer, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Called when all the bits have been shifted out of the shift register */
|
|
void uart_char_clock(void *dat)
|
|
{
|
|
struct dev_16450 *uart = dat;
|
|
|
|
TRACE("Sending data in shift reg: 0x%02lx\n", uart->iregs.txser);
|
|
/* We've sent all bits */
|
|
send_char(uart, (uart->regs.lcr & UART_LCR_WLEN8) + 5);
|
|
|
|
if(!uart->istat.txbuf_full)
|
|
uart->regs.lsr |= UART_LSR_TXSERE;
|
|
else
|
|
uart_tx_send(uart);
|
|
}
|
|
|
|
/* Called when a break has been shifted out of the shift register */
|
|
void uart_send_break(void *dat)
|
|
{
|
|
struct dev_16450 *uart = dat;
|
|
|
|
TRACE("Sending break\n");
|
|
#if 0
|
|
/* Send broken frame */
|
|
int nbits_sent = ((uart->regs.lcr & UART_LCR_WLEN8) + 5) * (uart->istat.txser_clks - 1) / uart->char_clks;
|
|
send_char(i, nbits_sent);
|
|
#endif
|
|
/* Send one break signal */
|
|
vapi_send (uart->vapi_id, UART_LCR_SBC << 8);
|
|
|
|
/* Send the next char (if there is one) */
|
|
if(!uart->istat.txbuf_full)
|
|
uart->regs.lsr |= UART_LSR_TXSERE;
|
|
else
|
|
uart_tx_send(uart);
|
|
}
|
|
|
|
/* Scheduled whenever the TX buffer has characters in it and we aren't sending
|
|
* a character. */
|
|
void uart_tx_send(void *dat)
|
|
{
|
|
struct dev_16450 *uart = dat;
|
|
|
|
uart->iregs.txser = uart->regs.txbuf[uart->istat.txbuf_tail];
|
|
uart->istat.txbuf_tail = (uart->istat.txbuf_tail + 1) % uart->fifo_len;
|
|
uart->istat.txbuf_full--;
|
|
uart->regs.lsr &= ~UART_LSR_TXSERE;
|
|
|
|
TRACE("Moveing head of TX fifo (fill: %i) to shift reg 0x%02lx\n",
|
|
uart->istat.txbuf_full, uart->iregs.txser);
|
|
|
|
/* Schedules a char_clock to run in the correct amount of time */
|
|
if(!(uart->regs.lcr & UART_LCR_SBC)) {
|
|
SCHED_ADD(uart_char_clock, uart, uart->char_clks * UART_CLOCK_DIVIDER);
|
|
} else {
|
|
TRACE("Sending break not char\n");
|
|
SCHED_ADD(uart_send_break, uart, 0);
|
|
}
|
|
|
|
/* When UART is in either character mode, i.e. 16450 emulation mode, or FIFO
|
|
* mode, the THRE interrupt is raised when THR transitions from full to empty.
|
|
*/
|
|
if (!uart->istat.txbuf_full) {
|
|
uart->regs.lsr |= UART_LSR_TXBUFE;
|
|
uart->istat.thre_int = 1;
|
|
}
|
|
}
|
|
|
/* Set a specific UART register with value. */
|
/* Set a specific UART register with value. */
|
void uart_write_byte(oraddr_t addr, uint8_t value, void *dat)
|
void uart_write_byte(oraddr_t addr, uint8_t value, void *dat)
|
{
|
{
|
struct dev_16450 *uart = dat;
|
struct dev_16450 *uart = dat;
|
|
|
Line 97... |
Line 241... |
}
|
}
|
}
|
}
|
|
|
switch (addr) {
|
switch (addr) {
|
case UART_TXBUF:
|
case UART_TXBUF:
|
|
uart->regs.lsr &= ~UART_LSR_TXBUFE;
|
if (uart->istat.txbuf_full < uart->fifo_len) {
|
if (uart->istat.txbuf_full < uart->fifo_len) {
|
uart->istat.txbuf_full++;
|
|
uart->regs.txbuf[uart->istat.txbuf_head] = value;
|
uart->regs.txbuf[uart->istat.txbuf_head] = value;
|
uart->istat.txbuf_head = (uart->istat.txbuf_head + 1) % uart->fifo_len;
|
uart->istat.txbuf_head = (uart->istat.txbuf_head + 1) % uart->fifo_len;
|
|
if(!uart->istat.txbuf_full++ && (uart->regs.lsr & UART_LSR_TXSERE))
|
|
SCHED_ADD(uart_tx_send, uart, 0);
|
} else
|
} else
|
uart->regs.txbuf[uart->istat.txbuf_head] = value;
|
uart->regs.txbuf[uart->istat.txbuf_head] = value;
|
|
|
uart->regs.lsr &= ~(UART_LSR_TXSERE | UART_LSR_TXBUFE);
|
|
if (uart->regs.iir & UART_IIR_THRI)
|
if (uart->regs.iir & UART_IIR_THRI)
|
uart->istat.thre_int = 0;
|
uart->istat.thre_int = 0;
|
break;
|
break;
|
case UART_FCR:
|
case UART_FCR:
|
uart->regs.fcr = value & UART_VALID_FCR;
|
uart->regs.fcr = value & UART_VALID_FCR;
|
Line 121... |
Line 266... |
uart->istat.txbuf_full = 0;
|
uart->istat.txbuf_full = 0;
|
uart->regs.lsr |= UART_LSR_TXBUFE;
|
uart->regs.lsr |= UART_LSR_TXBUFE;
|
|
|
// For FIFO-mode only, THRE interrupt is set when THR and FIFO are empty
|
// For FIFO-mode only, THRE interrupt is set when THR and FIFO are empty
|
uart->istat.thre_int = (uart->fifo_len == 16);
|
uart->istat.thre_int = (uart->fifo_len == 16);
|
|
|
|
SCHED_FIND_REMOVE(uart_tx_send, uart);
|
}
|
}
|
if (value & UART_FCR_RRXFI) {
|
if (value & UART_FCR_RRXFI) {
|
uart->istat.rxbuf_head = uart->istat.rxbuf_tail = 0;
|
uart->istat.rxbuf_head = uart->istat.rxbuf_tail = 0;
|
uart->istat.rxbuf_full = 0;
|
uart->istat.rxbuf_full = 0;
|
uart->regs.lsr &= ~UART_LSR_RDRDY;
|
uart->regs.lsr &= ~UART_LSR_RDRDY;
|
Line 132... |
Line 279... |
break;
|
break;
|
case UART_IER:
|
case UART_IER:
|
uart->regs.ier = value & UART_VALID_IER;
|
uart->regs.ier = value & UART_VALID_IER;
|
break;
|
break;
|
case UART_LCR:
|
case UART_LCR:
|
|
if((uart->regs.lcr & UART_LCR_SBC) != (value & UART_LCR_SBC)) {
|
|
if((value & UART_LCR_SBC) && !(uart->regs.lsr & UART_LSR_TXSERE)) {
|
|
/* Schedule a job to send the break char */
|
|
SCHED_FIND_REMOVE(uart_char_clock, uart);
|
|
SCHED_ADD(uart_send_break, uart, 0);
|
|
}
|
|
if(!(value & UART_LCR_SBC) && !(uart->regs.lsr & UART_LSR_TXSERE)) {
|
|
/* Schedule a job to start sending characters */
|
|
SCHED_ADD(uart_tx_send, uart, 0);
|
|
/* Remove the uart_send_break job just in case it has not run yet */
|
|
SCHED_FIND_REMOVE(uart_char_clock, uart);
|
|
}
|
|
}
|
uart->regs.lcr = value & UART_VALID_LCR;
|
uart->regs.lcr = value & UART_VALID_LCR;
|
uart->char_clks = char_clks(uart->regs.dll, uart->regs.dlh, uart->regs.lcr);
|
uart->char_clks = char_clks(uart->regs.dll, uart->regs.dlh, uart->regs.lcr);
|
break;
|
break;
|
case UART_MCR:
|
case UART_MCR:
|
uart->regs.mcr = value & UART_VALID_MCR;
|
uart->regs.mcr = value & UART_VALID_MCR;
|
Line 237... |
Line 397... |
fprintf (stderr, "FATAL: uart VAPI buffer to small.\n");
|
fprintf (stderr, "FATAL: uart VAPI buffer to small.\n");
|
exit (1);
|
exit (1);
|
}
|
}
|
}
|
}
|
|
|
static void send_char (struct dev_16450 *uart, int bits_send)
|
|
{
|
|
PRINTF ("%c", (char)uart->iregs.txser);
|
|
TRACE("TX \'%c\' via UART at %"PRIxADDR"...\n", (char)uart->iregs.txser,
|
|
uart->baseaddr);
|
|
if (uart->regs.mcr & UART_MCR_LOOP)
|
|
uart->iregs.loopback = uart->iregs.txser;
|
|
else {
|
|
/* Send to either VAPI or to file */
|
|
if (uart->vapi_id) {
|
|
int par, pe, fe, nbits;
|
|
int j, data;
|
|
unsigned long packet = 0;
|
|
|
|
nbits = MIN (bits_send, (uart->regs.lcr & UART_LCR_WLEN8) + 5);
|
|
/* Encode a packet */
|
|
packet = uart->iregs.txser & ((1 << nbits) - 1);
|
|
|
|
/* Calculate parity */
|
|
for (j = 0, par = 0; j < nbits; j++)
|
|
par ^= (packet >> j) & 1;
|
|
|
|
if (uart->regs.lcr & UART_LCR_PARITY) {
|
|
if (uart->regs.lcr & UART_LCR_SPAR) {
|
|
packet |= 1 << nbits;
|
|
} else {
|
|
if (uart->regs.lcr & UART_LCR_EPAR)
|
|
packet |= par << nbits;
|
|
else
|
|
packet |= (par ^ 1) << nbits;
|
|
}
|
|
nbits++;
|
|
}
|
|
packet |= 1 << (nbits++);
|
|
if (uart->regs.lcr & UART_LCR_STOP)
|
|
packet |= 1 << (nbits++);
|
|
|
|
/* Decode a packet */
|
|
nbits = (uart->vapi.lcr & UART_LCR_WLEN8) + 5;
|
|
data = packet & ((1 << nbits) - 1);
|
|
|
|
/* Calculate parity, including parity bit */
|
|
for (j = 0, par = 0; j < nbits + 1; j++)
|
|
par ^= (packet >> j) & 1;
|
|
|
|
if (uart->vapi.lcr & UART_LCR_PARITY) {
|
|
if (uart->vapi.lcr & UART_LCR_SPAR) {
|
|
pe = !((packet >> nbits) & 1);
|
|
} else {
|
|
if (uart->vapi.lcr & UART_LCR_EPAR)
|
|
pe = par != 0;
|
|
else
|
|
pe = par != 1;
|
|
}
|
|
nbits++;
|
|
} else
|
|
pe = 0;
|
|
|
|
fe = ((packet >> (nbits++)) & 1) ^ 1;
|
|
if (uart->vapi.lcr & UART_LCR_STOP)
|
|
fe |= ((packet >> (nbits++)) & 1) ^ 1;
|
|
|
|
TRACE ("lcr vapi %02x, uart %02x\n", uart->vapi.lcr, uart->regs.lcr);
|
|
data |= (uart->vapi.lcr << 8) | (pe << 16) | (fe << 17) | (uart->vapi.lcr << 8);
|
|
PRINTF ("vapi_send (%08lx, %08x)\n", uart->vapi_id, data);
|
|
TRACE ("vapi_send (%08lx, %08x)\n", uart->vapi_id, data);
|
|
vapi_send (uart->vapi_id, data);
|
|
} else {
|
|
char buffer[1] = { uart->iregs.txser & 0xFF };
|
|
channel_write(uart->channel, buffer, 1);
|
|
}
|
|
}
|
|
uart->istat.txser_full = 0;
|
|
uart->istat.txser_clks = 0;
|
|
}
|
|
|
|
/* Adds a character to the FIFO */
|
/* Adds a character to the FIFO */
|
|
|
void uart_add_char (struct dev_16450 *uart, int ch)
|
void uart_add_char (struct dev_16450 *uart, int ch)
|
{
|
{
|
if (uart->istat.rxbuf_full + 1 > uart->fifo_len)
|
if (uart->istat.rxbuf_full + 1 > uart->fifo_len)
|
Line 354... |
Line 438... |
if (--uart->vapi.next_break_cnt < 0) {
|
if (--uart->vapi.next_break_cnt < 0) {
|
if (!(uart->vapi.cur_break = uart->vapi.next_break))
|
if (!(uart->vapi.cur_break = uart->vapi.next_break))
|
uart->istat.break_set = 0;
|
uart->istat.break_set = 0;
|
}
|
}
|
|
|
/***************** Transmit *****************/
|
|
TRACE("\tuart->istat.txser_full = %i\n", uart->istat.txser_full);
|
|
TRACE("\tuart->istat.txbuf_full = %i\n", uart->istat.txser_full);
|
|
TRACE("\tuart->char_clks = %li\n", uart->char_clks);
|
|
if (!uart->istat.txser_full) {
|
|
// uart->regs.lsr |= UART_LSR_TXBUFE;
|
|
if (uart->istat.txbuf_full) {
|
|
uart->iregs.txser = uart->regs.txbuf[uart->istat.txbuf_tail];
|
|
uart->istat.txbuf_tail = (uart->istat.txbuf_tail + 1) % uart->fifo_len;
|
|
uart->istat.txser_full = 1;
|
|
uart->istat.txbuf_full--;
|
|
uart->regs.lsr &= ~UART_LSR_TXSERE;
|
|
|
|
// When UART is in either character mode, i.e. 16450 emulation mode, or FIFO mode,
|
|
// the THRE interrupt is raised when THR transitions from full to empty.
|
|
if (!uart->istat.txbuf_full) {
|
|
uart->istat.thre_int = 1;
|
|
uart->regs.lsr |= UART_LSR_TXBUFE;
|
|
}
|
|
} else {
|
|
uart->regs.lsr |= UART_LSR_TXSERE;
|
|
}
|
|
} else if (uart->char_clks <= uart->istat.txser_clks++) {
|
|
send_char(uart, (uart->regs.lcr & UART_LCR_WLEN8) + 5); /* We've sent all bits */
|
|
} else {
|
|
/* We are still sending char here*/
|
|
|
|
/* Check if we set the break bit */
|
|
if (uart->regs.lcr & UART_LCR_SBC) {
|
|
if (!uart->vapi.break_sent) {
|
|
#if 0
|
|
/* Send broken frame */
|
|
int nbits_sent = ((uart->regs.lcr & UART_LCR_WLEN8) + 5) * (uart->istat.txser_clks - 1) / uart->char_clks;
|
|
send_char(i, nbits_sent);
|
|
#endif
|
|
/* Send one break signal */
|
|
vapi_send (uart->vapi_id, UART_LCR_SBC << 8);
|
|
uart->vapi.break_sent = 1;
|
|
}
|
|
/* mark as character was sent */
|
|
uart->istat.txser_full = 0;
|
|
uart->istat.txser_clks = 0;
|
|
} else
|
|
uart->vapi.break_sent = 0;
|
|
|
|
}
|
|
|
|
/***************** Receive *****************/
|
/***************** Receive *****************/
|
|
|
/* Is there a break? */
|
/* Is there a break? */
|
if (uart->vapi.cur_break) {
|
if (uart->vapi.cur_break) {
|
uart->vapi.cur_break_cnt++;
|
uart->vapi.cur_break_cnt++;
|
Line 601... |
Line 638... |
uart->fifo_len = 1;
|
uart->fifo_len = 1;
|
|
|
uart->istat.rxbuf_head = uart->istat.rxbuf_tail = 0;
|
uart->istat.rxbuf_head = uart->istat.rxbuf_tail = 0;
|
uart->istat.txbuf_head = uart->istat.txbuf_tail = 0;
|
uart->istat.txbuf_head = uart->istat.txbuf_tail = 0;
|
|
|
uart->istat.txser_full = uart->istat.rxser_full = 0;
|
uart->istat.rxser_full = 0;
|
uart->istat.txbuf_full = uart->istat.rxbuf_full = 0;
|
uart->istat.txbuf_full = uart->istat.rxbuf_full = 0;
|
|
|
uart->istat.txser_clks = uart->istat.rxser_clks = 0;
|
uart->istat.rxser_clks = 0;
|
|
|
uart->istat.thre_int = 0;
|
uart->istat.thre_int = 0;
|
uart->istat.break_set = 0;
|
uart->istat.break_set = 0;
|
uart->istat.timeout_count = 0;
|
uart->istat.timeout_count = 0;
|
|
|
Line 630... |
Line 667... |
uart->regs.ier = 0;
|
uart->regs.ier = 0;
|
uart->regs.iir = 0;
|
uart->regs.iir = 0;
|
uart->regs.fcr = 0;
|
uart->regs.fcr = 0;
|
uart->regs.lcr = UART_LCR_RESET;
|
uart->regs.lcr = UART_LCR_RESET;
|
uart->regs.mcr = 0;
|
uart->regs.mcr = 0;
|
uart->regs.lsr = 0;
|
uart->regs.lsr = UART_LSR_TXBUFE | UART_LSR_TXSERE;
|
uart->regs.msr = 0;
|
uart->regs.msr = 0;
|
uart->regs.scr = 0;
|
uart->regs.scr = 0;
|
|
|
uart->vapi.cur_break = uart->vapi.cur_break_cnt = uart->vapi.next_break = 0;
|
uart->vapi.cur_break = uart->vapi.cur_break_cnt = uart->vapi.next_break = 0;
|
uart->vapi.next_break_cnt = -1;
|
uart->vapi.next_break_cnt = -1;
|
uart->vapi.break_sent = 0;
|
|
uart->vapi.skew = 0;
|
uart->vapi.skew = 0;
|
uart->vapi.lcr = 0;
|
uart->vapi.lcr = 0;
|
uart->vapi.dll = 0;
|
uart->vapi.dll = 0;
|
uart->vapi.char_clks = 0;
|
uart->vapi.char_clks = 0;
|
|
|
Line 674... |
Line 710... |
PRINTF("\nInternal registers (sim debug):\n");
|
PRINTF("\nInternal registers (sim debug):\n");
|
PRINTF("RXSER: %.2lx TXSER: %.2lx\n", uart->iregs.rxser, uart->iregs.txser);
|
PRINTF("RXSER: %.2lx TXSER: %.2lx\n", uart->iregs.rxser, uart->iregs.txser);
|
|
|
PRINTF("\nInternal status (sim debug):\n");
|
PRINTF("\nInternal status (sim debug):\n");
|
PRINTF("char_clks: %ld\n", uart->char_clks);
|
PRINTF("char_clks: %ld\n", uart->char_clks);
|
PRINTF("rxser_clks: %ld txser_clks: %ld\n", uart->istat.rxser_clks,
|
PRINTF("rxser_clks: %ld\n", uart->istat.rxser_clks);
|
uart->istat.txser_clks);
|
PRINTF("rxser: %d\n", uart->istat.rxser_full);
|
PRINTF("rxser: %d txser: %d\n", uart->istat.rxser_full, uart->istat.txser_full);
|
|
PRINTF("rxbuf_full: %d txbuf_full: %d\n", uart->istat.rxbuf_full, uart->istat.txbuf_full);
|
PRINTF("rxbuf_full: %d txbuf_full: %d\n", uart->istat.rxbuf_full, uart->istat.txbuf_full);
|
PRINTF("Using IRQ%i\n", uart->irq);
|
PRINTF("Using IRQ%i\n", uart->irq);
|
if (uart->vapi_id)
|
if (uart->vapi_id)
|
PRINTF ("Connected to vapi ID=%lx\n\n", uart->vapi_id);
|
PRINTF ("Connected to vapi ID=%lx\n\n", uart->vapi_id);
|
/* TODO: replace by a channel_status
|
/* TODO: replace by a channel_status
|