Revision | 83d3a09666280b97536484d34a9ec7f275613d44 (tree) |
---|---|
Time | 2020-06-01 00:49:33 |
Author | Yoshinori Sato <ysato@user...> |
Commiter | Yoshinori Sato |
hw/char: Renesas SCI module.
This module supported SCI / SCIa / SCIF.
Hardware manual.
SCI / SCIF
https://www.renesas.com/us/en/doc/products/mpumcu/001/r01uh0457ej0401_sh7751.pdf
SCIa
https://www.renesas.com/us/en/doc/products/mpumcu/doc/rx_family/r01uh0033ej0140_rx62n.pdf
Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
@@ -46,3 +46,6 @@ config SCLPCONSOLE | ||
46 | 46 | |
47 | 47 | config TERMINAL3270 |
48 | 48 | bool |
49 | + | |
50 | +config RENESAS_SCI | |
51 | + bool |
@@ -16,7 +16,6 @@ common-obj-$(CONFIG_CADENCE) += cadence_uart.o | ||
16 | 16 | common-obj-$(CONFIG_EXYNOS4) += exynos4210_uart.o |
17 | 17 | common-obj-$(CONFIG_COLDFIRE) += mcf_uart.o |
18 | 18 | common-obj-$(CONFIG_OMAP) += omap_uart.o |
19 | -common-obj-$(CONFIG_SH4) += sh_serial.o | |
20 | 19 | common-obj-$(CONFIG_DIGIC) += digic-uart.o |
21 | 20 | common-obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o |
22 | 21 | common-obj-$(CONFIG_RASPI) += bcm2835_aux.o |
@@ -31,6 +30,8 @@ common-obj-$(CONFIG_LM32) += lm32_uart.o | ||
31 | 30 | common-obj-$(CONFIG_MILKYMIST) += milkymist-uart.o |
32 | 31 | common-obj-$(CONFIG_SCLPCONSOLE) += sclpconsole.o sclpconsole-lm.o |
33 | 32 | |
33 | +common-obj-$(CONFIG_RENESAS_SCI) += renesas_sci.o | |
34 | + | |
34 | 35 | obj-$(CONFIG_VIRTIO) += virtio-serial-bus.o |
35 | 36 | obj-$(CONFIG_PSERIES) += spapr_vty.o |
36 | 37 | obj-$(CONFIG_TERMINAL3270) += terminal3270.o |
@@ -0,0 +1,786 @@ | ||
1 | +/* | |
2 | + * Renesas Serial Communication Interface (SCI / SCIa / SCIF) | |
3 | + * | |
4 | + * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware | |
5 | + * (Rev.1.40 R01UH0033EJ0140) | |
6 | + * And SH7751 Group, SH7751R Group User's Manual: Hardware | |
7 | + * (Rev.4.01 R01UH0457EJ0401) | |
8 | + * | |
9 | + * Copyright (c) 2020 Yoshinori Sato | |
10 | + * | |
11 | + * This program is free software; you can redistribute it and/or modify it | |
12 | + * under the terms and conditions of the GNU General Public License, | |
13 | + * version 2 or later, as published by the Free Software Foundation. | |
14 | + * | |
15 | + * This program is distributed in the hope it will be useful, but WITHOUT | |
16 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
17 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
18 | + * more details. | |
19 | + * | |
20 | + * You should have received a copy of the GNU General Public License along with | |
21 | + * this program. If not, see <http://www.gnu.org/licenses/>. | |
22 | + */ | |
23 | + | |
24 | +#include "qemu/osdep.h" | |
25 | +#include "qemu/log.h" | |
26 | +#include "qapi/error.h" | |
27 | +#include "qemu-common.h" | |
28 | +#include "hw/hw.h" | |
29 | +#include "hw/irq.h" | |
30 | +#include "hw/sysbus.h" | |
31 | +#include "hw/registerfields.h" | |
32 | +#include "hw/qdev-properties.h" | |
33 | +#include "hw/char/renesas_sci.h" | |
34 | +#include "migration/vmstate.h" | |
35 | +#include "qemu/error-report.h" | |
36 | + | |
37 | +/* SCI register map */ | |
38 | +REG8(SMR, 0) | |
39 | + FIELD(SMR, CKS, 0, 2) | |
40 | + FIELD(SMR, MP, 2, 1) | |
41 | + FIELD(SMR, STOP, 3, 1) | |
42 | + FIELD(SMR, PM, 4, 1) | |
43 | + FIELD(SMR, PE, 5, 1) | |
44 | + FIELD(SMR, CHR, 6, 1) | |
45 | + FIELD(SMR, CM, 7, 1) | |
46 | +REG16(BRR, 2) | |
47 | +REG16(SCR, 4) | |
48 | + FIELD(SCR, CKE, 0, 2) | |
49 | + FIELD(SCR, TEIE, 2, 1) | |
50 | + FIELD(SCR, MPIE, 3, 1) | |
51 | + FIELD(SCR, REIE, 3, 1) | |
52 | + FIELD(SCR, RE, 4, 1) | |
53 | + FIELD(SCR, TE, 5, 1) | |
54 | + FIELD(SCR, RIE, 6, 1) | |
55 | + FIELD(SCR, TIE, 7, 1) | |
56 | +REG16(TDR, 6) | |
57 | +REG16(SSR, 8) | |
58 | + FIELD(SSR, MPBT, 0, 1) | |
59 | + FIELD(SSR, MPB, 1, 1) | |
60 | + FIELD(SSR, TEND, 2, 1) | |
61 | + FIELD(SSR, ERR, 3, 3) | |
62 | + FIELD(SSR, PER, 3, 1) | |
63 | + FIELD(SSR, FER, 4, 1) | |
64 | + FIELD(SSR, ORER, 5, 1) | |
65 | + FIELD(SSR, RDRF, 6, 1) | |
66 | + FIELD(SSR, TDRE, 7, 1) | |
67 | +REG16(FSR, 8) | |
68 | + FIELD(FSR, DR, 0, 1) | |
69 | + FIELD(FSR, RDF, 1, 1) | |
70 | + FIELD(FSR, RDF_DR, 0, 2) | |
71 | + FIELD(FSR, PER, 2, 1) | |
72 | + FIELD(FSR, FER, 3, 1) | |
73 | + FIELD(FSR, BRK, 4, 1) | |
74 | + FIELD(FSR, TDFE, 5, 1) | |
75 | + FIELD(FSR, TEND, 6, 1) | |
76 | + FIELD(FSR, ER, 7, 1) | |
77 | + FIELD(FSR, FERn, 8, 4) | |
78 | + FIELD(FSR, PERn, 12, 4) | |
79 | +REG16(RDR, 10) | |
80 | +REG16(SCMR, 12) | |
81 | + FIELD(SCMR, SMIF, 0, 1) | |
82 | + FIELD(SCMR, SINV, 2, 1) | |
83 | + FIELD(SCMR, SDIR, 3, 1) | |
84 | + FIELD(SCMR, BCP2, 7, 1) | |
85 | +REG16(FCR, 12) | |
86 | + FIELD(FCR, LOOP, 0, 1) | |
87 | + FIELD(FCR, RFRST, 1, 1) | |
88 | + FIELD(FCR, TFRST, 2, 1) | |
89 | + FIELD(FCR, MCE, 3, 1) | |
90 | + FIELD(FCR, TTRG, 4, 2) | |
91 | + FIELD(FCR, RTRG, 6, 2) | |
92 | + FIELD(FCR, RSTRG, 8, 3) | |
93 | +REG16(SEMR, 14) | |
94 | + FIELD(SEMR, ACS0, 0, 1) | |
95 | + FIELD(SEMR, ABCS, 4, 1) | |
96 | +REG16(FDR, 14) | |
97 | + FIELD(FDR, Rn, 0, 4) | |
98 | + FIELD(FDR, Tn, 8, 4) | |
99 | +REG16(SPTR, 16) | |
100 | + FIELD(SPTR, SPB2DT, 0, 1) | |
101 | + FIELD(SPTR, SPB2IO, 1, 1) | |
102 | + FIELD(SPTR, SCKDT, 2, 1) | |
103 | + FIELD(SPTR, SCKIO, 3, 1) | |
104 | + FIELD(SPTR, CTSDT, 4, 1) | |
105 | + FIELD(SPTR, CTSIO, 5, 1) | |
106 | + FIELD(SPTR, RTSDT, 6, 1) | |
107 | + FIELD(SPTR, RTSIO, 7, 1) | |
108 | + FIELD(SPTR, EIO, 7, 1) | |
109 | +REG16(LSR, 18) | |
110 | + FIELD(LSR, ORER, 0, 1) | |
111 | + | |
112 | +#define SCIF_FIFO_DEPTH 16 | |
113 | +#define IS_SCI(sci) (sci->feature < SCI_FEAT_SCIF) | |
114 | +#define IS_SCIA(sci) (IS_SCI(sci) && sci->feature >= SCI_FEAT_SCIA) | |
115 | +#define IS_SCIF(sci) (!IS_SCI(sci)) | |
116 | + | |
117 | +static const int sci_rtrg[] = {1, 4, 8, 14}; | |
118 | + | |
119 | +static void update_event_time(RSCIState *sci, int evt, int64_t t) | |
120 | +{ | |
121 | + if (t > 0) { | |
122 | + t += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | |
123 | + sci->event[evt].time = t; | |
124 | + if (timer_expire_time_ns(sci->event_timer) > t) { | |
125 | + timer_mod(sci->event_timer, t); | |
126 | + } | |
127 | + } else { | |
128 | + sci->event[evt].time = 0; | |
129 | + } | |
130 | +} | |
131 | + | |
132 | +static int sci_is_rxi(RSCIState *sci) | |
133 | +{ | |
134 | + int eio; | |
135 | + int enable; | |
136 | + enable = FIELD_EX16(sci->scr, SCR, RIE); | |
137 | + if (IS_SCI(sci)) { | |
138 | + eio = (sci->feature != SCI_FEAT_SCI) || | |
139 | + (FIELD_EX16(sci->sptr, SPTR, EIO) == 0); | |
140 | + return FIELD_EX16(sci->Xsr, SSR, RDRF) && enable && eio; | |
141 | + } else { | |
142 | + return (FIELD_EX16(sci->Xsr, FSR, RDF_DR) != 0) && enable; | |
143 | + } | |
144 | +} | |
145 | + | |
146 | +static int sci_is_txi(RSCIState *sci) | |
147 | +{ | |
148 | + int enable = FIELD_EX16(sci->scr, SCR, TIE); | |
149 | + if (IS_SCI(sci)) { | |
150 | + return enable && FIELD_EX16(sci->Xsr, SSR, TDRE); | |
151 | + } else { | |
152 | + return enable && FIELD_EX16(sci->Xsr, FSR, TDFE); | |
153 | + } | |
154 | +} | |
155 | + | |
156 | +static void sci_irq(RSCIState *sci, int req) | |
157 | +{ | |
158 | + int br; | |
159 | + int irq; | |
160 | + int rie; | |
161 | + | |
162 | + switch (req) { | |
163 | + case ERI: | |
164 | + rie = FIELD_EX16(sci->scr, SCR, RIE); | |
165 | + if (IS_SCI(sci)) { | |
166 | + irq = rie && (FIELD_EX16(sci->Xsr, SSR, ERR) != 0); | |
167 | + } else { | |
168 | + irq = (rie || FIELD_EX16(sci->scr, SCR, REIE)) && | |
169 | + FIELD_EX16(sci->Xsr, FSR, ER); | |
170 | + } | |
171 | + qemu_set_irq(sci->irq[ERI], irq); | |
172 | + break; | |
173 | + case RXI: | |
174 | + if (IS_SCIA(sci)) { | |
175 | + if (sci_is_rxi(sci)) { | |
176 | + qemu_irq_pulse(sci->irq[RXI]); | |
177 | + } | |
178 | + } else { | |
179 | + qemu_set_irq(sci->irq[RXI], sci_is_rxi(sci)); | |
180 | + } | |
181 | + break; | |
182 | + case TXI: | |
183 | + if (IS_SCIA(sci)) { | |
184 | + if (sci_is_txi(sci)) { | |
185 | + qemu_irq_pulse(sci->irq[TXI]); | |
186 | + } | |
187 | + } else { | |
188 | + qemu_set_irq(sci->irq[TXI], sci_is_txi(sci)); | |
189 | + } | |
190 | + break; | |
191 | + case BRI: /* TEI */ | |
192 | + if (IS_SCI(sci)) { | |
193 | + qemu_set_irq(sci->irq[TEI], | |
194 | + FIELD_EX16(sci->Xsr, SSR, TEND) && | |
195 | + FIELD_EX16(sci->scr, SCR, TEIE)); | |
196 | + } else { | |
197 | + rie = FIELD_EX16(sci->scr, SCR, RIE); | |
198 | + br = (rie || FIELD_EX16(sci->scr, SCR, REIE)) && | |
199 | + FIELD_EX16(sci->Xsr, FSR, BRK); | |
200 | + qemu_set_irq(sci->irq[BRI], br); | |
201 | + } | |
202 | + break; | |
203 | + } | |
204 | +} | |
205 | + | |
206 | +static int can_receive(void *opaque) | |
207 | +{ | |
208 | + RSCIState *sci = RSCI(opaque); | |
209 | + int fifo_free = 0; | |
210 | + if (FIELD_EX16(sci->scr, SCR, RE)) { | |
211 | + /* Receiver enabled */ | |
212 | + fifo_free = fifo8_num_free(&sci->rxfifo); | |
213 | + if (IS_SCIF(sci) && fifo_free == 0) { | |
214 | + /* FIFO overrun */ | |
215 | + sci->lsr = FIELD_DP16(sci->lsr, LSR, ORER, 1); | |
216 | + sci_irq(sci, ERI); | |
217 | + } | |
218 | + } | |
219 | + return fifo_free; | |
220 | +} | |
221 | + | |
222 | +static void sci_receive(void *opaque, const uint8_t *buf, int size) | |
223 | +{ | |
224 | + RSCIState *sci = RSCI(opaque); | |
225 | + int rtrg; | |
226 | + | |
227 | + fifo8_push_all(&sci->rxfifo, buf, size); | |
228 | + if (sci->event[RXNEXT].time == 0) { | |
229 | + if (IS_SCI(sci)) { | |
230 | + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, RDRF, 1); | |
231 | + update_event_time(sci, RXNEXT, sci->trtime); | |
232 | + } else { | |
233 | + rtrg = sci_rtrg[FIELD_EX16(sci->fcr, FCR, RTRG)]; | |
234 | + if (fifo8_num_used(&sci->rxfifo) >= rtrg) { | |
235 | + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, RDF, 1); | |
236 | + } else { | |
237 | + update_event_time(sci, RXTOUT, 15 * sci->etu); | |
238 | + } | |
239 | + } | |
240 | + sci_irq(sci, RXI); | |
241 | + } | |
242 | +} | |
243 | + | |
244 | +static void sci_send_byte(RSCIState *sci) | |
245 | +{ | |
246 | + if (IS_SCI(sci)) { | |
247 | + if (qemu_chr_fe_backend_connected(&sci->chr)) { | |
248 | + qemu_chr_fe_write_all(&sci->chr, &sci->tdr, 1); | |
249 | + } | |
250 | + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TEND, 0); | |
251 | + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TDRE, 1); | |
252 | + } | |
253 | +} | |
254 | + | |
255 | +static int transmit_byte(RSCIState *sci) | |
256 | +{ | |
257 | + int64_t elapsed; | |
258 | + int byte = 0; | |
259 | + if (sci->tx_start_time > 0) { | |
260 | + elapsed = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - sci->tx_start_time; | |
261 | + byte = elapsed / sci->trtime; | |
262 | + if (byte > sci->tdcnt) { | |
263 | + byte = sci->tdcnt; | |
264 | + } | |
265 | + } | |
266 | + return byte; | |
267 | +} | |
268 | + | |
269 | +static int64_t sci_rx_timeout(RSCIState *sci) | |
270 | +{ | |
271 | + if (IS_SCIF(sci)) { | |
272 | + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, DR, 1); | |
273 | + sci_irq(sci, RXI); | |
274 | + } | |
275 | + return 0; | |
276 | +} | |
277 | + | |
278 | +static int64_t sci_rx_next(RSCIState *sci) | |
279 | +{ | |
280 | + int64_t next_event = 0; | |
281 | + if (IS_SCI(sci) && !fifo8_is_empty(&sci->rxfifo)) { | |
282 | + if (FIELD_EX16(sci->Xsr, SSR, RDRF)) { | |
283 | + /* Receiver overrun */ | |
284 | + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, ORER, 1); | |
285 | + sci_irq(sci, ERI); | |
286 | + } else { | |
287 | + /* Trigger next event */ | |
288 | + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, RDRF, 1); | |
289 | + sci_irq(sci, RXI); | |
290 | + next_event = sci->trtime; | |
291 | + } | |
292 | + } | |
293 | + return next_event; | |
294 | +} | |
295 | + | |
296 | +static int64_t sci_tx_empty(RSCIState *sci) | |
297 | +{ | |
298 | + int64_t ret = 0; | |
299 | + if (IS_SCI(sci)) { | |
300 | + if (!FIELD_EX16(sci->Xsr, SSR, TDRE)) { | |
301 | + sci_send_byte(sci); | |
302 | + ret = sci->trtime; | |
303 | + sci_irq(sci, TXI); | |
304 | + } else { | |
305 | + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TEND, 1); | |
306 | + sci_irq(sci, TEI); | |
307 | + } | |
308 | + } else { | |
309 | + sci->tdcnt -= transmit_byte(sci); | |
310 | + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TDFE, 1); | |
311 | + sci_irq(sci, TXI); | |
312 | + } | |
313 | + return ret; | |
314 | +} | |
315 | + | |
316 | +static int64_t sci_tx_end(RSCIState *sci) | |
317 | +{ | |
318 | + if (IS_SCIF(sci)) { | |
319 | + sci->tdcnt = 0; | |
320 | + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TEND, 1); | |
321 | + sci_irq(sci, TEI); | |
322 | + } | |
323 | + return 0; | |
324 | +} | |
325 | + | |
326 | +static void sci_timer_event(void *opaque) | |
327 | +{ | |
328 | + RSCIState *sci = RSCI(opaque); | |
329 | + int64_t now, next, t; | |
330 | + int i; | |
331 | + | |
332 | + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | |
333 | + next = INT64_MAX; | |
334 | + for (i = 0; i < NR_SCI_EVENT; i++) { | |
335 | + if (sci->event[i].time > 0 && sci->event[i].time <= now) { | |
336 | + t = sci->event[i].handler(sci); | |
337 | + sci->event[i].time = (t > 0) ? now + t : 0; | |
338 | + } | |
339 | + if (sci->event[i].time > 0) { | |
340 | + next = MIN(next, sci->event[i].time); | |
341 | + } | |
342 | + } | |
343 | + if (next < INT64_MAX) { | |
344 | + timer_mod(sci->event_timer, next); | |
345 | + } else { | |
346 | + timer_del(sci->event_timer); | |
347 | + } | |
348 | +} | |
349 | + | |
350 | +static void update_trtime(RSCIState *sci) | |
351 | +{ | |
352 | + int divrate; | |
353 | + if (IS_SCIA(sci)) { | |
354 | + divrate = 16 * (2 - FIELD_EX8(sci->semr, SEMR, ABCS)); | |
355 | + } else { | |
356 | + divrate = 32; | |
357 | + } | |
358 | + | |
359 | + /* x bit transmit time (divrate * brr) / base freq */ | |
360 | + sci->etu = divrate * 1 << (2 * FIELD_EX16(sci->smr, SMR, CKS)); | |
361 | + sci->etu *= sci->brr + 1; | |
362 | + sci->etu *= NANOSECONDS_PER_SECOND; | |
363 | + sci->etu /= sci->input_freq; | |
364 | + | |
365 | + /* char per bits */ | |
366 | + sci->trtime = 8 - FIELD_EX16(sci->smr, SMR, CHR); | |
367 | + sci->trtime += FIELD_EX16(sci->smr, SMR, PE); | |
368 | + sci->trtime += FIELD_EX16(sci->smr, SMR, STOP) + 1 + 1; | |
369 | + sci->trtime *= sci->etu; | |
370 | +} | |
371 | + | |
372 | +#define IS_TR_ENABLED(scr) \ | |
373 | + (FIELD_EX16(scr, SCR, TE) || FIELD_EX16(scr, SCR, RE)) | |
374 | + | |
375 | +#define SCI_IS_NOT_SUPPORTED(sci, name) \ | |
376 | + if (sci->feature == SCI_FEAT_SCI) { \ | |
377 | + qemu_log_mask(LOG_GUEST_ERROR, \ | |
378 | + "reneas_sci: " #name " is not supported.\n"); \ | |
379 | + } | |
380 | + | |
381 | +static void sci_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) | |
382 | +{ | |
383 | + RSCIState *sci = RSCI(opaque); | |
384 | + int txtrg; | |
385 | + int rxtrg; | |
386 | + uint8_t txd; | |
387 | + uint16_t ssr_mask; | |
388 | + bool tx_start = false; | |
389 | + | |
390 | + if (IS_SCI(sci)) { | |
391 | + txtrg = 1; | |
392 | + } else { | |
393 | + txtrg = 1 << (3 - FIELD_EX16(sci->fcr, FCR, TTRG)); | |
394 | + } | |
395 | + switch (sci->regsize) { | |
396 | + case 8: | |
397 | + addr <<= 1; break; | |
398 | + case 32: | |
399 | + addr >>= 1; break; | |
400 | + } | |
401 | + switch (addr) { | |
402 | + case A_SMR: | |
403 | + if (IS_SCIA(sci) && IS_TR_ENABLED(sci->scr)) { | |
404 | + qemu_log_mask(LOG_GUEST_ERROR, | |
405 | + "reneas_sci: SMR write protected.\n"); | |
406 | + break; | |
407 | + } | |
408 | + sci->smr = val; | |
409 | + update_trtime(sci); | |
410 | + break; | |
411 | + case A_BRR: | |
412 | + if (IS_SCIA(sci) && IS_TR_ENABLED(sci->scr)) { | |
413 | + qemu_log_mask(LOG_GUEST_ERROR, | |
414 | + "reneas_sci: BRR write protected.\n"); | |
415 | + break; | |
416 | + } | |
417 | + sci->brr = val; | |
418 | + update_trtime(sci); | |
419 | + break; | |
420 | + case A_SCR: | |
421 | + sci->scr = val; | |
422 | + if (FIELD_EX16(sci->scr, SCR, TE)) { | |
423 | + /* Transmitter enable */ | |
424 | + if (IS_SCI(sci)) { | |
425 | + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TDRE, 1); | |
426 | + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TEND, 1); | |
427 | + } else { | |
428 | + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TEND, 1); | |
429 | + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TDFE, 1); | |
430 | + sci->tx_start_time = 0; | |
431 | + } | |
432 | + sci_irq(sci, TXI); | |
433 | + sci_irq(sci, TEI); | |
434 | + } else { | |
435 | + /* Transmitter disable */ | |
436 | + update_event_time(sci, TXEND, 0); | |
437 | + update_event_time(sci, TXEMPTY, 0); | |
438 | + } | |
439 | + break; | |
440 | + case A_TDR: | |
441 | + if (IS_SCI(sci)) { | |
442 | + sci->tdr = val; | |
443 | + if (IS_SCIA(sci)) { | |
444 | + if (FIELD_EX16(sci->Xsr, SSR, TEND)) { | |
445 | + update_event_time(sci, TXEMPTY, sci->trtime); | |
446 | + sci_send_byte(sci); | |
447 | + } else { | |
448 | + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, TDRE, 0); | |
449 | + } | |
450 | + sci_irq(sci, TXI); | |
451 | + sci_irq(sci, TEI); | |
452 | + } | |
453 | + } else { | |
454 | + if (sci->tx_start_time > 0) { | |
455 | + sci->tdcnt -= transmit_byte(sci); | |
456 | + } else { | |
457 | + sci->tx_start_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); | |
458 | + } | |
459 | + if (sci->tdcnt >= SCIF_FIFO_DEPTH) { | |
460 | + break; | |
461 | + } | |
462 | + txd = val; | |
463 | + if (qemu_chr_fe_backend_connected(&sci->chr)) { | |
464 | + qemu_chr_fe_write_all(&sci->chr, &txd, 1); | |
465 | + } | |
466 | + if (FIELD_EX16(sci->fcr, FCR, LOOP) && can_receive(sci) > 0) { | |
467 | + /* Loopback mode */ | |
468 | + sci_receive(sci, &txd, 1); | |
469 | + } | |
470 | + sci->tdcnt++; | |
471 | + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TEND, 0); | |
472 | + update_event_time(sci, TXEND, sci->tdcnt); | |
473 | + if (sci->tdcnt > txtrg) { | |
474 | + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, TDFE, 0); | |
475 | + update_event_time(sci, TXEMPTY, sci->tdcnt - txtrg + 1); | |
476 | + sci_irq(sci, TXI); | |
477 | + } | |
478 | + } | |
479 | + break; | |
480 | + case A_FSR: /* A_SSR */ | |
481 | + if (IS_SCI(sci)) { | |
482 | + /* Mask for read only bits */ | |
483 | + ssr_mask = IS_SCIA(sci) ? 0xc7 : 0x07; | |
484 | + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, MPBT, | |
485 | + FIELD_EX16(val, SSR, MPBT)); | |
486 | + sci->Xsr &= (val | ssr_mask); | |
487 | + /* Clear ERI */ | |
488 | + sci_irq(sci, ERI); | |
489 | + if (sci->feature == SCI_FEAT_SCI) { | |
490 | + tx_start = FIELD_EX16(sci->read_Xsr, SSR, TDRE) && | |
491 | + !FIELD_EX16(sci->Xsr, SSR, TDRE) && | |
492 | + (FIELD_EX16(sci->Xsr, SSR, ERR) == 0); | |
493 | + if (tx_start) { | |
494 | + sci_send_byte(sci); | |
495 | + update_event_time(sci, TXEMPTY, sci->trtime); | |
496 | + sci_irq(sci, TXI); | |
497 | + } | |
498 | + } | |
499 | + } else { | |
500 | + rxtrg = sci_rtrg[FIELD_EX16(sci->fcr, FCR, RTRG)]; | |
501 | + ssr_mask = ~(sci->read_Xsr & 0xf3); | |
502 | + sci->tdcnt -= transmit_byte(sci); | |
503 | + if (sci->tdcnt < txtrg) { | |
504 | + ssr_mask = FIELD_DP16(ssr_mask, FSR, TDFE, 1); | |
505 | + } | |
506 | + if (fifo8_num_used(&sci->rxfifo) >= rxtrg) { | |
507 | + ssr_mask = FIELD_DP16(ssr_mask, FSR, RDF, 1); | |
508 | + } | |
509 | + sci->Xsr &= (val | ssr_mask); | |
510 | + sci_irq(sci, ERI); | |
511 | + sci_irq(sci, RXI); | |
512 | + sci_irq(sci, TXI); | |
513 | + } | |
514 | + break; | |
515 | + case A_RDR: | |
516 | + qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: RDR is read only.\n"); | |
517 | + break; | |
518 | + case A_FCR: /* A_SCMR / A_SPTR */ | |
519 | + if (IS_SCI(sci)) { | |
520 | + if (sci->feature == SCI_FEAT_SCI) { | |
521 | + sci->sptr = val; | |
522 | + } else { | |
523 | + sci->scmr = val; | |
524 | + } | |
525 | + } else { | |
526 | + sci->fcr = val; | |
527 | + if (FIELD_EX16(sci->fcr, FCR, RFRST)) { | |
528 | + fifo8_reset(&sci->rxfifo); | |
529 | + update_event_time(sci, RXTOUT, 0); | |
530 | + update_event_time(sci, RXNEXT, 0); | |
531 | + } | |
532 | + if (FIELD_EX16(sci->fcr, FCR, TFRST)) { | |
533 | + sci->tdcnt = 0; | |
534 | + } | |
535 | + } | |
536 | + break; | |
537 | + case A_FDR: /* A_SEMR */ | |
538 | + if (IS_SCI(sci)) { | |
539 | + SCI_IS_NOT_SUPPORTED(sci, SEMR); | |
540 | + sci->semr = val; | |
541 | + } else { | |
542 | + qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: FDR is read only.\n"); | |
543 | + } | |
544 | + break; | |
545 | + case A_SPTR: | |
546 | + if (IS_SCI(sci)) { | |
547 | + goto error; | |
548 | + } else { | |
549 | + sci->sptr = val; | |
550 | + } | |
551 | + break; | |
552 | + case A_LSR: | |
553 | + if (IS_SCI(sci)) { | |
554 | + goto error; | |
555 | + } else { | |
556 | + if (FIELD_EX16(sci->read_lsr, LSR, ORER) != 1) { | |
557 | + val = FIELD_DP16(val, LSR, ORER, 1); | |
558 | + } | |
559 | + sci->lsr &= val; | |
560 | + sci_irq(sci, ERI); | |
561 | + } | |
562 | + break; | |
563 | + default: | |
564 | + error: | |
565 | + qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX | |
566 | + " not implemented\n", addr); | |
567 | + } | |
568 | +} | |
569 | + | |
570 | +static uint64_t sci_read(void *opaque, hwaddr addr, unsigned size) | |
571 | +{ | |
572 | + RSCIState *sci = RSCI(opaque); | |
573 | + uint64_t ret; | |
574 | + | |
575 | + switch (sci->regsize) { | |
576 | + case 8: | |
577 | + addr <<= 1; break; | |
578 | + case 32: | |
579 | + addr >>= 1; break; | |
580 | + } | |
581 | + | |
582 | + switch (addr) { | |
583 | + case A_SMR: | |
584 | + return sci->smr; | |
585 | + case A_BRR: | |
586 | + return sci->brr; | |
587 | + case A_SCR: | |
588 | + return sci->scr; | |
589 | + case A_TDR: | |
590 | + if (IS_SCI(sci)) { | |
591 | + return sci->tdr; | |
592 | + } else { | |
593 | + qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: TDR is write only.\n"); | |
594 | + return UINT64_MAX; | |
595 | + } | |
596 | + case A_FSR: /* A_SSR */ | |
597 | + sci->read_Xsr = sci->Xsr; | |
598 | + return sci->Xsr; | |
599 | + case A_RDR: | |
600 | + ret = fifo8_pop(&sci->rxfifo); | |
601 | + if (IS_SCIA(sci)) { | |
602 | + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, RDRF, 0); | |
603 | + } | |
604 | + return ret; | |
605 | + case A_FCR: /* A_SCMR / A_SPTR */ | |
606 | + if (IS_SCI(sci)) { | |
607 | + if (IS_SCIA(sci)) { | |
608 | + return sci->scmr; | |
609 | + } else { | |
610 | + return sci->sptr; | |
611 | + } | |
612 | + } else { | |
613 | + return sci->fcr & 0x7ff; | |
614 | + } | |
615 | + case A_FDR: /* A_SEMR */ | |
616 | + if (IS_SCI(sci)) { | |
617 | + SCI_IS_NOT_SUPPORTED(sci, SEMR); | |
618 | + return sci->semr; | |
619 | + } else { | |
620 | + ret = 0; | |
621 | + ret = FIELD_DP16(ret, FDR, Rn, fifo8_num_used(&sci->rxfifo)); | |
622 | + ret = FIELD_DP16(ret, FDR, Tn, sci->tdcnt - transmit_byte(sci)); | |
623 | + return ret; | |
624 | + } | |
625 | + case A_SPTR: | |
626 | + if (IS_SCI(sci)) { | |
627 | + goto error; | |
628 | + } else { | |
629 | + return sci->sptr; | |
630 | + } | |
631 | + case A_LSR: | |
632 | + if (IS_SCI(sci)) { | |
633 | + goto error; | |
634 | + } else { | |
635 | + sci->read_lsr = sci->lsr; | |
636 | + return sci->lsr; | |
637 | + } | |
638 | + default: | |
639 | + error: | |
640 | + qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX | |
641 | + " not implemented.\n", addr); | |
642 | + } | |
643 | + return UINT64_MAX; | |
644 | +} | |
645 | + | |
646 | +static const MemoryRegionOps sci_ops = { | |
647 | + .write = sci_write, | |
648 | + .read = sci_read, | |
649 | + .endianness = DEVICE_NATIVE_ENDIAN, | |
650 | + .impl = { | |
651 | + .max_access_size = 4, | |
652 | + }, | |
653 | +}; | |
654 | + | |
655 | +static void sci_register_init(RSCIState *sci) | |
656 | +{ | |
657 | + int i; | |
658 | + sci->smr = sci->scr = 0x00; | |
659 | + sci->brr = 0xff; | |
660 | + if (IS_SCI(sci)) { | |
661 | + sci->tdr = 0xff; | |
662 | + sci->Xsr = 0x84; | |
663 | + sci->scmr = 0x00; | |
664 | + sci->semr = 0x00; | |
665 | + sci->sptr = 0x00; | |
666 | + } else { | |
667 | + sci->Xsr = 0x0060; | |
668 | + sci->fcr = 0x0000; | |
669 | + sci->sptr = 0x0000; | |
670 | + sci->lsr = 0x0000; | |
671 | + } | |
672 | + update_trtime(sci); | |
673 | + for (i = 0; i < NR_SCI_EVENT; i++) { | |
674 | + sci->event[i].time = 0; | |
675 | + } | |
676 | +} | |
677 | + | |
678 | +static void sci_event(void *opaque, QEMUChrEvent event) | |
679 | +{ | |
680 | + RSCIState *sci = RSCI(opaque); | |
681 | + if (event == CHR_EVENT_BREAK) { | |
682 | + if (IS_SCI(sci)) { | |
683 | + sci->Xsr = FIELD_DP16(sci->Xsr, SSR, FER, 1); | |
684 | + } else { | |
685 | + sci->Xsr = FIELD_DP16(sci->Xsr, FSR, BRK, 1); | |
686 | + } | |
687 | + sci_irq(sci, ERI); | |
688 | + } | |
689 | +} | |
690 | + | |
691 | +static void rsci_realize(DeviceState *dev, Error **errp) | |
692 | +{ | |
693 | + SysBusDevice *d = SYS_BUS_DEVICE(dev); | |
694 | + RSCIState *sci = RSCI(dev); | |
695 | + int i; | |
696 | + int size; | |
697 | + | |
698 | + if (sci->input_freq == 0) { | |
699 | + qemu_log_mask(LOG_GUEST_ERROR, | |
700 | + "renesas_sci: input-freq property must be set."); | |
701 | + return; | |
702 | + } | |
703 | + if (sci->regsize != 8 && sci->regsize != 16 && sci->regsize != 32) { | |
704 | + qemu_log_mask(LOG_GUEST_ERROR, | |
705 | + "renesas_sci: Invalid regsize."); | |
706 | + return; | |
707 | + } | |
708 | + | |
709 | + size = IS_SCI(sci) ? 16 : 20; | |
710 | + switch (sci->regsize) { | |
711 | + case 8: | |
712 | + size >>= 1; | |
713 | + break; | |
714 | + case 32: | |
715 | + size <<= 1; | |
716 | + break; | |
717 | + } | |
718 | + memory_region_init_io(&sci->memory, OBJECT(sci), &sci_ops, | |
719 | + sci, "renesas-sci", size); | |
720 | + sysbus_init_mmio(d, &sci->memory); | |
721 | + memory_region_init_alias(&sci->memory_p4, NULL, "renesas-sci-p4", | |
722 | + &sci->memory, 0, size); | |
723 | + sysbus_init_mmio(d, &sci->memory_p4); | |
724 | + memory_region_init_alias(&sci->memory_a7, NULL, "renesas-sci-a7", | |
725 | + &sci->memory, 0, size); | |
726 | + sysbus_init_mmio(d, &sci->memory_a7); | |
727 | + | |
728 | + for (i = 0; i < SCI_NR_IRQ; i++) { | |
729 | + sysbus_init_irq(d, &sci->irq[i]); | |
730 | + } | |
731 | + sci->event_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sci_timer_event, sci); | |
732 | + | |
733 | + qemu_chr_fe_set_handlers(&sci->chr, can_receive, sci_receive, | |
734 | + sci_event, NULL, sci, NULL, true); | |
735 | + fifo8_create(&sci->rxfifo, SCIF_FIFO_DEPTH); | |
736 | + sci_register_init(sci); | |
737 | +} | |
738 | + | |
739 | +static const VMStateDescription vmstate_rsci = { | |
740 | + .name = "renesas-sci", | |
741 | + .version_id = 1, | |
742 | + .minimum_version_id = 1, | |
743 | + .fields = (VMStateField[]) { | |
744 | + VMSTATE_END_OF_LIST() | |
745 | + } | |
746 | +}; | |
747 | + | |
748 | +static Property rsci_properties[] = { | |
749 | + DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0), | |
750 | + DEFINE_PROP_INT32("register-size", RSCIState, regsize, 8), | |
751 | + DEFINE_PROP_INT32("feature", RSCIState, feature, 0), | |
752 | + DEFINE_PROP_CHR("chardev", RSCIState, chr), | |
753 | + DEFINE_PROP_END_OF_LIST(), | |
754 | +}; | |
755 | + | |
756 | +static void rsci_init(Object *obj) | |
757 | +{ | |
758 | + RSCIState *sci = RSCI(obj); | |
759 | + sci->event[RXTOUT].handler = sci_rx_timeout; | |
760 | + sci->event[RXNEXT].handler = sci_rx_next; | |
761 | + sci->event[TXEMPTY].handler = sci_tx_empty; | |
762 | + sci->event[TXEND].handler = sci_tx_end; | |
763 | +} | |
764 | +static void rsci_class_init(ObjectClass *klass, void *data) | |
765 | +{ | |
766 | + DeviceClass *dc = DEVICE_CLASS(klass); | |
767 | + | |
768 | + dc->realize = rsci_realize; | |
769 | + dc->vmsd = &vmstate_rsci; | |
770 | + device_class_set_props(dc, rsci_properties); | |
771 | +} | |
772 | + | |
773 | +static const TypeInfo rsci_info = { | |
774 | + .name = TYPE_RENESAS_SCI, | |
775 | + .parent = TYPE_SYS_BUS_DEVICE, | |
776 | + .instance_size = sizeof(RSCIState), | |
777 | + .instance_init = rsci_init, | |
778 | + .class_init = rsci_class_init, | |
779 | +}; | |
780 | + | |
781 | +static void rsci_register_types(void) | |
782 | +{ | |
783 | + type_register_static(&rsci_info); | |
784 | +} | |
785 | + | |
786 | +type_init(rsci_register_types) |
@@ -0,0 +1,77 @@ | ||
1 | +/* | |
2 | + * Renesas Serial Communication Interface | |
3 | + * | |
4 | + * Copyright (c) 2020 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 "qemu/fifo8.h" | |
13 | +#include "hw/sysbus.h" | |
14 | + | |
15 | +#define TYPE_RENESAS_SCI "renesas-sci" | |
16 | +#define RSCI(obj) OBJECT_CHECK(RSCIState, (obj), TYPE_RENESAS_SCI) | |
17 | + | |
18 | +enum { | |
19 | + ERI = 0, | |
20 | + RXI = 1, | |
21 | + TXI = 2, | |
22 | + TEI = 3, | |
23 | + BRI = 3, | |
24 | + SCI_NR_IRQ = 4, | |
25 | +}; | |
26 | + | |
27 | +enum { | |
28 | + SCI_FEAT_SCI = 0x00, | |
29 | + SCI_FEAT_SCIA = 0x01, | |
30 | + SCI_FEAT_SCIF = 0x10, | |
31 | +}; | |
32 | + | |
33 | +enum { | |
34 | + RXTOUT, | |
35 | + RXNEXT, | |
36 | + TXEMPTY, | |
37 | + TXEND, | |
38 | + NR_SCI_EVENT, | |
39 | +}; | |
40 | + | |
41 | +typedef struct RSCIState { | |
42 | + SysBusDevice parent_obj; | |
43 | + MemoryRegion memory; | |
44 | + MemoryRegion memory_p4; | |
45 | + MemoryRegion memory_a7; | |
46 | + | |
47 | + /* SCI register */ | |
48 | + uint8_t smr; | |
49 | + uint8_t brr; | |
50 | + uint8_t scr; | |
51 | + uint8_t tdr; | |
52 | + uint16_t Xsr; | |
53 | + uint8_t scmr; | |
54 | + uint8_t semr; | |
55 | + uint16_t fcr; | |
56 | + uint16_t sptr; | |
57 | + uint16_t lsr; | |
58 | + | |
59 | + /* internal use */ | |
60 | + uint16_t read_Xsr; | |
61 | + uint16_t read_lsr; | |
62 | + int64_t etu; | |
63 | + int64_t trtime; | |
64 | + int64_t tx_start_time; | |
65 | + int tdcnt; | |
66 | + int regsize; | |
67 | + struct { | |
68 | + int64_t time; | |
69 | + int64_t (*handler)(struct RSCIState *sci); | |
70 | + } event[NR_SCI_EVENT]; | |
71 | + QEMUTimer *event_timer; | |
72 | + CharBackend chr; | |
73 | + uint64_t input_freq; | |
74 | + int feature; | |
75 | + qemu_irq irq[SCI_NR_IRQ]; | |
76 | + Fifo8 rxfifo; | |
77 | +} RSCIState; |