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,6 +37,8 @@ | ||
| 37 | #define SH_SERIAL_FLAG_BRK (1 << 3) | 37 | #define SH_SERIAL_FLAG_BRK (1 << 3) |
| 38 | #define SH_SERIAL_FLAG_DR (1 << 4) | 38 | #define SH_SERIAL_FLAG_DR (1 << 4) |
| 39 | 39 | ||
| 40 | +#define SH_RX_FIFO_LENGTH (16) | ||
| 41 | + | ||
| 40 | typedef struct { | 42 | typedef struct { |
| 41 | uint8_t smr; | 43 | uint8_t smr; |
| 42 | uint8_t brr; | 44 | uint8_t brr; |
| @@ -46,13 +48,16 @@ typedef struct { | @@ -46,13 +48,16 @@ typedef struct { | ||
| 46 | uint16_t fcr; | 48 | uint16_t fcr; |
| 47 | uint8_t sptr; | 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 | uint8_t rx_cnt; | 52 | uint8_t rx_cnt; |
| 53 | + uint8_t rx_tail; | ||
| 54 | + uint8_t rx_head; | ||
| 51 | 55 | ||
| 52 | target_phys_addr_t base; | 56 | target_phys_addr_t base; |
| 53 | int freq; | 57 | int freq; |
| 54 | int feat; | 58 | int feat; |
| 55 | int flags; | 59 | int flags; |
| 60 | + int rtrg; | ||
| 56 | 61 | ||
| 57 | CharDriverState *chr; | 62 | CharDriverState *chr; |
| 58 | 63 | ||
| @@ -63,6 +68,14 @@ typedef struct { | @@ -63,6 +68,14 @@ typedef struct { | ||
| 63 | struct intc_source *bri; | 68 | struct intc_source *bri; |
| 64 | } sh_serial_state; | 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 | static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) | 79 | static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) |
| 67 | { | 80 | { |
| 68 | sh_serial_state *s = opaque; | 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,6 +93,7 @@ static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) | ||
| 80 | s->brr = val; | 93 | s->brr = val; |
| 81 | return; | 94 | return; |
| 82 | case 0x08: /* SCR */ | 95 | case 0x08: /* SCR */ |
| 96 | + /* TODO : For SH7751, SCIF mask should be 0xfb. */ | ||
| 83 | s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff); | 97 | s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff); |
| 84 | if (!(val & (1 << 5))) | 98 | if (!(val & (1 << 5))) |
| 85 | s->flags |= SH_SERIAL_FLAG_TEND; | 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,6 +103,9 @@ static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) | ||
| 89 | else if (!(val & (1 << 7)) && s->txi->asserted) | 103 | else if (!(val & (1 << 7)) && s->txi->asserted) |
| 90 | sh_intc_toggle_source(s->txi, 0, -1); | 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 | return; | 109 | return; |
| 93 | case 0x0c: /* FTDR / TDR */ | 110 | case 0x0c: /* FTDR / TDR */ |
| 94 | if (s->chr) { | 111 | if (s->chr) { |
| @@ -117,12 +134,37 @@ static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) | @@ -117,12 +134,37 @@ static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) | ||
| 117 | s->flags &= ~SH_SERIAL_FLAG_RDF; | 134 | s->flags &= ~SH_SERIAL_FLAG_RDF; |
| 118 | if (!(val & (1 << 0))) | 135 | if (!(val & (1 << 0))) |
| 119 | s->flags &= ~SH_SERIAL_FLAG_DR; | 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 | return; | 143 | return; |
| 121 | case 0x18: /* FCR */ | 144 | case 0x18: /* FCR */ |
| 122 | s->fcr = val; | 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 | return; | 165 | return; |
| 124 | case 0x20: /* SPTR */ | 166 | case 0x20: /* SPTR */ |
| 125 | - s->sptr = val; | 167 | + s->sptr = val & 0xf3; |
| 126 | return; | 168 | return; |
| 127 | case 0x24: /* LSR */ | 169 | case 0x24: /* LSR */ |
| 128 | return; | 170 | return; |
| @@ -190,10 +232,20 @@ static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs) | @@ -190,10 +232,20 @@ static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs) | ||
| 190 | if (s->flags & SH_SERIAL_FLAG_DR) | 232 | if (s->flags & SH_SERIAL_FLAG_DR) |
| 191 | ret |= (1 << 0); | 233 | ret |= (1 << 0); |
| 192 | 234 | ||
| 193 | - if (s->scr & (1 << 5)) | 235 | + if (s->scr & (1 << 5)) |
| 194 | s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND; | 236 | s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND; |
| 195 | 237 | ||
| 196 | break; | 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 | #if 0 | 249 | #if 0 |
| 198 | case 0x18: | 250 | case 0x18: |
| 199 | ret = s->fcr; | 251 | ret = s->fcr; |
| @@ -219,6 +271,9 @@ static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs) | @@ -219,6 +271,9 @@ static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs) | ||
| 219 | case 0x10: | 271 | case 0x10: |
| 220 | ret = 0; | 272 | ret = 0; |
| 221 | break; | 273 | break; |
| 274 | + case 0x14: | ||
| 275 | + ret = s->rx_fifo[0]; | ||
| 276 | + break; | ||
| 222 | case 0x1c: | 277 | case 0x1c: |
| 223 | ret = s->sptr; | 278 | ret = s->sptr; |
| 224 | break; | 279 | break; |
| @@ -240,15 +295,33 @@ static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs) | @@ -240,15 +295,33 @@ static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs) | ||
| 240 | 295 | ||
| 241 | static int sh_serial_can_receive(sh_serial_state *s) | 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 | static void sh_serial_receive_byte(sh_serial_state *s, int ch) | 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 | static void sh_serial_receive_break(sh_serial_state *s) | 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 | static int sh_serial_can_receive1(void *opaque) | 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,6 +386,7 @@ void sh_serial_init (target_phys_addr_t base, int feat, | ||
| 313 | s->base = base; | 386 | s->base = base; |
| 314 | s->feat = feat; | 387 | s->feat = feat; |
| 315 | s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE; | 388 | s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE; |
| 389 | + s->rtrg = 1; | ||
| 316 | 390 | ||
| 317 | s->smr = 0; | 391 | s->smr = 0; |
| 318 | s->brr = 0xff; | 392 | s->brr = 0xff; |
| @@ -326,7 +400,7 @@ void sh_serial_init (target_phys_addr_t base, int feat, | @@ -326,7 +400,7 @@ void sh_serial_init (target_phys_addr_t base, int feat, | ||
| 326 | s->dr = 0xff; | 400 | s->dr = 0xff; |
| 327 | } | 401 | } |
| 328 | 402 | ||
| 329 | - s->rx_cnt = 0; | 403 | + sh_serial_clear_fifo(s); |
| 330 | 404 | ||
| 331 | s_io_memory = cpu_register_io_memory(0, sh_serial_readfn, | 405 | s_io_memory = cpu_register_io_memory(0, sh_serial_readfn, |
| 332 | sh_serial_writefn, s); | 406 | sh_serial_writefn, s); |