修订版 | cb74f1c57811a0c3cb8754838a6b6ff75a0ae710 (tree) |
---|---|
时间 | 2019-06-16 22:31:41 |
作者 | Yoshinori Sato <ysato@user...> |
Commiter | Yoshinori Sato |
hw/char: RX62N serial communication interface (SCI)
This module supported only non FIFO type.
Hardware manual.
https://www.renesas.com/us/en/doc/products/mpumcu/doc/rx_family/r01uh0033ej0140_rx62n.pdf
Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-Id: <20190607091116.49044-8-ysato@users.sourceforge.jp>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
@@ -46,3 +46,6 @@ config SCLPCONSOLE | ||
46 | 46 | |
47 | 47 | config TERMINAL3270 |
48 | 48 | bool |
49 | + | |
50 | +config RENESAS_SCI | |
51 | + bool |
@@ -21,6 +21,7 @@ obj-$(CONFIG_PSERIES) += spapr_vty.o | ||
21 | 21 | obj-$(CONFIG_DIGIC) += digic-uart.o |
22 | 22 | obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o |
23 | 23 | obj-$(CONFIG_RASPI) += bcm2835_aux.o |
24 | +obj-$(CONFIG_RENESAS_SCI) += renesas_sci.o | |
24 | 25 | |
25 | 26 | common-obj-$(CONFIG_CMSDK_APB_UART) += cmsdk-apb-uart.o |
26 | 27 | common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o |
@@ -0,0 +1,340 @@ | ||
1 | +/* | |
2 | + * Renesas Serial Communication Interface | |
3 | + * | |
4 | + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware | |
5 | + * (Rev.1.40 R01UH0033EJ0140) | |
6 | + * | |
7 | + * Copyright (c) 2019 Yoshinori Sato | |
8 | + * | |
9 | + * This program is free software; you can redistribute it and/or modify it | |
10 | + * under the terms and conditions of the GNU General Public License, | |
11 | + * version 2 or later, as published by the Free Software Foundation. | |
12 | + * | |
13 | + * This program is distributed in the hope it will be useful, but WITHOUT | |
14 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
16 | + * more details. | |
17 | + * | |
18 | + * You should have received a copy of the GNU General Public License along with | |
19 | + * this program. If not, see <http://www.gnu.org/licenses/>. | |
20 | + */ | |
21 | + | |
22 | +#include "qemu/osdep.h" | |
23 | +#include "qemu/log.h" | |
24 | +#include "qapi/error.h" | |
25 | +#include "qemu-common.h" | |
26 | +#include "cpu.h" | |
27 | +#include "hw/hw.h" | |
28 | +#include "hw/sysbus.h" | |
29 | +#include "hw/registerfields.h" | |
30 | +#include "hw/char/renesas_sci.h" | |
31 | +#include "qemu/error-report.h" | |
32 | + | |
33 | +/* SCI register map */ | |
34 | +REG8(SMR, 0) | |
35 | + FIELD(SMR, CKS, 0, 2) | |
36 | + FIELD(SMR, MP, 2, 1) | |
37 | + FIELD(SMR, STOP, 3, 1) | |
38 | + FIELD(SMR, PM, 4, 1) | |
39 | + FIELD(SMR, PE, 5, 1) | |
40 | + FIELD(SMR, CHR, 6, 1) | |
41 | + FIELD(SMR, CM, 7, 1) | |
42 | +REG8(BRR, 1) | |
43 | +REG8(SCR, 2) | |
44 | + FIELD(SCR, CKE, 0, 2) | |
45 | + FIELD(SCR, TEIE, 2, 1) | |
46 | + FIELD(SCR, MPIE, 3, 1) | |
47 | + FIELD(SCR, RE, 4, 1) | |
48 | + FIELD(SCR, TE, 5, 1) | |
49 | + FIELD(SCR, RIE, 6, 1) | |
50 | + FIELD(SCR, TIE, 7, 1) | |
51 | +REG8(TDR, 3) | |
52 | +REG8(SSR, 4) | |
53 | + FIELD(SSR, MPBT, 0, 1) | |
54 | + FIELD(SSR, MPB, 1, 1) | |
55 | + FIELD(SSR, TEND, 2, 1) | |
56 | + FIELD(SSR, ERR, 3, 3) | |
57 | + FIELD(SSR, PER, 3, 1) | |
58 | + FIELD(SSR, FER, 4, 1) | |
59 | + FIELD(SSR, ORER, 5, 1) | |
60 | + FIELD(SSR, RDRF, 6, 1) | |
61 | + FIELD(SSR, TDRE, 7, 1) | |
62 | +REG8(RDR, 5) | |
63 | +REG8(SCMR, 6) | |
64 | + FIELD(SCMR, SMIF, 0, 1) | |
65 | + FIELD(SCMR, SINV, 2, 1) | |
66 | + FIELD(SCMR, SDIR, 3, 1) | |
67 | + FIELD(SCMR, BCP2, 7, 1) | |
68 | +REG8(SEMR, 7) | |
69 | + FIELD(SEMR, ACS0, 0, 1) | |
70 | + FIELD(SEMR, ABCS, 4, 1) | |
71 | + | |
72 | +static int can_receive(void *opaque) | |
73 | +{ | |
74 | + RSCIState *sci = RSCI(opaque); | |
75 | + if (sci->rx_next > qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) { | |
76 | + return 0; | |
77 | + } else { | |
78 | + return FIELD_EX8(sci->scr, SCR, RE); | |
79 | + } | |
80 | +} | |
81 | + | |
82 | +static void receive(void *opaque, const uint8_t *buf, int size) | |
83 | +{ | |
84 | + RSCIState *sci = RSCI(opaque); | |
85 | + sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime; | |
86 | + if (FIELD_EX8(sci->ssr, SSR, RDRF) || size > 1) { | |
87 | + sci->ssr = FIELD_DP8(sci->ssr, SSR, ORER, 1); | |
88 | + if (FIELD_EX8(sci->scr, SCR, RIE)) { | |
89 | + qemu_set_irq(sci->irq[ERI], 1); | |
90 | + } | |
91 | + } else { | |
92 | + sci->rdr = buf[0]; | |
93 | + sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 1); | |
94 | + if (FIELD_EX8(sci->scr, SCR, RIE)) { | |
95 | + qemu_irq_pulse(sci->irq[RXI]); | |
96 | + } | |
97 | + } | |
98 | +} | |
99 | + | |
100 | +static void send_byte(RSCIState *sci) | |
101 | +{ | |
102 | + if (qemu_chr_fe_backend_connected(&sci->chr)) { | |
103 | + qemu_chr_fe_write_all(&sci->chr, &sci->tdr, 1); | |
104 | + } | |
105 | + timer_mod(sci->timer, | |
106 | + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime); | |
107 | + sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 0); | |
108 | + sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1); | |
109 | + qemu_set_irq(sci->irq[TEI], 0); | |
110 | + if (FIELD_EX8(sci->scr, SCR, TIE)) { | |
111 | + qemu_irq_pulse(sci->irq[TXI]); | |
112 | + } | |
113 | +} | |
114 | + | |
115 | +static void txend(void *opaque) | |
116 | +{ | |
117 | + RSCIState *sci = RSCI(opaque); | |
118 | + if (!FIELD_EX8(sci->ssr, SSR, TDRE)) { | |
119 | + send_byte(sci); | |
120 | + } else { | |
121 | + sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1); | |
122 | + if (FIELD_EX8(sci->scr, SCR, TEIE)) { | |
123 | + qemu_set_irq(sci->irq[TEI], 1); | |
124 | + } | |
125 | + } | |
126 | +} | |
127 | + | |
128 | +static void update_trtime(RSCIState *sci) | |
129 | +{ | |
130 | + /* char per bits */ | |
131 | + sci->trtime = 8 - FIELD_EX8(sci->smr, SMR, CHR); | |
132 | + sci->trtime += FIELD_EX8(sci->smr, SMR, PE); | |
133 | + sci->trtime += FIELD_EX8(sci->smr, SMR, STOP) + 1; | |
134 | + /* x bit transmit time (32 * divrate * brr) / base freq */ | |
135 | + sci->trtime *= 32 * sci->brr; | |
136 | + sci->trtime *= 1 << (2 * FIELD_EX8(sci->smr, SMR, CKS)); | |
137 | + sci->trtime *= NANOSECONDS_PER_SECOND; | |
138 | + sci->trtime /= sci->input_freq; | |
139 | +} | |
140 | + | |
141 | +#define IS_TR_ENABLED(scr) \ | |
142 | + (FIELD_EX8(scr, SCR, TE) || FIELD_EX8(scr, SCR, RE)) | |
143 | + | |
144 | +static void sci_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) | |
145 | +{ | |
146 | + hwaddr offset = addr & 0x07; | |
147 | + RSCIState *sci = RSCI(opaque); | |
148 | + | |
149 | + switch (offset) { | |
150 | + case A_SMR: | |
151 | + if (!IS_TR_ENABLED(sci->scr)) { | |
152 | + sci->smr = val; | |
153 | + update_trtime(sci); | |
154 | + } | |
155 | + break; | |
156 | + case A_BRR: | |
157 | + if (!IS_TR_ENABLED(sci->scr)) { | |
158 | + sci->brr = val; | |
159 | + update_trtime(sci); | |
160 | + } | |
161 | + break; | |
162 | + case A_SCR: | |
163 | + sci->scr = val; | |
164 | + if (FIELD_EX8(sci->scr, SCR, TE)) { | |
165 | + sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1); | |
166 | + sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1); | |
167 | + if (FIELD_EX8(sci->scr, SCR, TIE)) { | |
168 | + qemu_irq_pulse(sci->irq[TXI]); | |
169 | + } | |
170 | + } | |
171 | + if (!FIELD_EX8(sci->scr, SCR, TEIE)) { | |
172 | + qemu_set_irq(sci->irq[TEI], 0); | |
173 | + } | |
174 | + if (!FIELD_EX8(sci->scr, SCR, RIE)) { | |
175 | + qemu_set_irq(sci->irq[ERI], 0); | |
176 | + } | |
177 | + break; | |
178 | + case A_TDR: | |
179 | + sci->tdr = val; | |
180 | + if (FIELD_EX8(sci->ssr, SSR, TEND)) { | |
181 | + send_byte(sci); | |
182 | + } else { | |
183 | + sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 0); | |
184 | + } | |
185 | + break; | |
186 | + case A_SSR: | |
187 | + sci->ssr = FIELD_DP8(sci->ssr, SSR, MPBT, | |
188 | + FIELD_EX8(val, SSR, MPBT)); | |
189 | + sci->ssr = FIELD_DP8(sci->ssr, SSR, ERR, | |
190 | + FIELD_EX8(val, SSR, ERR) & 0x07); | |
191 | + if (FIELD_EX8(sci->read_ssr, SSR, ERR) && | |
192 | + FIELD_EX8(sci->ssr, SSR, ERR) == 0) { | |
193 | + qemu_set_irq(sci->irq[ERI], 0); | |
194 | + } | |
195 | + break; | |
196 | + case A_RDR: | |
197 | + qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: RDR is read only.\n"); | |
198 | + break; | |
199 | + case A_SCMR: | |
200 | + sci->scmr = val; break; | |
201 | + case A_SEMR: /* SEMR */ | |
202 | + sci->semr = val; break; | |
203 | + default: | |
204 | + qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX | |
205 | + " not implemented\n", offset); | |
206 | + } | |
207 | +} | |
208 | + | |
209 | +static uint64_t sci_read(void *opaque, hwaddr addr, unsigned size) | |
210 | +{ | |
211 | + hwaddr offset = addr & 0x07; | |
212 | + RSCIState *sci = RSCI(opaque); | |
213 | + | |
214 | + switch (offset) { | |
215 | + case A_SMR: | |
216 | + return sci->smr; | |
217 | + case A_BRR: | |
218 | + return sci->brr; | |
219 | + case A_SCR: | |
220 | + return sci->scr; | |
221 | + case A_TDR: | |
222 | + return sci->tdr; | |
223 | + case A_SSR: | |
224 | + sci->read_ssr = sci->ssr; | |
225 | + return sci->ssr; | |
226 | + case A_RDR: | |
227 | + sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 0); | |
228 | + return sci->rdr; | |
229 | + case A_SCMR: | |
230 | + return sci->scmr; | |
231 | + case A_SEMR: | |
232 | + return sci->semr; | |
233 | + default: | |
234 | + qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX | |
235 | + " not implemented.\n", offset); | |
236 | + } | |
237 | + return UINT64_MAX; | |
238 | +} | |
239 | + | |
240 | +static const MemoryRegionOps sci_ops = { | |
241 | + .write = sci_write, | |
242 | + .read = sci_read, | |
243 | + .endianness = DEVICE_NATIVE_ENDIAN, | |
244 | + .impl = { | |
245 | + .max_access_size = 1, | |
246 | + }, | |
247 | +}; | |
248 | + | |
249 | +static void rsci_reset(DeviceState *dev) | |
250 | +{ | |
251 | + RSCIState *sci = RSCI(dev); | |
252 | + sci->smr = sci->scr = 0x00; | |
253 | + sci->brr = 0xff; | |
254 | + sci->tdr = 0xff; | |
255 | + sci->rdr = 0x00; | |
256 | + sci->ssr = 0x84; | |
257 | + sci->scmr = 0x00; | |
258 | + sci->semr = 0x00; | |
259 | + sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | |
260 | +} | |
261 | + | |
262 | +static void sci_event(void *opaque, int event) | |
263 | +{ | |
264 | + RSCIState *sci = RSCI(opaque); | |
265 | + if (event == CHR_EVENT_BREAK) { | |
266 | + sci->ssr = FIELD_DP8(sci->ssr, SSR, FER, 1); | |
267 | + if (FIELD_EX8(sci->scr, SCR, RIE)) { | |
268 | + qemu_set_irq(sci->irq[ERI], 1); | |
269 | + } | |
270 | + } | |
271 | +} | |
272 | + | |
273 | +static void rsci_realize(DeviceState *dev, Error **errp) | |
274 | +{ | |
275 | + RSCIState *sci = RSCI(dev); | |
276 | + | |
277 | + if (sci->input_freq == 0) { | |
278 | + qemu_log_mask(LOG_GUEST_ERROR, | |
279 | + "renesas_sci: input-freq property must be set."); | |
280 | + return; | |
281 | + } | |
282 | + qemu_chr_fe_set_handlers(&sci->chr, can_receive, receive, | |
283 | + sci_event, NULL, sci, NULL, true); | |
284 | +} | |
285 | + | |
286 | +static void rsci_init(Object *obj) | |
287 | +{ | |
288 | + SysBusDevice *d = SYS_BUS_DEVICE(obj); | |
289 | + RSCIState *sci = RSCI(obj); | |
290 | + int i; | |
291 | + | |
292 | + memory_region_init_io(&sci->memory, OBJECT(sci), &sci_ops, | |
293 | + sci, "renesas-sci", 0x8); | |
294 | + sysbus_init_mmio(d, &sci->memory); | |
295 | + | |
296 | + for (i = 0; i < SCI_NR_IRQ; i++) { | |
297 | + sysbus_init_irq(d, &sci->irq[i]); | |
298 | + } | |
299 | + sci->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, txend, sci); | |
300 | +} | |
301 | + | |
302 | +static const VMStateDescription vmstate_rcmt = { | |
303 | + .name = "renesas-sci", | |
304 | + .version_id = 1, | |
305 | + .minimum_version_id = 1, | |
306 | + .fields = (VMStateField[]) { | |
307 | + VMSTATE_END_OF_LIST() | |
308 | + } | |
309 | +}; | |
310 | + | |
311 | +static Property rsci_properties[] = { | |
312 | + DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0), | |
313 | + DEFINE_PROP_CHR("chardev", RSCIState, chr), | |
314 | + DEFINE_PROP_END_OF_LIST(), | |
315 | +}; | |
316 | + | |
317 | +static void rsci_class_init(ObjectClass *klass, void *data) | |
318 | +{ | |
319 | + DeviceClass *dc = DEVICE_CLASS(klass); | |
320 | + | |
321 | + dc->realize = rsci_realize; | |
322 | + dc->props = rsci_properties; | |
323 | + dc->vmsd = &vmstate_rcmt; | |
324 | + dc->reset = rsci_reset; | |
325 | +} | |
326 | + | |
327 | +static const TypeInfo rsci_info = { | |
328 | + .name = TYPE_RENESAS_SCI, | |
329 | + .parent = TYPE_SYS_BUS_DEVICE, | |
330 | + .instance_size = sizeof(RSCIState), | |
331 | + .instance_init = rsci_init, | |
332 | + .class_init = rsci_class_init, | |
333 | +}; | |
334 | + | |
335 | +static void rsci_register_types(void) | |
336 | +{ | |
337 | + type_register_static(&rsci_info); | |
338 | +} | |
339 | + | |
340 | +type_init(rsci_register_types) |
@@ -0,0 +1,45 @@ | ||
1 | +/* | |
2 | + * Renesas Serial Communication Interface | |
3 | + * | |
4 | + * Copyright (c) 2018 Yoshinori Sato | |
5 | + * | |
6 | + * This code is licensed under the GPL version 2 or later. | |
7 | + * | |
8 | + */ | |
9 | + | |
10 | +#include "chardev/char-fe.h" | |
11 | +#include "qemu/timer.h" | |
12 | +#include "hw/sysbus.h" | |
13 | + | |
14 | +#define TYPE_RENESAS_SCI "renesas-sci" | |
15 | +#define RSCI(obj) OBJECT_CHECK(RSCIState, (obj), TYPE_RENESAS_SCI) | |
16 | + | |
17 | +enum { | |
18 | + ERI = 0, | |
19 | + RXI = 1, | |
20 | + TXI = 2, | |
21 | + TEI = 3, | |
22 | + SCI_NR_IRQ = 4, | |
23 | +}; | |
24 | + | |
25 | +typedef struct { | |
26 | + SysBusDevice parent_obj; | |
27 | + MemoryRegion memory; | |
28 | + | |
29 | + uint8_t smr; | |
30 | + uint8_t brr; | |
31 | + uint8_t scr; | |
32 | + uint8_t tdr; | |
33 | + uint8_t ssr; | |
34 | + uint8_t rdr; | |
35 | + uint8_t scmr; | |
36 | + uint8_t semr; | |
37 | + | |
38 | + uint8_t read_ssr; | |
39 | + int64_t trtime; | |
40 | + int64_t rx_next; | |
41 | + QEMUTimer *timer; | |
42 | + CharBackend chr; | |
43 | + uint64_t input_freq; | |
44 | + qemu_irq irq[SCI_NR_IRQ]; | |
45 | +} RSCIState; |