root / hw / milkymist-uart.c @ 0200db65
History | View | Annotate | Download (5.4 kB)
1 | 883de16b | Michael Walle | /*
|
---|---|---|---|
2 | 883de16b | Michael Walle | * QEMU model of the Milkymist UART block.
|
3 | 883de16b | Michael Walle | *
|
4 | 883de16b | Michael Walle | * Copyright (c) 2010 Michael Walle <michael@walle.cc>
|
5 | 883de16b | Michael Walle | *
|
6 | 883de16b | Michael Walle | * This library is free software; you can redistribute it and/or
|
7 | 883de16b | Michael Walle | * modify it under the terms of the GNU Lesser General Public
|
8 | 883de16b | Michael Walle | * License as published by the Free Software Foundation; either
|
9 | 883de16b | Michael Walle | * version 2 of the License, or (at your option) any later version.
|
10 | 883de16b | Michael Walle | *
|
11 | 883de16b | Michael Walle | * This library is distributed in the hope that it will be useful,
|
12 | 883de16b | Michael Walle | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 883de16b | Michael Walle | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | 883de16b | Michael Walle | * Lesser General Public License for more details.
|
15 | 883de16b | Michael Walle | *
|
16 | 883de16b | Michael Walle | * You should have received a copy of the GNU Lesser General Public
|
17 | 883de16b | Michael Walle | * License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
18 | 883de16b | Michael Walle | *
|
19 | 883de16b | Michael Walle | *
|
20 | 883de16b | Michael Walle | * Specification available at:
|
21 | 883de16b | Michael Walle | * http://www.milkymist.org/socdoc/uart.pdf
|
22 | 883de16b | Michael Walle | */
|
23 | 883de16b | Michael Walle | |
24 | 883de16b | Michael Walle | #include "hw.h" |
25 | 883de16b | Michael Walle | #include "sysbus.h" |
26 | 883de16b | Michael Walle | #include "trace.h" |
27 | 883de16b | Michael Walle | #include "qemu-char.h" |
28 | 883de16b | Michael Walle | #include "qemu-error.h" |
29 | 883de16b | Michael Walle | |
30 | 883de16b | Michael Walle | enum {
|
31 | 883de16b | Michael Walle | R_RXTX = 0,
|
32 | 883de16b | Michael Walle | R_DIV, |
33 | fcfa3397 | Michael Walle | R_STAT, |
34 | fcfa3397 | Michael Walle | R_CTRL, |
35 | fcfa3397 | Michael Walle | R_DBG, |
36 | 883de16b | Michael Walle | R_MAX |
37 | 883de16b | Michael Walle | }; |
38 | 883de16b | Michael Walle | |
39 | fcfa3397 | Michael Walle | enum {
|
40 | fcfa3397 | Michael Walle | STAT_THRE = (1<<0), |
41 | fcfa3397 | Michael Walle | STAT_RX_EVT = (1<<1), |
42 | fcfa3397 | Michael Walle | STAT_TX_EVT = (1<<2), |
43 | fcfa3397 | Michael Walle | }; |
44 | fcfa3397 | Michael Walle | |
45 | fcfa3397 | Michael Walle | enum {
|
46 | fcfa3397 | Michael Walle | CTRL_RX_IRQ_EN = (1<<0), |
47 | fcfa3397 | Michael Walle | CTRL_TX_IRQ_EN = (1<<1), |
48 | fcfa3397 | Michael Walle | CTRL_THRU_EN = (1<<2), |
49 | fcfa3397 | Michael Walle | }; |
50 | fcfa3397 | Michael Walle | |
51 | fcfa3397 | Michael Walle | enum {
|
52 | fcfa3397 | Michael Walle | DBG_BREAK_EN = (1<<0), |
53 | fcfa3397 | Michael Walle | }; |
54 | fcfa3397 | Michael Walle | |
55 | 883de16b | Michael Walle | struct MilkymistUartState {
|
56 | 883de16b | Michael Walle | SysBusDevice busdev; |
57 | 5adb30d3 | Michael Walle | MemoryRegion regs_region; |
58 | 883de16b | Michael Walle | CharDriverState *chr; |
59 | fcfa3397 | Michael Walle | qemu_irq irq; |
60 | 883de16b | Michael Walle | |
61 | 883de16b | Michael Walle | uint32_t regs[R_MAX]; |
62 | 883de16b | Michael Walle | }; |
63 | 883de16b | Michael Walle | typedef struct MilkymistUartState MilkymistUartState; |
64 | 883de16b | Michael Walle | |
65 | fcfa3397 | Michael Walle | static void uart_update_irq(MilkymistUartState *s) |
66 | fcfa3397 | Michael Walle | { |
67 | fcfa3397 | Michael Walle | int rx_event = s->regs[R_STAT] & STAT_RX_EVT;
|
68 | fcfa3397 | Michael Walle | int tx_event = s->regs[R_STAT] & STAT_TX_EVT;
|
69 | fcfa3397 | Michael Walle | int rx_irq_en = s->regs[R_CTRL] & CTRL_RX_IRQ_EN;
|
70 | fcfa3397 | Michael Walle | int tx_irq_en = s->regs[R_CTRL] & CTRL_TX_IRQ_EN;
|
71 | fcfa3397 | Michael Walle | |
72 | fcfa3397 | Michael Walle | if ((rx_irq_en && rx_event) || (tx_irq_en && tx_event)) {
|
73 | fcfa3397 | Michael Walle | trace_milkymist_uart_raise_irq(); |
74 | fcfa3397 | Michael Walle | qemu_irq_raise(s->irq); |
75 | fcfa3397 | Michael Walle | } else {
|
76 | fcfa3397 | Michael Walle | trace_milkymist_uart_lower_irq(); |
77 | fcfa3397 | Michael Walle | qemu_irq_lower(s->irq); |
78 | fcfa3397 | Michael Walle | } |
79 | fcfa3397 | Michael Walle | } |
80 | fcfa3397 | Michael Walle | |
81 | 5adb30d3 | Michael Walle | static uint64_t uart_read(void *opaque, target_phys_addr_t addr, |
82 | 5adb30d3 | Michael Walle | unsigned size)
|
83 | 883de16b | Michael Walle | { |
84 | 883de16b | Michael Walle | MilkymistUartState *s = opaque; |
85 | 883de16b | Michael Walle | uint32_t r = 0;
|
86 | 883de16b | Michael Walle | |
87 | 883de16b | Michael Walle | addr >>= 2;
|
88 | 883de16b | Michael Walle | switch (addr) {
|
89 | 883de16b | Michael Walle | case R_RXTX:
|
90 | fcfa3397 | Michael Walle | r = s->regs[addr]; |
91 | fcfa3397 | Michael Walle | break;
|
92 | 883de16b | Michael Walle | case R_DIV:
|
93 | fcfa3397 | Michael Walle | case R_STAT:
|
94 | fcfa3397 | Michael Walle | case R_CTRL:
|
95 | fcfa3397 | Michael Walle | case R_DBG:
|
96 | 883de16b | Michael Walle | r = s->regs[addr]; |
97 | 883de16b | Michael Walle | break;
|
98 | 883de16b | Michael Walle | |
99 | 883de16b | Michael Walle | default:
|
100 | 883de16b | Michael Walle | error_report("milkymist_uart: read access to unknown register 0x"
|
101 | 883de16b | Michael Walle | TARGET_FMT_plx, addr << 2);
|
102 | 883de16b | Michael Walle | break;
|
103 | 883de16b | Michael Walle | } |
104 | 883de16b | Michael Walle | |
105 | 883de16b | Michael Walle | trace_milkymist_uart_memory_read(addr << 2, r);
|
106 | 883de16b | Michael Walle | |
107 | 883de16b | Michael Walle | return r;
|
108 | 883de16b | Michael Walle | } |
109 | 883de16b | Michael Walle | |
110 | 5adb30d3 | Michael Walle | static void uart_write(void *opaque, target_phys_addr_t addr, uint64_t value, |
111 | 5adb30d3 | Michael Walle | unsigned size)
|
112 | 883de16b | Michael Walle | { |
113 | 883de16b | Michael Walle | MilkymistUartState *s = opaque; |
114 | 883de16b | Michael Walle | unsigned char ch = value; |
115 | 883de16b | Michael Walle | |
116 | 883de16b | Michael Walle | trace_milkymist_uart_memory_write(addr, value); |
117 | 883de16b | Michael Walle | |
118 | 883de16b | Michael Walle | addr >>= 2;
|
119 | 883de16b | Michael Walle | switch (addr) {
|
120 | 883de16b | Michael Walle | case R_RXTX:
|
121 | 883de16b | Michael Walle | if (s->chr) {
|
122 | 2cc6e0a1 | Anthony Liguori | qemu_chr_fe_write(s->chr, &ch, 1);
|
123 | 883de16b | Michael Walle | } |
124 | fcfa3397 | Michael Walle | s->regs[R_STAT] |= STAT_TX_EVT; |
125 | 883de16b | Michael Walle | break;
|
126 | 883de16b | Michael Walle | case R_DIV:
|
127 | fcfa3397 | Michael Walle | case R_CTRL:
|
128 | fcfa3397 | Michael Walle | case R_DBG:
|
129 | 883de16b | Michael Walle | s->regs[addr] = value; |
130 | 883de16b | Michael Walle | break;
|
131 | 883de16b | Michael Walle | |
132 | fcfa3397 | Michael Walle | case R_STAT:
|
133 | fcfa3397 | Michael Walle | /* write one to clear bits */
|
134 | fcfa3397 | Michael Walle | s->regs[addr] &= ~(value & (STAT_RX_EVT | STAT_TX_EVT)); |
135 | fcfa3397 | Michael Walle | break;
|
136 | fcfa3397 | Michael Walle | |
137 | 883de16b | Michael Walle | default:
|
138 | 883de16b | Michael Walle | error_report("milkymist_uart: write access to unknown register 0x"
|
139 | 883de16b | Michael Walle | TARGET_FMT_plx, addr << 2);
|
140 | 883de16b | Michael Walle | break;
|
141 | 883de16b | Michael Walle | } |
142 | fcfa3397 | Michael Walle | |
143 | fcfa3397 | Michael Walle | uart_update_irq(s); |
144 | 883de16b | Michael Walle | } |
145 | 883de16b | Michael Walle | |
146 | 5adb30d3 | Michael Walle | static const MemoryRegionOps uart_mmio_ops = { |
147 | 5adb30d3 | Michael Walle | .read = uart_read, |
148 | 5adb30d3 | Michael Walle | .write = uart_write, |
149 | 5adb30d3 | Michael Walle | .valid = { |
150 | 5adb30d3 | Michael Walle | .min_access_size = 4,
|
151 | 5adb30d3 | Michael Walle | .max_access_size = 4,
|
152 | 5adb30d3 | Michael Walle | }, |
153 | 5adb30d3 | Michael Walle | .endianness = DEVICE_NATIVE_ENDIAN, |
154 | 883de16b | Michael Walle | }; |
155 | 883de16b | Michael Walle | |
156 | 883de16b | Michael Walle | static void uart_rx(void *opaque, const uint8_t *buf, int size) |
157 | 883de16b | Michael Walle | { |
158 | 883de16b | Michael Walle | MilkymistUartState *s = opaque; |
159 | 883de16b | Michael Walle | |
160 | fcfa3397 | Michael Walle | assert(!(s->regs[R_STAT] & STAT_RX_EVT)); |
161 | fcfa3397 | Michael Walle | |
162 | fcfa3397 | Michael Walle | s->regs[R_STAT] |= STAT_RX_EVT; |
163 | 883de16b | Michael Walle | s->regs[R_RXTX] = *buf; |
164 | fcfa3397 | Michael Walle | |
165 | fcfa3397 | Michael Walle | uart_update_irq(s); |
166 | 883de16b | Michael Walle | } |
167 | 883de16b | Michael Walle | |
168 | 883de16b | Michael Walle | static int uart_can_rx(void *opaque) |
169 | 883de16b | Michael Walle | { |
170 | fcfa3397 | Michael Walle | MilkymistUartState *s = opaque; |
171 | fcfa3397 | Michael Walle | |
172 | fcfa3397 | Michael Walle | return !(s->regs[R_STAT] & STAT_RX_EVT);
|
173 | 883de16b | Michael Walle | } |
174 | 883de16b | Michael Walle | |
175 | 883de16b | Michael Walle | static void uart_event(void *opaque, int event) |
176 | 883de16b | Michael Walle | { |
177 | 883de16b | Michael Walle | } |
178 | 883de16b | Michael Walle | |
179 | 883de16b | Michael Walle | static void milkymist_uart_reset(DeviceState *d) |
180 | 883de16b | Michael Walle | { |
181 | 883de16b | Michael Walle | MilkymistUartState *s = container_of(d, MilkymistUartState, busdev.qdev); |
182 | 883de16b | Michael Walle | int i;
|
183 | 883de16b | Michael Walle | |
184 | 883de16b | Michael Walle | for (i = 0; i < R_MAX; i++) { |
185 | 883de16b | Michael Walle | s->regs[i] = 0;
|
186 | 883de16b | Michael Walle | } |
187 | fcfa3397 | Michael Walle | |
188 | fcfa3397 | Michael Walle | /* THRE is always set */
|
189 | fcfa3397 | Michael Walle | s->regs[R_STAT] = STAT_THRE; |
190 | 883de16b | Michael Walle | } |
191 | 883de16b | Michael Walle | |
192 | 883de16b | Michael Walle | static int milkymist_uart_init(SysBusDevice *dev) |
193 | 883de16b | Michael Walle | { |
194 | 883de16b | Michael Walle | MilkymistUartState *s = FROM_SYSBUS(typeof(*s), dev); |
195 | 883de16b | Michael Walle | |
196 | fcfa3397 | Michael Walle | sysbus_init_irq(dev, &s->irq); |
197 | 883de16b | Michael Walle | |
198 | 5adb30d3 | Michael Walle | memory_region_init_io(&s->regs_region, &uart_mmio_ops, s, |
199 | 5adb30d3 | Michael Walle | "milkymist-uart", R_MAX * 4); |
200 | 5adb30d3 | Michael Walle | sysbus_init_mmio_region(dev, &s->regs_region); |
201 | 883de16b | Michael Walle | |
202 | 883de16b | Michael Walle | s->chr = qdev_init_chardev(&dev->qdev); |
203 | 883de16b | Michael Walle | if (s->chr) {
|
204 | 883de16b | Michael Walle | qemu_chr_add_handlers(s->chr, uart_can_rx, uart_rx, uart_event, s); |
205 | 883de16b | Michael Walle | } |
206 | 883de16b | Michael Walle | |
207 | 883de16b | Michael Walle | return 0; |
208 | 883de16b | Michael Walle | } |
209 | 883de16b | Michael Walle | |
210 | 883de16b | Michael Walle | static const VMStateDescription vmstate_milkymist_uart = { |
211 | 883de16b | Michael Walle | .name = "milkymist-uart",
|
212 | 883de16b | Michael Walle | .version_id = 1,
|
213 | 883de16b | Michael Walle | .minimum_version_id = 1,
|
214 | 883de16b | Michael Walle | .minimum_version_id_old = 1,
|
215 | 883de16b | Michael Walle | .fields = (VMStateField[]) { |
216 | 883de16b | Michael Walle | VMSTATE_UINT32_ARRAY(regs, MilkymistUartState, R_MAX), |
217 | 883de16b | Michael Walle | VMSTATE_END_OF_LIST() |
218 | 883de16b | Michael Walle | } |
219 | 883de16b | Michael Walle | }; |
220 | 883de16b | Michael Walle | |
221 | 883de16b | Michael Walle | static SysBusDeviceInfo milkymist_uart_info = {
|
222 | 883de16b | Michael Walle | .init = milkymist_uart_init, |
223 | 883de16b | Michael Walle | .qdev.name = "milkymist-uart",
|
224 | 883de16b | Michael Walle | .qdev.size = sizeof(MilkymistUartState),
|
225 | 883de16b | Michael Walle | .qdev.vmsd = &vmstate_milkymist_uart, |
226 | 883de16b | Michael Walle | .qdev.reset = milkymist_uart_reset, |
227 | 883de16b | Michael Walle | }; |
228 | 883de16b | Michael Walle | |
229 | 883de16b | Michael Walle | static void milkymist_uart_register(void) |
230 | 883de16b | Michael Walle | { |
231 | 883de16b | Michael Walle | sysbus_register_withprop(&milkymist_uart_info); |
232 | 883de16b | Michael Walle | } |
233 | 883de16b | Michael Walle | |
234 | 883de16b | Michael Walle | device_init(milkymist_uart_register) |