mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-05 00:33:55 -06:00
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:
parent
cd1a3f6840
commit
2f062c7227
6 changed files with 325 additions and 585 deletions
345
hw/sh7750.c
345
hw/sh7750.c
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue