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