filename | src/sh4/scif.c |
changeset | 929:fd8cb0c82f5f |
prev | 828:b42865f00fb5 |
next | 975:007bf7eb944f |
author | nkeynes |
date | Sat Dec 27 02:59:35 2008 +0000 (15 years ago) |
branch | lxdream-mem |
permissions | -rw-r--r-- |
last change | Replace fpscr_mask/fpscr flags in xlat_cache_block with a single xlat_sh4_mode, which tracks the field of the same name in sh4r - actually a little faster this way. Now depends on SR.MD, FPSCR.PR and FPSCR.SZ (although it doesn't benefit from the SR flag yet). Also fixed the failure to check the flags in the common case (code address returned by previous block) which took away the performance benefits, but oh well. |
file | annotate | diff | log | raw |
nkeynes@20 | 1 | /** |
nkeynes@561 | 2 | * $Id$ |
nkeynes@20 | 3 | * SCIF (Serial Communication Interface with FIFO) implementation - part of the |
nkeynes@20 | 4 | * SH4 standard on-chip peripheral set. The SCIF is hooked up to the DCs |
nkeynes@20 | 5 | * external serial port |
nkeynes@20 | 6 | * |
nkeynes@20 | 7 | * Copyright (c) 2005 Nathan Keynes. |
nkeynes@20 | 8 | * |
nkeynes@20 | 9 | * This program is free software; you can redistribute it and/or modify |
nkeynes@20 | 10 | * it under the terms of the GNU General Public License as published by |
nkeynes@20 | 11 | * the Free Software Foundation; either version 2 of the License, or |
nkeynes@20 | 12 | * (at your option) any later version. |
nkeynes@20 | 13 | * |
nkeynes@20 | 14 | * This program is distributed in the hope that it will be useful, |
nkeynes@20 | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
nkeynes@20 | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
nkeynes@20 | 17 | * GNU General Public License for more details. |
nkeynes@20 | 18 | */ |
nkeynes@20 | 19 | |
nkeynes@20 | 20 | #include <glib.h> |
nkeynes@20 | 21 | #include "dream.h" |
nkeynes@20 | 22 | #include "mem.h" |
nkeynes@54 | 23 | #include "sh4/sh4core.h" |
nkeynes@54 | 24 | #include "sh4/sh4mmio.h" |
nkeynes@54 | 25 | #include "sh4/intc.h" |
nkeynes@54 | 26 | #include "sh4/dmac.h" |
nkeynes@20 | 27 | #include "clock.h" |
nkeynes@20 | 28 | #include "serial.h" |
nkeynes@20 | 29 | |
nkeynes@20 | 30 | void SCIF_set_break(void); |
nkeynes@20 | 31 | |
nkeynes@20 | 32 | /************************* External serial interface ************************/ |
nkeynes@20 | 33 | |
nkeynes@20 | 34 | /** |
nkeynes@20 | 35 | * Note: serial_* operations are called from outside the SH4, and as such are |
nkeynes@20 | 36 | * named relative to the external serial device. SCIF_* operations are only |
nkeynes@20 | 37 | * called internally to the SH4 and so are named relative to the CPU. |
nkeynes@20 | 38 | */ |
nkeynes@20 | 39 | |
nkeynes@20 | 40 | /** |
nkeynes@20 | 41 | * Storage space for inbound/outbound data blocks. It's a little more |
nkeynes@20 | 42 | * convenient for serial consumers to be able to deal with block-sized pieces |
nkeynes@20 | 43 | * rather than a byte at a time, even if it makes all this look rather |
nkeynes@20 | 44 | * complicated. |
nkeynes@20 | 45 | * |
nkeynes@20 | 46 | * Currently there's no limit on the number of blocks that can be queued up. |
nkeynes@20 | 47 | */ |
nkeynes@20 | 48 | typedef struct serial_data_block { |
nkeynes@20 | 49 | uint32_t length; |
nkeynes@20 | 50 | uint32_t offset; |
nkeynes@20 | 51 | struct serial_data_block *next; |
nkeynes@20 | 52 | char data[]; |
nkeynes@20 | 53 | } *serial_data_block_t; |
nkeynes@20 | 54 | |
nkeynes@20 | 55 | serial_data_block_t serial_recvq_head = NULL, serial_recvq_tail = NULL; |
nkeynes@20 | 56 | serial_device_t serial_device = NULL; |
nkeynes@20 | 57 | |
nkeynes@20 | 58 | void serial_attach_device( serial_device_t dev ) |
nkeynes@20 | 59 | { |
nkeynes@20 | 60 | if( serial_device != NULL ) |
nkeynes@736 | 61 | serial_detach_device(); |
nkeynes@20 | 62 | serial_device = dev; |
nkeynes@20 | 63 | } |
nkeynes@20 | 64 | |
nkeynes@20 | 65 | |
nkeynes@20 | 66 | void serial_detach_device( void ) |
nkeynes@20 | 67 | { |
nkeynes@20 | 68 | serial_device = NULL; |
nkeynes@20 | 69 | } |
nkeynes@20 | 70 | |
nkeynes@20 | 71 | /** |
nkeynes@20 | 72 | * Add a block of data to the serial receive queue. The data will be received |
nkeynes@20 | 73 | * by the CPU at the appropriate baud rate. |
nkeynes@20 | 74 | */ |
nkeynes@20 | 75 | void serial_transmit_data( char *data, int length ) { |
nkeynes@20 | 76 | if( length == 0 ) |
nkeynes@736 | 77 | return; |
nkeynes@20 | 78 | serial_data_block_t block = |
nkeynes@736 | 79 | g_malloc( sizeof( struct serial_data_block ) + length ); |
nkeynes@20 | 80 | block->length = length; |
nkeynes@20 | 81 | block->offset = 0; |
nkeynes@20 | 82 | block->next = NULL; |
nkeynes@20 | 83 | memcpy( block->data, data, length ); |
nkeynes@736 | 84 | |
nkeynes@20 | 85 | if( serial_recvq_head == NULL ) { |
nkeynes@736 | 86 | serial_recvq_head = serial_recvq_tail = block; |
nkeynes@20 | 87 | } else { |
nkeynes@736 | 88 | serial_recvq_tail->next = block; |
nkeynes@736 | 89 | serial_recvq_tail = block; |
nkeynes@20 | 90 | } |
nkeynes@20 | 91 | } |
nkeynes@20 | 92 | |
nkeynes@20 | 93 | /** |
nkeynes@20 | 94 | * Dequeue a byte from the serial input queue |
nkeynes@20 | 95 | */ |
nkeynes@20 | 96 | static int serial_transmit_dequeue( ) { |
nkeynes@20 | 97 | if( serial_recvq_head != NULL ) { |
nkeynes@736 | 98 | uint8_t val = serial_recvq_head->data[serial_recvq_head->offset++]; |
nkeynes@736 | 99 | if( serial_recvq_head->offset >= serial_recvq_head->length ) { |
nkeynes@736 | 100 | serial_data_block_t next = serial_recvq_head->next; |
nkeynes@736 | 101 | g_free( serial_recvq_head ); |
nkeynes@736 | 102 | serial_recvq_head = next; |
nkeynes@736 | 103 | if( next == NULL ) |
nkeynes@736 | 104 | serial_recvq_tail = NULL; |
nkeynes@736 | 105 | } |
nkeynes@736 | 106 | return (int)(unsigned int)val; |
nkeynes@20 | 107 | } |
nkeynes@20 | 108 | return -1; |
nkeynes@20 | 109 | |
nkeynes@20 | 110 | } |
nkeynes@20 | 111 | |
nkeynes@20 | 112 | void serial_transmit_break() { |
nkeynes@20 | 113 | SCIF_set_break(); |
nkeynes@20 | 114 | } |
nkeynes@20 | 115 | |
nkeynes@20 | 116 | /********************************* SCIF *************************************/ |
nkeynes@20 | 117 | |
nkeynes@20 | 118 | #define FIFO_LENGTH 16 |
nkeynes@20 | 119 | #define FIFO_ARR_LENGTH (FIFO_LENGTH+1) |
nkeynes@20 | 120 | |
nkeynes@20 | 121 | /* Serial control register flags */ |
nkeynes@20 | 122 | #define SCSCR2_TIE 0x80 |
nkeynes@20 | 123 | #define SCSCR2_RIE 0x40 |
nkeynes@20 | 124 | #define SCSCR2_TE 0x20 |
nkeynes@20 | 125 | #define SCSCR2_RE 0x10 |
nkeynes@20 | 126 | #define SCSCR2_REIE 0x08 |
nkeynes@20 | 127 | #define SCSCR2_CKE 0x02 |
nkeynes@20 | 128 | |
nkeynes@20 | 129 | #define IS_TRANSMIT_IRQ_ENABLED() (MMIO_READ(SCIF,SCSCR2) & SCSCR2_TIE) |
nkeynes@20 | 130 | #define IS_RECEIVE_IRQ_ENABLED() (MMIO_READ(SCIF,SCSCR2) & SCSCR2_RIE) |
nkeynes@20 | 131 | #define IS_RECEIVE_ERROR_IRQ_ENABLED() (MMIO_READ(SCIF,SCSCR2) & (SCSCR2_RIE|SCSCR2_REIE)) |
nkeynes@20 | 132 | /* Receive is enabled if the RE bit is set in SCSCR2, and the ORER bit is cleared in SCLSR2 */ |
nkeynes@428 | 133 | #define IS_RECEIVE_ENABLED() ( (MMIO_READ(SCIF,SCSCR2) & SCSCR2_RE) && ((MMIO_READ(SCIF,SCLSR2) & SCLSR2_ORER) == 0) ) |
nkeynes@20 | 134 | /* Transmit is enabled if the TE bit is set in SCSCR2 */ |
nkeynes@20 | 135 | #define IS_TRANSMIT_ENABLED() (MMIO_READ(SCIF,SCSCR2) & SCSCR2_TE) |
nkeynes@20 | 136 | #define IS_LOOPBACK_ENABLED() (MMIO_READ(SCIF,SCFCR2) & SCFCR2_LOOP) |
nkeynes@20 | 137 | |
nkeynes@20 | 138 | /* Serial status register flags */ |
nkeynes@20 | 139 | #define SCFSR2_ER 0x80 |
nkeynes@20 | 140 | #define SCFSR2_TEND 0x40 |
nkeynes@20 | 141 | #define SCFSR2_TDFE 0x20 |
nkeynes@20 | 142 | #define SCFSR2_BRK 0x10 |
nkeynes@20 | 143 | #define SCFSR2_RDF 0x02 |
nkeynes@20 | 144 | #define SCFSR2_DR 0x01 |
nkeynes@20 | 145 | |
nkeynes@20 | 146 | /* FIFO control register flags */ |
nkeynes@20 | 147 | #define SCFCR2_MCE 0x08 |
nkeynes@20 | 148 | #define SCFCR2_TFRST 0x04 |
nkeynes@20 | 149 | #define SCFCR2_RFRST 0x02 |
nkeynes@20 | 150 | #define SCFCR2_LOOP 0x01 |
nkeynes@20 | 151 | |
nkeynes@20 | 152 | /* Line Status Register */ |
nkeynes@20 | 153 | #define SCLSR2_ORER 0x01 |
nkeynes@20 | 154 | |
nkeynes@20 | 155 | struct SCIF_fifo { |
nkeynes@20 | 156 | int head; |
nkeynes@20 | 157 | int tail; |
nkeynes@20 | 158 | int trigger; |
nkeynes@20 | 159 | uint8_t data[FIFO_ARR_LENGTH]; |
nkeynes@20 | 160 | }; |
nkeynes@20 | 161 | |
nkeynes@22 | 162 | int SCIF_recvq_triggers[4] = {1, 4, 8, 14}; |
nkeynes@22 | 163 | struct SCIF_fifo SCIF_recvq = {0,0,1}; |
nkeynes@22 | 164 | |
nkeynes@22 | 165 | int SCIF_sendq_triggers[4] = {8, 4, 2, 1}; |
nkeynes@22 | 166 | struct SCIF_fifo SCIF_sendq = {0,0,8}; |
nkeynes@22 | 167 | |
nkeynes@22 | 168 | /** |
nkeynes@22 | 169 | * Flag to indicate if data was received (ie added to the receive queue) |
nkeynes@22 | 170 | * during the last SCIF clock tick. Used to determine when to set the DR |
nkeynes@22 | 171 | * flag. |
nkeynes@22 | 172 | */ |
nkeynes@22 | 173 | gboolean SCIF_rcvd_last_tick = FALSE; |
nkeynes@22 | 174 | |
nkeynes@30 | 175 | uint32_t SCIF_tick_period = 0; |
nkeynes@30 | 176 | uint32_t SCIF_tick_remainder = 0; |
nkeynes@30 | 177 | |
nkeynes@20 | 178 | void SCIF_save_state( FILE *f ) |
nkeynes@20 | 179 | { |
nkeynes@22 | 180 | fwrite( &SCIF_recvq, sizeof(SCIF_recvq), 1, f ); |
nkeynes@22 | 181 | fwrite( &SCIF_sendq, sizeof(SCIF_sendq), 1, f ); |
nkeynes@22 | 182 | fwrite( &SCIF_rcvd_last_tick, sizeof(gboolean), 1, f ); |
nkeynes@20 | 183 | |
nkeynes@20 | 184 | } |
nkeynes@20 | 185 | |
nkeynes@20 | 186 | int SCIF_load_state( FILE *f ) |
nkeynes@20 | 187 | { |
nkeynes@22 | 188 | fread( &SCIF_recvq, sizeof(SCIF_recvq), 1, f ); |
nkeynes@22 | 189 | fread( &SCIF_sendq, sizeof(SCIF_sendq), 1, f ); |
nkeynes@22 | 190 | fread( &SCIF_rcvd_last_tick, sizeof(gboolean), 1, f ); |
nkeynes@20 | 191 | return 0; |
nkeynes@20 | 192 | } |
nkeynes@20 | 193 | |
nkeynes@20 | 194 | static inline uint8_t SCIF_recvq_size( ) |
nkeynes@20 | 195 | { |
nkeynes@20 | 196 | int val = SCIF_recvq.tail - SCIF_recvq.head; |
nkeynes@20 | 197 | if( val < 0 ) { |
nkeynes@736 | 198 | val = FIFO_ARR_LENGTH - SCIF_recvq.head + SCIF_recvq.tail; |
nkeynes@20 | 199 | } |
nkeynes@20 | 200 | return val; |
nkeynes@20 | 201 | } |
nkeynes@20 | 202 | |
nkeynes@20 | 203 | int SCIF_recvq_dequeue( gboolean clearFlags ) |
nkeynes@20 | 204 | { |
nkeynes@20 | 205 | uint8_t result; |
nkeynes@20 | 206 | uint32_t tmp, length; |
nkeynes@20 | 207 | if( SCIF_recvq.head == SCIF_recvq.tail ) |
nkeynes@736 | 208 | return -1; /* No data */ |
nkeynes@20 | 209 | result = SCIF_recvq.data[SCIF_recvq.head++]; |
nkeynes@20 | 210 | if( SCIF_recvq.head > FIFO_LENGTH ) |
nkeynes@736 | 211 | SCIF_recvq.head = 0; |
nkeynes@20 | 212 | |
nkeynes@20 | 213 | /* Update data count register */ |
nkeynes@20 | 214 | tmp = MMIO_READ( SCIF, SCFDR2 ) & 0xF0; |
nkeynes@20 | 215 | length = SCIF_recvq_size(); |
nkeynes@20 | 216 | MMIO_WRITE( SCIF, SCFDR2, tmp | length ); |
nkeynes@20 | 217 | |
nkeynes@20 | 218 | /* Clear flags (if requested ) */ |
nkeynes@20 | 219 | if( clearFlags && length < SCIF_recvq.trigger ) { |
nkeynes@736 | 220 | tmp = SCFSR2_RDF; |
nkeynes@736 | 221 | if( length == 0 ) |
nkeynes@736 | 222 | tmp |= SCFSR2_DR; |
nkeynes@736 | 223 | tmp = MMIO_READ( SCIF, SCFSR2 ) & (~tmp); |
nkeynes@736 | 224 | MMIO_WRITE( SCIF, SCFSR2, tmp ); |
nkeynes@736 | 225 | /* If both flags are cleared, clear the interrupt as well */ |
nkeynes@736 | 226 | if( (tmp & (SCFSR2_DR|SCFSR2_RDF)) == 0 && IS_RECEIVE_IRQ_ENABLED() ) |
nkeynes@736 | 227 | intc_clear_interrupt( INT_SCIF_RXI ); |
nkeynes@20 | 228 | } |
nkeynes@736 | 229 | |
nkeynes@20 | 230 | return (int)(unsigned int)result; |
nkeynes@20 | 231 | } |
nkeynes@20 | 232 | |
nkeynes@20 | 233 | gboolean SCIF_recvq_enqueue( uint8_t value ) |
nkeynes@20 | 234 | { |
nkeynes@20 | 235 | uint32_t tmp, length; |
nkeynes@20 | 236 | int newpos = SCIF_recvq.tail + 1; |
nkeynes@20 | 237 | if( newpos > FIFO_LENGTH ) |
nkeynes@736 | 238 | newpos = 0; |
nkeynes@20 | 239 | if( newpos == SCIF_recvq.head ) { |
nkeynes@736 | 240 | /* FIFO full - set ORER and discard the value */ |
nkeynes@736 | 241 | MMIO_WRITE( SCIF, SCLSR2, SCLSR2_ORER ); |
nkeynes@736 | 242 | if( IS_RECEIVE_ERROR_IRQ_ENABLED() ) |
nkeynes@736 | 243 | intc_raise_interrupt( INT_SCIF_ERI ); |
nkeynes@736 | 244 | return FALSE; |
nkeynes@20 | 245 | } |
nkeynes@20 | 246 | SCIF_recvq.data[SCIF_recvq.tail] = value; |
nkeynes@20 | 247 | |
nkeynes@20 | 248 | /* Update data count register */ |
nkeynes@20 | 249 | tmp = MMIO_READ( SCIF, SCFDR2 ) & 0xF0; |
nkeynes@20 | 250 | length = SCIF_recvq_size(); |
nkeynes@20 | 251 | MMIO_WRITE( SCIF, SCFDR2, tmp | length ); |
nkeynes@20 | 252 | |
nkeynes@20 | 253 | /* Update status register */ |
nkeynes@20 | 254 | tmp = MMIO_READ( SCIF, SCFSR2 ); |
nkeynes@20 | 255 | if( length >= SCIF_recvq.trigger ) { |
nkeynes@736 | 256 | tmp |= SCFSR2_RDF; |
nkeynes@736 | 257 | if( IS_RECEIVE_IRQ_ENABLED() ) |
nkeynes@736 | 258 | intc_raise_interrupt( INT_SCIF_RXI ); |
nkeynes@54 | 259 | DMAC_trigger( DMAC_SCIF_RDF ); |
nkeynes@20 | 260 | } |
nkeynes@20 | 261 | MMIO_WRITE( SCIF, SCFSR2, tmp ); |
nkeynes@20 | 262 | return TRUE; |
nkeynes@20 | 263 | } |
nkeynes@20 | 264 | |
nkeynes@20 | 265 | |
nkeynes@20 | 266 | /** |
nkeynes@20 | 267 | * Reset the receive FIFO to its initial state. Manual is unclear as to |
nkeynes@20 | 268 | * whether this also clears flags/interrupts, but we're assuming here that |
nkeynes@20 | 269 | * it does until proven otherwise. |
nkeynes@20 | 270 | */ |
nkeynes@20 | 271 | void SCIF_recvq_clear( void ) |
nkeynes@20 | 272 | { |
nkeynes@20 | 273 | SCIF_recvq.head = SCIF_recvq.tail = 0; |
nkeynes@20 | 274 | MMIO_WRITE( SCIF, SCFDR2, MMIO_READ( SCIF, SCFDR2 ) & 0xF0 ); |
nkeynes@20 | 275 | MMIO_WRITE( SCIF, SCFSR2, MMIO_READ( SCIF, SCFSR2 ) & ~(SCFSR2_DR|SCFSR2_RDF) ); |
nkeynes@21 | 276 | if( IS_RECEIVE_IRQ_ENABLED() ) |
nkeynes@736 | 277 | intc_clear_interrupt( INT_SCIF_RXI ); |
nkeynes@20 | 278 | } |
nkeynes@20 | 279 | |
nkeynes@20 | 280 | static inline uint8_t SCIF_sendq_size( ) |
nkeynes@20 | 281 | { |
nkeynes@20 | 282 | int val = SCIF_sendq.tail - SCIF_sendq.head; |
nkeynes@20 | 283 | if( val < 0 ) { |
nkeynes@736 | 284 | val = FIFO_ARR_LENGTH - SCIF_sendq.head + SCIF_sendq.tail; |
nkeynes@20 | 285 | } |
nkeynes@20 | 286 | return val; |
nkeynes@20 | 287 | } |
nkeynes@20 | 288 | |
nkeynes@20 | 289 | /** |
nkeynes@20 | 290 | * Dequeue one byte from the SCIF transmit queue (ie transmit the byte), |
nkeynes@20 | 291 | * updating all status flags as required. |
nkeynes@20 | 292 | * @return The byte dequeued, or -1 if the queue is empty. |
nkeynes@20 | 293 | */ |
nkeynes@20 | 294 | int SCIF_sendq_dequeue( ) |
nkeynes@20 | 295 | { |
nkeynes@20 | 296 | uint8_t result; |
nkeynes@20 | 297 | uint32_t tmp, length; |
nkeynes@20 | 298 | if( SCIF_sendq.head == SCIF_sendq.tail ) |
nkeynes@736 | 299 | return -1; /* No data */ |
nkeynes@20 | 300 | |
nkeynes@20 | 301 | /* Update queue head pointer */ |
nkeynes@20 | 302 | result = SCIF_sendq.data[SCIF_sendq.head++]; |
nkeynes@20 | 303 | if( SCIF_sendq.head > FIFO_LENGTH ) |
nkeynes@736 | 304 | SCIF_sendq.head = 0; |
nkeynes@20 | 305 | |
nkeynes@20 | 306 | /* Update data count register */ |
nkeynes@20 | 307 | tmp = MMIO_READ( SCIF, SCFDR2 ) & 0x0F; |
nkeynes@20 | 308 | length = SCIF_sendq_size(); |
nkeynes@20 | 309 | MMIO_WRITE( SCIF, SCFDR2, tmp | (length << 8) ); |
nkeynes@736 | 310 | |
nkeynes@20 | 311 | /* Update status register */ |
nkeynes@20 | 312 | if( length <= SCIF_sendq.trigger ) { |
nkeynes@736 | 313 | tmp = MMIO_READ( SCIF, SCFSR2 ) | SCFSR2_TDFE; |
nkeynes@736 | 314 | if( length == 0 ) |
nkeynes@736 | 315 | tmp |= SCFSR2_TEND; /* Transmission ended - no data waiting */ |
nkeynes@736 | 316 | if( IS_TRANSMIT_IRQ_ENABLED() ) |
nkeynes@736 | 317 | intc_raise_interrupt( INT_SCIF_TXI ); |
nkeynes@54 | 318 | DMAC_trigger( DMAC_SCIF_TDE ); |
nkeynes@736 | 319 | MMIO_WRITE( SCIF, SCFSR2, tmp ); |
nkeynes@20 | 320 | } |
nkeynes@20 | 321 | return (int)(unsigned int)result; |
nkeynes@20 | 322 | } |
nkeynes@20 | 323 | |
nkeynes@20 | 324 | /** |
nkeynes@20 | 325 | * Enqueue a single byte in the SCIF transmit queue. If the queue is full, |
nkeynes@20 | 326 | * the value will be discarded. |
nkeynes@20 | 327 | * @param value to be queued. |
nkeynes@20 | 328 | * @param clearFlags TRUE if the TEND/TDFE flags should be cleared |
nkeynes@20 | 329 | * if the queue exceeds the trigger level. (According to the manual, |
nkeynes@20 | 330 | * DMAC writes will clear the flag, whereas regular SH4 writes do NOT |
nkeynes@20 | 331 | * automatically clear it. Go figure). |
nkeynes@20 | 332 | * @return gboolean TRUE if the value was queued, FALSE if the queue was |
nkeynes@20 | 333 | * full. |
nkeynes@20 | 334 | */ |
nkeynes@20 | 335 | gboolean SCIF_sendq_enqueue( uint8_t value, gboolean clearFlags ) |
nkeynes@20 | 336 | { |
nkeynes@20 | 337 | uint32_t tmp, length; |
nkeynes@20 | 338 | int newpos = SCIF_sendq.tail + 1; |
nkeynes@20 | 339 | if( newpos > FIFO_LENGTH ) |
nkeynes@736 | 340 | newpos = 0; |
nkeynes@20 | 341 | if( newpos == SCIF_sendq.head ) { |
nkeynes@736 | 342 | /* FIFO full - discard */ |
nkeynes@736 | 343 | return FALSE; |
nkeynes@20 | 344 | } |
nkeynes@20 | 345 | SCIF_sendq.data[SCIF_sendq.tail] = value; |
nkeynes@20 | 346 | SCIF_sendq.tail = newpos; |
nkeynes@20 | 347 | |
nkeynes@20 | 348 | /* Update data count register */ |
nkeynes@20 | 349 | tmp = MMIO_READ( SCIF, SCFDR2 ) & 0x0F; |
nkeynes@20 | 350 | length = SCIF_sendq_size(); |
nkeynes@20 | 351 | MMIO_WRITE( SCIF, SCFDR2, tmp | (length << 8) ); |
nkeynes@736 | 352 | |
nkeynes@20 | 353 | /* Update flags if requested */ |
nkeynes@20 | 354 | if( clearFlags ) { |
nkeynes@736 | 355 | tmp = SCFSR2_TEND; |
nkeynes@736 | 356 | if( length > SCIF_sendq.trigger ) { |
nkeynes@736 | 357 | tmp |= SCFSR2_TDFE; |
nkeynes@736 | 358 | if( IS_TRANSMIT_IRQ_ENABLED() ) |
nkeynes@736 | 359 | intc_clear_interrupt( INT_SCIF_TXI ); |
nkeynes@736 | 360 | } |
nkeynes@736 | 361 | tmp = MMIO_READ( SCIF, SCFSR2 ) & (~tmp); |
nkeynes@736 | 362 | MMIO_WRITE( SCIF, SCFSR2, tmp ); |
nkeynes@20 | 363 | } |
nkeynes@20 | 364 | return TRUE; |
nkeynes@20 | 365 | } |
nkeynes@20 | 366 | |
nkeynes@20 | 367 | void SCIF_sendq_clear( void ) |
nkeynes@20 | 368 | { |
nkeynes@20 | 369 | SCIF_sendq.head = SCIF_sendq.tail = 0; |
nkeynes@20 | 370 | MMIO_WRITE( SCIF, SCFDR2, MMIO_READ( SCIF, SCFDR2 ) & 0x0F ); |
nkeynes@20 | 371 | MMIO_WRITE( SCIF, SCFSR2, MMIO_READ( SCIF, SCFSR2 ) | SCFSR2_TEND | SCFSR2_TDFE ); |
nkeynes@20 | 372 | if( IS_TRANSMIT_IRQ_ENABLED() ) { |
nkeynes@736 | 373 | intc_raise_interrupt( INT_SCIF_TXI ); |
nkeynes@54 | 374 | DMAC_trigger( DMAC_SCIF_TDE ); |
nkeynes@20 | 375 | } |
nkeynes@20 | 376 | } |
nkeynes@20 | 377 | |
nkeynes@21 | 378 | /** |
nkeynes@21 | 379 | * Update the SCFSR2 status register with the given mask (ie clear any values |
nkeynes@21 | 380 | * that are set to 0 in the mask. According to a strict reading of the doco |
nkeynes@21 | 381 | * though, the bits will only actually clear if the flag state is no longer |
nkeynes@21 | 382 | * true, so we need to recheck everything... |
nkeynes@21 | 383 | */ |
nkeynes@21 | 384 | void SCIF_update_status( uint32_t mask ) |
nkeynes@21 | 385 | { |
nkeynes@21 | 386 | uint32_t value = MMIO_READ( SCIF, SCFSR2 ); |
nkeynes@21 | 387 | uint32_t result = value & mask; |
nkeynes@21 | 388 | uint32_t sendq_size = SCIF_sendq_size(); |
nkeynes@21 | 389 | uint32_t recvq_size = SCIF_recvq_size(); |
nkeynes@21 | 390 | |
nkeynes@21 | 391 | if( sendq_size != 0 ) |
nkeynes@736 | 392 | result |= SCFSR2_TEND; |
nkeynes@21 | 393 | |
nkeynes@21 | 394 | if( sendq_size <= SCIF_sendq.trigger ) |
nkeynes@736 | 395 | result |= SCFSR2_TDFE; |
nkeynes@428 | 396 | else if( (result & SCFSR2_TDFE) == 0 && IS_TRANSMIT_IRQ_ENABLED() ) |
nkeynes@736 | 397 | intc_clear_interrupt( INT_SCIF_TXI ); |
nkeynes@21 | 398 | |
nkeynes@21 | 399 | if( recvq_size >= SCIF_recvq.trigger ) |
nkeynes@736 | 400 | result |= SCFSR2_RDF; |
nkeynes@21 | 401 | if( (value & SCFSR2_DR) != 0 && (result & SCFSR2_DR) == 0 && |
nkeynes@736 | 402 | recvq_size != 0 ) |
nkeynes@736 | 403 | result |= SCFSR2_DR; |
nkeynes@21 | 404 | if( (result & (SCFSR2_DR|SCFSR2_RDF)) == 0 && IS_RECEIVE_IRQ_ENABLED() ) |
nkeynes@736 | 405 | intc_clear_interrupt( INT_SCIF_RXI ); |
nkeynes@21 | 406 | |
nkeynes@21 | 407 | if( IS_RECEIVE_ERROR_IRQ_ENABLED() ) { |
nkeynes@736 | 408 | if( (result & SCFSR2_BRK) == 0 ) |
nkeynes@736 | 409 | intc_clear_interrupt( INT_SCIF_BRI ); |
nkeynes@736 | 410 | if( (result & SCFSR2_ER) == 0 && |
nkeynes@736 | 411 | (MMIO_READ( SCIF, SCLSR2 ) & SCLSR2_ORER) == 0 ) |
nkeynes@736 | 412 | intc_clear_interrupt( INT_SCIF_ERI ); |
nkeynes@21 | 413 | } |
nkeynes@21 | 414 | } |
nkeynes@20 | 415 | |
nkeynes@20 | 416 | /** |
nkeynes@20 | 417 | * Set the break detected flag |
nkeynes@20 | 418 | */ |
nkeynes@20 | 419 | void SCIF_set_break( void ) |
nkeynes@20 | 420 | { |
nkeynes@20 | 421 | MMIO_WRITE( SCIF, SCFSR2, MMIO_READ( SCIF, SCFSR2 ) | SCFSR2_BRK ); |
nkeynes@20 | 422 | if( IS_RECEIVE_ERROR_IRQ_ENABLED() ) |
nkeynes@736 | 423 | intc_raise_interrupt( INT_SCIF_BRI ); |
nkeynes@20 | 424 | } |
nkeynes@20 | 425 | |
nkeynes@20 | 426 | const static int SCIF_CLOCK_MULTIPLIER[4] = {1, 4, 16, 64}; |
nkeynes@20 | 427 | |
nkeynes@20 | 428 | /** |
nkeynes@20 | 429 | * Calculate the current line speed. |
nkeynes@20 | 430 | */ |
nkeynes@20 | 431 | void SCIF_update_line_speed( void ) |
nkeynes@20 | 432 | { |
nkeynes@20 | 433 | /* If CKE1 is set, use the external clock as a base */ |
nkeynes@20 | 434 | if( MMIO_READ( SCIF, SCSCR2 ) & SCSCR2_CKE ) { |
nkeynes@20 | 435 | |
nkeynes@20 | 436 | |
nkeynes@20 | 437 | } else { |
nkeynes@20 | 438 | |
nkeynes@736 | 439 | /* Otherwise, SH4 peripheral clock divided by n */ |
nkeynes@736 | 440 | int mult = SCIF_CLOCK_MULTIPLIER[MMIO_READ( SCIF, SCSMR2 ) & 0x03]; |
nkeynes@20 | 441 | |
nkeynes@736 | 442 | /* Then process the bitrate register */ |
nkeynes@736 | 443 | int bbr = MMIO_READ( SCIF, SCBRR2 ) & 0xFF; |
nkeynes@30 | 444 | |
nkeynes@736 | 445 | int baudrate = sh4_peripheral_freq / (32 * mult * (bbr+1) ); |
nkeynes@30 | 446 | |
nkeynes@736 | 447 | if( serial_device != NULL && serial_device->set_line_speed != NULL ) |
nkeynes@736 | 448 | serial_device->set_line_speed( baudrate ); |
nkeynes@736 | 449 | |
nkeynes@736 | 450 | SCIF_tick_period = sh4_peripheral_period * (32 * mult * (bbr+1)); |
nkeynes@736 | 451 | |
nkeynes@736 | 452 | /* |
nkeynes@20 | 453 | clock_set_tick_rate( CLOCK_SCIF, baudrate / 10 ); |
nkeynes@736 | 454 | */ |
nkeynes@20 | 455 | } |
nkeynes@20 | 456 | } |
nkeynes@20 | 457 | |
nkeynes@929 | 458 | MMIO_REGION_READ_FN( SCIF, reg ) |
nkeynes@20 | 459 | { |
nkeynes@929 | 460 | reg &= 0xFFF; |
nkeynes@20 | 461 | switch( reg ) { |
nkeynes@20 | 462 | case SCFRDR2: /* Receive data */ |
nkeynes@736 | 463 | return SCIF_recvq_dequeue(FALSE); |
nkeynes@20 | 464 | default: |
nkeynes@736 | 465 | return MMIO_READ( SCIF, reg ); |
nkeynes@20 | 466 | } |
nkeynes@20 | 467 | } |
nkeynes@20 | 468 | |
nkeynes@929 | 469 | MMIO_REGION_WRITE_FN( SCIF, reg, val ) |
nkeynes@20 | 470 | { |
nkeynes@20 | 471 | uint32_t tmp; |
nkeynes@929 | 472 | reg &= 0xFFF; |
nkeynes@20 | 473 | switch( reg ) { |
nkeynes@20 | 474 | case SCSMR2: /* Serial mode register */ |
nkeynes@736 | 475 | /* Bit 6 => 0 = 8-bit, 1 = 7-bit |
nkeynes@736 | 476 | * Bit 5 => 0 = Parity disabled, 1 = parity enabled |
nkeynes@736 | 477 | * Bit 4 => 0 = Even parity, 1 = Odd parity |
nkeynes@736 | 478 | * Bit 3 => 0 = 1 stop bit, 1 = 2 stop bits |
nkeynes@736 | 479 | * Bits 0-1 => Clock select 00 = P, 01 = P/4, 10 = P/16, 11 = P/64 |
nkeynes@736 | 480 | */ |
nkeynes@736 | 481 | val &= 0x007B; |
nkeynes@736 | 482 | if( serial_device != NULL ) { |
nkeynes@736 | 483 | serial_device->set_line_params( val ); |
nkeynes@736 | 484 | } |
nkeynes@736 | 485 | tmp = MMIO_READ( SCIF, SCSMR2 ); |
nkeynes@736 | 486 | if( (tmp & 0x03) != (val & 0x03) ) { |
nkeynes@736 | 487 | /* Clock change */ |
nkeynes@736 | 488 | SCIF_update_line_speed( ); |
nkeynes@736 | 489 | } |
nkeynes@736 | 490 | /* Save for later read-back */ |
nkeynes@736 | 491 | MMIO_WRITE( SCIF, SCSMR2, val ); |
nkeynes@736 | 492 | break; |
nkeynes@20 | 493 | case SCBRR2: /* Bit rate register */ |
nkeynes@736 | 494 | MMIO_WRITE( SCIF, SCBRR2, val ); |
nkeynes@736 | 495 | SCIF_update_line_speed( ); |
nkeynes@736 | 496 | break; |
nkeynes@20 | 497 | case SCSCR2: /* Serial control register */ |
nkeynes@736 | 498 | /* Bit 7 => Transmit-FIFO-data-empty interrupt enabled |
nkeynes@736 | 499 | * Bit 6 => Receive-data-full interrupt enabled |
nkeynes@736 | 500 | * Bit 5 => Transmit enable |
nkeynes@736 | 501 | * Bit 4 => Receive enable |
nkeynes@736 | 502 | * Bit 3 => Receive-error/break interrupt enabled |
nkeynes@736 | 503 | * Bit 1 => Clock enable |
nkeynes@736 | 504 | */ |
nkeynes@736 | 505 | val &= 0x00FA; |
nkeynes@736 | 506 | /* Clear any interrupts that just became disabled */ |
nkeynes@736 | 507 | if( (val & SCSCR2_TIE) == 0 ) |
nkeynes@736 | 508 | intc_clear_interrupt( INT_SCIF_TXI ); |
nkeynes@736 | 509 | if( (val & SCSCR2_RIE) == 0 ) |
nkeynes@736 | 510 | intc_clear_interrupt( INT_SCIF_RXI ); |
nkeynes@736 | 511 | if( (val & (SCSCR2_RIE|SCSCR2_REIE)) == 0 ) { |
nkeynes@736 | 512 | intc_clear_interrupt( INT_SCIF_ERI ); |
nkeynes@736 | 513 | intc_clear_interrupt( INT_SCIF_BRI ); |
nkeynes@736 | 514 | } |
nkeynes@736 | 515 | |
nkeynes@736 | 516 | MMIO_WRITE( SCIF, reg, val ); |
nkeynes@736 | 517 | break; |
nkeynes@20 | 518 | case SCFTDR2: /* Transmit FIFO data register */ |
nkeynes@736 | 519 | SCIF_sendq_enqueue( val, FALSE ); |
nkeynes@736 | 520 | break; |
nkeynes@20 | 521 | case SCFSR2: /* Serial status register */ |
nkeynes@736 | 522 | /* Bits 12-15 Parity error count |
nkeynes@736 | 523 | * Bits 8-11 Framing erro count |
nkeynes@736 | 524 | * Bit 7 - Receive error |
nkeynes@736 | 525 | * Bit 6 - Transmit end |
nkeynes@736 | 526 | * Bit 5 - Transmit FIFO data empty |
nkeynes@736 | 527 | * Bit 4 - Break detect |
nkeynes@736 | 528 | * Bit 3 - Framing error |
nkeynes@736 | 529 | * Bit 2 - Parity error |
nkeynes@736 | 530 | * Bit 1 - Receive FIFO data full |
nkeynes@736 | 531 | * Bit 0 - Receive data ready |
nkeynes@736 | 532 | */ |
nkeynes@736 | 533 | /* Clear off any flags/interrupts that are being set to 0 */ |
nkeynes@736 | 534 | SCIF_update_status( val ); |
nkeynes@736 | 535 | break; |
nkeynes@20 | 536 | case SCFCR2: /* FIFO control register */ |
nkeynes@736 | 537 | val &= 0x0F; |
nkeynes@736 | 538 | SCIF_recvq.trigger = SCIF_recvq_triggers[val >> 6]; |
nkeynes@736 | 539 | SCIF_sendq.trigger = SCIF_sendq_triggers[(val >> 4) & 0x03]; |
nkeynes@736 | 540 | if( val & SCFCR2_TFRST ) { |
nkeynes@736 | 541 | SCIF_sendq_clear(); |
nkeynes@736 | 542 | } |
nkeynes@736 | 543 | if( val & SCFCR2_RFRST ) { |
nkeynes@736 | 544 | SCIF_recvq_clear(); |
nkeynes@736 | 545 | } |
nkeynes@20 | 546 | |
nkeynes@736 | 547 | MMIO_WRITE( SCIF, reg, val ); |
nkeynes@736 | 548 | break; |
nkeynes@20 | 549 | case SCSPTR2: /* Serial Port Register */ |
nkeynes@736 | 550 | MMIO_WRITE( SCIF, reg, val ); |
nkeynes@828 | 551 | /* NOT IMPLEMENTED - 'direct' serial I/O */ |
nkeynes@828 | 552 | if( val != 0 ) { |
nkeynes@828 | 553 | WARN( "SCSPTR2 not implemented: Write %08X", val ); |
nkeynes@828 | 554 | } |
nkeynes@736 | 555 | break; |
nkeynes@20 | 556 | case SCLSR2: |
nkeynes@736 | 557 | val = val & SCLSR2_ORER; |
nkeynes@736 | 558 | if( val == 0 ) { |
nkeynes@736 | 559 | MMIO_WRITE( SCIF, SCLSR2, val ); |
nkeynes@736 | 560 | if( (MMIO_READ( SCIF, SCFSR2 ) & SCFSR2_ER) == 0 && |
nkeynes@736 | 561 | IS_RECEIVE_ERROR_IRQ_ENABLED() ) |
nkeynes@736 | 562 | intc_clear_interrupt( INT_SCIF_ERI ); |
nkeynes@736 | 563 | } |
nkeynes@736 | 564 | |
nkeynes@736 | 565 | break; |
nkeynes@20 | 566 | } |
nkeynes@20 | 567 | } |
nkeynes@20 | 568 | |
nkeynes@20 | 569 | /** |
nkeynes@20 | 570 | * Actions for a single tick of the serial clock, defined as the transmission |
nkeynes@20 | 571 | * time of a single frame. |
nkeynes@20 | 572 | * |
nkeynes@20 | 573 | * If transmit queue is non-empty: |
nkeynes@20 | 574 | * Transmit one byte and remove from queue |
nkeynes@20 | 575 | * If input receive source is non-empty: |
nkeynes@20 | 576 | * Transfer one byte to the receive queue (if queue is full, byte is lost) |
nkeynes@20 | 577 | * If recvq is non-empty, less than the trigger level, and no data has been |
nkeynes@20 | 578 | * received in the last 2 ticks (including this one), set the DR flag and |
nkeynes@20 | 579 | * IRQ if appropriate. |
nkeynes@20 | 580 | */ |
nkeynes@20 | 581 | void SCIF_clock_tick( void ) |
nkeynes@20 | 582 | { |
nkeynes@20 | 583 | gboolean rcvd = FALSE; |
nkeynes@20 | 584 | |
nkeynes@20 | 585 | if( IS_LOOPBACK_ENABLED() ) { |
nkeynes@736 | 586 | if( IS_TRANSMIT_ENABLED() ) { |
nkeynes@736 | 587 | int val = SCIF_sendq_dequeue(); |
nkeynes@736 | 588 | if( val != -1 && IS_RECEIVE_ENABLED() ) { |
nkeynes@736 | 589 | SCIF_recvq_enqueue( val ); |
nkeynes@736 | 590 | rcvd = TRUE; |
nkeynes@736 | 591 | } |
nkeynes@736 | 592 | } |
nkeynes@20 | 593 | } else { |
nkeynes@736 | 594 | if( IS_TRANSMIT_ENABLED() ) { |
nkeynes@736 | 595 | int val = SCIF_sendq_dequeue(); |
nkeynes@736 | 596 | if( val != -1 && serial_device != NULL && |
nkeynes@736 | 597 | serial_device->receive_data != NULL ) { |
nkeynes@736 | 598 | serial_device->receive_data( val ); |
nkeynes@736 | 599 | } |
nkeynes@736 | 600 | } |
nkeynes@736 | 601 | |
nkeynes@736 | 602 | if( IS_RECEIVE_ENABLED() ) { |
nkeynes@736 | 603 | int val = serial_transmit_dequeue(); |
nkeynes@736 | 604 | if( val != -1 ) { |
nkeynes@736 | 605 | SCIF_recvq_enqueue( val ); |
nkeynes@736 | 606 | rcvd = TRUE; |
nkeynes@736 | 607 | } |
nkeynes@736 | 608 | } |
nkeynes@20 | 609 | } |
nkeynes@20 | 610 | |
nkeynes@20 | 611 | /* Check if we need to set the DR flag */ |
nkeynes@20 | 612 | if( !rcvd && !SCIF_rcvd_last_tick && |
nkeynes@736 | 613 | SCIF_recvq.head != SCIF_recvq.tail && |
nkeynes@736 | 614 | SCIF_recvq_size() < SCIF_recvq.trigger ) { |
nkeynes@736 | 615 | uint32_t tmp = MMIO_READ( SCIF, SCFSR2 ); |
nkeynes@736 | 616 | if( (tmp & SCFSR2_DR) == 0 ) { |
nkeynes@736 | 617 | MMIO_WRITE( SCIF, SCFSR2, tmp | SCFSR2_DR ); |
nkeynes@736 | 618 | if( IS_RECEIVE_IRQ_ENABLED() ) |
nkeynes@736 | 619 | intc_raise_interrupt( INT_SCIF_RXI ); |
nkeynes@736 | 620 | DMAC_trigger( DMAC_SCIF_RDF ); |
nkeynes@736 | 621 | } |
nkeynes@20 | 622 | } |
nkeynes@20 | 623 | SCIF_rcvd_last_tick = rcvd; |
nkeynes@20 | 624 | } |
nkeynes@23 | 625 | |
nkeynes@30 | 626 | void SCIF_reset( void ) |
nkeynes@23 | 627 | { |
nkeynes@32 | 628 | SCIF_recvq_clear(); |
nkeynes@32 | 629 | SCIF_sendq_clear(); |
nkeynes@32 | 630 | SCIF_update_line_speed(); |
nkeynes@23 | 631 | } |
nkeynes@30 | 632 | |
nkeynes@30 | 633 | void SCIF_run_slice( uint32_t nanosecs ) |
nkeynes@30 | 634 | { |
nkeynes@30 | 635 | SCIF_tick_remainder += nanosecs; |
nkeynes@30 | 636 | while( SCIF_tick_remainder >= SCIF_tick_period ) { |
nkeynes@736 | 637 | SCIF_tick_remainder -= SCIF_tick_period; |
nkeynes@736 | 638 | SCIF_clock_tick(); |
nkeynes@30 | 639 | } |
nkeynes@30 | 640 | } |
.