]> Git Repo - qemu.git/blob - hw/sh_serial.c
SH4: Serial controller improvement
[qemu.git] / hw / sh_serial.c
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     target_phys_addr_t base;
57     int freq;
58     int feat;
59     int flags;
60     int rtrg;
61
62     CharDriverState *chr;
63
64     struct intc_source *eri;
65     struct intc_source *rxi;
66     struct intc_source *txi;
67     struct intc_source *tei;
68     struct intc_source *bri;
69 } sh_serial_state;
70
71 static void sh_serial_clear_fifo(sh_serial_state * s)
72 {
73     memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH);
74     s->rx_cnt = 0;
75     s->rx_head = 0;
76     s->rx_tail = 0;
77 }
78
79 static void sh_serial_ioport_write(void *opaque, uint32_t offs, uint32_t val)
80 {
81     sh_serial_state *s = opaque;
82     unsigned char ch;
83
84 #ifdef DEBUG_SERIAL
85     printf("sh_serial: write base=0x%08lx offs=0x%02x val=0x%02x\n",
86            (unsigned long) s->base, offs, val);
87 #endif
88     switch(offs) {
89     case 0x00: /* SMR */
90         s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff);
91         return;
92     case 0x04: /* BRR */
93         s->brr = val;
94         return;
95     case 0x08: /* SCR */
96         /* TODO : For SH7751, SCIF mask should be 0xfb. */
97         s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff);
98         if (!(val & (1 << 5)))
99             s->flags |= SH_SERIAL_FLAG_TEND;
100         if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) {
101             if ((val & (1 << 7)) && !(s->txi->asserted))
102                 sh_intc_toggle_source(s->txi, 0, 1);
103             else if (!(val & (1 << 7)) && s->txi->asserted)
104                 sh_intc_toggle_source(s->txi, 0, -1);
105         }
106         if (!(val & (1 << 6)) && s->rxi->asserted) {
107             sh_intc_toggle_source(s->rxi, 0, -1);
108         }
109         return;
110     case 0x0c: /* FTDR / TDR */
111         if (s->chr) {
112             ch = val;
113             qemu_chr_write(s->chr, &ch, 1);
114         }
115         s->dr = val;
116         s->flags &= ~SH_SERIAL_FLAG_TDE;
117         return;
118 #if 0
119     case 0x14: /* FRDR / RDR */
120         ret = 0;
121         break;
122 #endif
123     }
124     if (s->feat & SH_SERIAL_FEAT_SCIF) {
125         switch(offs) {
126         case 0x10: /* FSR */
127             if (!(val & (1 << 6)))
128                 s->flags &= ~SH_SERIAL_FLAG_TEND;
129             if (!(val & (1 << 5)))
130                 s->flags &= ~SH_SERIAL_FLAG_TDE;
131             if (!(val & (1 << 4)))
132                 s->flags &= ~SH_SERIAL_FLAG_BRK;
133             if (!(val & (1 << 1)))
134                 s->flags &= ~SH_SERIAL_FLAG_RDF;
135             if (!(val & (1 << 0)))
136                 s->flags &= ~SH_SERIAL_FLAG_DR;
137
138             if (!(val & (1 << 1)) || !(val & (1 << 0))) {
139                 if (s->rxi && s->rxi->asserted) {
140                     sh_intc_toggle_source(s->rxi, 0, -1);
141                 }
142             }
143             return;
144         case 0x18: /* FCR */
145             s->fcr = val;
146             switch ((val >> 6) & 3) {
147             case 0:
148                 s->rtrg = 1;
149                 break;
150             case 1:
151                 s->rtrg = 4;
152                 break;
153             case 2:
154                 s->rtrg = 8;
155                 break;
156             case 3:
157                 s->rtrg = 14;
158                 break;
159             }
160             if (val & (1 << 1)) {
161                 sh_serial_clear_fifo(s);
162                 s->sr &= ~(1 << 1);
163             }
164
165             return;
166         case 0x20: /* SPTR */
167             s->sptr = val & 0xf3;
168             return;
169         case 0x24: /* LSR */
170             return;
171         }
172     }
173     else {
174 #if 0
175         switch(offs) {
176         case 0x0c:
177             ret = s->dr;
178             break;
179         case 0x10:
180             ret = 0;
181             break;
182         case 0x1c:
183             ret = s->sptr;
184             break;
185         }
186 #endif
187     }
188
189     fprintf(stderr, "sh_serial: unsupported write to 0x%02x\n", offs);
190     assert(0);
191 }
192
193 static uint32_t sh_serial_ioport_read(void *opaque, uint32_t offs)
194 {
195     sh_serial_state *s = opaque;
196     uint32_t ret = ~0;
197
198 #if 0
199     switch(offs) {
200     case 0x00:
201         ret = s->smr;
202         break;
203     case 0x04:
204         ret = s->brr;
205         break;
206     case 0x08:
207         ret = s->scr;
208         break;
209     case 0x14:
210         ret = 0;
211         break;
212     }
213 #endif
214     if (s->feat & SH_SERIAL_FEAT_SCIF) {
215         switch(offs) {
216         case 0x00: /* SMR */
217             ret = s->smr;
218             break;
219         case 0x08: /* SCR */
220             ret = s->scr;
221             break;
222         case 0x10: /* FSR */
223             ret = 0;
224             if (s->flags & SH_SERIAL_FLAG_TEND)
225                 ret |= (1 << 6);
226             if (s->flags & SH_SERIAL_FLAG_TDE)
227                 ret |= (1 << 5);
228             if (s->flags & SH_SERIAL_FLAG_BRK)
229                 ret |= (1 << 4);
230             if (s->flags & SH_SERIAL_FLAG_RDF)
231                 ret |= (1 << 1);
232             if (s->flags & SH_SERIAL_FLAG_DR)
233                 ret |= (1 << 0);
234
235             if (s->scr & (1 << 5))
236                 s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND;
237
238             break;
239         case 0x14:
240             if (s->rx_cnt > 0) {
241                 ret = s->rx_fifo[s->rx_tail++];
242                 s->rx_cnt--;
243                 if (s->rx_tail == SH_RX_FIFO_LENGTH)
244                     s->rx_tail = 0;
245                 if (s->rx_cnt < s->rtrg)
246                     s->flags &= ~SH_SERIAL_FLAG_RDF;
247             }
248             break;
249 #if 0
250         case 0x18:
251             ret = s->fcr;
252             break;
253 #endif
254         case 0x1c:
255             ret = s->rx_cnt;
256             break;
257         case 0x20:
258             ret = s->sptr;
259             break;
260         case 0x24:
261             ret = 0;
262             break;
263         }
264     }
265     else {
266 #if 0
267         switch(offs) {
268         case 0x0c:
269             ret = s->dr;
270             break;
271         case 0x10:
272             ret = 0;
273             break;
274         case 0x14:
275             ret = s->rx_fifo[0];
276             break;
277         case 0x1c:
278             ret = s->sptr;
279             break;
280         }
281 #endif
282     }
283 #ifdef DEBUG_SERIAL
284     printf("sh_serial: read base=0x%08lx offs=0x%02x val=0x%x\n",
285            (unsigned long) s->base, offs, ret);
286 #endif
287
288     if (ret & ~((1 << 16) - 1)) {
289         fprintf(stderr, "sh_serial: unsupported read from 0x%02x\n", offs);
290         assert(0);
291     }
292
293     return ret;
294 }
295
296 static int sh_serial_can_receive(sh_serial_state *s)
297 {
298     return s->scr & (1 << 4);
299 }
300
301 static void sh_serial_receive_byte(sh_serial_state *s, int ch)
302 {
303     if (s->feat & SH_SERIAL_FEAT_SCIF) {
304         if (s->rx_cnt < SH_RX_FIFO_LENGTH) {
305             s->rx_fifo[s->rx_head++] = ch;
306             if (s->rx_head == SH_RX_FIFO_LENGTH)
307                 s->rx_head = 0;
308             s->rx_cnt++;
309             if (s->rx_cnt >= s->rtrg) {
310                 s->flags |= SH_SERIAL_FLAG_RDF;
311                 if (s->scr & (1 << 6) && s->rxi) {
312                     sh_intc_toggle_source(s->rxi, 0, 1);
313                 }
314             }
315         }
316     } else {
317         s->rx_fifo[0] = ch;
318     }
319 }
320
321 static void sh_serial_receive_break(sh_serial_state *s)
322 {
323     if (s->feat & SH_SERIAL_FEAT_SCIF)
324         s->sr |= (1 << 4);
325 }
326
327 static int sh_serial_can_receive1(void *opaque)
328 {
329     sh_serial_state *s = opaque;
330     return sh_serial_can_receive(s);
331 }
332
333 static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
334 {
335     sh_serial_state *s = opaque;
336     sh_serial_receive_byte(s, buf[0]);
337 }
338
339 static void sh_serial_event(void *opaque, int event)
340 {
341     sh_serial_state *s = opaque;
342     if (event == CHR_EVENT_BREAK)
343         sh_serial_receive_break(s);
344 }
345
346 static uint32_t sh_serial_read (void *opaque, target_phys_addr_t addr)
347 {
348     sh_serial_state *s = opaque;
349     return sh_serial_ioport_read(s, addr - s->base);
350 }
351
352 static void sh_serial_write (void *opaque,
353                              target_phys_addr_t addr, uint32_t value)
354 {
355     sh_serial_state *s = opaque;
356     sh_serial_ioport_write(s, addr - s->base, value);
357 }
358
359 static CPUReadMemoryFunc *sh_serial_readfn[] = {
360     &sh_serial_read,
361     &sh_serial_read,
362     &sh_serial_read,
363 };
364
365 static CPUWriteMemoryFunc *sh_serial_writefn[] = {
366     &sh_serial_write,
367     &sh_serial_write,
368     &sh_serial_write,
369 };
370
371 void sh_serial_init (target_phys_addr_t base, int feat,
372                      uint32_t freq, CharDriverState *chr,
373                      struct intc_source *eri_source,
374                      struct intc_source *rxi_source,
375                      struct intc_source *txi_source,
376                      struct intc_source *tei_source,
377                      struct intc_source *bri_source)
378 {
379     sh_serial_state *s;
380     int s_io_memory;
381
382     s = qemu_mallocz(sizeof(sh_serial_state));
383     if (!s)
384         return;
385
386     s->base = base;
387     s->feat = feat;
388     s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
389     s->rtrg = 1;
390
391     s->smr = 0;
392     s->brr = 0xff;
393     s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */
394     s->sptr = 0;
395
396     if (feat & SH_SERIAL_FEAT_SCIF) {
397         s->fcr = 0;
398     }
399     else {
400         s->dr = 0xff;
401     }
402
403     sh_serial_clear_fifo(s);
404
405     s_io_memory = cpu_register_io_memory(0, sh_serial_readfn,
406                                          sh_serial_writefn, s);
407     cpu_register_physical_memory(base, 0x28, s_io_memory);
408
409     s->chr = chr;
410
411     if (chr)
412         qemu_chr_add_handlers(chr, sh_serial_can_receive1, sh_serial_receive1,
413                               sh_serial_event, s);
414
415     s->eri = eri_source;
416     s->rxi = rxi_source;
417     s->txi = txi_source;
418     s->tei = tei_source;
419     s->bri = bri_source;
420 }
This page took 0.045796 seconds and 4 git commands to generate.