nkeynes@20: /** nkeynes@23: * $Id: scif.c,v 1.4 2005-12-23 11:44:55 nkeynes Exp $ nkeynes@20: * SCIF (Serial Communication Interface with FIFO) implementation - part of the nkeynes@20: * SH4 standard on-chip peripheral set. The SCIF is hooked up to the DCs nkeynes@20: * external serial port nkeynes@20: * nkeynes@20: * Copyright (c) 2005 Nathan Keynes. nkeynes@20: * nkeynes@20: * This program is free software; you can redistribute it and/or modify nkeynes@20: * it under the terms of the GNU General Public License as published by nkeynes@20: * the Free Software Foundation; either version 2 of the License, or nkeynes@20: * (at your option) any later version. nkeynes@20: * nkeynes@20: * This program is distributed in the hope that it will be useful, nkeynes@20: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@20: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@20: * GNU General Public License for more details. nkeynes@20: */ nkeynes@20: nkeynes@20: #include nkeynes@20: #include "dream.h" nkeynes@20: #include "mem.h" nkeynes@20: #include "sh4core.h" nkeynes@20: #include "sh4mmio.h" nkeynes@20: #include "intc.h" nkeynes@20: #include "clock.h" nkeynes@20: #include "serial.h" nkeynes@20: #include "modules.h" nkeynes@20: nkeynes@20: void SCIF_set_break(void); nkeynes@20: nkeynes@20: /************************* External serial interface ************************/ nkeynes@20: nkeynes@20: /** nkeynes@20: * Note: serial_* operations are called from outside the SH4, and as such are nkeynes@20: * named relative to the external serial device. SCIF_* operations are only nkeynes@20: * called internally to the SH4 and so are named relative to the CPU. nkeynes@20: */ nkeynes@20: nkeynes@20: /** nkeynes@20: * Storage space for inbound/outbound data blocks. It's a little more nkeynes@20: * convenient for serial consumers to be able to deal with block-sized pieces nkeynes@20: * rather than a byte at a time, even if it makes all this look rather nkeynes@20: * complicated. nkeynes@20: * nkeynes@20: * Currently there's no limit on the number of blocks that can be queued up. nkeynes@20: */ nkeynes@20: typedef struct serial_data_block { nkeynes@20: uint32_t length; nkeynes@20: uint32_t offset; nkeynes@20: struct serial_data_block *next; nkeynes@20: char data[]; nkeynes@20: } *serial_data_block_t; nkeynes@20: nkeynes@20: serial_data_block_t serial_recvq_head = NULL, serial_recvq_tail = NULL; nkeynes@20: serial_device_t serial_device = NULL; nkeynes@20: nkeynes@20: void serial_attach_device( serial_device_t dev ) nkeynes@20: { nkeynes@20: if( serial_device != NULL ) nkeynes@20: serial_detach_device(); nkeynes@20: serial_device = dev; nkeynes@20: } nkeynes@20: nkeynes@20: nkeynes@20: void serial_detach_device( void ) nkeynes@20: { nkeynes@20: serial_device = NULL; nkeynes@20: } nkeynes@20: nkeynes@20: /** nkeynes@20: * Add a block of data to the serial receive queue. The data will be received nkeynes@20: * by the CPU at the appropriate baud rate. nkeynes@20: */ nkeynes@20: void serial_transmit_data( char *data, int length ) { nkeynes@20: if( length == 0 ) nkeynes@20: return; nkeynes@20: serial_data_block_t block = nkeynes@20: g_malloc( sizeof( struct serial_data_block ) + length ); nkeynes@20: block->length = length; nkeynes@20: block->offset = 0; nkeynes@20: block->next = NULL; nkeynes@20: memcpy( block->data, data, length ); nkeynes@20: nkeynes@20: if( serial_recvq_head == NULL ) { nkeynes@20: serial_recvq_head = serial_recvq_tail = block; nkeynes@20: } else { nkeynes@20: serial_recvq_tail->next = block; nkeynes@20: serial_recvq_tail = block; nkeynes@20: } nkeynes@20: } nkeynes@20: nkeynes@20: /** nkeynes@20: * Dequeue a byte from the serial input queue nkeynes@20: */ nkeynes@20: static int serial_transmit_dequeue( ) { nkeynes@20: if( serial_recvq_head != NULL ) { nkeynes@20: uint8_t val = serial_recvq_head->data[serial_recvq_head->offset++]; nkeynes@20: if( serial_recvq_head->offset >= serial_recvq_head->length ) { nkeynes@20: serial_data_block_t next = serial_recvq_head->next; nkeynes@20: g_free( serial_recvq_head ); nkeynes@20: serial_recvq_head = next; nkeynes@20: if( next == NULL ) nkeynes@20: serial_recvq_tail = NULL; nkeynes@20: } nkeynes@20: return (int)(unsigned int)val; nkeynes@20: } nkeynes@20: return -1; nkeynes@20: nkeynes@20: } nkeynes@20: nkeynes@20: void serial_transmit_break() { nkeynes@20: SCIF_set_break(); nkeynes@20: } nkeynes@20: nkeynes@20: /********************************* SCIF *************************************/ nkeynes@20: nkeynes@20: #define FIFO_LENGTH 16 nkeynes@20: #define FIFO_ARR_LENGTH (FIFO_LENGTH+1) nkeynes@20: nkeynes@20: /* Serial control register flags */ nkeynes@20: #define SCSCR2_TIE 0x80 nkeynes@20: #define SCSCR2_RIE 0x40 nkeynes@20: #define SCSCR2_TE 0x20 nkeynes@20: #define SCSCR2_RE 0x10 nkeynes@20: #define SCSCR2_REIE 0x08 nkeynes@20: #define SCSCR2_CKE 0x02 nkeynes@20: nkeynes@20: #define IS_TRANSMIT_IRQ_ENABLED() (MMIO_READ(SCIF,SCSCR2) & SCSCR2_TIE) nkeynes@20: #define IS_RECEIVE_IRQ_ENABLED() (MMIO_READ(SCIF,SCSCR2) & SCSCR2_RIE) nkeynes@20: #define IS_RECEIVE_ERROR_IRQ_ENABLED() (MMIO_READ(SCIF,SCSCR2) & (SCSCR2_RIE|SCSCR2_REIE)) nkeynes@20: /* Receive is enabled if the RE bit is set in SCSCR2, and the ORER bit is cleared in SCLSR2 */ nkeynes@20: #define IS_RECEIVE_ENABLED() ( (MMIO_READ(SCIF,SCSCR2) & SCSCR2_RE) && (MMIO_READ(SCIF,SCLSR2) & SCLSR2_ORER == 0) ) nkeynes@20: /* Transmit is enabled if the TE bit is set in SCSCR2 */ nkeynes@20: #define IS_TRANSMIT_ENABLED() (MMIO_READ(SCIF,SCSCR2) & SCSCR2_TE) nkeynes@20: #define IS_LOOPBACK_ENABLED() (MMIO_READ(SCIF,SCFCR2) & SCFCR2_LOOP) nkeynes@20: nkeynes@20: /* Serial status register flags */ nkeynes@20: #define SCFSR2_ER 0x80 nkeynes@20: #define SCFSR2_TEND 0x40 nkeynes@20: #define SCFSR2_TDFE 0x20 nkeynes@20: #define SCFSR2_BRK 0x10 nkeynes@20: #define SCFSR2_RDF 0x02 nkeynes@20: #define SCFSR2_DR 0x01 nkeynes@20: nkeynes@20: /* FIFO control register flags */ nkeynes@20: #define SCFCR2_MCE 0x08 nkeynes@20: #define SCFCR2_TFRST 0x04 nkeynes@20: #define SCFCR2_RFRST 0x02 nkeynes@20: #define SCFCR2_LOOP 0x01 nkeynes@20: nkeynes@20: /* Line Status Register */ nkeynes@20: #define SCLSR2_ORER 0x01 nkeynes@20: nkeynes@20: struct SCIF_fifo { nkeynes@20: int head; nkeynes@20: int tail; nkeynes@20: int trigger; nkeynes@20: uint8_t data[FIFO_ARR_LENGTH]; nkeynes@20: }; nkeynes@20: nkeynes@22: int SCIF_recvq_triggers[4] = {1, 4, 8, 14}; nkeynes@22: struct SCIF_fifo SCIF_recvq = {0,0,1}; nkeynes@22: nkeynes@22: int SCIF_sendq_triggers[4] = {8, 4, 2, 1}; nkeynes@22: struct SCIF_fifo SCIF_sendq = {0,0,8}; nkeynes@22: nkeynes@22: /** nkeynes@22: * Flag to indicate if data was received (ie added to the receive queue) nkeynes@22: * during the last SCIF clock tick. Used to determine when to set the DR nkeynes@22: * flag. nkeynes@22: */ nkeynes@22: gboolean SCIF_rcvd_last_tick = FALSE; nkeynes@22: nkeynes@20: void SCIF_save_state( FILE *f ) nkeynes@20: { nkeynes@22: fwrite( &SCIF_recvq, sizeof(SCIF_recvq), 1, f ); nkeynes@22: fwrite( &SCIF_sendq, sizeof(SCIF_sendq), 1, f ); nkeynes@22: fwrite( &SCIF_rcvd_last_tick, sizeof(gboolean), 1, f ); nkeynes@20: nkeynes@20: } nkeynes@20: nkeynes@20: int SCIF_load_state( FILE *f ) nkeynes@20: { nkeynes@22: fread( &SCIF_recvq, sizeof(SCIF_recvq), 1, f ); nkeynes@22: fread( &SCIF_sendq, sizeof(SCIF_sendq), 1, f ); nkeynes@22: fread( &SCIF_rcvd_last_tick, sizeof(gboolean), 1, f ); nkeynes@20: return 0; nkeynes@20: } nkeynes@20: nkeynes@20: static inline uint8_t SCIF_recvq_size( ) nkeynes@20: { nkeynes@20: int val = SCIF_recvq.tail - SCIF_recvq.head; nkeynes@20: if( val < 0 ) { nkeynes@20: val = FIFO_ARR_LENGTH - SCIF_recvq.head + SCIF_recvq.tail; nkeynes@20: } nkeynes@20: return val; nkeynes@20: } nkeynes@20: nkeynes@20: int SCIF_recvq_dequeue( gboolean clearFlags ) nkeynes@20: { nkeynes@20: uint8_t result; nkeynes@20: uint32_t tmp, length; nkeynes@20: if( SCIF_recvq.head == SCIF_recvq.tail ) nkeynes@20: return -1; /* No data */ nkeynes@20: result = SCIF_recvq.data[SCIF_recvq.head++]; nkeynes@20: if( SCIF_recvq.head > FIFO_LENGTH ) nkeynes@20: SCIF_recvq.head = 0; nkeynes@20: nkeynes@20: /* Update data count register */ nkeynes@20: tmp = MMIO_READ( SCIF, SCFDR2 ) & 0xF0; nkeynes@20: length = SCIF_recvq_size(); nkeynes@20: MMIO_WRITE( SCIF, SCFDR2, tmp | length ); nkeynes@20: nkeynes@20: /* Clear flags (if requested ) */ nkeynes@20: if( clearFlags && length < SCIF_recvq.trigger ) { nkeynes@20: tmp = SCFSR2_RDF; nkeynes@20: if( length == 0 ) nkeynes@20: tmp |= SCFSR2_DR; nkeynes@20: tmp = MMIO_READ( SCIF, SCFSR2 ) & (~tmp); nkeynes@20: MMIO_WRITE( SCIF, SCFSR2, tmp ); nkeynes@20: /* If both flags are cleared, clear the interrupt as well */ nkeynes@21: if( (tmp & (SCFSR2_DR|SCFSR2_RDF)) == 0 && IS_RECEIVE_IRQ_ENABLED() ) nkeynes@20: intc_clear_interrupt( INT_SCIF_RXI ); nkeynes@20: } nkeynes@20: nkeynes@20: return (int)(unsigned int)result; nkeynes@20: } nkeynes@20: nkeynes@20: gboolean SCIF_recvq_enqueue( uint8_t value ) nkeynes@20: { nkeynes@20: uint32_t tmp, length; nkeynes@20: int newpos = SCIF_recvq.tail + 1; nkeynes@20: if( newpos > FIFO_LENGTH ) nkeynes@20: newpos = 0; nkeynes@20: if( newpos == SCIF_recvq.head ) { nkeynes@20: /* FIFO full - set ORER and discard the value */ nkeynes@20: MMIO_WRITE( SCIF, SCLSR2, SCLSR2_ORER ); nkeynes@20: if( IS_RECEIVE_ERROR_IRQ_ENABLED() ) nkeynes@20: intc_raise_interrupt( INT_SCIF_ERI ); nkeynes@20: return FALSE; nkeynes@20: } nkeynes@20: SCIF_recvq.data[SCIF_recvq.tail] = value; nkeynes@20: nkeynes@20: /* Update data count register */ nkeynes@20: tmp = MMIO_READ( SCIF, SCFDR2 ) & 0xF0; nkeynes@20: length = SCIF_recvq_size(); nkeynes@20: MMIO_WRITE( SCIF, SCFDR2, tmp | length ); nkeynes@20: nkeynes@20: /* Update status register */ nkeynes@20: tmp = MMIO_READ( SCIF, SCFSR2 ); nkeynes@20: if( length >= SCIF_recvq.trigger ) { nkeynes@20: tmp |= SCFSR2_RDF; nkeynes@20: if( IS_RECEIVE_IRQ_ENABLED() ) nkeynes@20: intc_raise_interrupt( INT_SCIF_RXI ); nkeynes@20: } nkeynes@20: MMIO_WRITE( SCIF, SCFSR2, tmp ); nkeynes@20: return TRUE; nkeynes@20: } nkeynes@20: nkeynes@20: nkeynes@20: /** nkeynes@20: * Reset the receive FIFO to its initial state. Manual is unclear as to nkeynes@20: * whether this also clears flags/interrupts, but we're assuming here that nkeynes@20: * it does until proven otherwise. nkeynes@20: */ nkeynes@20: void SCIF_recvq_clear( void ) nkeynes@20: { nkeynes@20: SCIF_recvq.head = SCIF_recvq.tail = 0; nkeynes@20: MMIO_WRITE( SCIF, SCFDR2, MMIO_READ( SCIF, SCFDR2 ) & 0xF0 ); nkeynes@20: MMIO_WRITE( SCIF, SCFSR2, MMIO_READ( SCIF, SCFSR2 ) & ~(SCFSR2_DR|SCFSR2_RDF) ); nkeynes@21: if( IS_RECEIVE_IRQ_ENABLED() ) nkeynes@21: intc_clear_interrupt( INT_SCIF_RXI ); nkeynes@20: } nkeynes@20: nkeynes@20: static inline uint8_t SCIF_sendq_size( ) nkeynes@20: { nkeynes@20: int val = SCIF_sendq.tail - SCIF_sendq.head; nkeynes@20: if( val < 0 ) { nkeynes@20: val = FIFO_ARR_LENGTH - SCIF_sendq.head + SCIF_sendq.tail; nkeynes@20: } nkeynes@20: return val; nkeynes@20: } nkeynes@20: nkeynes@20: /** nkeynes@20: * Dequeue one byte from the SCIF transmit queue (ie transmit the byte), nkeynes@20: * updating all status flags as required. nkeynes@20: * @return The byte dequeued, or -1 if the queue is empty. nkeynes@20: */ nkeynes@20: int SCIF_sendq_dequeue( ) nkeynes@20: { nkeynes@20: uint8_t result; nkeynes@20: uint32_t tmp, length; nkeynes@20: if( SCIF_sendq.head == SCIF_sendq.tail ) nkeynes@20: return -1; /* No data */ nkeynes@20: nkeynes@20: /* Update queue head pointer */ nkeynes@20: result = SCIF_sendq.data[SCIF_sendq.head++]; nkeynes@20: if( SCIF_sendq.head > FIFO_LENGTH ) nkeynes@20: SCIF_sendq.head = 0; nkeynes@20: nkeynes@20: /* Update data count register */ nkeynes@20: tmp = MMIO_READ( SCIF, SCFDR2 ) & 0x0F; nkeynes@20: length = SCIF_sendq_size(); nkeynes@20: MMIO_WRITE( SCIF, SCFDR2, tmp | (length << 8) ); nkeynes@20: nkeynes@20: /* Update status register */ nkeynes@20: if( length <= SCIF_sendq.trigger ) { nkeynes@20: tmp = MMIO_READ( SCIF, SCFSR2 ) | SCFSR2_TDFE; nkeynes@20: if( length == 0 ) nkeynes@20: tmp |= SCFSR2_TEND; /* Transmission ended - no data waiting */ nkeynes@20: if( IS_TRANSMIT_IRQ_ENABLED() ) nkeynes@20: intc_raise_interrupt( INT_SCIF_TXI ); nkeynes@20: MMIO_WRITE( SCIF, SCFSR2, tmp ); nkeynes@20: } nkeynes@20: return (int)(unsigned int)result; nkeynes@20: } nkeynes@20: nkeynes@20: /** nkeynes@20: * Enqueue a single byte in the SCIF transmit queue. If the queue is full, nkeynes@20: * the value will be discarded. nkeynes@20: * @param value to be queued. nkeynes@20: * @param clearFlags TRUE if the TEND/TDFE flags should be cleared nkeynes@20: * if the queue exceeds the trigger level. (According to the manual, nkeynes@20: * DMAC writes will clear the flag, whereas regular SH4 writes do NOT nkeynes@20: * automatically clear it. Go figure). nkeynes@20: * @return gboolean TRUE if the value was queued, FALSE if the queue was nkeynes@20: * full. nkeynes@20: */ nkeynes@20: gboolean SCIF_sendq_enqueue( uint8_t value, gboolean clearFlags ) nkeynes@20: { nkeynes@20: uint32_t tmp, length; nkeynes@20: int newpos = SCIF_sendq.tail + 1; nkeynes@20: if( newpos > FIFO_LENGTH ) nkeynes@20: newpos = 0; nkeynes@20: if( newpos == SCIF_sendq.head ) { nkeynes@20: /* FIFO full - discard */ nkeynes@20: return FALSE; nkeynes@20: } nkeynes@20: SCIF_sendq.data[SCIF_sendq.tail] = value; nkeynes@20: SCIF_sendq.tail = newpos; nkeynes@20: nkeynes@20: /* Update data count register */ nkeynes@20: tmp = MMIO_READ( SCIF, SCFDR2 ) & 0x0F; nkeynes@20: length = SCIF_sendq_size(); nkeynes@20: MMIO_WRITE( SCIF, SCFDR2, tmp | (length << 8) ); nkeynes@20: nkeynes@20: /* Update flags if requested */ nkeynes@20: if( clearFlags ) { nkeynes@20: tmp = SCFSR2_TEND; nkeynes@20: if( length > SCIF_sendq.trigger ) { nkeynes@20: tmp |= SCFSR2_TDFE; nkeynes@21: if( IS_TRANSMIT_IRQ_ENABLED() ) nkeynes@21: intc_clear_interrupt( INT_SCIF_TXI ); nkeynes@20: } nkeynes@20: tmp = MMIO_READ( SCIF, SCFSR2 ) & (~tmp); nkeynes@20: MMIO_WRITE( SCIF, SCFSR2, tmp ); nkeynes@20: } nkeynes@20: return TRUE; nkeynes@20: } nkeynes@20: nkeynes@20: void SCIF_sendq_clear( void ) nkeynes@20: { nkeynes@20: SCIF_sendq.head = SCIF_sendq.tail = 0; nkeynes@20: MMIO_WRITE( SCIF, SCFDR2, MMIO_READ( SCIF, SCFDR2 ) & 0x0F ); nkeynes@20: MMIO_WRITE( SCIF, SCFSR2, MMIO_READ( SCIF, SCFSR2 ) | SCFSR2_TEND | SCFSR2_TDFE ); nkeynes@20: if( IS_TRANSMIT_IRQ_ENABLED() ) { nkeynes@20: intc_raise_interrupt( INT_SCIF_TXI ); nkeynes@20: } nkeynes@20: } nkeynes@20: nkeynes@21: /** nkeynes@21: * Update the SCFSR2 status register with the given mask (ie clear any values nkeynes@21: * that are set to 0 in the mask. According to a strict reading of the doco nkeynes@21: * though, the bits will only actually clear if the flag state is no longer nkeynes@21: * true, so we need to recheck everything... nkeynes@21: */ nkeynes@21: void SCIF_update_status( uint32_t mask ) nkeynes@21: { nkeynes@21: uint32_t value = MMIO_READ( SCIF, SCFSR2 ); nkeynes@21: uint32_t result = value & mask; nkeynes@21: uint32_t sendq_size = SCIF_sendq_size(); nkeynes@21: uint32_t recvq_size = SCIF_recvq_size(); nkeynes@21: nkeynes@21: if( sendq_size != 0 ) nkeynes@21: result |= SCFSR2_TEND; nkeynes@21: nkeynes@21: if( sendq_size <= SCIF_sendq.trigger ) nkeynes@21: result |= SCFSR2_TDFE; nkeynes@21: else if( result & SCFSR2_TDFE == 0 && IS_TRANSMIT_IRQ_ENABLED() ) nkeynes@21: intc_clear_interrupt( INT_SCIF_TXI ); nkeynes@21: nkeynes@21: if( recvq_size >= SCIF_recvq.trigger ) nkeynes@21: result |= SCFSR2_RDF; nkeynes@21: if( (value & SCFSR2_DR) != 0 && (result & SCFSR2_DR) == 0 && nkeynes@21: recvq_size != 0 ) nkeynes@21: result |= SCFSR2_DR; nkeynes@21: if( (result & (SCFSR2_DR|SCFSR2_RDF)) == 0 && IS_RECEIVE_IRQ_ENABLED() ) nkeynes@21: intc_clear_interrupt( INT_SCIF_RXI ); nkeynes@21: nkeynes@21: if( IS_RECEIVE_ERROR_IRQ_ENABLED() ) { nkeynes@21: if( (result & SCFSR2_BRK) == 0 ) nkeynes@21: intc_clear_interrupt( INT_SCIF_BRI ); nkeynes@21: if( (result & SCFSR2_ER) == 0 && nkeynes@21: (MMIO_READ( SCIF, SCLSR2 ) & SCLSR2_ORER) == 0 ) nkeynes@21: intc_clear_interrupt( INT_SCIF_ERI ); nkeynes@21: } nkeynes@21: } nkeynes@20: nkeynes@20: /** nkeynes@20: * Set the break detected flag nkeynes@20: */ nkeynes@20: void SCIF_set_break( void ) nkeynes@20: { nkeynes@20: MMIO_WRITE( SCIF, SCFSR2, MMIO_READ( SCIF, SCFSR2 ) | SCFSR2_BRK ); nkeynes@20: if( IS_RECEIVE_ERROR_IRQ_ENABLED() ) nkeynes@20: intc_raise_interrupt( INT_SCIF_BRI ); nkeynes@20: } nkeynes@20: nkeynes@20: const static int SCIF_CLOCK_MULTIPLIER[4] = {1, 4, 16, 64}; nkeynes@20: nkeynes@20: /** nkeynes@20: * Calculate the current line speed. nkeynes@20: */ nkeynes@20: void SCIF_update_line_speed( void ) nkeynes@20: { nkeynes@20: /* If CKE1 is set, use the external clock as a base */ nkeynes@20: if( MMIO_READ( SCIF, SCSCR2 ) & SCSCR2_CKE ) { nkeynes@20: nkeynes@20: nkeynes@20: } else { nkeynes@20: nkeynes@20: /* Otherwise, SH4 peripheral clock divided by n */ nkeynes@20: int mult = SCIF_CLOCK_MULTIPLIER[MMIO_READ( SCIF, SCSMR2 ) & 0x03]; nkeynes@20: nkeynes@20: /* Then process the bitrate register */ nkeynes@20: int bbr = MMIO_READ( SCIF, SCBRR2 ) & 0xFF; nkeynes@20: nkeynes@20: int baudrate = sh4_peripheral_freq / (32 * mult * (bbr+1) ); nkeynes@20: nkeynes@20: if( serial_device != NULL && serial_device->set_line_speed != NULL ) nkeynes@20: serial_device->set_line_speed( baudrate ); nkeynes@20: INFO( "SCIF baud rate set to %d", baudrate ); nkeynes@20: /* nkeynes@20: clock_set_tick_rate( CLOCK_SCIF, baudrate / 10 ); nkeynes@20: */ nkeynes@20: } nkeynes@20: } nkeynes@20: nkeynes@20: int32_t mmio_region_SCIF_read( uint32_t reg ) nkeynes@20: { nkeynes@20: switch( reg ) { nkeynes@20: case SCFRDR2: /* Receive data */ nkeynes@20: return SCIF_recvq_dequeue(FALSE); nkeynes@20: default: nkeynes@20: return MMIO_READ( SCIF, reg ); nkeynes@20: } nkeynes@20: } nkeynes@20: nkeynes@20: void mmio_region_SCIF_write( uint32_t reg, uint32_t val ) nkeynes@20: { nkeynes@20: uint32_t tmp; nkeynes@20: switch( reg ) { nkeynes@20: case SCSMR2: /* Serial mode register */ nkeynes@20: /* Bit 6 => 0 = 8-bit, 1 = 7-bit nkeynes@20: * Bit 5 => 0 = Parity disabled, 1 = parity enabled nkeynes@20: * Bit 4 => 0 = Even parity, 1 = Odd parity nkeynes@20: * Bit 3 => 0 = 1 stop bit, 1 = 2 stop bits nkeynes@20: * Bits 0-1 => Clock select 00 = P, 01 = P/4, 10 = P/16, 11 = P/64 nkeynes@20: */ nkeynes@20: val &= 0x007B; nkeynes@20: if( serial_device != NULL ) { nkeynes@20: serial_device->set_line_params( val ); nkeynes@20: } nkeynes@20: tmp = MMIO_READ( SCIF, SCSMR2 ); nkeynes@20: if( tmp & 0x03 != val & 0x03 ) { nkeynes@20: /* Clock change */ nkeynes@20: SCIF_update_line_speed( ); nkeynes@20: } nkeynes@20: /* Save for later read-back */ nkeynes@20: MMIO_WRITE( SCIF, SCSMR2, val ); nkeynes@20: break; nkeynes@20: case SCBRR2: /* Bit rate register */ nkeynes@20: MMIO_WRITE( SCIF, SCBRR2, val ); nkeynes@20: SCIF_update_line_speed( ); nkeynes@20: break; nkeynes@20: case SCSCR2: /* Serial control register */ nkeynes@20: /* Bit 7 => Transmit-FIFO-data-empty interrupt enabled nkeynes@20: * Bit 6 => Receive-data-full interrupt enabled nkeynes@20: * Bit 5 => Transmit enable nkeynes@20: * Bit 4 => Receive enable nkeynes@20: * Bit 3 => Receive-error/break interrupt enabled nkeynes@20: * Bit 1 => Clock enable nkeynes@20: */ nkeynes@20: val &= 0x00FA; nkeynes@20: /* Clear any interrupts that just became disabled */ nkeynes@20: if( val & SCSCR2_TIE == 0 ) nkeynes@20: intc_clear_interrupt( INT_SCIF_TXI ); nkeynes@20: if( val & SCSCR2_RIE == 0 ) nkeynes@20: intc_clear_interrupt( INT_SCIF_RXI ); nkeynes@20: if( val & (SCSCR2_RIE|SCSCR2_REIE) == 0 ) { nkeynes@20: intc_clear_interrupt( INT_SCIF_ERI ); nkeynes@20: intc_clear_interrupt( INT_SCIF_BRI ); nkeynes@20: } nkeynes@20: nkeynes@20: MMIO_WRITE( SCIF, reg, val ); nkeynes@20: break; nkeynes@20: case SCFTDR2: /* Transmit FIFO data register */ nkeynes@20: SCIF_sendq_enqueue( val, FALSE ); nkeynes@20: break; nkeynes@20: case SCFSR2: /* Serial status register */ nkeynes@20: /* Bits 12-15 Parity error count nkeynes@20: * Bits 8-11 Framing erro count nkeynes@20: * Bit 7 - Receive error nkeynes@20: * Bit 6 - Transmit end nkeynes@20: * Bit 5 - Transmit FIFO data empty nkeynes@20: * Bit 4 - Break detect nkeynes@20: * Bit 3 - Framing error nkeynes@20: * Bit 2 - Parity error nkeynes@20: * Bit 1 - Receive FIFO data full nkeynes@20: * Bit 0 - Receive data ready nkeynes@20: */ nkeynes@20: /* Clear off any flags/interrupts that are being set to 0 */ nkeynes@21: SCIF_update_status( val ); nkeynes@20: break; nkeynes@20: case SCFCR2: /* FIFO control register */ nkeynes@20: val &= 0x0F; nkeynes@20: SCIF_recvq.trigger = SCIF_recvq_triggers[val >> 6]; nkeynes@20: SCIF_sendq.trigger = SCIF_sendq_triggers[(val >> 4) & 0x03]; nkeynes@20: if( val & SCFCR2_TFRST ) { nkeynes@20: SCIF_sendq_clear(); nkeynes@20: } nkeynes@20: if( val & SCFCR2_RFRST ) { nkeynes@20: SCIF_recvq_clear(); nkeynes@20: } nkeynes@20: nkeynes@20: MMIO_WRITE( SCIF, reg, val ); nkeynes@20: break; nkeynes@20: case SCSPTR2: /* Serial Port Register */ nkeynes@20: MMIO_WRITE( SCIF, reg, val ); nkeynes@20: /* NOT IMPLEMENTED */ nkeynes@21: WARN( "SCSPTR2 not implemented: Write %08X", val ); nkeynes@20: break; nkeynes@20: case SCLSR2: nkeynes@20: val = val & SCLSR2_ORER; nkeynes@20: if( val == 0 ) { nkeynes@20: MMIO_WRITE( SCIF, SCLSR2, val ); nkeynes@21: if( (MMIO_READ( SCIF, SCFSR2 ) & SCFSR2_ER) == 0 && nkeynes@21: IS_RECEIVE_ERROR_IRQ_ENABLED() ) nkeynes@20: intc_clear_interrupt( INT_SCIF_ERI ); nkeynes@20: } nkeynes@20: nkeynes@20: break; nkeynes@20: } nkeynes@20: } nkeynes@20: nkeynes@20: /** nkeynes@20: * Actions for a single tick of the serial clock, defined as the transmission nkeynes@20: * time of a single frame. nkeynes@20: * nkeynes@20: * If transmit queue is non-empty: nkeynes@20: * Transmit one byte and remove from queue nkeynes@20: * If input receive source is non-empty: nkeynes@20: * Transfer one byte to the receive queue (if queue is full, byte is lost) nkeynes@20: * If recvq is non-empty, less than the trigger level, and no data has been nkeynes@20: * received in the last 2 ticks (including this one), set the DR flag and nkeynes@20: * IRQ if appropriate. nkeynes@20: */ nkeynes@20: void SCIF_clock_tick( void ) nkeynes@20: { nkeynes@20: gboolean rcvd = FALSE; nkeynes@20: nkeynes@20: if( IS_LOOPBACK_ENABLED() ) { nkeynes@20: if( IS_TRANSMIT_ENABLED() ) { nkeynes@20: int val = SCIF_sendq_dequeue(); nkeynes@20: if( val != -1 && IS_RECEIVE_ENABLED() ) { nkeynes@20: SCIF_recvq_enqueue( val ); nkeynes@20: rcvd = TRUE; nkeynes@20: } nkeynes@20: } nkeynes@20: } else { nkeynes@20: if( IS_TRANSMIT_ENABLED() ) { nkeynes@20: int val = SCIF_sendq_dequeue(); nkeynes@20: if( val != -1 && serial_device != NULL && nkeynes@20: serial_device->receive_data != NULL ) { nkeynes@20: serial_device->receive_data( val ); nkeynes@20: } nkeynes@20: } nkeynes@20: nkeynes@20: if( IS_RECEIVE_ENABLED() ) { nkeynes@20: int val = serial_transmit_dequeue(); nkeynes@20: if( val != -1 ) { nkeynes@20: SCIF_recvq_enqueue( val ); nkeynes@20: rcvd = TRUE; nkeynes@20: } nkeynes@20: } nkeynes@20: } nkeynes@20: nkeynes@20: /* Check if we need to set the DR flag */ nkeynes@20: if( !rcvd && !SCIF_rcvd_last_tick && nkeynes@20: SCIF_recvq.head != SCIF_recvq.tail && nkeynes@20: SCIF_recvq_size() < SCIF_recvq.trigger ) { nkeynes@20: uint32_t tmp = MMIO_READ( SCIF, SCFSR2 ); nkeynes@20: if( tmp & SCFSR2_DR == 0 ) { nkeynes@20: MMIO_WRITE( SCIF, SCFSR2, tmp | SCFSR2_DR ); nkeynes@20: if( IS_RECEIVE_IRQ_ENABLED() ) nkeynes@20: intc_raise_interrupt( INT_SCIF_RXI ); nkeynes@20: } nkeynes@20: } nkeynes@20: SCIF_rcvd_last_tick = rcvd; nkeynes@20: } nkeynes@23: nkeynes@23: void SCIF_run_slice( int microsecs ) nkeynes@23: { nkeynes@23: /* TODO */ nkeynes@23: SCIF_clock_tick(); nkeynes@23: }