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