1429ca9d6SMichael Rolnik /*
2429ca9d6SMichael Rolnik * AVR USART
3429ca9d6SMichael Rolnik *
4429ca9d6SMichael Rolnik * Copyright (c) 2018 University of Kent
5429ca9d6SMichael Rolnik * Author: Sarah Harris
6429ca9d6SMichael Rolnik *
7429ca9d6SMichael Rolnik * This library is free software; you can redistribute it and/or
8429ca9d6SMichael Rolnik * modify it under the terms of the GNU Lesser General Public
9429ca9d6SMichael Rolnik * License as published by the Free Software Foundation; either
10429ca9d6SMichael Rolnik * version 2.1 of the License, or (at your option) any later version.
11429ca9d6SMichael Rolnik *
12429ca9d6SMichael Rolnik * This library is distributed in the hope that it will be useful,
13429ca9d6SMichael Rolnik * but WITHOUT ANY WARRANTY; without even the implied warranty of
14429ca9d6SMichael Rolnik * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15429ca9d6SMichael Rolnik * Lesser General Public License for more details.
16429ca9d6SMichael Rolnik *
17429ca9d6SMichael Rolnik * You should have received a copy of the GNU Lesser General Public
18429ca9d6SMichael Rolnik * License along with this library; if not, see
19429ca9d6SMichael Rolnik * <http://www.gnu.org/licenses/lgpl-2.1.html>
20429ca9d6SMichael Rolnik */
21429ca9d6SMichael Rolnik
22429ca9d6SMichael Rolnik #include "qemu/osdep.h"
23429ca9d6SMichael Rolnik #include "hw/char/avr_usart.h"
24429ca9d6SMichael Rolnik #include "qemu/log.h"
25429ca9d6SMichael Rolnik #include "hw/irq.h"
26429ca9d6SMichael Rolnik #include "hw/qdev-properties.h"
27ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
28429ca9d6SMichael Rolnik
avr_usart_can_receive(void * opaque)29429ca9d6SMichael Rolnik static int avr_usart_can_receive(void *opaque)
30429ca9d6SMichael Rolnik {
31429ca9d6SMichael Rolnik AVRUsartState *usart = opaque;
32429ca9d6SMichael Rolnik
33429ca9d6SMichael Rolnik if (usart->data_valid || !(usart->csrb & USART_CSRB_RXEN)) {
34429ca9d6SMichael Rolnik return 0;
35429ca9d6SMichael Rolnik }
36429ca9d6SMichael Rolnik return 1;
37429ca9d6SMichael Rolnik }
38429ca9d6SMichael Rolnik
avr_usart_receive(void * opaque,const uint8_t * buffer,int size)39429ca9d6SMichael Rolnik static void avr_usart_receive(void *opaque, const uint8_t *buffer, int size)
40429ca9d6SMichael Rolnik {
41429ca9d6SMichael Rolnik AVRUsartState *usart = opaque;
42429ca9d6SMichael Rolnik assert(size == 1);
43429ca9d6SMichael Rolnik assert(!usart->data_valid);
44429ca9d6SMichael Rolnik usart->data = buffer[0];
45429ca9d6SMichael Rolnik usart->data_valid = true;
46429ca9d6SMichael Rolnik usart->csra |= USART_CSRA_RXC;
47429ca9d6SMichael Rolnik if (usart->csrb & USART_CSRB_RXCIE) {
48429ca9d6SMichael Rolnik qemu_set_irq(usart->rxc_irq, 1);
49429ca9d6SMichael Rolnik }
50429ca9d6SMichael Rolnik }
51429ca9d6SMichael Rolnik
update_char_mask(AVRUsartState * usart)52429ca9d6SMichael Rolnik static void update_char_mask(AVRUsartState *usart)
53429ca9d6SMichael Rolnik {
54429ca9d6SMichael Rolnik uint8_t mode = ((usart->csrc & USART_CSRC_CSZ0) ? 1 : 0) |
55429ca9d6SMichael Rolnik ((usart->csrc & USART_CSRC_CSZ1) ? 2 : 0) |
56429ca9d6SMichael Rolnik ((usart->csrb & USART_CSRB_CSZ2) ? 4 : 0);
57429ca9d6SMichael Rolnik switch (mode) {
58429ca9d6SMichael Rolnik case 0:
59429ca9d6SMichael Rolnik usart->char_mask = 0b11111;
60429ca9d6SMichael Rolnik break;
61429ca9d6SMichael Rolnik case 1:
62429ca9d6SMichael Rolnik usart->char_mask = 0b111111;
63429ca9d6SMichael Rolnik break;
64429ca9d6SMichael Rolnik case 2:
65429ca9d6SMichael Rolnik usart->char_mask = 0b1111111;
66429ca9d6SMichael Rolnik break;
67429ca9d6SMichael Rolnik case 3:
68429ca9d6SMichael Rolnik usart->char_mask = 0b11111111;
69429ca9d6SMichael Rolnik break;
70429ca9d6SMichael Rolnik case 4:
71429ca9d6SMichael Rolnik /* Fallthrough. */
72429ca9d6SMichael Rolnik case 5:
73429ca9d6SMichael Rolnik /* Fallthrough. */
74429ca9d6SMichael Rolnik case 6:
75429ca9d6SMichael Rolnik qemu_log_mask(
76429ca9d6SMichael Rolnik LOG_GUEST_ERROR,
77429ca9d6SMichael Rolnik "%s: Reserved character size 0x%x\n",
78429ca9d6SMichael Rolnik __func__,
79429ca9d6SMichael Rolnik mode);
80429ca9d6SMichael Rolnik break;
81429ca9d6SMichael Rolnik case 7:
82429ca9d6SMichael Rolnik qemu_log_mask(
83429ca9d6SMichael Rolnik LOG_GUEST_ERROR,
84429ca9d6SMichael Rolnik "%s: Nine bit character size not supported (forcing eight)\n",
85429ca9d6SMichael Rolnik __func__);
86429ca9d6SMichael Rolnik usart->char_mask = 0b11111111;
87429ca9d6SMichael Rolnik break;
88429ca9d6SMichael Rolnik default:
89*283e0d9dSPierrick Bouvier g_assert_not_reached();
90429ca9d6SMichael Rolnik }
91429ca9d6SMichael Rolnik }
92429ca9d6SMichael Rolnik
avr_usart_reset(DeviceState * dev)93429ca9d6SMichael Rolnik static void avr_usart_reset(DeviceState *dev)
94429ca9d6SMichael Rolnik {
95429ca9d6SMichael Rolnik AVRUsartState *usart = AVR_USART(dev);
96429ca9d6SMichael Rolnik usart->data_valid = false;
97429ca9d6SMichael Rolnik usart->csra = 0b00100000;
98429ca9d6SMichael Rolnik usart->csrb = 0b00000000;
99429ca9d6SMichael Rolnik usart->csrc = 0b00000110;
100429ca9d6SMichael Rolnik usart->brrl = 0;
101429ca9d6SMichael Rolnik usart->brrh = 0;
102429ca9d6SMichael Rolnik update_char_mask(usart);
103429ca9d6SMichael Rolnik qemu_set_irq(usart->rxc_irq, 0);
104429ca9d6SMichael Rolnik qemu_set_irq(usart->txc_irq, 0);
105429ca9d6SMichael Rolnik qemu_set_irq(usart->dre_irq, 0);
106429ca9d6SMichael Rolnik }
107429ca9d6SMichael Rolnik
avr_usart_read(void * opaque,hwaddr addr,unsigned int size)108429ca9d6SMichael Rolnik static uint64_t avr_usart_read(void *opaque, hwaddr addr, unsigned int size)
109429ca9d6SMichael Rolnik {
110429ca9d6SMichael Rolnik AVRUsartState *usart = opaque;
111429ca9d6SMichael Rolnik uint8_t data;
112429ca9d6SMichael Rolnik assert(size == 1);
113429ca9d6SMichael Rolnik
114429ca9d6SMichael Rolnik if (!usart->enabled) {
115429ca9d6SMichael Rolnik return 0;
116429ca9d6SMichael Rolnik }
117429ca9d6SMichael Rolnik
118429ca9d6SMichael Rolnik switch (addr) {
119429ca9d6SMichael Rolnik case USART_DR:
120429ca9d6SMichael Rolnik if (!(usart->csrb & USART_CSRB_RXEN)) {
121429ca9d6SMichael Rolnik /* Receiver disabled, ignore. */
122429ca9d6SMichael Rolnik return 0;
123429ca9d6SMichael Rolnik }
124429ca9d6SMichael Rolnik if (usart->data_valid) {
125429ca9d6SMichael Rolnik data = usart->data & usart->char_mask;
126429ca9d6SMichael Rolnik usart->data_valid = false;
127429ca9d6SMichael Rolnik } else {
128429ca9d6SMichael Rolnik data = 0;
129429ca9d6SMichael Rolnik }
130429ca9d6SMichael Rolnik usart->csra &= 0xff ^ USART_CSRA_RXC;
131429ca9d6SMichael Rolnik qemu_set_irq(usart->rxc_irq, 0);
132429ca9d6SMichael Rolnik qemu_chr_fe_accept_input(&usart->chr);
133429ca9d6SMichael Rolnik return data;
134429ca9d6SMichael Rolnik case USART_CSRA:
135429ca9d6SMichael Rolnik return usart->csra;
136429ca9d6SMichael Rolnik case USART_CSRB:
137429ca9d6SMichael Rolnik return usart->csrb;
138429ca9d6SMichael Rolnik case USART_CSRC:
139429ca9d6SMichael Rolnik return usart->csrc;
140429ca9d6SMichael Rolnik case USART_BRRL:
141429ca9d6SMichael Rolnik return usart->brrl;
142429ca9d6SMichael Rolnik case USART_BRRH:
143429ca9d6SMichael Rolnik return usart->brrh;
144429ca9d6SMichael Rolnik default:
145429ca9d6SMichael Rolnik qemu_log_mask(
146429ca9d6SMichael Rolnik LOG_GUEST_ERROR,
147429ca9d6SMichael Rolnik "%s: Bad offset 0x%"HWADDR_PRIx"\n",
148429ca9d6SMichael Rolnik __func__,
149429ca9d6SMichael Rolnik addr);
150429ca9d6SMichael Rolnik }
151429ca9d6SMichael Rolnik return 0;
152429ca9d6SMichael Rolnik }
153429ca9d6SMichael Rolnik
avr_usart_write(void * opaque,hwaddr addr,uint64_t value,unsigned int size)154429ca9d6SMichael Rolnik static void avr_usart_write(void *opaque, hwaddr addr, uint64_t value,
155429ca9d6SMichael Rolnik unsigned int size)
156429ca9d6SMichael Rolnik {
157429ca9d6SMichael Rolnik AVRUsartState *usart = opaque;
158429ca9d6SMichael Rolnik uint8_t mask;
159429ca9d6SMichael Rolnik uint8_t data;
160429ca9d6SMichael Rolnik assert((value & 0xff) == value);
161429ca9d6SMichael Rolnik assert(size == 1);
162429ca9d6SMichael Rolnik
163429ca9d6SMichael Rolnik if (!usart->enabled) {
164429ca9d6SMichael Rolnik return;
165429ca9d6SMichael Rolnik }
166429ca9d6SMichael Rolnik
167429ca9d6SMichael Rolnik switch (addr) {
168429ca9d6SMichael Rolnik case USART_DR:
169429ca9d6SMichael Rolnik if (!(usart->csrb & USART_CSRB_TXEN)) {
170429ca9d6SMichael Rolnik /* Transmitter disabled, ignore. */
171429ca9d6SMichael Rolnik return;
172429ca9d6SMichael Rolnik }
173429ca9d6SMichael Rolnik usart->csra |= USART_CSRA_TXC;
174429ca9d6SMichael Rolnik usart->csra |= USART_CSRA_DRE;
175429ca9d6SMichael Rolnik if (usart->csrb & USART_CSRB_TXCIE) {
176429ca9d6SMichael Rolnik qemu_set_irq(usart->txc_irq, 1);
177429ca9d6SMichael Rolnik usart->csra &= 0xff ^ USART_CSRA_TXC;
178429ca9d6SMichael Rolnik }
179429ca9d6SMichael Rolnik if (usart->csrb & USART_CSRB_DREIE) {
180429ca9d6SMichael Rolnik qemu_set_irq(usart->dre_irq, 1);
181429ca9d6SMichael Rolnik }
182429ca9d6SMichael Rolnik data = value;
183429ca9d6SMichael Rolnik qemu_chr_fe_write_all(&usart->chr, &data, 1);
184429ca9d6SMichael Rolnik break;
185429ca9d6SMichael Rolnik case USART_CSRA:
186429ca9d6SMichael Rolnik mask = 0b01000011;
187429ca9d6SMichael Rolnik /* Mask read-only bits. */
188429ca9d6SMichael Rolnik value = (value & mask) | (usart->csra & (0xff ^ mask));
189429ca9d6SMichael Rolnik usart->csra = value;
190429ca9d6SMichael Rolnik if (value & USART_CSRA_TXC) {
191429ca9d6SMichael Rolnik usart->csra ^= USART_CSRA_TXC;
192429ca9d6SMichael Rolnik qemu_set_irq(usart->txc_irq, 0);
193429ca9d6SMichael Rolnik }
194429ca9d6SMichael Rolnik if (value & USART_CSRA_MPCM) {
195429ca9d6SMichael Rolnik qemu_log_mask(
196429ca9d6SMichael Rolnik LOG_GUEST_ERROR,
197429ca9d6SMichael Rolnik "%s: MPCM not supported by USART\n",
198429ca9d6SMichael Rolnik __func__);
199429ca9d6SMichael Rolnik }
200429ca9d6SMichael Rolnik break;
201429ca9d6SMichael Rolnik case USART_CSRB:
202429ca9d6SMichael Rolnik mask = 0b11111101;
203429ca9d6SMichael Rolnik /* Mask read-only bits. */
204429ca9d6SMichael Rolnik value = (value & mask) | (usart->csrb & (0xff ^ mask));
205429ca9d6SMichael Rolnik usart->csrb = value;
206429ca9d6SMichael Rolnik if (!(value & USART_CSRB_RXEN)) {
207429ca9d6SMichael Rolnik /* Receiver disabled, flush input buffer. */
208429ca9d6SMichael Rolnik usart->data_valid = false;
209429ca9d6SMichael Rolnik }
210429ca9d6SMichael Rolnik qemu_set_irq(usart->rxc_irq,
211429ca9d6SMichael Rolnik ((value & USART_CSRB_RXCIE) &&
212429ca9d6SMichael Rolnik (usart->csra & USART_CSRA_RXC)) ? 1 : 0);
213429ca9d6SMichael Rolnik qemu_set_irq(usart->txc_irq,
214429ca9d6SMichael Rolnik ((value & USART_CSRB_TXCIE) &&
215429ca9d6SMichael Rolnik (usart->csra & USART_CSRA_TXC)) ? 1 : 0);
216429ca9d6SMichael Rolnik qemu_set_irq(usart->dre_irq,
217429ca9d6SMichael Rolnik ((value & USART_CSRB_DREIE) &&
218429ca9d6SMichael Rolnik (usart->csra & USART_CSRA_DRE)) ? 1 : 0);
219429ca9d6SMichael Rolnik update_char_mask(usart);
220429ca9d6SMichael Rolnik break;
221429ca9d6SMichael Rolnik case USART_CSRC:
222429ca9d6SMichael Rolnik usart->csrc = value;
223429ca9d6SMichael Rolnik if ((value & USART_CSRC_MSEL1) && (value & USART_CSRC_MSEL0)) {
224429ca9d6SMichael Rolnik qemu_log_mask(
225429ca9d6SMichael Rolnik LOG_GUEST_ERROR,
226429ca9d6SMichael Rolnik "%s: SPI mode not supported by USART\n",
227429ca9d6SMichael Rolnik __func__);
228429ca9d6SMichael Rolnik }
229429ca9d6SMichael Rolnik if ((value & USART_CSRC_MSEL1) && !(value & USART_CSRC_MSEL0)) {
230429ca9d6SMichael Rolnik qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad USART mode\n", __func__);
231429ca9d6SMichael Rolnik }
232429ca9d6SMichael Rolnik if (!(value & USART_CSRC_PM1) && (value & USART_CSRC_PM0)) {
233429ca9d6SMichael Rolnik qemu_log_mask(
234429ca9d6SMichael Rolnik LOG_GUEST_ERROR,
235429ca9d6SMichael Rolnik "%s: Bad USART parity mode\n",
236429ca9d6SMichael Rolnik __func__);
237429ca9d6SMichael Rolnik }
238429ca9d6SMichael Rolnik update_char_mask(usart);
239429ca9d6SMichael Rolnik break;
240429ca9d6SMichael Rolnik case USART_BRRL:
241429ca9d6SMichael Rolnik usart->brrl = value;
242429ca9d6SMichael Rolnik break;
243429ca9d6SMichael Rolnik case USART_BRRH:
244429ca9d6SMichael Rolnik usart->brrh = value & 0b00001111;
245429ca9d6SMichael Rolnik break;
246429ca9d6SMichael Rolnik default:
247429ca9d6SMichael Rolnik qemu_log_mask(
248429ca9d6SMichael Rolnik LOG_GUEST_ERROR,
249429ca9d6SMichael Rolnik "%s: Bad offset 0x%"HWADDR_PRIx"\n",
250429ca9d6SMichael Rolnik __func__,
251429ca9d6SMichael Rolnik addr);
252429ca9d6SMichael Rolnik }
253429ca9d6SMichael Rolnik }
254429ca9d6SMichael Rolnik
255429ca9d6SMichael Rolnik static const MemoryRegionOps avr_usart_ops = {
256429ca9d6SMichael Rolnik .read = avr_usart_read,
257429ca9d6SMichael Rolnik .write = avr_usart_write,
258429ca9d6SMichael Rolnik .endianness = DEVICE_NATIVE_ENDIAN,
259429ca9d6SMichael Rolnik .impl = {.min_access_size = 1, .max_access_size = 1}
260429ca9d6SMichael Rolnik };
261429ca9d6SMichael Rolnik
262429ca9d6SMichael Rolnik static Property avr_usart_properties[] = {
263429ca9d6SMichael Rolnik DEFINE_PROP_CHR("chardev", AVRUsartState, chr),
264429ca9d6SMichael Rolnik DEFINE_PROP_END_OF_LIST(),
265429ca9d6SMichael Rolnik };
266429ca9d6SMichael Rolnik
avr_usart_pr(void * opaque,int irq,int level)267429ca9d6SMichael Rolnik static void avr_usart_pr(void *opaque, int irq, int level)
268429ca9d6SMichael Rolnik {
269429ca9d6SMichael Rolnik AVRUsartState *s = AVR_USART(opaque);
270429ca9d6SMichael Rolnik
271429ca9d6SMichael Rolnik s->enabled = !level;
272429ca9d6SMichael Rolnik
273429ca9d6SMichael Rolnik if (!s->enabled) {
274429ca9d6SMichael Rolnik avr_usart_reset(DEVICE(s));
275429ca9d6SMichael Rolnik }
276429ca9d6SMichael Rolnik }
277429ca9d6SMichael Rolnik
avr_usart_init(Object * obj)278429ca9d6SMichael Rolnik static void avr_usart_init(Object *obj)
279429ca9d6SMichael Rolnik {
280429ca9d6SMichael Rolnik AVRUsartState *s = AVR_USART(obj);
281429ca9d6SMichael Rolnik sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rxc_irq);
282429ca9d6SMichael Rolnik sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->dre_irq);
283429ca9d6SMichael Rolnik sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->txc_irq);
284429ca9d6SMichael Rolnik memory_region_init_io(&s->mmio, obj, &avr_usart_ops, s, TYPE_AVR_USART, 7);
285429ca9d6SMichael Rolnik sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
286429ca9d6SMichael Rolnik qdev_init_gpio_in(DEVICE(s), avr_usart_pr, 1);
287429ca9d6SMichael Rolnik s->enabled = true;
288429ca9d6SMichael Rolnik }
289429ca9d6SMichael Rolnik
avr_usart_realize(DeviceState * dev,Error ** errp)290429ca9d6SMichael Rolnik static void avr_usart_realize(DeviceState *dev, Error **errp)
291429ca9d6SMichael Rolnik {
292429ca9d6SMichael Rolnik AVRUsartState *s = AVR_USART(dev);
293429ca9d6SMichael Rolnik qemu_chr_fe_set_handlers(&s->chr, avr_usart_can_receive,
294429ca9d6SMichael Rolnik avr_usart_receive, NULL, NULL,
295429ca9d6SMichael Rolnik s, NULL, true);
296429ca9d6SMichael Rolnik avr_usart_reset(dev);
297429ca9d6SMichael Rolnik }
298429ca9d6SMichael Rolnik
avr_usart_class_init(ObjectClass * klass,void * data)299429ca9d6SMichael Rolnik static void avr_usart_class_init(ObjectClass *klass, void *data)
300429ca9d6SMichael Rolnik {
301429ca9d6SMichael Rolnik DeviceClass *dc = DEVICE_CLASS(klass);
302429ca9d6SMichael Rolnik
303e3d08143SPeter Maydell device_class_set_legacy_reset(dc, avr_usart_reset);
304429ca9d6SMichael Rolnik device_class_set_props(dc, avr_usart_properties);
305429ca9d6SMichael Rolnik dc->realize = avr_usart_realize;
306429ca9d6SMichael Rolnik }
307429ca9d6SMichael Rolnik
308429ca9d6SMichael Rolnik static const TypeInfo avr_usart_info = {
309429ca9d6SMichael Rolnik .name = TYPE_AVR_USART,
310429ca9d6SMichael Rolnik .parent = TYPE_SYS_BUS_DEVICE,
311429ca9d6SMichael Rolnik .instance_size = sizeof(AVRUsartState),
312429ca9d6SMichael Rolnik .instance_init = avr_usart_init,
313429ca9d6SMichael Rolnik .class_init = avr_usart_class_init,
314429ca9d6SMichael Rolnik };
315429ca9d6SMichael Rolnik
avr_usart_register_types(void)316429ca9d6SMichael Rolnik static void avr_usart_register_types(void)
317429ca9d6SMichael Rolnik {
318429ca9d6SMichael Rolnik type_register_static(&avr_usart_info);
319429ca9d6SMichael Rolnik }
320429ca9d6SMichael Rolnik
321429ca9d6SMichael Rolnik type_init(avr_usart_register_types)
322