Commit 63242a007a1898f504837bb7c3c97ce90ae0c8e4
1 parent
f24f381b
SH4: Serial controller improvement
Add receive character feature to SH4 SCIF. SH4-SCI feature implementation work is left. (Shin-ichiro KAWASAKI) git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5221 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
79 additions
and
5 deletions
hw/sh_serial.c
| ... | ... | @@ -37,6 +37,8 @@ |
| 37 | 37 | #define SH_SERIAL_FLAG_BRK (1 << 3) |
| 38 | 38 | #define SH_SERIAL_FLAG_DR (1 << 4) |
| 39 | 39 | |
| 40 | +#define SH_RX_FIFO_LENGTH (16) | |
| 41 | + | |
| 40 | 42 | typedef struct { |
| 41 | 43 | uint8_t smr; |
| 42 | 44 | uint8_t brr; |
| ... | ... | @@ -46,13 +48,16 @@ typedef struct { |
| 46 | 48 | uint16_t fcr; |
| 47 | 49 | uint8_t sptr; |
| 48 | 50 | |
| 49 | - uint8_t rx_fifo[16]; /* frdr / rdr */ | |
| 51 | + uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */ | |
| 50 | 52 | uint8_t rx_cnt; |
| 53 | + uint8_t rx_tail; | |
| 54 | + uint8_t rx_head; | |
| 51 | 55 | |
| 52 | 56 | target_phys_addr_t base; |
| 53 | 57 | int freq; |
| 54 | 58 | int feat; |
| 55 | 59 | int flags; |
| 60 | + int rtrg; | |
| 56 | 61 | |
| 57 | 62 | CharDriverState *chr; |
| 58 | 63 | |
| ... | ... | @@ -63,6 +68,14 @@ typedef struct { |
| 63 | 68 | struct intc_source *bri; |
| 64 | 69 | } sh_serial_state; |
| 65 | 70 | |
| 71 | +static void sh_serial_clear_fifo(sh_serial_state * s) | |
| 72 | +{ | |
| 73 | + memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH); | |
| 74 | + s->rx_cnt = 0; | |
| 75 | + s->rx_head = 0; | |
| 76 | + s->rx_tail = 0; | |
| 77 | +} | |
| 78 | + | |
| 66 | 79 | static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) |
| 67 | 80 | { |
| 68 | 81 | sh_serial_state *s = opaque; |
| ... | ... | @@ -80,6 +93,7 @@ static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) |
| 80 | 93 | s->brr = val; |
| 81 | 94 | return; |
| 82 | 95 | case 0x08: /* SCR */ |
| 96 | + /* TODO : For SH7751, SCIF mask should be 0xfb. */ | |
| 83 | 97 | s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff); |
| 84 | 98 | if (!(val & (1 << 5))) |
| 85 | 99 | s->flags |= SH_SERIAL_FLAG_TEND; |
| ... | ... | @@ -89,6 +103,9 @@ static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) |
| 89 | 103 | else if (!(val & (1 << 7)) && s->txi->asserted) |
| 90 | 104 | sh_intc_toggle_source(s->txi, 0, -1); |
| 91 | 105 | } |
| 106 | + if (!(val & (1 << 6)) && s->rxi->asserted) { | |
| 107 | + sh_intc_toggle_source(s->rxi, 0, -1); | |
| 108 | + } | |
| 92 | 109 | return; |
| 93 | 110 | case 0x0c: /* FTDR / TDR */ |
| 94 | 111 | if (s->chr) { |
| ... | ... | @@ -117,12 +134,37 @@ static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) |
| 117 | 134 | s->flags &= ~SH_SERIAL_FLAG_RDF; |
| 118 | 135 | if (!(val & (1 << 0))) |
| 119 | 136 | s->flags &= ~SH_SERIAL_FLAG_DR; |
| 137 | + | |
| 138 | + if (!(val & (1 << 1)) || !(val & (1 << 0))) { | |
| 139 | + if (s->rxi && s->rxi->asserted) { | |
| 140 | + sh_intc_toggle_source(s->rxi, 0, -1); | |
| 141 | + } | |
| 142 | + } | |
| 120 | 143 | return; |
| 121 | 144 | case 0x18: /* FCR */ |
| 122 | 145 | s->fcr = val; |
| 146 | + switch ((val >> 6) & 3) { | |
| 147 | + case 0: | |
| 148 | + s->rtrg = 1; | |
| 149 | + break; | |
| 150 | + case 1: | |
| 151 | + s->rtrg = 4; | |
| 152 | + break; | |
| 153 | + case 2: | |
| 154 | + s->rtrg = 8; | |
| 155 | + break; | |
| 156 | + case 3: | |
| 157 | + s->rtrg = 14; | |
| 158 | + break; | |
| 159 | + } | |
| 160 | + if (val & (1 << 1)) { | |
| 161 | + sh_serial_clear_fifo(s); | |
| 162 | + s->sr &= ~(1 << 1); | |
| 163 | + } | |
| 164 | + | |
| 123 | 165 | return; |
| 124 | 166 | case 0x20: /* SPTR */ |
| 125 | - s->sptr = val; | |
| 167 | + s->sptr = val & 0xf3; | |
| 126 | 168 | return; |
| 127 | 169 | case 0x24: /* LSR */ |
| 128 | 170 | return; |
| ... | ... | @@ -190,10 +232,20 @@ static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs) |
| 190 | 232 | if (s->flags & SH_SERIAL_FLAG_DR) |
| 191 | 233 | ret |= (1 << 0); |
| 192 | 234 | |
| 193 | - if (s->scr & (1 << 5)) | |
| 235 | + if (s->scr & (1 << 5)) | |
| 194 | 236 | s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND; |
| 195 | 237 | |
| 196 | 238 | break; |
| 239 | + case 0x14: | |
| 240 | + if (s->rx_cnt > 0) { | |
| 241 | + ret = s->rx_fifo[s->rx_tail++]; | |
| 242 | + s->rx_cnt--; | |
| 243 | + if (s->rx_tail == SH_RX_FIFO_LENGTH) | |
| 244 | + s->rx_tail = 0; | |
| 245 | + if (s->rx_cnt < s->rtrg) | |
| 246 | + s->flags &= ~SH_SERIAL_FLAG_RDF; | |
| 247 | + } | |
| 248 | + break; | |
| 197 | 249 | #if 0 |
| 198 | 250 | case 0x18: |
| 199 | 251 | ret = s->fcr; |
| ... | ... | @@ -219,6 +271,9 @@ static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs) |
| 219 | 271 | case 0x10: |
| 220 | 272 | ret = 0; |
| 221 | 273 | break; |
| 274 | + case 0x14: | |
| 275 | + ret = s->rx_fifo[0]; | |
| 276 | + break; | |
| 222 | 277 | case 0x1c: |
| 223 | 278 | ret = s->sptr; |
| 224 | 279 | break; |
| ... | ... | @@ -240,15 +295,33 @@ static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs) |
| 240 | 295 | |
| 241 | 296 | static int sh_serial_can_receive(sh_serial_state *s) |
| 242 | 297 | { |
| 243 | - return 0; | |
| 298 | + return s->scr & (1 << 4); | |
| 244 | 299 | } |
| 245 | 300 | |
| 246 | 301 | static void sh_serial_receive_byte(sh_serial_state *s, int ch) |
| 247 | 302 | { |
| 303 | + if (s->feat & SH_SERIAL_FEAT_SCIF) { | |
| 304 | + if (s->rx_cnt < SH_RX_FIFO_LENGTH) { | |
| 305 | + s->rx_fifo[s->rx_head++] = ch; | |
| 306 | + if (s->rx_head == SH_RX_FIFO_LENGTH) | |
| 307 | + s->rx_head = 0; | |
| 308 | + s->rx_cnt++; | |
| 309 | + if (s->rx_cnt >= s->rtrg) { | |
| 310 | + s->flags |= SH_SERIAL_FLAG_RDF; | |
| 311 | + if (s->scr & (1 << 6) && s->rxi) { | |
| 312 | + sh_intc_toggle_source(s->rxi, 0, 1); | |
| 313 | + } | |
| 314 | + } | |
| 315 | + } | |
| 316 | + } else { | |
| 317 | + s->rx_fifo[0] = ch; | |
| 318 | + } | |
| 248 | 319 | } |
| 249 | 320 | |
| 250 | 321 | static void sh_serial_receive_break(sh_serial_state *s) |
| 251 | 322 | { |
| 323 | + if (s->feat & SH_SERIAL_FEAT_SCIF) | |
| 324 | + s->sr |= (1 << 4); | |
| 252 | 325 | } |
| 253 | 326 | |
| 254 | 327 | static int sh_serial_can_receive1(void *opaque) |
| ... | ... | @@ -313,6 +386,7 @@ void sh_serial_init (target_phys_addr_t base, int feat, |
| 313 | 386 | s->base = base; |
| 314 | 387 | s->feat = feat; |
| 315 | 388 | s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE; |
| 389 | + s->rtrg = 1; | |
| 316 | 390 | |
| 317 | 391 | s->smr = 0; |
| 318 | 392 | s->brr = 0xff; |
| ... | ... | @@ -326,7 +400,7 @@ void sh_serial_init (target_phys_addr_t base, int feat, |
| 326 | 400 | s->dr = 0xff; |
| 327 | 401 | } |
| 328 | 402 | |
| 329 | - s->rx_cnt = 0; | |
| 403 | + sh_serial_clear_fifo(s); | |
| 330 | 404 | |
| 331 | 405 | s_io_memory = cpu_register_io_memory(0, sh_serial_readfn, |
| 332 | 406 | sh_serial_writefn, s); | ... | ... |