xref: /qemu/hw/char/sh_serial.c (revision ad52cfc13750662fd003565e8035bb8ffbd4f0ef)
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 
28 #include "qemu/osdep.h"
29 #include "hw/irq.h"
30 #include "hw/sh4/sh.h"
31 #include "chardev/char-fe.h"
32 #include "qapi/error.h"
33 #include "qemu/timer.h"
34 #include "trace.h"
35 
36 #define SH_SERIAL_FLAG_TEND (1 << 0)
37 #define SH_SERIAL_FLAG_TDE  (1 << 1)
38 #define SH_SERIAL_FLAG_RDF  (1 << 2)
39 #define SH_SERIAL_FLAG_BRK  (1 << 3)
40 #define SH_SERIAL_FLAG_DR   (1 << 4)
41 
42 #define SH_RX_FIFO_LENGTH (16)
43 
44 typedef struct {
45     MemoryRegion iomem;
46     MemoryRegion iomem_p4;
47     MemoryRegion iomem_a7;
48     uint8_t smr;
49     uint8_t brr;
50     uint8_t scr;
51     uint8_t dr; /* ftdr / tdr */
52     uint8_t sr; /* fsr / ssr */
53     uint16_t fcr;
54     uint8_t sptr;
55 
56     uint8_t rx_fifo[SH_RX_FIFO_LENGTH]; /* frdr / rdr */
57     uint8_t rx_cnt;
58     uint8_t rx_tail;
59     uint8_t rx_head;
60 
61     int freq;
62     int feat;
63     int flags;
64     int rtrg;
65 
66     CharBackend chr;
67     QEMUTimer *fifo_timeout_timer;
68     uint64_t etu; /* Elementary Time Unit (ns) */
69 
70     qemu_irq eri;
71     qemu_irq rxi;
72     qemu_irq txi;
73     qemu_irq tei;
74     qemu_irq bri;
75 } sh_serial_state;
76 
77 static void sh_serial_clear_fifo(sh_serial_state *s)
78 {
79     memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH);
80     s->rx_cnt = 0;
81     s->rx_head = 0;
82     s->rx_tail = 0;
83 }
84 
85 static void sh_serial_write(void *opaque, hwaddr offs,
86                             uint64_t val, unsigned size)
87 {
88     sh_serial_state *s = opaque;
89     unsigned char ch;
90 
91     trace_sh_serial_write(size, offs, val);
92     switch (offs) {
93     case 0x00: /* SMR */
94         s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff);
95         return;
96     case 0x04: /* BRR */
97         s->brr = val;
98         return;
99     case 0x08: /* SCR */
100         /* TODO : For SH7751, SCIF mask should be 0xfb. */
101         s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff);
102         if (!(val & (1 << 5))) {
103             s->flags |= SH_SERIAL_FLAG_TEND;
104         }
105         if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) {
106             qemu_set_irq(s->txi, val & (1 << 7));
107         }
108         if (!(val & (1 << 6))) {
109             qemu_set_irq(s->rxi, 0);
110         }
111         return;
112     case 0x0c: /* FTDR / TDR */
113         if (qemu_chr_fe_backend_connected(&s->chr)) {
114             ch = val;
115             /*
116              * XXX this blocks entire thread. Rewrite to use
117              * qemu_chr_fe_write and background I/O callbacks
118              */
119             qemu_chr_fe_write_all(&s->chr, &ch, 1);
120         }
121         s->dr = val;
122         s->flags &= ~SH_SERIAL_FLAG_TDE;
123         return;
124 #if 0
125     case 0x14: /* FRDR / RDR */
126         ret = 0;
127         break;
128 #endif
129     }
130     if (s->feat & SH_SERIAL_FEAT_SCIF) {
131         switch (offs) {
132         case 0x10: /* FSR */
133             if (!(val & (1 << 6))) {
134                 s->flags &= ~SH_SERIAL_FLAG_TEND;
135             }
136             if (!(val & (1 << 5))) {
137                 s->flags &= ~SH_SERIAL_FLAG_TDE;
138             }
139             if (!(val & (1 << 4))) {
140                 s->flags &= ~SH_SERIAL_FLAG_BRK;
141             }
142             if (!(val & (1 << 1))) {
143                 s->flags &= ~SH_SERIAL_FLAG_RDF;
144             }
145             if (!(val & (1 << 0))) {
146                 s->flags &= ~SH_SERIAL_FLAG_DR;
147             }
148 
149             if (!(val & (1 << 1)) || !(val & (1 << 0))) {
150                 if (s->rxi) {
151                     qemu_set_irq(s->rxi, 0);
152                 }
153             }
154             return;
155         case 0x18: /* FCR */
156             s->fcr = val;
157             switch ((val >> 6) & 3) {
158             case 0:
159                 s->rtrg = 1;
160                 break;
161             case 1:
162                 s->rtrg = 4;
163                 break;
164             case 2:
165                 s->rtrg = 8;
166                 break;
167             case 3:
168                 s->rtrg = 14;
169                 break;
170             }
171             if (val & (1 << 1)) {
172                 sh_serial_clear_fifo(s);
173                 s->sr &= ~(1 << 1);
174             }
175 
176             return;
177         case 0x20: /* SPTR */
178             s->sptr = val & 0xf3;
179             return;
180         case 0x24: /* LSR */
181             return;
182         }
183     } else {
184         switch (offs) {
185 #if 0
186         case 0x0c:
187             ret = s->dr;
188             break;
189         case 0x10:
190             ret = 0;
191             break;
192 #endif
193         case 0x1c:
194             s->sptr = val & 0x8f;
195             return;
196         }
197     }
198 
199     fprintf(stderr, "sh_serial: unsupported write to 0x%02"
200             HWADDR_PRIx "\n", offs);
201     abort();
202 }
203 
204 static uint64_t sh_serial_read(void *opaque, hwaddr offs,
205                                unsigned size)
206 {
207     sh_serial_state *s = opaque;
208     uint32_t ret = ~0;
209 
210 #if 0
211     switch (offs) {
212     case 0x00:
213         ret = s->smr;
214         break;
215     case 0x04:
216         ret = s->brr;
217         break;
218     case 0x08:
219         ret = s->scr;
220         break;
221     case 0x14:
222         ret = 0;
223         break;
224     }
225 #endif
226     if (s->feat & SH_SERIAL_FEAT_SCIF) {
227         switch (offs) {
228         case 0x00: /* SMR */
229             ret = s->smr;
230             break;
231         case 0x08: /* SCR */
232             ret = s->scr;
233             break;
234         case 0x10: /* FSR */
235             ret = 0;
236             if (s->flags & SH_SERIAL_FLAG_TEND) {
237                 ret |= (1 << 6);
238             }
239             if (s->flags & SH_SERIAL_FLAG_TDE) {
240                 ret |= (1 << 5);
241             }
242             if (s->flags & SH_SERIAL_FLAG_BRK) {
243                 ret |= (1 << 4);
244             }
245             if (s->flags & SH_SERIAL_FLAG_RDF) {
246                 ret |= (1 << 1);
247             }
248             if (s->flags & SH_SERIAL_FLAG_DR) {
249                 ret |= (1 << 0);
250             }
251 
252             if (s->scr & (1 << 5)) {
253                 s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND;
254             }
255 
256             break;
257         case 0x14:
258             if (s->rx_cnt > 0) {
259                 ret = s->rx_fifo[s->rx_tail++];
260                 s->rx_cnt--;
261                 if (s->rx_tail == SH_RX_FIFO_LENGTH) {
262                     s->rx_tail = 0;
263                 }
264                 if (s->rx_cnt < s->rtrg) {
265                     s->flags &= ~SH_SERIAL_FLAG_RDF;
266                 }
267             }
268             break;
269         case 0x18:
270             ret = s->fcr;
271             break;
272         case 0x1c:
273             ret = s->rx_cnt;
274             break;
275         case 0x20:
276             ret = s->sptr;
277             break;
278         case 0x24:
279             ret = 0;
280             break;
281         }
282     } else {
283         switch (offs) {
284 #if 0
285         case 0x0c:
286             ret = s->dr;
287             break;
288         case 0x10:
289             ret = 0;
290             break;
291         case 0x14:
292             ret = s->rx_fifo[0];
293             break;
294 #endif
295         case 0x1c:
296             ret = s->sptr;
297             break;
298         }
299     }
300     trace_sh_serial_read(size, offs, ret);
301 
302     if (ret & ~((1 << 16) - 1)) {
303         fprintf(stderr, "sh_serial: unsupported read from 0x%02"
304                 HWADDR_PRIx "\n", offs);
305         abort();
306     }
307 
308     return ret;
309 }
310 
311 static int sh_serial_can_receive(sh_serial_state *s)
312 {
313     return s->scr & (1 << 4);
314 }
315 
316 static void sh_serial_receive_break(sh_serial_state *s)
317 {
318     if (s->feat & SH_SERIAL_FEAT_SCIF) {
319         s->sr |= (1 << 4);
320     }
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_timeout_int(void *opaque)
330 {
331     sh_serial_state *s = opaque;
332 
333     s->flags |= SH_SERIAL_FLAG_RDF;
334     if (s->scr & (1 << 6) && s->rxi) {
335         qemu_set_irq(s->rxi, 1);
336     }
337 }
338 
339 static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
340 {
341     sh_serial_state *s = opaque;
342 
343     if (s->feat & SH_SERIAL_FEAT_SCIF) {
344         int i;
345         for (i = 0; i < size; i++) {
346             if (s->rx_cnt < SH_RX_FIFO_LENGTH) {
347                 s->rx_fifo[s->rx_head++] = buf[i];
348                 if (s->rx_head == SH_RX_FIFO_LENGTH) {
349                     s->rx_head = 0;
350                 }
351                 s->rx_cnt++;
352                 if (s->rx_cnt >= s->rtrg) {
353                     s->flags |= SH_SERIAL_FLAG_RDF;
354                     if (s->scr & (1 << 6) && s->rxi) {
355                         timer_del(s->fifo_timeout_timer);
356                         qemu_set_irq(s->rxi, 1);
357                     }
358                 } else {
359                     timer_mod(s->fifo_timeout_timer,
360                         qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu);
361                 }
362             }
363         }
364     } else {
365         s->rx_fifo[0] = buf[0];
366     }
367 }
368 
369 static void sh_serial_event(void *opaque, QEMUChrEvent event)
370 {
371     sh_serial_state *s = opaque;
372     if (event == CHR_EVENT_BREAK) {
373         sh_serial_receive_break(s);
374     }
375 }
376 
377 static const MemoryRegionOps sh_serial_ops = {
378     .read = sh_serial_read,
379     .write = sh_serial_write,
380     .endianness = DEVICE_NATIVE_ENDIAN,
381 };
382 
383 void sh_serial_init(MemoryRegion *sysmem,
384                     hwaddr base, int feat,
385                     uint32_t freq, Chardev *chr,
386                     qemu_irq eri_source,
387                     qemu_irq rxi_source,
388                     qemu_irq txi_source,
389                     qemu_irq tei_source,
390                     qemu_irq bri_source)
391 {
392     sh_serial_state *s;
393 
394     s = g_malloc0(sizeof(sh_serial_state));
395 
396     s->feat = feat;
397     s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
398     s->rtrg = 1;
399 
400     s->smr = 0;
401     s->brr = 0xff;
402     s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */
403     s->sptr = 0;
404 
405     if (feat & SH_SERIAL_FEAT_SCIF) {
406         s->fcr = 0;
407     } else {
408         s->dr = 0xff;
409     }
410 
411     sh_serial_clear_fifo(s);
412 
413     memory_region_init_io(&s->iomem, NULL, &sh_serial_ops, s,
414                           "serial", 0x100000000ULL);
415 
416     memory_region_init_alias(&s->iomem_p4, NULL, "serial-p4", &s->iomem,
417                              0, 0x28);
418     memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4);
419 
420     memory_region_init_alias(&s->iomem_a7, NULL, "serial-a7", &s->iomem,
421                              0, 0x28);
422     memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
423 
424     if (chr) {
425         qemu_chr_fe_init(&s->chr, chr, &error_abort);
426         qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1,
427                                  sh_serial_receive1,
428                                  sh_serial_event, NULL, s, NULL, true);
429     }
430 
431     s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
432                                          sh_serial_timeout_int, s);
433     s->etu = NANOSECONDS_PER_SECOND / 9600;
434     s->eri = eri_source;
435     s->rxi = rxi_source;
436     s->txi = txi_source;
437     s->tei = tei_source;
438     s->bri = bri_source;
439 }
440