Commit f062058fa174611d4c1bbc28e40b14be4bce89f8
1 parent
b41f7df0
ETRAX serial port:
* Simulate basic interrupt driven serial io. * Connect to qemu char dev. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4300 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
164 additions
and
29 deletions
hw/etraxfs_ser.c
| @@ -25,21 +25,68 @@ | @@ -25,21 +25,68 @@ | ||
| 25 | #include <stdio.h> | 25 | #include <stdio.h> |
| 26 | #include <ctype.h> | 26 | #include <ctype.h> |
| 27 | #include "hw.h" | 27 | #include "hw.h" |
| 28 | +#include "qemu-char.h" | ||
| 28 | 29 | ||
| 29 | #define D(x) | 30 | #define D(x) |
| 30 | 31 | ||
| 31 | -#define RW_TR_DMA_EN 0x04 | ||
| 32 | -#define RW_DOUT 0x1c | ||
| 33 | -#define RW_STAT_DIN 0x20 | ||
| 34 | -#define R_STAT_DIN 0x24 | 32 | +#define RW_TR_CTRL 0x00 |
| 33 | +#define RW_TR_DMA_EN 0x04 | ||
| 34 | +#define RW_REC_CTRL 0x08 | ||
| 35 | +#define RW_DOUT 0x1c | ||
| 36 | +#define RS_STAT_DIN 0x20 | ||
| 37 | +#define R_STAT_DIN 0x24 | ||
| 38 | +#define RW_INTR_MASK 0x2c | ||
| 39 | +#define RW_ACK_INTR 0x30 | ||
| 40 | +#define R_INTR 0x34 | ||
| 41 | +#define R_MASKED_INTR 0x38 | ||
| 35 | 42 | ||
| 36 | -static uint32_t ser_readb (void *opaque, target_phys_addr_t addr) | 43 | +#define STAT_DAV 16 |
| 44 | +#define STAT_TR_IDLE 22 | ||
| 45 | +#define STAT_TR_RDY 24 | ||
| 46 | + | ||
| 47 | +struct etrax_serial_t | ||
| 37 | { | 48 | { |
| 38 | - D(CPUState *env = opaque); | ||
| 39 | - D(printf ("%s %x pc=%x\n", __func__, addr, env->pc)); | ||
| 40 | - return 0; | 49 | + CPUState *env; |
| 50 | + CharDriverState *chr; | ||
| 51 | + qemu_irq *irq; | ||
| 52 | + | ||
| 53 | + target_phys_addr_t base; | ||
| 54 | + | ||
| 55 | + int pending_tx; | ||
| 56 | + | ||
| 57 | + /* Control registers. */ | ||
| 58 | + uint32_t rw_tr_ctrl; | ||
| 59 | + uint32_t rw_tr_dma_en; | ||
| 60 | + uint32_t rw_rec_ctrl; | ||
| 61 | + uint32_t rs_stat_din; | ||
| 62 | + uint32_t r_stat_din; | ||
| 63 | + uint32_t rw_intr_mask; | ||
| 64 | + uint32_t rw_ack_intr; | ||
| 65 | + uint32_t r_intr; | ||
| 66 | + uint32_t r_masked_intr; | ||
| 67 | +}; | ||
| 68 | + | ||
| 69 | +static void ser_update_irq(struct etrax_serial_t *s) | ||
| 70 | +{ | ||
| 71 | + uint32_t o_irq = s->r_masked_intr; | ||
| 72 | + | ||
| 73 | + s->r_intr &= ~(s->rw_ack_intr); | ||
| 74 | + s->r_masked_intr = s->r_intr & s->rw_intr_mask; | ||
| 75 | + | ||
| 76 | + if (o_irq != s->r_masked_intr) { | ||
| 77 | + D(printf("irq_mask=%x r_intr=%x rmi=%x airq=%x \n", | ||
| 78 | + s->rw_intr_mask, s->r_intr, | ||
| 79 | + s->r_masked_intr, s->rw_ack_intr)); | ||
| 80 | + if (s->r_masked_intr) | ||
| 81 | + qemu_irq_raise(s->irq[0]); | ||
| 82 | + else | ||
| 83 | + qemu_irq_lower(s->irq[0]); | ||
| 84 | + } | ||
| 85 | + s->rw_ack_intr = 0; | ||
| 41 | } | 86 | } |
| 42 | -static uint32_t ser_readw (void *opaque, target_phys_addr_t addr) | 87 | + |
| 88 | + | ||
| 89 | +static uint32_t ser_readb (void *opaque, target_phys_addr_t addr) | ||
| 43 | { | 90 | { |
| 44 | D(CPUState *env = opaque); | 91 | D(CPUState *env = opaque); |
| 45 | D(printf ("%s %x pc=%x\n", __func__, addr, env->pc)); | 92 | D(printf ("%s %x pc=%x\n", __func__, addr, env->pc)); |
| @@ -48,16 +95,40 @@ static uint32_t ser_readw (void *opaque, target_phys_addr_t addr) | @@ -48,16 +95,40 @@ static uint32_t ser_readw (void *opaque, target_phys_addr_t addr) | ||
| 48 | 95 | ||
| 49 | static uint32_t ser_readl (void *opaque, target_phys_addr_t addr) | 96 | static uint32_t ser_readl (void *opaque, target_phys_addr_t addr) |
| 50 | { | 97 | { |
| 51 | - D(CPUState *env = opaque); | 98 | + struct etrax_serial_t *s = opaque; |
| 99 | + D(CPUState *env = s->env); | ||
| 52 | uint32_t r = 0; | 100 | uint32_t r = 0; |
| 53 | 101 | ||
| 54 | switch (addr & 0xfff) | 102 | switch (addr & 0xfff) |
| 55 | { | 103 | { |
| 104 | + case RW_TR_CTRL: | ||
| 105 | + r = s->rw_tr_ctrl; | ||
| 106 | + break; | ||
| 56 | case RW_TR_DMA_EN: | 107 | case RW_TR_DMA_EN: |
| 108 | + r = s->rw_tr_dma_en; | ||
| 109 | + break; | ||
| 110 | + case RS_STAT_DIN: | ||
| 111 | + r = s->rs_stat_din; | ||
| 112 | + /* clear dav. */ | ||
| 113 | + s->rs_stat_din &= ~(1 << STAT_DAV); | ||
| 57 | break; | 114 | break; |
| 58 | case R_STAT_DIN: | 115 | case R_STAT_DIN: |
| 59 | - r |= 1 << 24; /* set tr_rdy. */ | ||
| 60 | - r |= 1 << 22; /* set tr_idle. */ | 116 | + r = s->rs_stat_din; |
| 117 | + break; | ||
| 118 | + case RW_ACK_INTR: | ||
| 119 | + D(printf("load rw_ack_intr=%x\n", s->rw_ack_intr)); | ||
| 120 | + r = s->rw_ack_intr; | ||
| 121 | + break; | ||
| 122 | + case RW_INTR_MASK: | ||
| 123 | + r = s->rw_intr_mask; | ||
| 124 | + break; | ||
| 125 | + case R_INTR: | ||
| 126 | + D(printf("load r_intr=%x\n", s->r_intr)); | ||
| 127 | + r = s->r_intr; | ||
| 128 | + break; | ||
| 129 | + case R_MASKED_INTR: | ||
| 130 | + D(printf("load r_maked_intr=%x\n", s->r_masked_intr)); | ||
| 131 | + r = s->r_masked_intr; | ||
| 61 | break; | 132 | break; |
| 62 | 133 | ||
| 63 | default: | 134 | default: |
| @@ -70,53 +141,117 @@ static uint32_t ser_readl (void *opaque, target_phys_addr_t addr) | @@ -70,53 +141,117 @@ static uint32_t ser_readl (void *opaque, target_phys_addr_t addr) | ||
| 70 | static void | 141 | static void |
| 71 | ser_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) | 142 | ser_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) |
| 72 | { | 143 | { |
| 73 | - D(CPUState *env = opaque); | 144 | + D(struct etrax_serial_t *s = opaque); |
| 145 | + D(CPUState *env = s->env); | ||
| 74 | D(printf ("%s %x %x pc=%x\n", __func__, addr, value, env->pc)); | 146 | D(printf ("%s %x %x pc=%x\n", __func__, addr, value, env->pc)); |
| 75 | } | 147 | } |
| 76 | static void | 148 | static void |
| 77 | -ser_writew (void *opaque, target_phys_addr_t addr, uint32_t value) | ||
| 78 | -{ | ||
| 79 | - D(CPUState *env = opaque); | ||
| 80 | - D(printf ("%s %x %x pc=%x\n", __func__, addr, value, env->pc)); | ||
| 81 | -} | ||
| 82 | -static void | ||
| 83 | ser_writel (void *opaque, target_phys_addr_t addr, uint32_t value) | 149 | ser_writel (void *opaque, target_phys_addr_t addr, uint32_t value) |
| 84 | { | 150 | { |
| 85 | - D(CPUState *env = opaque); | 151 | + struct etrax_serial_t *s = opaque; |
| 152 | + unsigned char ch = value; | ||
| 153 | + D(CPUState *env = s->env); | ||
| 86 | 154 | ||
| 87 | switch (addr & 0xfff) | 155 | switch (addr & 0xfff) |
| 88 | { | 156 | { |
| 157 | + case RW_TR_CTRL: | ||
| 158 | + D(printf("rw_tr_ctrl=%x\n", value)); | ||
| 159 | + s->rw_tr_ctrl = value; | ||
| 160 | + break; | ||
| 89 | case RW_TR_DMA_EN: | 161 | case RW_TR_DMA_EN: |
| 162 | + D(printf("rw_tr_dma_en=%x\n", value)); | ||
| 163 | + s->rw_tr_dma_en = value; | ||
| 90 | break; | 164 | break; |
| 91 | case RW_DOUT: | 165 | case RW_DOUT: |
| 92 | - if (isprint(value) || isspace(value)) | ||
| 93 | - putchar(value); | ||
| 94 | - else | ||
| 95 | - putchar('.'); | ||
| 96 | - fflush(stdout); | 166 | + qemu_chr_write(s->chr, &ch, 1); |
| 167 | + s->r_intr |= 1; | ||
| 168 | + s->pending_tx = 1; | ||
| 169 | + break; | ||
| 170 | + case RW_ACK_INTR: | ||
| 171 | + D(printf("rw_ack_intr=%x\n", value)); | ||
| 172 | + s->rw_ack_intr = value; | ||
| 173 | + if (s->pending_tx && (s->rw_ack_intr & 1)) { | ||
| 174 | + s->r_intr |= 1; | ||
| 175 | + s->pending_tx = 0; | ||
| 176 | + s->rw_ack_intr &= ~1; | ||
| 177 | + } | ||
| 178 | + break; | ||
| 179 | + case RW_INTR_MASK: | ||
| 180 | + D(printf("r_intr_mask=%x\n", value)); | ||
| 181 | + s->rw_intr_mask = value; | ||
| 97 | break; | 182 | break; |
| 98 | default: | 183 | default: |
| 99 | D(printf ("%s %x %x pc=%x\n", | 184 | D(printf ("%s %x %x pc=%x\n", |
| 100 | __func__, addr, value, env->pc)); | 185 | __func__, addr, value, env->pc)); |
| 101 | break; | 186 | break; |
| 102 | } | 187 | } |
| 188 | + ser_update_irq(s); | ||
| 103 | } | 189 | } |
| 104 | 190 | ||
| 105 | static CPUReadMemoryFunc *ser_read[] = { | 191 | static CPUReadMemoryFunc *ser_read[] = { |
| 106 | &ser_readb, | 192 | &ser_readb, |
| 107 | - &ser_readw, | 193 | + &ser_readb, |
| 108 | &ser_readl, | 194 | &ser_readl, |
| 109 | }; | 195 | }; |
| 110 | 196 | ||
| 111 | static CPUWriteMemoryFunc *ser_write[] = { | 197 | static CPUWriteMemoryFunc *ser_write[] = { |
| 112 | &ser_writeb, | 198 | &ser_writeb, |
| 113 | - &ser_writew, | 199 | + &ser_writeb, |
| 114 | &ser_writel, | 200 | &ser_writel, |
| 115 | }; | 201 | }; |
| 116 | 202 | ||
| 117 | -void etraxfs_ser_init(CPUState *env, qemu_irq *irqs, target_phys_addr_t base) | 203 | +static void serial_receive(void *opaque, const uint8_t *buf, int size) |
| 118 | { | 204 | { |
| 205 | + struct etrax_serial_t *s = opaque; | ||
| 206 | + | ||
| 207 | + s->r_intr |= 8; | ||
| 208 | + s->rs_stat_din &= ~0xff; | ||
| 209 | + s->rs_stat_din |= (buf[0] & 0xff); | ||
| 210 | + s->rs_stat_din |= (1 << STAT_DAV); /* dav. */ | ||
| 211 | + ser_update_irq(s); | ||
| 212 | +} | ||
| 213 | + | ||
| 214 | +static int serial_can_receive(void *opaque) | ||
| 215 | +{ | ||
| 216 | + struct etrax_serial_t *s = opaque; | ||
| 217 | + int r; | ||
| 218 | + | ||
| 219 | + /* Is the receiver enabled? */ | ||
| 220 | + r = s->rw_rec_ctrl & 1; | ||
| 221 | + | ||
| 222 | + /* Pending rx data? */ | ||
| 223 | + r |= !(s->r_intr & 8); | ||
| 224 | + return r; | ||
| 225 | +} | ||
| 226 | + | ||
| 227 | +static void serial_event(void *opaque, int event) | ||
| 228 | +{ | ||
| 229 | + | ||
| 230 | +} | ||
| 231 | + | ||
| 232 | +void etraxfs_ser_init(CPUState *env, qemu_irq *irq, CharDriverState *chr, | ||
| 233 | + target_phys_addr_t base) | ||
| 234 | +{ | ||
| 235 | + struct etrax_serial_t *s; | ||
| 119 | int ser_regs; | 236 | int ser_regs; |
| 120 | - ser_regs = cpu_register_io_memory(0, ser_read, ser_write, env); | 237 | + |
| 238 | + s = qemu_mallocz(sizeof *s); | ||
| 239 | + if (!s) | ||
| 240 | + return; | ||
| 241 | + | ||
| 242 | + s->env = env; | ||
| 243 | + s->irq = irq; | ||
| 244 | + s->base = base; | ||
| 245 | + | ||
| 246 | + s->chr = chr; | ||
| 247 | + | ||
| 248 | + /* transmitter begins ready and idle. */ | ||
| 249 | + s->rs_stat_din |= (1 << STAT_TR_RDY); | ||
| 250 | + s->rs_stat_din |= (1 << STAT_TR_IDLE); | ||
| 251 | + | ||
| 252 | + qemu_chr_add_handlers(chr, serial_can_receive, serial_receive, | ||
| 253 | + serial_event, s); | ||
| 254 | + | ||
| 255 | + ser_regs = cpu_register_io_memory(0, ser_read, ser_write, s); | ||
| 121 | cpu_register_physical_memory (base, 0x3c, ser_regs); | 256 | cpu_register_physical_memory (base, 0x3c, ser_regs); |
| 122 | } | 257 | } |