|
1 /* |
|
2 * QEMU SCI/SCIF serial port emulation |
|
3 * |
|
4 * Copyright (c) 2007 Magnus Damm |
|
5 * |
|
6 * Based on serial.c - QEMU 16450 UART emulation |
|
7 * Copyright (c) 2003-2004 Fabrice Bellard |
|
8 * |
|
9 * Permission is hereby granted, free of charge, to any person obtaining a copy |
|
10 * of this software and associated documentation files (the "Software"), to deal |
|
11 * in the Software without restriction, including without limitation the rights |
|
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
13 * copies of the Software, and to permit persons to whom the Software is |
|
14 * furnished to do so, subject to the following conditions: |
|
15 * |
|
16 * The above copyright notice and this permission notice shall be included in |
|
17 * all copies or substantial portions of the Software. |
|
18 * |
|
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
25 * THE SOFTWARE. |
|
26 */ |
|
27 #include "hw.h" |
|
28 #include "sh.h" |
|
29 #include "qemu-char.h" |
|
30 #include <assert.h> |
|
31 |
|
32 //#define DEBUG_SERIAL |
|
33 |
|
34 #define SH_SERIAL_FLAG_TEND (1 << 0) |
|
35 #define SH_SERIAL_FLAG_TDE (1 << 1) |
|
36 #define SH_SERIAL_FLAG_RDF (1 << 2) |
|
37 #define SH_SERIAL_FLAG_BRK (1 << 3) |
|
38 #define SH_SERIAL_FLAG_DR (1 << 4) |
|
39 |
|
40 #define SH_RX_FIFO_LENGTH (16) |
|
41 |
|
42 typedef struct { |
|
43 uint8_t smr; |
|
44 uint8_t brr; |
|
45 uint8_t scr; |
|
46 uint8_t dr; /* ftdr / tdr */ |
|
47 uint8_t sr; /* fsr / ssr */ |
|
48 uint16_t fcr; |
|
49 uint8_t sptr; |
|
50 |
|
51 uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */ |
|
52 uint8_t rx_cnt; |
|
53 uint8_t rx_tail; |
|
54 uint8_t rx_head; |
|
55 |
|
56 int freq; |
|
57 int feat; |
|
58 int flags; |
|
59 int rtrg; |
|
60 |
|
61 CharDriverState *chr; |
|
62 |
|
63 qemu_irq eri; |
|
64 qemu_irq rxi; |
|
65 qemu_irq txi; |
|
66 qemu_irq tei; |
|
67 qemu_irq bri; |
|
68 } sh_serial_state; |
|
69 |
|
70 static void sh_serial_clear_fifo(sh_serial_state * s) |
|
71 { |
|
72 memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH); |
|
73 s->rx_cnt = 0; |
|
74 s->rx_head = 0; |
|
75 s->rx_tail = 0; |
|
76 } |
|
77 |
|
78 static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val) |
|
79 { |
|
80 sh_serial_state *s = opaque; |
|
81 unsigned char ch; |
|
82 |
|
83 #ifdef DEBUG_SERIAL |
|
84 printf("sh_serial: write offs=0x%02x val=0x%02x\n", |
|
85 offs, val); |
|
86 #endif |
|
87 switch(offs) { |
|
88 case 0x00: /* SMR */ |
|
89 s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff); |
|
90 return; |
|
91 case 0x04: /* BRR */ |
|
92 s->brr = val; |
|
93 return; |
|
94 case 0x08: /* SCR */ |
|
95 /* TODO : For SH7751, SCIF mask should be 0xfb. */ |
|
96 s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff); |
|
97 if (!(val & (1 << 5))) |
|
98 s->flags |= SH_SERIAL_FLAG_TEND; |
|
99 if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) { |
|
100 qemu_set_irq(s->txi, val & (1 << 7)); |
|
101 } |
|
102 if (!(val & (1 << 6))) { |
|
103 qemu_set_irq(s->rxi, 0); |
|
104 } |
|
105 return; |
|
106 case 0x0c: /* FTDR / TDR */ |
|
107 if (s->chr) { |
|
108 ch = val; |
|
109 qemu_chr_write(s->chr, &ch, 1); |
|
110 } |
|
111 s->dr = val; |
|
112 s->flags &= ~SH_SERIAL_FLAG_TDE; |
|
113 return; |
|
114 #if 0 |
|
115 case 0x14: /* FRDR / RDR */ |
|
116 ret = 0; |
|
117 break; |
|
118 #endif |
|
119 } |
|
120 if (s->feat & SH_SERIAL_FEAT_SCIF) { |
|
121 switch(offs) { |
|
122 case 0x10: /* FSR */ |
|
123 if (!(val & (1 << 6))) |
|
124 s->flags &= ~SH_SERIAL_FLAG_TEND; |
|
125 if (!(val & (1 << 5))) |
|
126 s->flags &= ~SH_SERIAL_FLAG_TDE; |
|
127 if (!(val & (1 << 4))) |
|
128 s->flags &= ~SH_SERIAL_FLAG_BRK; |
|
129 if (!(val & (1 << 1))) |
|
130 s->flags &= ~SH_SERIAL_FLAG_RDF; |
|
131 if (!(val & (1 << 0))) |
|
132 s->flags &= ~SH_SERIAL_FLAG_DR; |
|
133 |
|
134 if (!(val & (1 << 1)) || !(val & (1 << 0))) { |
|
135 if (s->rxi) { |
|
136 qemu_set_irq(s->rxi, 0); |
|
137 } |
|
138 } |
|
139 return; |
|
140 case 0x18: /* FCR */ |
|
141 s->fcr = val; |
|
142 switch ((val >> 6) & 3) { |
|
143 case 0: |
|
144 s->rtrg = 1; |
|
145 break; |
|
146 case 1: |
|
147 s->rtrg = 4; |
|
148 break; |
|
149 case 2: |
|
150 s->rtrg = 8; |
|
151 break; |
|
152 case 3: |
|
153 s->rtrg = 14; |
|
154 break; |
|
155 } |
|
156 if (val & (1 << 1)) { |
|
157 sh_serial_clear_fifo(s); |
|
158 s->sr &= ~(1 << 1); |
|
159 } |
|
160 |
|
161 return; |
|
162 case 0x20: /* SPTR */ |
|
163 s->sptr = val & 0xf3; |
|
164 return; |
|
165 case 0x24: /* LSR */ |
|
166 return; |
|
167 } |
|
168 } |
|
169 else { |
|
170 switch(offs) { |
|
171 #if 0 |
|
172 case 0x0c: |
|
173 ret = s->dr; |
|
174 break; |
|
175 case 0x10: |
|
176 ret = 0; |
|
177 break; |
|
178 #endif |
|
179 case 0x1c: |
|
180 s->sptr = val & 0x8f; |
|
181 return; |
|
182 } |
|
183 } |
|
184 |
|
185 fprintf(stderr, "sh_serial: unsupported write to 0x%02x\n", offs); |
|
186 assert(0); |
|
187 } |
|
188 |
|
189 static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs) |
|
190 { |
|
191 sh_serial_state *s = opaque; |
|
192 uint32_t ret = ~0; |
|
193 |
|
194 #if 0 |
|
195 switch(offs) { |
|
196 case 0x00: |
|
197 ret = s->smr; |
|
198 break; |
|
199 case 0x04: |
|
200 ret = s->brr; |
|
201 break; |
|
202 case 0x08: |
|
203 ret = s->scr; |
|
204 break; |
|
205 case 0x14: |
|
206 ret = 0; |
|
207 break; |
|
208 } |
|
209 #endif |
|
210 if (s->feat & SH_SERIAL_FEAT_SCIF) { |
|
211 switch(offs) { |
|
212 case 0x00: /* SMR */ |
|
213 ret = s->smr; |
|
214 break; |
|
215 case 0x08: /* SCR */ |
|
216 ret = s->scr; |
|
217 break; |
|
218 case 0x10: /* FSR */ |
|
219 ret = 0; |
|
220 if (s->flags & SH_SERIAL_FLAG_TEND) |
|
221 ret |= (1 << 6); |
|
222 if (s->flags & SH_SERIAL_FLAG_TDE) |
|
223 ret |= (1 << 5); |
|
224 if (s->flags & SH_SERIAL_FLAG_BRK) |
|
225 ret |= (1 << 4); |
|
226 if (s->flags & SH_SERIAL_FLAG_RDF) |
|
227 ret |= (1 << 1); |
|
228 if (s->flags & SH_SERIAL_FLAG_DR) |
|
229 ret |= (1 << 0); |
|
230 |
|
231 if (s->scr & (1 << 5)) |
|
232 s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND; |
|
233 |
|
234 break; |
|
235 case 0x14: |
|
236 if (s->rx_cnt > 0) { |
|
237 ret = s->rx_fifo[s->rx_tail++]; |
|
238 s->rx_cnt--; |
|
239 if (s->rx_tail == SH_RX_FIFO_LENGTH) |
|
240 s->rx_tail = 0; |
|
241 if (s->rx_cnt < s->rtrg) |
|
242 s->flags &= ~SH_SERIAL_FLAG_RDF; |
|
243 } |
|
244 break; |
|
245 #if 0 |
|
246 case 0x18: |
|
247 ret = s->fcr; |
|
248 break; |
|
249 #endif |
|
250 case 0x1c: |
|
251 ret = s->rx_cnt; |
|
252 break; |
|
253 case 0x20: |
|
254 ret = s->sptr; |
|
255 break; |
|
256 case 0x24: |
|
257 ret = 0; |
|
258 break; |
|
259 } |
|
260 } |
|
261 else { |
|
262 switch(offs) { |
|
263 #if 0 |
|
264 case 0x0c: |
|
265 ret = s->dr; |
|
266 break; |
|
267 case 0x10: |
|
268 ret = 0; |
|
269 break; |
|
270 case 0x14: |
|
271 ret = s->rx_fifo[0]; |
|
272 break; |
|
273 #endif |
|
274 case 0x1c: |
|
275 ret = s->sptr; |
|
276 break; |
|
277 } |
|
278 } |
|
279 #ifdef DEBUG_SERIAL |
|
280 printf("sh_serial: read offs=0x%02x val=0x%x\n", |
|
281 offs, ret); |
|
282 #endif |
|
283 |
|
284 if (ret & ~((1 << 16) - 1)) { |
|
285 fprintf(stderr, "sh_serial: unsupported read from 0x%02x\n", offs); |
|
286 assert(0); |
|
287 } |
|
288 |
|
289 return ret; |
|
290 } |
|
291 |
|
292 static int sh_serial_can_receive(sh_serial_state *s) |
|
293 { |
|
294 return s->scr & (1 << 4); |
|
295 } |
|
296 |
|
297 static void sh_serial_receive_byte(sh_serial_state *s, int ch) |
|
298 { |
|
299 if (s->feat & SH_SERIAL_FEAT_SCIF) { |
|
300 if (s->rx_cnt < SH_RX_FIFO_LENGTH) { |
|
301 s->rx_fifo[s->rx_head++] = ch; |
|
302 if (s->rx_head == SH_RX_FIFO_LENGTH) |
|
303 s->rx_head = 0; |
|
304 s->rx_cnt++; |
|
305 if (s->rx_cnt >= s->rtrg) { |
|
306 s->flags |= SH_SERIAL_FLAG_RDF; |
|
307 if (s->scr & (1 << 6) && s->rxi) { |
|
308 qemu_set_irq(s->rxi, 1); |
|
309 } |
|
310 } |
|
311 } |
|
312 } else { |
|
313 s->rx_fifo[0] = ch; |
|
314 } |
|
315 } |
|
316 |
|
317 static void sh_serial_receive_break(sh_serial_state *s) |
|
318 { |
|
319 if (s->feat & SH_SERIAL_FEAT_SCIF) |
|
320 s->sr |= (1 << 4); |
|
321 } |
|
322 |
|
323 static int sh_serial_can_receive1(void *opaque) |
|
324 { |
|
325 sh_serial_state *s = opaque; |
|
326 return sh_serial_can_receive(s); |
|
327 } |
|
328 |
|
329 static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) |
|
330 { |
|
331 sh_serial_state *s = opaque; |
|
332 sh_serial_receive_byte(s, buf[0]); |
|
333 } |
|
334 |
|
335 static void sh_serial_event(void *opaque, int event) |
|
336 { |
|
337 sh_serial_state *s = opaque; |
|
338 if (event == CHR_EVENT_BREAK) |
|
339 sh_serial_receive_break(s); |
|
340 } |
|
341 |
|
342 static uint32_t sh_serial_read (void *opaque, target_phys_addr_t addr) |
|
343 { |
|
344 sh_serial_state *s = opaque; |
|
345 return sh_serial_ioport_read(s, addr); |
|
346 } |
|
347 |
|
348 static void sh_serial_write (void *opaque, |
|
349 target_phys_addr_t addr, uint32_t value) |
|
350 { |
|
351 sh_serial_state *s = opaque; |
|
352 sh_serial_ioport_write(s, addr, value); |
|
353 } |
|
354 |
|
355 static CPUReadMemoryFunc *sh_serial_readfn[] = { |
|
356 &sh_serial_read, |
|
357 &sh_serial_read, |
|
358 &sh_serial_read, |
|
359 }; |
|
360 |
|
361 static CPUWriteMemoryFunc *sh_serial_writefn[] = { |
|
362 &sh_serial_write, |
|
363 &sh_serial_write, |
|
364 &sh_serial_write, |
|
365 }; |
|
366 |
|
367 void sh_serial_init (target_phys_addr_t base, int feat, |
|
368 uint32_t freq, CharDriverState *chr, |
|
369 qemu_irq eri_source, |
|
370 qemu_irq rxi_source, |
|
371 qemu_irq txi_source, |
|
372 qemu_irq tei_source, |
|
373 qemu_irq bri_source) |
|
374 { |
|
375 sh_serial_state *s; |
|
376 int s_io_memory; |
|
377 |
|
378 s = qemu_mallocz(sizeof(sh_serial_state)); |
|
379 if (!s) |
|
380 return; |
|
381 |
|
382 s->feat = feat; |
|
383 s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE; |
|
384 s->rtrg = 1; |
|
385 |
|
386 s->smr = 0; |
|
387 s->brr = 0xff; |
|
388 s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */ |
|
389 s->sptr = 0; |
|
390 |
|
391 if (feat & SH_SERIAL_FEAT_SCIF) { |
|
392 s->fcr = 0; |
|
393 } |
|
394 else { |
|
395 s->dr = 0xff; |
|
396 } |
|
397 |
|
398 sh_serial_clear_fifo(s); |
|
399 |
|
400 s_io_memory = cpu_register_io_memory(0, sh_serial_readfn, |
|
401 sh_serial_writefn, s); |
|
402 cpu_register_physical_memory(P4ADDR(base), 0x28, s_io_memory); |
|
403 cpu_register_physical_memory(A7ADDR(base), 0x28, s_io_memory); |
|
404 |
|
405 s->chr = chr; |
|
406 |
|
407 if (chr) |
|
408 qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1, |
|
409 sh_serial_event, s); |
|
410 |
|
411 s->eri = eri_source; |
|
412 s->rxi = rxi_source; |
|
413 s->txi = txi_source; |
|
414 s->tei = tei_source; |
|
415 s->bri = bri_source; |
|
416 } |