Stand-alone SCI/SCIF emulation code, by Magnus Damm.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3270 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
ths 2007-09-29 19:43:54 +00:00
parent cd1a3f6840
commit 2f062c7227
6 changed files with 325 additions and 585 deletions

View file

@ -27,13 +27,6 @@
#include "sh7750_regs.h"
#include "sh7750_regnames.h"
typedef struct {
uint8_t data[16];
uint8_t length; /* Number of characters in the FIFO */
uint8_t write_idx; /* Index of first character to write */
uint8_t read_idx; /* Index of first character to read */
} fifo;
#define NB_DEVICES 4
typedef struct SH7750State {
@ -43,27 +36,6 @@ typedef struct SH7750State {
uint32_t periph_freq;
/* SDRAM controller */
uint16_t rfcr;
/* First serial port */
CharDriverState *serial1;
uint8_t scscr1;
uint8_t scsmr1;
uint8_t scbrr1;
uint8_t scssr1;
uint8_t scssr1_read;
uint8_t sctsr1;
uint8_t sctsr1_loaded;
uint8_t sctdr1;
uint8_t scrdr1;
/* Second serial port */
CharDriverState *serial2;
uint16_t sclsr2;
uint16_t scscr2;
uint16_t scfcr2;
uint16_t scfsr2;
uint16_t scsmr2;
uint8_t scbrr2;
fifo serial2_receive_fifo;
fifo serial2_transmit_fifo;
/* IO ports */
uint16_t gpioic;
uint32_t pctra;
@ -84,263 +56,6 @@ typedef struct SH7750State {
} SH7750State;
/**********************************************************************
First serial port
**********************************************************************/
static int serial1_can_receive(void *opaque)
{
SH7750State *s = opaque;
return s->scscr1 & SH7750_SCSCR_RE;
}
static void serial1_receive_char(SH7750State * s, uint8_t c)
{
if (s->scssr1 & SH7750_SCSSR1_RDRF) {
s->scssr1 |= SH7750_SCSSR1_ORER;
return;
}
s->scrdr1 = c;
s->scssr1 |= SH7750_SCSSR1_RDRF;
}
static void serial1_receive(void *opaque, const uint8_t * buf, int size)
{
SH7750State *s = opaque;
int i;
for (i = 0; i < size; i++) {
serial1_receive_char(s, buf[i]);
}
}
static void serial1_event(void *opaque, int event)
{
assert(0);
}
static void serial1_maybe_send(SH7750State * s)
{
uint8_t c;
if (s->scssr1 & SH7750_SCSSR1_TDRE)
return;
c = s->sctdr1;
s->scssr1 |= SH7750_SCSSR1_TDRE | SH7750_SCSSR1_TEND;
if (s->scscr1 & SH7750_SCSCR_TIE) {
fprintf(stderr, "interrupts for serial port 1 not implemented\n");
assert(0);
}
/* XXXXX Check for errors in write */
qemu_chr_write(s->serial1, &c, 1);
}
static void serial1_change_scssr1(SH7750State * s, uint8_t mem_value)
{
uint8_t new_flags;
/* If transmit disable, TDRE and TEND stays up */
if ((s->scscr1 & SH7750_SCSCR_TE) == 0) {
mem_value |= SH7750_SCSSR1_TDRE | SH7750_SCSSR1_TEND;
}
/* Only clear bits which have been read before and do not set any bit
in the flags */
new_flags = s->scssr1 & ~s->scssr1_read; /* Preserve unread flags */
new_flags &= mem_value | ~s->scssr1_read; /* Clear read flags */
s->scssr1 = (new_flags & 0xf8) | (mem_value & 1);
s->scssr1_read &= mem_value;
/* If TDRE has been cleared, TEND will also be cleared */
if ((s->scssr1 & SH7750_SCSSR1_TDRE) == 0) {
s->scssr1 &= ~SH7750_SCSSR1_TEND;
}
/* Check for transmission to start */
serial1_maybe_send(s);
}
static void serial1_update_parameters(SH7750State * s)
{
QEMUSerialSetParams ssp;
if (s->scsmr1 & SH7750_SCSMR_CHR_7)
ssp.data_bits = 7;
else
ssp.data_bits = 8;
if (s->scsmr1 & SH7750_SCSMR_PE) {
if (s->scsmr1 & SH7750_SCSMR_PM_ODD)
ssp.parity = 'O';
else
ssp.parity = 'E';
} else
ssp.parity = 'N';
if (s->scsmr1 & SH7750_SCSMR_STOP_2)
ssp.stop_bits = 2;
else
ssp.stop_bits = 1;
fprintf(stderr, "SCSMR1=%04x SCBRR1=%02x\n", s->scsmr1, s->scbrr1);
ssp.speed = s->periph_freq /
(32 * s->scbrr1 * (1 << (2 * (s->scsmr1 & 3)))) - 1;
fprintf(stderr, "data bits=%d, stop bits=%d, parity=%c, speed=%d\n",
ssp.data_bits, ssp.stop_bits, ssp.parity, ssp.speed);
qemu_chr_ioctl(s->serial1, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
}
static void scscr1_changed(SH7750State * s)
{
if (s->scscr1 & (SH7750_SCSCR_TE | SH7750_SCSCR_RE)) {
if (!s->serial1) {
fprintf(stderr, "serial port 1 not bound to anything\n");
assert(0);
}
serial1_update_parameters(s);
}
if ((s->scscr1 & SH7750_SCSCR_RE) == 0) {
s->scssr1 |= SH7750_SCSSR1_TDRE;
}
}
static void init_serial1(SH7750State * s, int serial_nb)
{
CharDriverState *chr;
s->scssr1 = 0x84;
chr = serial_hds[serial_nb];
if (!chr) {
fprintf(stderr,
"no serial port associated to SH7750 first serial port\n");
return;
}
s->serial1 = chr;
qemu_chr_add_handlers(chr, serial1_can_receive,
serial1_receive, serial1_event, s);
}
/**********************************************************************
Second serial port
**********************************************************************/
static int serial2_can_receive(void *opaque)
{
SH7750State *s = opaque;
static uint8_t max_fifo_size[] = { 15, 1, 4, 6, 8, 10, 12, 14 };
return s->serial2_receive_fifo.length <
max_fifo_size[(s->scfcr2 >> 9) & 7];
}
static void serial2_adjust_receive_flags(SH7750State * s)
{
static uint8_t max_fifo_size[] = { 1, 4, 8, 14 };
/* XXXXX Add interrupt generation */
if (s->serial2_receive_fifo.length >=
max_fifo_size[(s->scfcr2 >> 7) & 3]) {
s->scfsr2 |= SH7750_SCFSR2_RDF;
s->scfsr2 &= ~SH7750_SCFSR2_DR;
} else {
s->scfsr2 &= ~SH7750_SCFSR2_RDF;
if (s->serial2_receive_fifo.length > 0)
s->scfsr2 |= SH7750_SCFSR2_DR;
else
s->scfsr2 &= ~SH7750_SCFSR2_DR;
}
}
static void serial2_append_char(SH7750State * s, uint8_t c)
{
if (s->serial2_receive_fifo.length == 16) {
/* Overflow */
s->sclsr2 |= SH7750_SCLSR2_ORER;
return;
}
s->serial2_receive_fifo.data[s->serial2_receive_fifo.write_idx++] = c;
s->serial2_receive_fifo.length++;
serial2_adjust_receive_flags(s);
}
static void serial2_receive(void *opaque, const uint8_t * buf, int size)
{
SH7750State *s = opaque;
int i;
for (i = 0; i < size; i++)
serial2_append_char(s, buf[i]);
}
static void serial2_event(void *opaque, int event)
{
/* XXXXX */
assert(0);
}
static void serial2_update_parameters(SH7750State * s)
{
QEMUSerialSetParams ssp;
if (s->scsmr2 & SH7750_SCSMR_CHR_7)
ssp.data_bits = 7;
else
ssp.data_bits = 8;
if (s->scsmr2 & SH7750_SCSMR_PE) {
if (s->scsmr2 & SH7750_SCSMR_PM_ODD)
ssp.parity = 'O';
else
ssp.parity = 'E';
} else
ssp.parity = 'N';
if (s->scsmr2 & SH7750_SCSMR_STOP_2)
ssp.stop_bits = 2;
else
ssp.stop_bits = 1;
fprintf(stderr, "SCSMR2=%04x SCBRR2=%02x\n", s->scsmr2, s->scbrr2);
ssp.speed = s->periph_freq /
(32 * s->scbrr2 * (1 << (2 * (s->scsmr2 & 3)))) - 1;
fprintf(stderr, "data bits=%d, stop bits=%d, parity=%c, speed=%d\n",
ssp.data_bits, ssp.stop_bits, ssp.parity, ssp.speed);
qemu_chr_ioctl(s->serial2, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
}
static void scscr2_changed(SH7750State * s)
{
if (s->scscr2 & (SH7750_SCSCR_TE | SH7750_SCSCR_RE)) {
if (!s->serial2) {
fprintf(stderr, "serial port 2 not bound to anything\n");
assert(0);
}
serial2_update_parameters(s);
}
}
static void init_serial2(SH7750State * s, int serial_nb)
{
CharDriverState *chr;
s->scfsr2 = 0x0060;
chr = serial_hds[serial_nb];
if (!chr) {
fprintf(stderr,
"no serial port associated to SH7750 second serial port\n");
return;
}
s->serial2 = chr;
qemu_chr_add_handlers(chr, serial2_can_receive,
serial2_receive, serial1_event, s);
}
static void init_serial_ports(SH7750State * s)
{
init_serial1(s, 0);
init_serial2(s, 1);
}
/**********************************************************************
I/O ports
@ -472,17 +187,7 @@ static void ignore_access(const char *kind, target_phys_addr_t addr)
static uint32_t sh7750_mem_readb(void *opaque, target_phys_addr_t addr)
{
SH7750State *s = opaque;
uint8_t r;
switch (addr) {
case SH7750_SCSSR1_A7:
r = s->scssr1;
s->scssr1_read |= r;
return s->scssr1;
case SH7750_SCRDR1_A7:
s->scssr1 &= ~SH7750_SCSSR1_RDRF;
return s->scrdr1;
default:
error_access("byte read", addr);
assert(0);
@ -492,20 +197,12 @@ static uint32_t sh7750_mem_readb(void *opaque, target_phys_addr_t addr)
static uint32_t sh7750_mem_readw(void *opaque, target_phys_addr_t addr)
{
SH7750State *s = opaque;
uint16_t r;
switch (addr) {
case SH7750_RFCR_A7:
fprintf(stderr,
"Read access to refresh count register, incrementing\n");
return s->rfcr++;
case SH7750_SCLSR2_A7:
/* Read and clear overflow bit */
r = s->sclsr2;
s->sclsr2 = 0;
return r;
case SH7750_SCSFR2_A7:
return s->scfsr2;
case SH7750_PDTRA_A7:
return porta_lines(s);
case SH7750_PDTRB_A7:
@ -554,34 +251,12 @@ static uint32_t sh7750_mem_readl(void *opaque, target_phys_addr_t addr)
static void sh7750_mem_writeb(void *opaque, target_phys_addr_t addr,
uint32_t mem_value)
{
SH7750State *s = opaque;
switch (addr) {
/* PRECHARGE ? XXXXX */
case SH7750_PRECHARGE0_A7:
case SH7750_PRECHARGE1_A7:
ignore_access("byte write", addr);
return;
case SH7750_SCBRR2_A7:
s->scbrr2 = mem_value;
return;
case SH7750_SCSCR1_A7:
s->scscr1 = mem_value;
scscr1_changed(s);
return;
case SH7750_SCSMR1_A7:
s->scsmr1 = mem_value;
return;
case SH7750_SCBRR1_A7:
s->scbrr1 = mem_value;
return;
case SH7750_SCTDR1_A7:
s->scssr1 &= ~SH7750_SCSSR1_TEND;
s->sctdr1 = mem_value;
return;
case SH7750_SCSSR1_A7:
serial1_change_scssr1(s, mem_value);
return;
default:
error_access("byte write", addr);
assert(0);
@ -596,8 +271,6 @@ static void sh7750_mem_writew(void *opaque, target_phys_addr_t addr,
switch (addr) {
/* SDRAM controller */
case SH7750_SCBRR1_A7:
case SH7750_SCBRR2_A7:
case SH7750_BCR2_A7:
case SH7750_BCR3_A7:
case SH7750_RTCOR_A7:
@ -620,19 +293,6 @@ static void sh7750_mem_writew(void *opaque, target_phys_addr_t addr,
fprintf(stderr, "Write access to refresh count register\n");
s->rfcr = mem_value;
return;
case SH7750_SCLSR2_A7:
s->sclsr2 = mem_value;
return;
case SH7750_SCSCR2_A7:
s->scscr2 = mem_value;
scscr2_changed(s);
return;
case SH7750_SCFCR2_A7:
s->scfcr2 = mem_value;
return;
case SH7750_SCSMR2_A7:
s->scsmr2 = mem_value;
return;
case SH7750_GPIOIC_A7:
s->gpioic = mem_value;
if (mem_value != 0) {
@ -734,7 +394,10 @@ SH7750State *sh7750_init(CPUSH4State * cpu)
sh7750_mem_read,
sh7750_mem_write, s);
cpu_register_physical_memory(0x1c000000, 0x04000000, sh7750_io_memory);
init_serial_ports(s);
sh_serial_init(0x1fe00000, 0, s->periph_freq, serial_hds[0]);
sh_serial_init(0x1fe80000, SH_SERIAL_FEAT_SCIF,
s->periph_freq, serial_hds[1]);
tmu012_init(0x1fd80000,
TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK,