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