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