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