1fa2ba3b8SLaurent Vivier /* 2fa2ba3b8SLaurent Vivier * QEMU Macintosh Nubus 3fa2ba3b8SLaurent Vivier * 4fa2ba3b8SLaurent Vivier * Copyright (c) 2013-2018 Laurent Vivier <laurent@vivier.eu> 5fa2ba3b8SLaurent Vivier * 6fa2ba3b8SLaurent Vivier * This work is licensed under the terms of the GNU GPL, version 2 or later. 7fa2ba3b8SLaurent Vivier * See the COPYING file in the top-level directory. 8fa2ba3b8SLaurent Vivier * 9fa2ba3b8SLaurent Vivier */ 10fa2ba3b8SLaurent Vivier 11fa2ba3b8SLaurent Vivier #include "qemu/osdep.h" 12fa2ba3b8SLaurent Vivier #include "hw/nubus/nubus.h" 13fa2ba3b8SLaurent Vivier #include "qapi/error.h" 14fa2ba3b8SLaurent Vivier 15fa2ba3b8SLaurent Vivier 16fa2ba3b8SLaurent Vivier /* The Format Block Structure */ 17fa2ba3b8SLaurent Vivier 18fa2ba3b8SLaurent Vivier #define FBLOCK_DIRECTORY_OFFSET 0 19fa2ba3b8SLaurent Vivier #define FBLOCK_LENGTH 4 20fa2ba3b8SLaurent Vivier #define FBLOCK_CRC 8 21fa2ba3b8SLaurent Vivier #define FBLOCK_REVISION_LEVEL 12 22fa2ba3b8SLaurent Vivier #define FBLOCK_FORMAT 13 23fa2ba3b8SLaurent Vivier #define FBLOCK_TEST_PATTERN 14 24fa2ba3b8SLaurent Vivier #define FBLOCK_RESERVED 18 25fa2ba3b8SLaurent Vivier #define FBLOCK_BYTE_LANES 19 26fa2ba3b8SLaurent Vivier 27fa2ba3b8SLaurent Vivier #define FBLOCK_SIZE 20 28fa2ba3b8SLaurent Vivier #define FBLOCK_PATTERN_VAL 0x5a932bc7 29fa2ba3b8SLaurent Vivier 30fa2ba3b8SLaurent Vivier static uint64_t nubus_fblock_read(void *opaque, hwaddr addr, unsigned int size) 31fa2ba3b8SLaurent Vivier { 32fa2ba3b8SLaurent Vivier NubusDevice *dev = opaque; 33fa2ba3b8SLaurent Vivier uint64_t val; 34fa2ba3b8SLaurent Vivier 35fa2ba3b8SLaurent Vivier #define BYTE(v, b) (((v) >> (24 - 8 * (b))) & 0xff) 36fa2ba3b8SLaurent Vivier switch (addr) { 37fa2ba3b8SLaurent Vivier case FBLOCK_BYTE_LANES: 38fa2ba3b8SLaurent Vivier val = dev->byte_lanes; 39fa2ba3b8SLaurent Vivier val |= (val ^ 0xf) << 4; 40fa2ba3b8SLaurent Vivier break; 41fa2ba3b8SLaurent Vivier case FBLOCK_RESERVED: 42fa2ba3b8SLaurent Vivier val = 0x00; 43fa2ba3b8SLaurent Vivier break; 44fa2ba3b8SLaurent Vivier case FBLOCK_TEST_PATTERN...FBLOCK_TEST_PATTERN + 3: 45fa2ba3b8SLaurent Vivier val = BYTE(FBLOCK_PATTERN_VAL, addr - FBLOCK_TEST_PATTERN); 46fa2ba3b8SLaurent Vivier break; 47fa2ba3b8SLaurent Vivier case FBLOCK_FORMAT: 48fa2ba3b8SLaurent Vivier val = dev->rom_format; 49fa2ba3b8SLaurent Vivier break; 50fa2ba3b8SLaurent Vivier case FBLOCK_REVISION_LEVEL: 51fa2ba3b8SLaurent Vivier val = dev->rom_rev; 52fa2ba3b8SLaurent Vivier break; 53fa2ba3b8SLaurent Vivier case FBLOCK_CRC...FBLOCK_CRC + 3: 54fa2ba3b8SLaurent Vivier val = BYTE(dev->rom_crc, addr - FBLOCK_CRC); 55fa2ba3b8SLaurent Vivier break; 56fa2ba3b8SLaurent Vivier case FBLOCK_LENGTH...FBLOCK_LENGTH + 3: 57fa2ba3b8SLaurent Vivier val = BYTE(dev->rom_length, addr - FBLOCK_LENGTH); 58fa2ba3b8SLaurent Vivier break; 59fa2ba3b8SLaurent Vivier case FBLOCK_DIRECTORY_OFFSET...FBLOCK_DIRECTORY_OFFSET + 3: 60fa2ba3b8SLaurent Vivier val = BYTE(dev->directory_offset, addr - FBLOCK_DIRECTORY_OFFSET); 61fa2ba3b8SLaurent Vivier break; 62fa2ba3b8SLaurent Vivier default: 63fa2ba3b8SLaurent Vivier val = 0; 64fa2ba3b8SLaurent Vivier break; 65fa2ba3b8SLaurent Vivier } 66fa2ba3b8SLaurent Vivier return val; 67fa2ba3b8SLaurent Vivier } 68fa2ba3b8SLaurent Vivier 69fa2ba3b8SLaurent Vivier static void nubus_fblock_write(void *opaque, hwaddr addr, uint64_t val, 70fa2ba3b8SLaurent Vivier unsigned int size) 71fa2ba3b8SLaurent Vivier { 72fa2ba3b8SLaurent Vivier /* read only */ 73fa2ba3b8SLaurent Vivier } 74fa2ba3b8SLaurent Vivier 75fa2ba3b8SLaurent Vivier static const MemoryRegionOps nubus_format_block_ops = { 76fa2ba3b8SLaurent Vivier .read = nubus_fblock_read, 77fa2ba3b8SLaurent Vivier .write = nubus_fblock_write, 78fa2ba3b8SLaurent Vivier .endianness = DEVICE_BIG_ENDIAN, 79fa2ba3b8SLaurent Vivier .valid = { 80fa2ba3b8SLaurent Vivier .min_access_size = 1, 81fa2ba3b8SLaurent Vivier .max_access_size = 1, 82fa2ba3b8SLaurent Vivier } 83fa2ba3b8SLaurent Vivier }; 84fa2ba3b8SLaurent Vivier 85fa2ba3b8SLaurent Vivier static void nubus_register_format_block(NubusDevice *dev) 86fa2ba3b8SLaurent Vivier { 87fa2ba3b8SLaurent Vivier char *fblock_name; 88fa2ba3b8SLaurent Vivier 89fa2ba3b8SLaurent Vivier fblock_name = g_strdup_printf("nubus-slot-%d-format-block", 90fa2ba3b8SLaurent Vivier dev->slot_nb); 91fa2ba3b8SLaurent Vivier 92fa2ba3b8SLaurent Vivier hwaddr fblock_offset = memory_region_size(&dev->slot_mem) - FBLOCK_SIZE; 93fa2ba3b8SLaurent Vivier memory_region_init_io(&dev->fblock_io, NULL, &nubus_format_block_ops, 94fa2ba3b8SLaurent Vivier dev, fblock_name, FBLOCK_SIZE); 95fa2ba3b8SLaurent Vivier memory_region_add_subregion(&dev->slot_mem, fblock_offset, 96fa2ba3b8SLaurent Vivier &dev->fblock_io); 97fa2ba3b8SLaurent Vivier 98fa2ba3b8SLaurent Vivier g_free(fblock_name); 99fa2ba3b8SLaurent Vivier } 100fa2ba3b8SLaurent Vivier 101fa2ba3b8SLaurent Vivier static void mac_nubus_rom_write(void *opaque, hwaddr addr, uint64_t val, 102fa2ba3b8SLaurent Vivier unsigned int size) 103fa2ba3b8SLaurent Vivier { 104fa2ba3b8SLaurent Vivier /* read only */ 105fa2ba3b8SLaurent Vivier } 106fa2ba3b8SLaurent Vivier 107fa2ba3b8SLaurent Vivier static uint64_t mac_nubus_rom_read(void *opaque, hwaddr addr, 108fa2ba3b8SLaurent Vivier unsigned int size) 109fa2ba3b8SLaurent Vivier { 110fa2ba3b8SLaurent Vivier NubusDevice *dev = opaque; 111fa2ba3b8SLaurent Vivier 112fa2ba3b8SLaurent Vivier return dev->rom[addr]; 113fa2ba3b8SLaurent Vivier } 114fa2ba3b8SLaurent Vivier 115fa2ba3b8SLaurent Vivier static const MemoryRegionOps mac_nubus_rom_ops = { 116fa2ba3b8SLaurent Vivier .read = mac_nubus_rom_read, 117fa2ba3b8SLaurent Vivier .write = mac_nubus_rom_write, 118fa2ba3b8SLaurent Vivier .endianness = DEVICE_BIG_ENDIAN, 119fa2ba3b8SLaurent Vivier .valid = { 120fa2ba3b8SLaurent Vivier .min_access_size = 1, 121fa2ba3b8SLaurent Vivier .max_access_size = 1, 122fa2ba3b8SLaurent Vivier }, 123fa2ba3b8SLaurent Vivier }; 124fa2ba3b8SLaurent Vivier 125fa2ba3b8SLaurent Vivier 126fa2ba3b8SLaurent Vivier void nubus_register_rom(NubusDevice *dev, const uint8_t *rom, uint32_t size, 127fa2ba3b8SLaurent Vivier int revision, int format, uint8_t byte_lanes) 128fa2ba3b8SLaurent Vivier { 129fa2ba3b8SLaurent Vivier hwaddr rom_offset; 130fa2ba3b8SLaurent Vivier char *rom_name; 131fa2ba3b8SLaurent Vivier 132fa2ba3b8SLaurent Vivier /* FIXME : really compute CRC */ 133fa2ba3b8SLaurent Vivier dev->rom_length = 0; 134fa2ba3b8SLaurent Vivier dev->rom_crc = 0; 135fa2ba3b8SLaurent Vivier 136fa2ba3b8SLaurent Vivier dev->rom_rev = revision; 137fa2ba3b8SLaurent Vivier dev->rom_format = format; 138fa2ba3b8SLaurent Vivier 139fa2ba3b8SLaurent Vivier dev->byte_lanes = byte_lanes; 140fa2ba3b8SLaurent Vivier dev->directory_offset = -size; 141fa2ba3b8SLaurent Vivier 142fa2ba3b8SLaurent Vivier /* ROM */ 143fa2ba3b8SLaurent Vivier 144fa2ba3b8SLaurent Vivier dev->rom = rom; 145fa2ba3b8SLaurent Vivier rom_name = g_strdup_printf("nubus-slot-%d-rom", dev->slot_nb); 146fa2ba3b8SLaurent Vivier memory_region_init_io(&dev->rom_io, NULL, &mac_nubus_rom_ops, 147fa2ba3b8SLaurent Vivier dev, rom_name, size); 148fa2ba3b8SLaurent Vivier memory_region_set_readonly(&dev->rom_io, true); 149fa2ba3b8SLaurent Vivier 150fa2ba3b8SLaurent Vivier rom_offset = memory_region_size(&dev->slot_mem) - FBLOCK_SIZE + 151fa2ba3b8SLaurent Vivier dev->directory_offset; 152fa2ba3b8SLaurent Vivier memory_region_add_subregion(&dev->slot_mem, rom_offset, &dev->rom_io); 153fa2ba3b8SLaurent Vivier 154fa2ba3b8SLaurent Vivier g_free(rom_name); 155fa2ba3b8SLaurent Vivier } 156fa2ba3b8SLaurent Vivier 157fa2ba3b8SLaurent Vivier static void nubus_device_realize(DeviceState *dev, Error **errp) 158fa2ba3b8SLaurent Vivier { 159*8e5c952bSPhilippe Mathieu-Daudé NubusBus *nubus = NUBUS_BUS(qdev_get_parent_bus(dev)); 160fa2ba3b8SLaurent Vivier NubusDevice *nd = NUBUS_DEVICE(dev); 161fa2ba3b8SLaurent Vivier char *name; 162fa2ba3b8SLaurent Vivier hwaddr slot_offset; 163fa2ba3b8SLaurent Vivier 164fa2ba3b8SLaurent Vivier if (nubus->current_slot < NUBUS_FIRST_SLOT || 165fa2ba3b8SLaurent Vivier nubus->current_slot > NUBUS_LAST_SLOT) { 166fa2ba3b8SLaurent Vivier error_setg(errp, "Cannot register nubus card, not enough slots"); 167fa2ba3b8SLaurent Vivier return; 168fa2ba3b8SLaurent Vivier } 169fa2ba3b8SLaurent Vivier 170fa2ba3b8SLaurent Vivier nd->slot_nb = nubus->current_slot++; 171fa2ba3b8SLaurent Vivier name = g_strdup_printf("nubus-slot-%d", nd->slot_nb); 172fa2ba3b8SLaurent Vivier 173fa2ba3b8SLaurent Vivier if (nd->slot_nb < NUBUS_FIRST_SLOT) { 174fa2ba3b8SLaurent Vivier /* Super */ 175fa2ba3b8SLaurent Vivier slot_offset = (nd->slot_nb - 6) * NUBUS_SUPER_SLOT_SIZE; 176fa2ba3b8SLaurent Vivier 177fa2ba3b8SLaurent Vivier memory_region_init(&nd->slot_mem, OBJECT(dev), name, 178fa2ba3b8SLaurent Vivier NUBUS_SUPER_SLOT_SIZE); 179fa2ba3b8SLaurent Vivier memory_region_add_subregion(&nubus->super_slot_io, slot_offset, 180fa2ba3b8SLaurent Vivier &nd->slot_mem); 181fa2ba3b8SLaurent Vivier } else { 182fa2ba3b8SLaurent Vivier /* Normal */ 183fa2ba3b8SLaurent Vivier slot_offset = nd->slot_nb * NUBUS_SLOT_SIZE; 184fa2ba3b8SLaurent Vivier 185fa2ba3b8SLaurent Vivier memory_region_init(&nd->slot_mem, OBJECT(dev), name, NUBUS_SLOT_SIZE); 186fa2ba3b8SLaurent Vivier memory_region_add_subregion(&nubus->slot_io, slot_offset, 187fa2ba3b8SLaurent Vivier &nd->slot_mem); 188fa2ba3b8SLaurent Vivier } 189fa2ba3b8SLaurent Vivier 190fa2ba3b8SLaurent Vivier g_free(name); 191fa2ba3b8SLaurent Vivier nubus_register_format_block(nd); 192fa2ba3b8SLaurent Vivier } 193fa2ba3b8SLaurent Vivier 194fa2ba3b8SLaurent Vivier static void nubus_device_class_init(ObjectClass *oc, void *data) 195fa2ba3b8SLaurent Vivier { 196fa2ba3b8SLaurent Vivier DeviceClass *dc = DEVICE_CLASS(oc); 197fa2ba3b8SLaurent Vivier 198fa2ba3b8SLaurent Vivier dc->realize = nubus_device_realize; 199fa2ba3b8SLaurent Vivier dc->bus_type = TYPE_NUBUS_BUS; 200fa2ba3b8SLaurent Vivier } 201fa2ba3b8SLaurent Vivier 202fa2ba3b8SLaurent Vivier static const TypeInfo nubus_device_type_info = { 203fa2ba3b8SLaurent Vivier .name = TYPE_NUBUS_DEVICE, 204fa2ba3b8SLaurent Vivier .parent = TYPE_DEVICE, 205fa2ba3b8SLaurent Vivier .abstract = true, 206fa2ba3b8SLaurent Vivier .instance_size = sizeof(NubusDevice), 207fa2ba3b8SLaurent Vivier .class_init = nubus_device_class_init, 208fa2ba3b8SLaurent Vivier }; 209fa2ba3b8SLaurent Vivier 210fa2ba3b8SLaurent Vivier static void nubus_register_types(void) 211fa2ba3b8SLaurent Vivier { 212fa2ba3b8SLaurent Vivier type_register_static(&nubus_device_type_info); 213fa2ba3b8SLaurent Vivier } 214fa2ba3b8SLaurent Vivier 215fa2ba3b8SLaurent Vivier type_init(nubus_register_types) 216