| 1 | /* |
| 2 | * QEMU ATmega MCU |
| 3 | * |
| 4 | * Copyright (c) 2019-2020 Philippe Mathieu-Daudé |
| 5 | * |
| 6 | * This work is licensed under the terms of the GNU GPLv2 or later. |
| 7 | * See the COPYING file in the top-level directory. |
| 8 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 9 | */ |
| 10 | |
| 11 | #include "qemu/osdep.h" |
| 12 | #include "qemu/module.h" |
| 13 | #include "qemu/units.h" |
| 14 | #include "qapi/error.h" |
| 15 | #include "exec/target_page.h" |
| 16 | #include "system/memory.h" |
| 17 | #include "system/address-spaces.h" |
| 18 | #include "system/system.h" |
| 19 | #include "hw/core/qdev-properties.h" |
| 20 | #include "hw/core/sysbus.h" |
| 21 | #include "qom/object.h" |
| 22 | #include "hw/misc/unimp.h" |
| 23 | #include "migration/vmstate.h" |
| 24 | #include "atmega.h" |
| 25 | |
| 26 | enum AtmegaPeripheral { |
| 27 | POWER0, POWER1, |
| 28 | GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, |
| 29 | GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK, GPIOL, |
| 30 | USART0, USART1, USART2, USART3, |
| 31 | TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5, |
| 32 | PERIFMAX |
| 33 | }; |
| 34 | |
| 35 | #define GPIO(n) (n + GPIOA) |
| 36 | #define USART(n) (n + USART0) |
| 37 | #define TIMER(n) (n + TIMER0) |
| 38 | #define POWER(n) (n + POWER0) |
| 39 | |
| 40 | typedef struct { |
| 41 | uint16_t addr; |
| 42 | enum AtmegaPeripheral power_index; |
| 43 | uint8_t power_bit; |
| 44 | /* timer specific */ |
| 45 | uint16_t intmask_addr; |
| 46 | uint16_t intflag_addr; |
| 47 | bool is_timer16; |
| 48 | } peripheral_cfg; |
| 49 | |
| 50 | struct AtmegaMcuClass { |
| 51 | /*< private >*/ |
| 52 | SysBusDeviceClass parent_class; |
| 53 | /*< public >*/ |
| 54 | const char *uc_name; |
| 55 | const char *cpu_type; |
| 56 | size_t flash_size; |
| 57 | size_t eeprom_size; |
| 58 | size_t sram_size; |
| 59 | size_t io_size; |
| 60 | size_t gpio_count; |
| 61 | size_t adc_count; |
| 62 | const uint8_t *irq; |
| 63 | const peripheral_cfg *dev; |
| 64 | }; |
| 65 | typedef struct AtmegaMcuClass AtmegaMcuClass; |
| 66 | |
| 67 | DECLARE_CLASS_CHECKERS(AtmegaMcuClass, ATMEGA_MCU, |
| 68 | TYPE_ATMEGA_MCU) |
| 69 | |
| 70 | static const peripheral_cfg dev168_328[PERIFMAX] = { |
| 71 | [USART0] = { 0xc0, POWER0, 1 }, |
| 72 | [TIMER2] = { 0xb0, POWER0, 6, 0x70, 0x37, false }, |
| 73 | [TIMER1] = { 0x80, POWER0, 3, 0x6f, 0x36, true }, |
| 74 | [POWER0] = { 0x64 }, |
| 75 | [TIMER0] = { 0x44, POWER0, 5, 0x6e, 0x35, false }, |
| 76 | [GPIOD] = { 0x29 }, |
| 77 | [GPIOC] = { 0x26 }, |
| 78 | [GPIOB] = { 0x23 }, |
| 79 | }, dev1280_2560[PERIFMAX] = { |
| 80 | [USART3] = { 0x130, POWER1, 2 }, |
| 81 | [TIMER5] = { 0x120, POWER1, 5, 0x73, 0x3a, true }, |
| 82 | [GPIOL] = { 0x109 }, |
| 83 | [GPIOK] = { 0x106 }, |
| 84 | [GPIOJ] = { 0x103 }, |
| 85 | [GPIOH] = { 0x100 }, |
| 86 | [USART2] = { 0xd0, POWER1, 1 }, |
| 87 | [USART1] = { 0xc8, POWER1, 0 }, |
| 88 | [USART0] = { 0xc0, POWER0, 1 }, |
| 89 | [TIMER2] = { 0xb0, POWER0, 6, 0x70, 0x37, false }, /* TODO async */ |
| 90 | [TIMER4] = { 0xa0, POWER1, 4, 0x72, 0x39, true }, |
| 91 | [TIMER3] = { 0x90, POWER1, 3, 0x71, 0x38, true }, |
| 92 | [TIMER1] = { 0x80, POWER0, 3, 0x6f, 0x36, true }, |
| 93 | [POWER1] = { 0x65 }, |
| 94 | [POWER0] = { 0x64 }, |
| 95 | [TIMER0] = { 0x44, POWER0, 5, 0x6e, 0x35, false }, |
| 96 | [GPIOG] = { 0x32 }, |
| 97 | [GPIOF] = { 0x2f }, |
| 98 | [GPIOE] = { 0x2c }, |
| 99 | [GPIOD] = { 0x29 }, |
| 100 | [GPIOC] = { 0x26 }, |
| 101 | [GPIOB] = { 0x23 }, |
| 102 | [GPIOA] = { 0x20 }, |
| 103 | }; |
| 104 | |
| 105 | enum AtmegaIrq { |
| 106 | USART0_RXC_IRQ, USART0_DRE_IRQ, USART0_TXC_IRQ, |
| 107 | USART1_RXC_IRQ, USART1_DRE_IRQ, USART1_TXC_IRQ, |
| 108 | USART2_RXC_IRQ, USART2_DRE_IRQ, USART2_TXC_IRQ, |
| 109 | USART3_RXC_IRQ, USART3_DRE_IRQ, USART3_TXC_IRQ, |
| 110 | TIMER0_CAPT_IRQ, TIMER0_COMPA_IRQ, TIMER0_COMPB_IRQ, |
| 111 | TIMER0_COMPC_IRQ, TIMER0_OVF_IRQ, |
| 112 | TIMER1_CAPT_IRQ, TIMER1_COMPA_IRQ, TIMER1_COMPB_IRQ, |
| 113 | TIMER1_COMPC_IRQ, TIMER1_OVF_IRQ, |
| 114 | TIMER2_CAPT_IRQ, TIMER2_COMPA_IRQ, TIMER2_COMPB_IRQ, |
| 115 | TIMER2_COMPC_IRQ, TIMER2_OVF_IRQ, |
| 116 | TIMER3_CAPT_IRQ, TIMER3_COMPA_IRQ, TIMER3_COMPB_IRQ, |
| 117 | TIMER3_COMPC_IRQ, TIMER3_OVF_IRQ, |
| 118 | TIMER4_CAPT_IRQ, TIMER4_COMPA_IRQ, TIMER4_COMPB_IRQ, |
| 119 | TIMER4_COMPC_IRQ, TIMER4_OVF_IRQ, |
| 120 | TIMER5_CAPT_IRQ, TIMER5_COMPA_IRQ, TIMER5_COMPB_IRQ, |
| 121 | TIMER5_COMPC_IRQ, TIMER5_OVF_IRQ, |
| 122 | IRQ_COUNT |
| 123 | }; |
| 124 | |
| 125 | #define USART_IRQ_COUNT 3 |
| 126 | #define USART_RXC_IRQ(n) (n * USART_IRQ_COUNT + USART0_RXC_IRQ) |
| 127 | #define USART_DRE_IRQ(n) (n * USART_IRQ_COUNT + USART0_DRE_IRQ) |
| 128 | #define USART_TXC_IRQ(n) (n * USART_IRQ_COUNT + USART0_TXC_IRQ) |
| 129 | #define TIMER_IRQ_COUNT 5 |
| 130 | #define TIMER_CAPT_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_CAPT_IRQ) |
| 131 | #define TIMER_COMPA_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPA_IRQ) |
| 132 | #define TIMER_COMPB_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPB_IRQ) |
| 133 | #define TIMER_COMPC_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPC_IRQ) |
| 134 | #define TIMER_OVF_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_OVF_IRQ) |
| 135 | |
| 136 | static const uint8_t irq168_328[IRQ_COUNT] = { |
| 137 | [TIMER2_COMPA_IRQ] = 8, |
| 138 | [TIMER2_COMPB_IRQ] = 9, |
| 139 | [TIMER2_OVF_IRQ] = 10, |
| 140 | [TIMER1_CAPT_IRQ] = 11, |
| 141 | [TIMER1_COMPA_IRQ] = 12, |
| 142 | [TIMER1_COMPB_IRQ] = 13, |
| 143 | [TIMER1_OVF_IRQ] = 14, |
| 144 | [TIMER0_COMPA_IRQ] = 15, |
| 145 | [TIMER0_COMPB_IRQ] = 16, |
| 146 | [TIMER0_OVF_IRQ] = 17, |
| 147 | [USART0_RXC_IRQ] = 19, |
| 148 | [USART0_DRE_IRQ] = 20, |
| 149 | [USART0_TXC_IRQ] = 21, |
| 150 | }, irq1280_2560[IRQ_COUNT] = { |
| 151 | [TIMER2_COMPA_IRQ] = 14, |
| 152 | [TIMER2_COMPB_IRQ] = 15, |
| 153 | [TIMER2_OVF_IRQ] = 16, |
| 154 | [TIMER1_CAPT_IRQ] = 17, |
| 155 | [TIMER1_COMPA_IRQ] = 18, |
| 156 | [TIMER1_COMPB_IRQ] = 19, |
| 157 | [TIMER1_COMPC_IRQ] = 20, |
| 158 | [TIMER1_OVF_IRQ] = 21, |
| 159 | [TIMER0_COMPA_IRQ] = 22, |
| 160 | [TIMER0_COMPB_IRQ] = 23, |
| 161 | [TIMER0_OVF_IRQ] = 24, |
| 162 | [USART0_RXC_IRQ] = 26, |
| 163 | [USART0_DRE_IRQ] = 27, |
| 164 | [USART0_TXC_IRQ] = 28, |
| 165 | [TIMER3_CAPT_IRQ] = 32, |
| 166 | [TIMER3_COMPA_IRQ] = 33, |
| 167 | [TIMER3_COMPB_IRQ] = 34, |
| 168 | [TIMER3_COMPC_IRQ] = 35, |
| 169 | [TIMER3_OVF_IRQ] = 36, |
| 170 | [USART1_RXC_IRQ] = 37, |
| 171 | [USART1_DRE_IRQ] = 38, |
| 172 | [USART1_TXC_IRQ] = 39, |
| 173 | [TIMER4_CAPT_IRQ] = 42, |
| 174 | [TIMER4_COMPA_IRQ] = 43, |
| 175 | [TIMER4_COMPB_IRQ] = 44, |
| 176 | [TIMER4_COMPC_IRQ] = 45, |
| 177 | [TIMER4_OVF_IRQ] = 46, |
| 178 | [TIMER5_CAPT_IRQ] = 47, |
| 179 | [TIMER5_COMPA_IRQ] = 48, |
| 180 | [TIMER5_COMPB_IRQ] = 49, |
| 181 | [TIMER5_COMPC_IRQ] = 50, |
| 182 | [TIMER5_OVF_IRQ] = 51, |
| 183 | [USART2_RXC_IRQ] = 52, |
| 184 | [USART2_DRE_IRQ] = 53, |
| 185 | [USART2_TXC_IRQ] = 54, |
| 186 | [USART3_RXC_IRQ] = 55, |
| 187 | [USART3_DRE_IRQ] = 56, |
| 188 | [USART3_TXC_IRQ] = 57, |
| 189 | }; |
| 190 | |
| 191 | static void connect_peripheral_irq(const AtmegaMcuClass *k, |
| 192 | SysBusDevice *dev, int dev_irqn, |
| 193 | DeviceState *cpu, |
| 194 | unsigned peripheral_index) |
| 195 | { |
| 196 | int cpu_irq = k->irq[peripheral_index]; |
| 197 | |
| 198 | if (!cpu_irq) { |
| 199 | return; |
| 200 | } |
| 201 | /* FIXME move that to avr_cpu_set_int() once 'sample' board is removed */ |
| 202 | assert(cpu_irq >= 2); |
| 203 | cpu_irq -= 2; |
| 204 | |
| 205 | sysbus_connect_irq(dev, dev_irqn, qdev_get_gpio_in(cpu, cpu_irq)); |
| 206 | } |
| 207 | |
| 208 | static void connect_power_reduction_gpio(AtmegaMcuState *s, |
| 209 | const AtmegaMcuClass *k, |
| 210 | DeviceState *cpu, |
| 211 | unsigned peripheral_index) |
| 212 | { |
| 213 | unsigned power_index = k->dev[peripheral_index].power_index; |
| 214 | assert(k->dev[power_index].addr); |
| 215 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->pwr[power_index - POWER0]), |
| 216 | k->dev[peripheral_index].power_bit, |
| 217 | qdev_get_gpio_in(cpu, 0)); |
| 218 | } |
| 219 | |
| 220 | static void atmega_realize(DeviceState *dev, Error **errp) |
| 221 | { |
| 222 | AtmegaMcuState *s = ATMEGA_MCU(dev); |
| 223 | const AtmegaMcuClass *mc = ATMEGA_MCU_GET_CLASS(dev); |
| 224 | DeviceState *cpudev; |
| 225 | SysBusDevice *sbd; |
| 226 | char *devname; |
| 227 | size_t i; |
| 228 | |
| 229 | if (!s->xtal_freq_hz) { |
| 230 | error_setg(errp, "\"xtal-frequency-hz\" property must be provided."); |
| 231 | return; |
| 232 | } |
| 233 | |
| 234 | /* CPU */ |
| 235 | object_initialize_child(OBJECT(dev), "cpu", &s->cpu, mc->cpu_type); |
| 236 | |
| 237 | object_property_set_uint(OBJECT(&s->cpu), "init-sp", |
| 238 | mc->io_size + mc->sram_size - 1, &error_abort); |
| 239 | |
| 240 | qdev_realize(DEVICE(&s->cpu), NULL, &error_abort); |
| 241 | cpudev = DEVICE(&s->cpu); |
| 242 | |
| 243 | /* |
| 244 | * SRAM |
| 245 | * |
| 246 | * Softmmu is not able mix i/o and ram on the same page. |
| 247 | * Therefore in all cases, the first page exclusively contains i/o. |
| 248 | * |
| 249 | * If the MCU's i/o region matches the page size, then we can simply |
| 250 | * allocate all ram starting at the second page. Otherwise, we must |
| 251 | * allocate some ram as i/o to complete the first page. |
| 252 | */ |
| 253 | assert(mc->io_size == 0x100 || mc->io_size == 0x200); |
| 254 | if (mc->io_size >= TARGET_PAGE_SIZE) { |
| 255 | memory_region_init_ram(&s->sram, OBJECT(dev), "sram", mc->sram_size, |
| 256 | &error_abort); |
| 257 | memory_region_add_subregion(get_system_memory(), |
| 258 | OFFSET_DATA + mc->io_size, &s->sram); |
| 259 | } else { |
| 260 | int sram_io_size = TARGET_PAGE_SIZE - mc->io_size; |
| 261 | void *sram_io_mem = g_malloc0(sram_io_size); |
| 262 | |
| 263 | memory_region_init_ram_device_ptr(&s->sram_io, OBJECT(dev), "sram-as-io", |
| 264 | sram_io_size, sram_io_mem); |
| 265 | memory_region_add_subregion(get_system_memory(), |
| 266 | OFFSET_DATA + mc->io_size, &s->sram_io); |
| 267 | vmstate_register_ram(&s->sram_io, dev); |
| 268 | |
| 269 | memory_region_init_ram(&s->sram, OBJECT(dev), "sram", |
| 270 | mc->sram_size - sram_io_size, &error_abort); |
| 271 | memory_region_add_subregion(get_system_memory(), |
| 272 | OFFSET_DATA + TARGET_PAGE_SIZE, &s->sram); |
| 273 | } |
| 274 | |
| 275 | /* Flash */ |
| 276 | memory_region_init_rom(&s->flash, OBJECT(dev), |
| 277 | "flash", mc->flash_size, &error_fatal); |
| 278 | memory_region_add_subregion(get_system_memory(), OFFSET_CODE, &s->flash); |
| 279 | |
| 280 | /* |
| 281 | * I/O |
| 282 | * |
| 283 | * 0x00 - 0x1f: Registers |
| 284 | * 0x20 - 0x5f: I/O memory |
| 285 | * 0x60 - 0xff: Extended I/O |
| 286 | */ |
| 287 | s->io = qdev_new(TYPE_UNIMPLEMENTED_DEVICE); |
| 288 | qdev_prop_set_string(s->io, "name", "I/O"); |
| 289 | qdev_prop_set_uint64(s->io, "size", mc->io_size); |
| 290 | sysbus_realize_and_unref(SYS_BUS_DEVICE(s->io), &error_fatal); |
| 291 | sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->io), 0, OFFSET_DATA, -1234); |
| 292 | |
| 293 | /* Power Reduction */ |
| 294 | for (i = 0; i < POWER_MAX; i++) { |
| 295 | int idx = POWER(i); |
| 296 | if (!mc->dev[idx].addr) { |
| 297 | continue; |
| 298 | } |
| 299 | devname = g_strdup_printf("power%zu", i); |
| 300 | object_initialize_child(OBJECT(dev), devname, &s->pwr[i], |
| 301 | TYPE_AVR_MASK); |
| 302 | sysbus_realize(SYS_BUS_DEVICE(&s->pwr[i]), &error_abort); |
| 303 | sysbus_mmio_map(SYS_BUS_DEVICE(&s->pwr[i]), 0, |
| 304 | OFFSET_DATA + mc->dev[idx].addr); |
| 305 | g_free(devname); |
| 306 | } |
| 307 | |
| 308 | /* GPIO */ |
| 309 | for (i = 0; i < GPIO_MAX; i++) { |
| 310 | int idx = GPIO(i); |
| 311 | if (!mc->dev[idx].addr) { |
| 312 | continue; |
| 313 | } |
| 314 | devname = g_strdup_printf("atmega-gpio-%c", 'a' + (char)i); |
| 315 | create_unimplemented_device(devname, |
| 316 | OFFSET_DATA + mc->dev[idx].addr, 3); |
| 317 | g_free(devname); |
| 318 | } |
| 319 | |
| 320 | /* USART */ |
| 321 | for (i = 0; i < USART_MAX; i++) { |
| 322 | int idx = USART(i); |
| 323 | if (!mc->dev[idx].addr) { |
| 324 | continue; |
| 325 | } |
| 326 | devname = g_strdup_printf("usart%zu", i); |
| 327 | object_initialize_child(OBJECT(dev), devname, &s->usart[i], |
| 328 | TYPE_AVR_USART); |
| 329 | qdev_prop_set_chr(DEVICE(&s->usart[i]), "chardev", serial_hd(i)); |
| 330 | sbd = SYS_BUS_DEVICE(&s->usart[i]); |
| 331 | sysbus_realize(sbd, &error_abort); |
| 332 | sysbus_mmio_map(sbd, 0, OFFSET_DATA + mc->dev[USART(i)].addr); |
| 333 | connect_peripheral_irq(mc, sbd, 0, cpudev, USART_RXC_IRQ(i)); |
| 334 | connect_peripheral_irq(mc, sbd, 1, cpudev, USART_DRE_IRQ(i)); |
| 335 | connect_peripheral_irq(mc, sbd, 2, cpudev, USART_TXC_IRQ(i)); |
| 336 | connect_power_reduction_gpio(s, mc, DEVICE(&s->usart[i]), idx); |
| 337 | g_free(devname); |
| 338 | } |
| 339 | |
| 340 | /* Timer */ |
| 341 | for (i = 0; i < TIMER_MAX; i++) { |
| 342 | int idx = TIMER(i); |
| 343 | if (!mc->dev[idx].addr) { |
| 344 | continue; |
| 345 | } |
| 346 | if (!mc->dev[idx].is_timer16) { |
| 347 | create_unimplemented_device("avr-timer8", |
| 348 | OFFSET_DATA + mc->dev[idx].addr, 5); |
| 349 | create_unimplemented_device("avr-timer8-intmask", |
| 350 | OFFSET_DATA |
| 351 | + mc->dev[idx].intmask_addr, 1); |
| 352 | create_unimplemented_device("avr-timer8-intflag", |
| 353 | OFFSET_DATA |
| 354 | + mc->dev[idx].intflag_addr, 1); |
| 355 | continue; |
| 356 | } |
| 357 | devname = g_strdup_printf("timer%zu", i); |
| 358 | object_initialize_child(OBJECT(dev), devname, &s->timer[i], |
| 359 | TYPE_AVR_TIMER16); |
| 360 | object_property_set_uint(OBJECT(&s->timer[i]), "cpu-frequency-hz", |
| 361 | s->xtal_freq_hz, &error_abort); |
| 362 | sbd = SYS_BUS_DEVICE(&s->timer[i]); |
| 363 | sysbus_realize(sbd, &error_abort); |
| 364 | sysbus_mmio_map(sbd, 0, OFFSET_DATA + mc->dev[idx].addr); |
| 365 | sysbus_mmio_map(sbd, 1, OFFSET_DATA + mc->dev[idx].intmask_addr); |
| 366 | sysbus_mmio_map(sbd, 2, OFFSET_DATA + mc->dev[idx].intflag_addr); |
| 367 | connect_peripheral_irq(mc, sbd, 0, cpudev, TIMER_CAPT_IRQ(i)); |
| 368 | connect_peripheral_irq(mc, sbd, 1, cpudev, TIMER_COMPA_IRQ(i)); |
| 369 | connect_peripheral_irq(mc, sbd, 2, cpudev, TIMER_COMPB_IRQ(i)); |
| 370 | connect_peripheral_irq(mc, sbd, 3, cpudev, TIMER_COMPC_IRQ(i)); |
| 371 | connect_peripheral_irq(mc, sbd, 4, cpudev, TIMER_OVF_IRQ(i)); |
| 372 | connect_power_reduction_gpio(s, mc, DEVICE(&s->timer[i]), idx); |
| 373 | g_free(devname); |
| 374 | } |
| 375 | |
| 376 | create_unimplemented_device("avr-twi", OFFSET_DATA + 0x0b8, 6); |
| 377 | create_unimplemented_device("avr-adc", OFFSET_DATA + 0x078, 8); |
| 378 | create_unimplemented_device("avr-ext-mem-ctrl", OFFSET_DATA + 0x074, 2); |
| 379 | create_unimplemented_device("avr-watchdog", OFFSET_DATA + 0x060, 1); |
| 380 | create_unimplemented_device("avr-spi", OFFSET_DATA + 0x04c, 3); |
| 381 | create_unimplemented_device("avr-eeprom", OFFSET_DATA + 0x03f, 3); |
| 382 | } |
| 383 | |
| 384 | static const Property atmega_props[] = { |
| 385 | DEFINE_PROP_UINT64("xtal-frequency-hz", AtmegaMcuState, |
| 386 | xtal_freq_hz, 0), |
| 387 | }; |
| 388 | |
| 389 | static void atmega_class_init(ObjectClass *oc, const void *data) |
| 390 | { |
| 391 | DeviceClass *dc = DEVICE_CLASS(oc); |
| 392 | |
| 393 | dc->realize = atmega_realize; |
| 394 | device_class_set_props(dc, atmega_props); |
| 395 | /* Reason: Mapped at fixed location on the system bus */ |
| 396 | dc->user_creatable = false; |
| 397 | } |
| 398 | |
| 399 | static void atmega168_class_init(ObjectClass *oc, const void *data) |
| 400 | { |
| 401 | AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); |
| 402 | |
| 403 | amc->cpu_type = AVR_CPU_TYPE_NAME("avr5"); |
| 404 | amc->flash_size = 16 * KiB; |
| 405 | amc->eeprom_size = 512; |
| 406 | amc->sram_size = 1 * KiB; |
| 407 | amc->io_size = 256; |
| 408 | amc->gpio_count = 23; |
| 409 | amc->adc_count = 6; |
| 410 | amc->irq = irq168_328; |
| 411 | amc->dev = dev168_328; |
| 412 | }; |
| 413 | |
| 414 | static void atmega328_class_init(ObjectClass *oc, const void *data) |
| 415 | { |
| 416 | AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); |
| 417 | |
| 418 | amc->cpu_type = AVR_CPU_TYPE_NAME("avr5"); |
| 419 | amc->flash_size = 32 * KiB; |
| 420 | amc->eeprom_size = 1 * KiB; |
| 421 | amc->sram_size = 2 * KiB; |
| 422 | amc->io_size = 256; |
| 423 | amc->gpio_count = 23; |
| 424 | amc->adc_count = 6; |
| 425 | amc->irq = irq168_328; |
| 426 | amc->dev = dev168_328; |
| 427 | }; |
| 428 | |
| 429 | static void atmega1280_class_init(ObjectClass *oc, const void *data) |
| 430 | { |
| 431 | AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); |
| 432 | |
| 433 | amc->cpu_type = AVR_CPU_TYPE_NAME("avr51"); |
| 434 | amc->flash_size = 128 * KiB; |
| 435 | amc->eeprom_size = 4 * KiB; |
| 436 | amc->sram_size = 8 * KiB; |
| 437 | amc->io_size = 512; |
| 438 | amc->gpio_count = 86; |
| 439 | amc->adc_count = 16; |
| 440 | amc->irq = irq1280_2560; |
| 441 | amc->dev = dev1280_2560; |
| 442 | }; |
| 443 | |
| 444 | static void atmega2560_class_init(ObjectClass *oc, const void *data) |
| 445 | { |
| 446 | AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); |
| 447 | |
| 448 | amc->cpu_type = AVR_CPU_TYPE_NAME("avr6"); |
| 449 | amc->flash_size = 256 * KiB; |
| 450 | amc->eeprom_size = 4 * KiB; |
| 451 | amc->sram_size = 8 * KiB; |
| 452 | amc->io_size = 512; |
| 453 | amc->gpio_count = 54; |
| 454 | amc->adc_count = 16; |
| 455 | amc->irq = irq1280_2560; |
| 456 | amc->dev = dev1280_2560; |
| 457 | }; |
| 458 | |
| 459 | static const TypeInfo atmega_mcu_types[] = { |
| 460 | { |
| 461 | .name = TYPE_ATMEGA168_MCU, |
| 462 | .parent = TYPE_ATMEGA_MCU, |
| 463 | .class_init = atmega168_class_init, |
| 464 | }, { |
| 465 | .name = TYPE_ATMEGA328_MCU, |
| 466 | .parent = TYPE_ATMEGA_MCU, |
| 467 | .class_init = atmega328_class_init, |
| 468 | }, { |
| 469 | .name = TYPE_ATMEGA1280_MCU, |
| 470 | .parent = TYPE_ATMEGA_MCU, |
| 471 | .class_init = atmega1280_class_init, |
| 472 | }, { |
| 473 | .name = TYPE_ATMEGA2560_MCU, |
| 474 | .parent = TYPE_ATMEGA_MCU, |
| 475 | .class_init = atmega2560_class_init, |
| 476 | }, { |
| 477 | .name = TYPE_ATMEGA_MCU, |
| 478 | .parent = TYPE_SYS_BUS_DEVICE, |
| 479 | .instance_size = sizeof(AtmegaMcuState), |
| 480 | .class_size = sizeof(AtmegaMcuClass), |
| 481 | .class_init = atmega_class_init, |
| 482 | .abstract = true, |
| 483 | } |
| 484 | }; |
| 485 | |
| 486 | DEFINE_TYPES(atmega_mcu_types) |