Search
lxdream.org :: lxdream/src/sh4/scif.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/scif.c
changeset 20:3ffb66aa25c7
next21:fda269b432a7
author nkeynes
date Thu Dec 22 13:28:16 2005 +0000 (14 years ago)
permissions -rw-r--r--
last change Add scif.c (oops)
Convert interrupts to be level-triggered rather than edge-triggered
(although shouldn't be any visible difference)
file annotate diff log raw
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/sh4/scif.c Thu Dec 22 13:28:16 2005 +0000
1.3 @@ -0,0 +1,567 @@
1.4 +/**
1.5 + * $Id: scif.c,v 1.1 2005-12-22 13:28:16 nkeynes Exp $
1.6 + * SCIF (Serial Communication Interface with FIFO) implementation - part of the
1.7 + * SH4 standard on-chip peripheral set. The SCIF is hooked up to the DCs
1.8 + * external serial port
1.9 + *
1.10 + * Copyright (c) 2005 Nathan Keynes.
1.11 + *
1.12 + * This program is free software; you can redistribute it and/or modify
1.13 + * it under the terms of the GNU General Public License as published by
1.14 + * the Free Software Foundation; either version 2 of the License, or
1.15 + * (at your option) any later version.
1.16 + *
1.17 + * This program is distributed in the hope that it will be useful,
1.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.20 + * GNU General Public License for more details.
1.21 + */
1.22 +
1.23 +#include <glib.h>
1.24 +#include "dream.h"
1.25 +#include "mem.h"
1.26 +#include "sh4core.h"
1.27 +#include "sh4mmio.h"
1.28 +#include "intc.h"
1.29 +#include "clock.h"
1.30 +#include "serial.h"
1.31 +#include "modules.h"
1.32 +
1.33 +void SCIF_set_break(void);
1.34 +
1.35 +/************************* External serial interface ************************/
1.36 +
1.37 +/**
1.38 + * Note: serial_* operations are called from outside the SH4, and as such are
1.39 + * named relative to the external serial device. SCIF_* operations are only
1.40 + * called internally to the SH4 and so are named relative to the CPU.
1.41 + */
1.42 +
1.43 +/**
1.44 + * Storage space for inbound/outbound data blocks. It's a little more
1.45 + * convenient for serial consumers to be able to deal with block-sized pieces
1.46 + * rather than a byte at a time, even if it makes all this look rather
1.47 + * complicated.
1.48 + *
1.49 + * Currently there's no limit on the number of blocks that can be queued up.
1.50 + */
1.51 +typedef struct serial_data_block {
1.52 + uint32_t length;
1.53 + uint32_t offset;
1.54 + struct serial_data_block *next;
1.55 + char data[];
1.56 +} *serial_data_block_t;
1.57 +
1.58 +serial_data_block_t serial_recvq_head = NULL, serial_recvq_tail = NULL;
1.59 +serial_device_t serial_device = NULL;
1.60 +
1.61 +void serial_attach_device( serial_device_t dev )
1.62 +{
1.63 + if( serial_device != NULL )
1.64 + serial_detach_device();
1.65 + serial_device = dev;
1.66 +}
1.67 +
1.68 +
1.69 +void serial_detach_device( void )
1.70 +{
1.71 + serial_device = NULL;
1.72 +}
1.73 +
1.74 +/**
1.75 + * Add a block of data to the serial receive queue. The data will be received
1.76 + * by the CPU at the appropriate baud rate.
1.77 + */
1.78 +void serial_transmit_data( char *data, int length ) {
1.79 + if( length == 0 )
1.80 + return;
1.81 + serial_data_block_t block =
1.82 + g_malloc( sizeof( struct serial_data_block ) + length );
1.83 + block->length = length;
1.84 + block->offset = 0;
1.85 + block->next = NULL;
1.86 + memcpy( block->data, data, length );
1.87 +
1.88 + if( serial_recvq_head == NULL ) {
1.89 + serial_recvq_head = serial_recvq_tail = block;
1.90 + } else {
1.91 + serial_recvq_tail->next = block;
1.92 + serial_recvq_tail = block;
1.93 + }
1.94 +}
1.95 +
1.96 +/**
1.97 + * Dequeue a byte from the serial input queue
1.98 + */
1.99 +static int serial_transmit_dequeue( ) {
1.100 + if( serial_recvq_head != NULL ) {
1.101 + uint8_t val = serial_recvq_head->data[serial_recvq_head->offset++];
1.102 + if( serial_recvq_head->offset >= serial_recvq_head->length ) {
1.103 + serial_data_block_t next = serial_recvq_head->next;
1.104 + g_free( serial_recvq_head );
1.105 + serial_recvq_head = next;
1.106 + if( next == NULL )
1.107 + serial_recvq_tail = NULL;
1.108 + }
1.109 + return (int)(unsigned int)val;
1.110 + }
1.111 + return -1;
1.112 +
1.113 +}
1.114 +
1.115 +void serial_transmit_break() {
1.116 + SCIF_set_break();
1.117 +}
1.118 +
1.119 +/********************************* SCIF *************************************/
1.120 +
1.121 +#define FIFO_LENGTH 16
1.122 +#define FIFO_ARR_LENGTH (FIFO_LENGTH+1)
1.123 +
1.124 +/* Serial control register flags */
1.125 +#define SCSCR2_TIE 0x80
1.126 +#define SCSCR2_RIE 0x40
1.127 +#define SCSCR2_TE 0x20
1.128 +#define SCSCR2_RE 0x10
1.129 +#define SCSCR2_REIE 0x08
1.130 +#define SCSCR2_CKE 0x02
1.131 +
1.132 +#define IS_TRANSMIT_IRQ_ENABLED() (MMIO_READ(SCIF,SCSCR2) & SCSCR2_TIE)
1.133 +#define IS_RECEIVE_IRQ_ENABLED() (MMIO_READ(SCIF,SCSCR2) & SCSCR2_RIE)
1.134 +#define IS_RECEIVE_ERROR_IRQ_ENABLED() (MMIO_READ(SCIF,SCSCR2) & (SCSCR2_RIE|SCSCR2_REIE))
1.135 +/* Receive is enabled if the RE bit is set in SCSCR2, and the ORER bit is cleared in SCLSR2 */
1.136 +#define IS_RECEIVE_ENABLED() ( (MMIO_READ(SCIF,SCSCR2) & SCSCR2_RE) && (MMIO_READ(SCIF,SCLSR2) & SCLSR2_ORER == 0) )
1.137 +/* Transmit is enabled if the TE bit is set in SCSCR2 */
1.138 +#define IS_TRANSMIT_ENABLED() (MMIO_READ(SCIF,SCSCR2) & SCSCR2_TE)
1.139 +#define IS_LOOPBACK_ENABLED() (MMIO_READ(SCIF,SCFCR2) & SCFCR2_LOOP)
1.140 +
1.141 +/* Serial status register flags */
1.142 +#define SCFSR2_ER 0x80
1.143 +#define SCFSR2_TEND 0x40
1.144 +#define SCFSR2_TDFE 0x20
1.145 +#define SCFSR2_BRK 0x10
1.146 +#define SCFSR2_RDF 0x02
1.147 +#define SCFSR2_DR 0x01
1.148 +
1.149 +/* FIFO control register flags */
1.150 +#define SCFCR2_MCE 0x08
1.151 +#define SCFCR2_TFRST 0x04
1.152 +#define SCFCR2_RFRST 0x02
1.153 +#define SCFCR2_LOOP 0x01
1.154 +
1.155 +/* Line Status Register */
1.156 +#define SCLSR2_ORER 0x01
1.157 +
1.158 +struct SCIF_fifo {
1.159 + int head;
1.160 + int tail;
1.161 + int trigger;
1.162 + uint8_t data[FIFO_ARR_LENGTH];
1.163 +};
1.164 +
1.165 +void SCIF_save_state( FILE *f )
1.166 +{
1.167 +
1.168 +
1.169 +}
1.170 +
1.171 +int SCIF_load_state( FILE *f )
1.172 +{
1.173 + return 0;
1.174 +}
1.175 +
1.176 +int SCIF_recvq_triggers[4] = {1, 4, 8, 14};
1.177 +struct SCIF_fifo SCIF_recvq = {0,0,1};
1.178 +
1.179 +int SCIF_sendq_triggers[4] = {8, 4, 2, 1};
1.180 +struct SCIF_fifo SCIF_sendq = {0,0,8};
1.181 +
1.182 +static inline uint8_t SCIF_recvq_size( )
1.183 +{
1.184 + int val = SCIF_recvq.tail - SCIF_recvq.head;
1.185 + if( val < 0 ) {
1.186 + val = FIFO_ARR_LENGTH - SCIF_recvq.head + SCIF_recvq.tail;
1.187 + }
1.188 + return val;
1.189 +}
1.190 +
1.191 +int SCIF_recvq_dequeue( gboolean clearFlags )
1.192 +{
1.193 + uint8_t result;
1.194 + uint32_t tmp, length;
1.195 + if( SCIF_recvq.head == SCIF_recvq.tail )
1.196 + return -1; /* No data */
1.197 + result = SCIF_recvq.data[SCIF_recvq.head++];
1.198 + if( SCIF_recvq.head > FIFO_LENGTH )
1.199 + SCIF_recvq.head = 0;
1.200 +
1.201 + /* Update data count register */
1.202 + tmp = MMIO_READ( SCIF, SCFDR2 ) & 0xF0;
1.203 + length = SCIF_recvq_size();
1.204 + MMIO_WRITE( SCIF, SCFDR2, tmp | length );
1.205 +
1.206 + /* Clear flags (if requested ) */
1.207 + if( clearFlags && length < SCIF_recvq.trigger ) {
1.208 + tmp = SCFSR2_RDF;
1.209 + if( length == 0 )
1.210 + tmp |= SCFSR2_DR;
1.211 + tmp = MMIO_READ( SCIF, SCFSR2 ) & (~tmp);
1.212 + MMIO_WRITE( SCIF, SCFSR2, tmp );
1.213 + /* If both flags are cleared, clear the interrupt as well */
1.214 + if( tmp & (SCFSR2_DR|SCFSR2_RDF) == 0 )
1.215 + intc_clear_interrupt( INT_SCIF_RXI );
1.216 + }
1.217 +
1.218 + return (int)(unsigned int)result;
1.219 +}
1.220 +
1.221 +gboolean SCIF_recvq_enqueue( uint8_t value )
1.222 +{
1.223 + uint32_t tmp, length;
1.224 + int newpos = SCIF_recvq.tail + 1;
1.225 + if( newpos > FIFO_LENGTH )
1.226 + newpos = 0;
1.227 + if( newpos == SCIF_recvq.head ) {
1.228 + /* FIFO full - set ORER and discard the value */
1.229 + MMIO_WRITE( SCIF, SCLSR2, SCLSR2_ORER );
1.230 + if( IS_RECEIVE_ERROR_IRQ_ENABLED() )
1.231 + intc_raise_interrupt( INT_SCIF_ERI );
1.232 + return FALSE;
1.233 + }
1.234 + SCIF_recvq.data[SCIF_recvq.tail] = value;
1.235 +
1.236 + /* Update data count register */
1.237 + tmp = MMIO_READ( SCIF, SCFDR2 ) & 0xF0;
1.238 + length = SCIF_recvq_size();
1.239 + MMIO_WRITE( SCIF, SCFDR2, tmp | length );
1.240 +
1.241 + /* Update status register */
1.242 + tmp = MMIO_READ( SCIF, SCFSR2 );
1.243 + if( length >= SCIF_recvq.trigger ) {
1.244 + tmp |= SCFSR2_RDF;
1.245 + if( IS_RECEIVE_IRQ_ENABLED() )
1.246 + intc_raise_interrupt( INT_SCIF_RXI );
1.247 + }
1.248 + MMIO_WRITE( SCIF, SCFSR2, tmp );
1.249 + return TRUE;
1.250 +}
1.251 +
1.252 +
1.253 +/**
1.254 + * Reset the receive FIFO to its initial state. Manual is unclear as to
1.255 + * whether this also clears flags/interrupts, but we're assuming here that
1.256 + * it does until proven otherwise.
1.257 + */
1.258 +void SCIF_recvq_clear( void )
1.259 +{
1.260 + SCIF_recvq.head = SCIF_recvq.tail = 0;
1.261 + MMIO_WRITE( SCIF, SCFDR2, MMIO_READ( SCIF, SCFDR2 ) & 0xF0 );
1.262 + MMIO_WRITE( SCIF, SCFSR2, MMIO_READ( SCIF, SCFSR2 ) & ~(SCFSR2_DR|SCFSR2_RDF) );
1.263 + intc_clear_interrupt( INT_SCIF_RXI );
1.264 +}
1.265 +
1.266 +static inline uint8_t SCIF_sendq_size( )
1.267 +{
1.268 + int val = SCIF_sendq.tail - SCIF_sendq.head;
1.269 + if( val < 0 ) {
1.270 + val = FIFO_ARR_LENGTH - SCIF_sendq.head + SCIF_sendq.tail;
1.271 + }
1.272 + return val;
1.273 +}
1.274 +
1.275 +/**
1.276 + * Dequeue one byte from the SCIF transmit queue (ie transmit the byte),
1.277 + * updating all status flags as required.
1.278 + * @return The byte dequeued, or -1 if the queue is empty.
1.279 + */
1.280 +int SCIF_sendq_dequeue( )
1.281 +{
1.282 + uint8_t result;
1.283 + uint32_t tmp, length;
1.284 + if( SCIF_sendq.head == SCIF_sendq.tail )
1.285 + return -1; /* No data */
1.286 +
1.287 + /* Update queue head pointer */
1.288 + result = SCIF_sendq.data[SCIF_sendq.head++];
1.289 + if( SCIF_sendq.head > FIFO_LENGTH )
1.290 + SCIF_sendq.head = 0;
1.291 +
1.292 + /* Update data count register */
1.293 + tmp = MMIO_READ( SCIF, SCFDR2 ) & 0x0F;
1.294 + length = SCIF_sendq_size();
1.295 + MMIO_WRITE( SCIF, SCFDR2, tmp | (length << 8) );
1.296 +
1.297 + /* Update status register */
1.298 + if( length <= SCIF_sendq.trigger ) {
1.299 + tmp = MMIO_READ( SCIF, SCFSR2 ) | SCFSR2_TDFE;
1.300 + if( length == 0 )
1.301 + tmp |= SCFSR2_TEND; /* Transmission ended - no data waiting */
1.302 + if( IS_TRANSMIT_IRQ_ENABLED() )
1.303 + intc_raise_interrupt( INT_SCIF_TXI );
1.304 + MMIO_WRITE( SCIF, SCFSR2, tmp );
1.305 + }
1.306 + return (int)(unsigned int)result;
1.307 +}
1.308 +
1.309 +/**
1.310 + * Enqueue a single byte in the SCIF transmit queue. If the queue is full,
1.311 + * the value will be discarded.
1.312 + * @param value to be queued.
1.313 + * @param clearFlags TRUE if the TEND/TDFE flags should be cleared
1.314 + * if the queue exceeds the trigger level. (According to the manual,
1.315 + * DMAC writes will clear the flag, whereas regular SH4 writes do NOT
1.316 + * automatically clear it. Go figure).
1.317 + * @return gboolean TRUE if the value was queued, FALSE if the queue was
1.318 + * full.
1.319 + */
1.320 +gboolean SCIF_sendq_enqueue( uint8_t value, gboolean clearFlags )
1.321 +{
1.322 + uint32_t tmp, length;
1.323 + int newpos = SCIF_sendq.tail + 1;
1.324 + if( newpos > FIFO_LENGTH )
1.325 + newpos = 0;
1.326 + if( newpos == SCIF_sendq.head ) {
1.327 + /* FIFO full - discard */
1.328 + return FALSE;
1.329 + }
1.330 + SCIF_sendq.data[SCIF_sendq.tail] = value;
1.331 + SCIF_sendq.tail = newpos;
1.332 +
1.333 + /* Update data count register */
1.334 + tmp = MMIO_READ( SCIF, SCFDR2 ) & 0x0F;
1.335 + length = SCIF_sendq_size();
1.336 + MMIO_WRITE( SCIF, SCFDR2, tmp | (length << 8) );
1.337 +
1.338 + /* Update flags if requested */
1.339 + if( clearFlags ) {
1.340 + tmp = SCFSR2_TEND;
1.341 + if( length > SCIF_sendq.trigger ) {
1.342 + tmp |= SCFSR2_TDFE;
1.343 + intc_clear_interrupt( INT_SCIF_TXI );
1.344 + }
1.345 + tmp = MMIO_READ( SCIF, SCFSR2 ) & (~tmp);
1.346 + MMIO_WRITE( SCIF, SCFSR2, tmp );
1.347 + }
1.348 + return TRUE;
1.349 +}
1.350 +
1.351 +void SCIF_sendq_clear( void )
1.352 +{
1.353 + SCIF_sendq.head = SCIF_sendq.tail = 0;
1.354 + MMIO_WRITE( SCIF, SCFDR2, MMIO_READ( SCIF, SCFDR2 ) & 0x0F );
1.355 + MMIO_WRITE( SCIF, SCFSR2, MMIO_READ( SCIF, SCFSR2 ) | SCFSR2_TEND | SCFSR2_TDFE );
1.356 + if( IS_TRANSMIT_IRQ_ENABLED() ) {
1.357 + intc_raise_interrupt( INT_SCIF_TXI );
1.358 + }
1.359 +}
1.360 +
1.361 +
1.362 +/**
1.363 + * Set the break detected flag
1.364 + */
1.365 +void SCIF_set_break( void )
1.366 +{
1.367 + MMIO_WRITE( SCIF, SCFSR2, MMIO_READ( SCIF, SCFSR2 ) | SCFSR2_BRK );
1.368 + if( IS_RECEIVE_ERROR_IRQ_ENABLED() )
1.369 + intc_raise_interrupt( INT_SCIF_BRI );
1.370 +}
1.371 +
1.372 +const static int SCIF_CLOCK_MULTIPLIER[4] = {1, 4, 16, 64};
1.373 +
1.374 +/**
1.375 + * Calculate the current line speed.
1.376 + */
1.377 +void SCIF_update_line_speed( void )
1.378 +{
1.379 + /* If CKE1 is set, use the external clock as a base */
1.380 + if( MMIO_READ( SCIF, SCSCR2 ) & SCSCR2_CKE ) {
1.381 +
1.382 +
1.383 + } else {
1.384 +
1.385 + /* Otherwise, SH4 peripheral clock divided by n */
1.386 + int mult = SCIF_CLOCK_MULTIPLIER[MMIO_READ( SCIF, SCSMR2 ) & 0x03];
1.387 +
1.388 + /* Then process the bitrate register */
1.389 + int bbr = MMIO_READ( SCIF, SCBRR2 ) & 0xFF;
1.390 +
1.391 + int baudrate = sh4_peripheral_freq / (32 * mult * (bbr+1) );
1.392 +
1.393 + if( serial_device != NULL && serial_device->set_line_speed != NULL )
1.394 + serial_device->set_line_speed( baudrate );
1.395 + INFO( "SCIF baud rate set to %d", baudrate );
1.396 + /*
1.397 + clock_set_tick_rate( CLOCK_SCIF, baudrate / 10 );
1.398 + */
1.399 + }
1.400 +}
1.401 +
1.402 +int32_t mmio_region_SCIF_read( uint32_t reg )
1.403 +{
1.404 + switch( reg ) {
1.405 + case SCFRDR2: /* Receive data */
1.406 + return SCIF_recvq_dequeue(FALSE);
1.407 + default:
1.408 + return MMIO_READ( SCIF, reg );
1.409 + }
1.410 +}
1.411 +
1.412 +void mmio_region_SCIF_write( uint32_t reg, uint32_t val )
1.413 +{
1.414 + uint32_t tmp;
1.415 + switch( reg ) {
1.416 + case SCSMR2: /* Serial mode register */
1.417 + /* Bit 6 => 0 = 8-bit, 1 = 7-bit
1.418 + * Bit 5 => 0 = Parity disabled, 1 = parity enabled
1.419 + * Bit 4 => 0 = Even parity, 1 = Odd parity
1.420 + * Bit 3 => 0 = 1 stop bit, 1 = 2 stop bits
1.421 + * Bits 0-1 => Clock select 00 = P, 01 = P/4, 10 = P/16, 11 = P/64
1.422 + */
1.423 + val &= 0x007B;
1.424 + if( serial_device != NULL ) {
1.425 + serial_device->set_line_params( val );
1.426 + }
1.427 + tmp = MMIO_READ( SCIF, SCSMR2 );
1.428 + if( tmp & 0x03 != val & 0x03 ) {
1.429 + /* Clock change */
1.430 + SCIF_update_line_speed( );
1.431 + }
1.432 + /* Save for later read-back */
1.433 + MMIO_WRITE( SCIF, SCSMR2, val );
1.434 + break;
1.435 + case SCBRR2: /* Bit rate register */
1.436 + MMIO_WRITE( SCIF, SCBRR2, val );
1.437 + SCIF_update_line_speed( );
1.438 + break;
1.439 + case SCSCR2: /* Serial control register */
1.440 + /* Bit 7 => Transmit-FIFO-data-empty interrupt enabled
1.441 + * Bit 6 => Receive-data-full interrupt enabled
1.442 + * Bit 5 => Transmit enable
1.443 + * Bit 4 => Receive enable
1.444 + * Bit 3 => Receive-error/break interrupt enabled
1.445 + * Bit 1 => Clock enable
1.446 + */
1.447 + val &= 0x00FA;
1.448 + /* Clear any interrupts that just became disabled */
1.449 + if( val & SCSCR2_TIE == 0 )
1.450 + intc_clear_interrupt( INT_SCIF_TXI );
1.451 + if( val & SCSCR2_RIE == 0 )
1.452 + intc_clear_interrupt( INT_SCIF_RXI );
1.453 + if( val & (SCSCR2_RIE|SCSCR2_REIE) == 0 ) {
1.454 + intc_clear_interrupt( INT_SCIF_ERI );
1.455 + intc_clear_interrupt( INT_SCIF_BRI );
1.456 + }
1.457 +
1.458 + MMIO_WRITE( SCIF, reg, val );
1.459 + break;
1.460 + case SCFTDR2: /* Transmit FIFO data register */
1.461 + SCIF_sendq_enqueue( val, FALSE );
1.462 + break;
1.463 + case SCFSR2: /* Serial status register */
1.464 + /* Bits 12-15 Parity error count
1.465 + * Bits 8-11 Framing erro count
1.466 + * Bit 7 - Receive error
1.467 + * Bit 6 - Transmit end
1.468 + * Bit 5 - Transmit FIFO data empty
1.469 + * Bit 4 - Break detect
1.470 + * Bit 3 - Framing error
1.471 + * Bit 2 - Parity error
1.472 + * Bit 1 - Receive FIFO data full
1.473 + * Bit 0 - Receive data ready
1.474 + */
1.475 + tmp = MMIO_READ( SCIF, SCFSR2 );
1.476 + tmp &= val;
1.477 + /* Clear off any flags/interrupts that are being set to 0 */
1.478 + MMIO_WRITE( SCIF, reg, tmp );
1.479 + break;
1.480 + case SCFCR2: /* FIFO control register */
1.481 + val &= 0x0F;
1.482 + SCIF_recvq.trigger = SCIF_recvq_triggers[val >> 6];
1.483 + SCIF_sendq.trigger = SCIF_sendq_triggers[(val >> 4) & 0x03];
1.484 + if( val & SCFCR2_TFRST ) {
1.485 + SCIF_sendq_clear();
1.486 + }
1.487 + if( val & SCFCR2_RFRST ) {
1.488 + SCIF_recvq_clear();
1.489 + }
1.490 +
1.491 + MMIO_WRITE( SCIF, reg, val );
1.492 + break;
1.493 + case SCSPTR2: /* Serial Port Register */
1.494 + MMIO_WRITE( SCIF, reg, val );
1.495 + /* NOT IMPLEMENTED */
1.496 + break;
1.497 + case SCLSR2:
1.498 + val = val & SCLSR2_ORER;
1.499 + if( val == 0 ) {
1.500 + MMIO_WRITE( SCIF, SCLSR2, val );
1.501 + if( MMIO_READ( SCIF, SCFSR2 ) & SCFSR2_ER == 0)
1.502 + intc_clear_interrupt( INT_SCIF_ERI );
1.503 + }
1.504 +
1.505 + break;
1.506 + }
1.507 +}
1.508 +
1.509 +/**
1.510 + * Flag to indicate if data was received (ie added to the receive queue)
1.511 + * during the last SCIF clock tick. Used to determine when to set the DR
1.512 + * flag.
1.513 + */
1.514 +gboolean SCIF_rcvd_last_tick = FALSE;
1.515 +
1.516 +/**
1.517 + * Actions for a single tick of the serial clock, defined as the transmission
1.518 + * time of a single frame.
1.519 + *
1.520 + * If transmit queue is non-empty:
1.521 + * Transmit one byte and remove from queue
1.522 + * If input receive source is non-empty:
1.523 + * Transfer one byte to the receive queue (if queue is full, byte is lost)
1.524 + * If recvq is non-empty, less than the trigger level, and no data has been
1.525 + * received in the last 2 ticks (including this one), set the DR flag and
1.526 + * IRQ if appropriate.
1.527 + */
1.528 +void SCIF_clock_tick( void )
1.529 +{
1.530 + gboolean rcvd = FALSE;
1.531 +
1.532 + if( IS_LOOPBACK_ENABLED() ) {
1.533 + if( IS_TRANSMIT_ENABLED() ) {
1.534 + int val = SCIF_sendq_dequeue();
1.535 + if( val != -1 && IS_RECEIVE_ENABLED() ) {
1.536 + SCIF_recvq_enqueue( val );
1.537 + rcvd = TRUE;
1.538 + }
1.539 + }
1.540 + } else {
1.541 + if( IS_TRANSMIT_ENABLED() ) {
1.542 + int val = SCIF_sendq_dequeue();
1.543 + if( val != -1 && serial_device != NULL &&
1.544 + serial_device->receive_data != NULL ) {
1.545 + serial_device->receive_data( val );
1.546 + }
1.547 + }
1.548 +
1.549 + if( IS_RECEIVE_ENABLED() ) {
1.550 + int val = serial_transmit_dequeue();
1.551 + if( val != -1 ) {
1.552 + SCIF_recvq_enqueue( val );
1.553 + rcvd = TRUE;
1.554 + }
1.555 + }
1.556 + }
1.557 +
1.558 + /* Check if we need to set the DR flag */
1.559 + if( !rcvd && !SCIF_rcvd_last_tick &&
1.560 + SCIF_recvq.head != SCIF_recvq.tail &&
1.561 + SCIF_recvq_size() < SCIF_recvq.trigger ) {
1.562 + uint32_t tmp = MMIO_READ( SCIF, SCFSR2 );
1.563 + if( tmp & SCFSR2_DR == 0 ) {
1.564 + MMIO_WRITE( SCIF, SCFSR2, tmp | SCFSR2_DR );
1.565 + if( IS_RECEIVE_IRQ_ENABLED() )
1.566 + intc_raise_interrupt( INT_SCIF_RXI );
1.567 + }
1.568 + }
1.569 + SCIF_rcvd_last_tick = rcvd;
1.570 +}
.