ETRAX: Simplify serport control logic.
[qemu] / hw / etraxfs_ser.c
1 /*
2  * QEMU ETRAX System Emulator
3  *
4  * Copyright (c) 2007 Edgar E. Iglesias, Axis Communications AB.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24
25 #include <stdio.h>
26 #include <ctype.h>
27 #include "hw.h"
28 #include "qemu-char.h"
29 #include "etraxfs.h"
30
31 #define D(x)
32
33 #define RW_TR_CTRL     (0x00 / 4)
34 #define RW_TR_DMA_EN   (0x04 / 4)
35 #define RW_REC_CTRL    (0x08 / 4)
36 #define RW_DOUT        (0x1c / 4)
37 #define RS_STAT_DIN    (0x20 / 4)
38 #define R_STAT_DIN     (0x24 / 4)
39 #define RW_INTR_MASK   (0x2c / 4)
40 #define RW_ACK_INTR    (0x30 / 4)
41 #define R_INTR         (0x34 / 4)
42 #define R_MASKED_INTR  (0x38 / 4)
43 #define R_MAX          (0x3c / 4)
44
45 #define STAT_DAV     16
46 #define STAT_TR_IDLE 22
47 #define STAT_TR_RDY  24
48
49 struct etrax_serial
50 {
51         CPUState *env;
52         CharDriverState *chr;
53         qemu_irq *irq;
54
55         /* This pending thing is a hack.  */
56         int pending_tx;
57
58         /* Control registers.  */
59         uint32_t regs[R_MAX];
60 };
61
62 static void ser_update_irq(struct etrax_serial *s)
63 {
64         s->regs[R_INTR] &= ~(s->regs[RW_ACK_INTR]);
65         s->regs[R_MASKED_INTR] = s->regs[R_INTR] & s->regs[RW_INTR_MASK];
66
67         qemu_set_irq(s->irq[0], !!s->regs[R_MASKED_INTR]);
68         s->regs[RW_ACK_INTR] = 0;
69 }
70
71 static uint32_t ser_readl (void *opaque, target_phys_addr_t addr)
72 {
73         struct etrax_serial *s = opaque;
74         D(CPUState *env = s->env);
75         uint32_t r = 0;
76
77         addr >>= 2;
78         switch (addr)
79         {
80                 case R_STAT_DIN:
81                         r = s->regs[RS_STAT_DIN];
82                         break;
83                 case RS_STAT_DIN:
84                         r = s->regs[addr];
85                         /* Read side-effect: clear dav.  */
86                         s->regs[addr] &= ~(1 << STAT_DAV);
87                         break;
88                 default:
89                         r = s->regs[addr];
90                         D(printf ("%s %x=%x\n", __func__, addr, r));
91                         break;
92         }
93         return r;
94 }
95
96 static void
97 ser_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
98 {
99         struct etrax_serial *s = opaque;
100         unsigned char ch = value;
101         D(CPUState *env = s->env);
102
103         D(printf ("%s %x %x\n",  __func__, addr, value));
104         addr >>= 2;
105         switch (addr)
106         {
107                 case RW_DOUT:
108                         qemu_chr_write(s->chr, &ch, 1);
109                         s->regs[R_INTR] |= 1;
110                         s->pending_tx = 1;
111                         s->regs[addr] = value;
112                         break;
113                 case RW_ACK_INTR:
114                         s->regs[addr] = value;
115                         if (s->pending_tx && (s->regs[addr] & 1)) {
116                                 s->regs[R_INTR] |= 1;
117                                 s->pending_tx = 0;
118                                 s->regs[addr] &= ~1;
119                         }
120                         break;
121                 default:
122                         s->regs[addr] = value;
123                         break;
124         }
125         ser_update_irq(s);
126 }
127
128 static CPUReadMemoryFunc *ser_read[] = {
129         NULL, NULL,
130         &ser_readl,
131 };
132
133 static CPUWriteMemoryFunc *ser_write[] = {
134         NULL, NULL,
135         &ser_writel,
136 };
137
138 static void serial_receive(void *opaque, const uint8_t *buf, int size)
139 {
140         struct etrax_serial *s = opaque;
141
142         s->regs[R_INTR] |= 8;
143         s->regs[RS_STAT_DIN] &= ~0xff;
144         s->regs[RS_STAT_DIN] |= (buf[0] & 0xff);
145         s->regs[RS_STAT_DIN] |= (1 << STAT_DAV); /* dav.  */
146         ser_update_irq(s);
147 }
148
149 static int serial_can_receive(void *opaque)
150 {
151         struct etrax_serial *s = opaque;
152         int r;
153
154         /* Is the receiver enabled?  */
155         r = s->regs[RW_REC_CTRL] & 1;
156
157         /* Pending rx data?  */
158         r |= !(s->regs[R_INTR] & 8);
159         return r;
160 }
161
162 static void serial_event(void *opaque, int event)
163 {
164
165 }
166
167 void etraxfs_ser_init(CPUState *env, qemu_irq *irq, CharDriverState *chr,
168                       target_phys_addr_t base)
169 {
170         struct etrax_serial *s;
171         int ser_regs;
172
173         s = qemu_mallocz(sizeof *s);
174
175         s->env = env;
176         s->irq = irq;
177         s->chr = chr;
178
179         /* transmitter begins ready and idle.  */
180         s->regs[RS_STAT_DIN] |= (1 << STAT_TR_RDY);
181         s->regs[RS_STAT_DIN] |= (1 << STAT_TR_IDLE);
182
183         qemu_chr_add_handlers(chr, serial_can_receive, serial_receive,
184                               serial_event, s);
185
186         ser_regs = cpu_register_io_memory(0, ser_read, ser_write, s);
187         cpu_register_physical_memory (base, R_MAX * 4, ser_regs);
188 }