├── README.md ├── driver_pci.c ├── driver_platform.c ├── hello_tic.c ├── hello_tic_sysbus.c ├── tic3.dts └── versatilepb.c /README.md: -------------------------------------------------------------------------------- 1 | # qemu_devices 2 | 3 | ## Hello World PCI device for qemu 4 | 5 | ### List of supported features the device emulate: 6 | 7 | - MMIO 8 | - PIO 9 | - IRQ 10 | - DMA without IOMMU (in vga space, just for the visual effect ^^) 11 | 12 | Hello World PCI driver for it. (linux 3.2, with little tweek it should work with 2.6 too (I use devm framework in some place)) 13 | 14 | ### install 15 | 16 | As this repo just contains the files for the device, you need to integrate it in Qemu. 17 | To do that: 18 | copy hello_tic.c in hw/char for example and add it to the Makefile too then make 19 | 20 | For the driver, you need to compile it with you kernel of choice 21 | insmoding the driver will test the device 22 | 23 | ### Qemu cmdline: 24 | 25 | i386-softmmu/qemu-system-i386 yourimg.qcow2 -device pci-hellodev --enable-kvm -monitor stdio 26 | 27 | ## Hello World Sysbus device 28 | 29 | Notice: Use this only as inspiration to create your own device. 30 | You should use the device tree way in qemu-arm and not the verstatilepb platform 31 | 32 | (for those who don't fear atrocious install process) 33 | 34 | - copy versatilepb in qemu/hw/arm 35 | - get buildroot 36 | - use dts and buildroot configs from this git repository 37 | - build the arm system 38 | - Buildroot will create a cross compilation toolchain in output/host/usr/bin 39 | - set ARCH=arm and CROSS_COMPILE=path-to-toolchain-prefix 40 | - cross compile the kernel module driver_platform for arm 41 | - cp it inside output/target (which will be shipped as the rootfs at the end of the build) 42 | - make again 43 | - in order to get the device tree blob you can do dtc -O dtb -I dts -o tic.dtb tic2.dts 44 | - /path_to_your_qemu/arm-softmmu/qemu-system-arm -M versatilepb -kernel output/images/zImage -append "root=/dev/ram console=ttyAMA0,115200 earlyprintk" -nographic -dtb tic.dtb 45 | 46 | versatilepb seems to use a deprecated api for the registration of the device, and we can't use the -device switch, this isn't good, as we have to add specific code inside the board to wire our device inside instead of dynamic mapping 47 | 48 | -------------------------------------------------------------------------------- /driver_pci.c: -------------------------------------------------------------------------------- 1 | /* PCI driver hello world 2 | * Copyright (C) 2015 Kevin Grandemange 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #define PCI_TIC_VENDOR 0x1337 30 | #define PCI_TIC_DEVICE 0x0001 31 | #define PCI_TIC_SUBVENDOR 0x1af4 32 | #define PCI_TIC_SUBDEVICE 0x1100 33 | 34 | #define MAX_TIC_MAPS 1 35 | #define MAX_TIC_PORT_REGIONS 1 36 | static struct pci_driver hello_tic; 37 | struct tic_mem { 38 | const char *name; 39 | void __iomem *start; 40 | unsigned long size; 41 | }; 42 | 43 | struct tic_io { 44 | const char *name; 45 | unsigned long start; 46 | unsigned long size; 47 | }; 48 | 49 | struct tic_info { 50 | struct tic_mem mem[MAX_TIC_MAPS]; 51 | struct tic_io port[MAX_TIC_PORT_REGIONS]; 52 | u8 irq; 53 | }; 54 | 55 | static irqreturn_t hello_tic_handler(int irq, void *dev_info) 56 | { 57 | struct tic_info *info = (struct tic_info *) dev_info; 58 | pr_alert("IRQ %d handled\n", irq); 59 | /* is it our device throwing an interrupt ? */ 60 | if (inl(info->port[0].start)) { 61 | /* deassert it */ 62 | outl(0, info->port[0].start ); 63 | return IRQ_HANDLED; 64 | } else { 65 | /* not mine, we have done nothing */ 66 | return IRQ_NONE; 67 | } 68 | } 69 | 70 | 71 | static int hello_tic_probe(struct pci_dev *dev, const struct pci_device_id *id) 72 | { 73 | struct tic_info *info; 74 | info = kzalloc(sizeof(struct tic_info), GFP_KERNEL); 75 | if (!info) 76 | return -ENOMEM; 77 | 78 | if (pci_enable_device(dev)) 79 | goto out_free; 80 | 81 | pr_alert("enabled device\n"); 82 | 83 | if (pci_request_regions(dev, "hello_tic")) 84 | goto out_disable; 85 | 86 | pr_alert("requested regions\n"); 87 | 88 | /* BAR 0 has IO */ 89 | info->port[0].name = "tic-io"; 90 | info->port[0].start = pci_resource_start(dev, 0); 91 | info->port[0].size = pci_resource_len(dev, 0); 92 | 93 | /* BAR 1 has MMIO */ 94 | info->mem[0].name = "tic-mmio"; 95 | info->mem[0].start = pci_ioremap_bar(dev, 1); 96 | info->mem[0].size = pci_resource_len(dev, 1); 97 | if (!info->mem[0].start) 98 | goto out_unrequest; 99 | 100 | pr_alert("remaped addr for kernel uses\n"); 101 | 102 | 103 | 104 | /* get device irq number */ 105 | if (pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &info->irq)) 106 | goto out_iounmap; 107 | 108 | /* request irq */ 109 | if (devm_request_irq(&dev->dev, info->irq, hello_tic_handler, IRQF_SHARED, hello_tic.name, (void *) info)) 110 | goto out_iounmap; 111 | 112 | /* get a mmio reg value and change it */ 113 | pr_alert("device id=%x\n", ioread32(info->mem[0].start + 4)); 114 | iowrite32(0x4567, info->mem[0].start + 4); 115 | pr_alert("modified device id=%x\n", ioread32(info->mem[0].start + 4)); 116 | 117 | /* assert an irq */ 118 | outb(1, info->port[0].start); 119 | /* try dma without iommu */ 120 | outl(1, info->port[0].start + 4); 121 | 122 | pci_set_drvdata(dev, info); 123 | return 0; 124 | 125 | out_iounmap: 126 | pr_alert("tic:probe_out:iounmap"); 127 | iounmap(info->mem[0].start); 128 | out_unrequest: 129 | pr_alert("tic:probe_out_unrequest\n"); 130 | pci_release_regions(dev); 131 | out_disable: 132 | pr_alert("tic:probe_out_disable\n"); 133 | pci_disable_device(dev); 134 | out_free: 135 | pr_alert("tic:probe_out_free\n"); 136 | kfree(info); 137 | return -ENODEV; 138 | } 139 | 140 | static void hello_tic_remove(struct pci_dev *dev) 141 | { 142 | /* we get back the driver data we store in 143 | * the pci_dev struct */ 144 | struct tic_info *info = pci_get_drvdata(dev); 145 | 146 | /* let's clean a little */ 147 | pci_release_regions(dev); 148 | pci_disable_device(dev); 149 | iounmap(info->mem[0].start); 150 | 151 | kfree(info); 152 | 153 | } 154 | 155 | 156 | /* vendor and device (+ subdevice and subvendor) 157 | * identifies a device we support 158 | */ 159 | static struct pci_device_id hello_tic_ids[] = { 160 | 161 | { PCI_DEVICE(PCI_TIC_VENDOR, PCI_TIC_DEVICE) }, 162 | { 0, }, 163 | }; 164 | 165 | /* id_table describe the device this driver support 166 | * probe is called when a device we support exist and 167 | * when we are chosen to drive it. 168 | * remove is called when the driver is unloaded or 169 | * when the device disappears 170 | */ 171 | static struct pci_driver hello_tic = { 172 | .name = "hello_tic", 173 | .id_table = hello_tic_ids, 174 | .probe = hello_tic_probe, 175 | .remove = hello_tic_remove, 176 | }; 177 | 178 | /* register driver in kernel pci framework */ 179 | module_pci_driver(hello_tic); 180 | MODULE_DEVICE_TABLE(pci, hello_tic_ids); 181 | 182 | MODULE_LICENSE("GPL"); 183 | MODULE_DESCRIPTION("hello world"); 184 | MODULE_AUTHOR("Kevin Grandemange"); 185 | -------------------------------------------------------------------------------- /driver_platform.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | 19 | 20 | struct dev{ 21 | void __iomem *regs; 22 | }; 23 | 24 | static struct dev test_dev; 25 | 26 | static irqreturn_t test_interrupt(int irq, void *data) 27 | { 28 | pr_alert("SYSBUS IRQ\n"); 29 | /* do irq stuff */ 30 | return IRQ_HANDLED; 31 | } 32 | 33 | static int test_probe(struct platform_device *pdev) 34 | { 35 | struct dev *tdev = &test_dev; 36 | int err, irq; 37 | struct resource *res; 38 | pr_alert("SYSBUS PROBE\n"); 39 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 40 | tdev->regs = devm_ioremap_resource(&pdev->dev, res); 41 | 42 | if (IS_ERR(tdev->regs)) 43 | return PTR_ERR(tdev->regs); 44 | irq = platform_get_irq(pdev, 0); 45 | pr_alert("irq=%d\n", irq); 46 | if (irq < 0) { 47 | dev_err(&pdev->dev, "missing irq\n"); 48 | err = irq; 49 | return err; 50 | } 51 | pr_alert("requesting irq\n"); 52 | err = devm_request_irq(&pdev->dev, irq, test_interrupt, IRQF_SHARED, dev_name(&pdev->dev),tdev); 53 | if (err) { 54 | dev_err(&pdev->dev, "failed to request IRQ #%d -> :%d\n", 55 | irq, err); 56 | return err; 57 | } 58 | pr_alert("requested irq\n"); 59 | 60 | iowrite32(34, tdev->regs); 61 | pr_alert("probe read: %d\n", ioread32(tdev->regs)); 62 | 63 | 64 | return 0; 65 | } 66 | 67 | static int test_remove(struct platform_device *pdev) 68 | { 69 | pr_alert("SYSBUS REMOVE\n"); 70 | return 0; 71 | } 72 | 73 | static const struct of_device_id test_of_match[] = { 74 | { .compatible = "tic_test"}, 75 | {} 76 | }; 77 | MODULE_DEVICE_TABLE(of, test_of_match); 78 | 79 | static struct platform_driver test_driver = { 80 | .remove = test_remove, 81 | .probe = test_probe, 82 | .driver = { 83 | .name = "test", 84 | .of_match_table = test_of_match, 85 | }, 86 | }; 87 | 88 | module_platform_driver(test_driver); 89 | 90 | MODULE_LICENSE("GPL v2"); 91 | MODULE_AUTHOR("Kevin Grandemange"); 92 | MODULE_DESCRIPTION("Test driver"); 93 | -------------------------------------------------------------------------------- /hello_tic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PCI Device HelloWorld 3 | * Copyright (C) 2015 Kevin Grandemange 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program. If not, see . 15 | */ 16 | 17 | 18 | 19 | #include "hw/hw.h" 20 | #include "hw/pci/pci.h" 21 | #include "qemu/event_notifier.h" 22 | #include 23 | #include "qemu/osdep.h" 24 | 25 | typedef struct PCIHelloDevState { 26 | PCIDevice parent_obj; 27 | 28 | /* for PIO */ 29 | MemoryRegion io; 30 | /* for MMIO */ 31 | MemoryRegion mmio; 32 | /* irq used */ 33 | qemu_irq irq; 34 | /* dma buf size */ 35 | unsigned int dma_size; 36 | /* buffer copied with the dma operation on RAM */ 37 | char *dma_buf; 38 | /* did we throw an interrupt ? */ 39 | int threw_irq; 40 | /* id of the device, writable */ 41 | int id; 42 | 43 | 44 | } PCIHelloDevState; 45 | 46 | #define TYPE_PCI_HELLO_DEV "pci-hellodev" 47 | #define PCI_HELLO_DEV(obj) OBJECT_CHECK(PCIHelloDevState, (obj), TYPE_PCI_HELLO_DEV) 48 | /* sizes must be power of 2 in PCI */ 49 | #define HELLO_IO_SIZE 1<<4 50 | #define HELLO_MMIO_SIZE 1<<6 51 | 52 | 53 | static void hello_iowrite(void *opaque, hwaddr addr, uint64_t value, unsigned size) 54 | { 55 | int i; 56 | PCIHelloDevState *d = (PCIHelloDevState *) opaque; 57 | PCIDevice *pci_dev = (PCIDevice *) opaque; 58 | 59 | printf("Write Ordered, addr=%x, value=%lu, size=%d\n", (unsigned) addr, value, size); 60 | 61 | switch (addr) { 62 | case 0: 63 | if (value) { 64 | /* throw an interrupt */ 65 | printf("irq assert\n"); 66 | d->threw_irq = 1; 67 | pci_irq_assert(pci_dev); 68 | 69 | } else { 70 | /* ack interrupt */ 71 | printf("irq deassert\n"); 72 | pci_irq_deassert(pci_dev); 73 | d->threw_irq = 0; 74 | } 75 | break; 76 | case 4: 77 | /* throw a random DMA */ 78 | for ( i = 0; i < d->dma_size; ++i) 79 | d->dma_buf[i] = rand(); 80 | cpu_physical_memory_write(value, (void *) d->dma_buf, d->dma_size); 81 | break; 82 | default: 83 | printf("Io not used\n"); 84 | 85 | } 86 | 87 | } 88 | 89 | static uint64_t hello_ioread(void *opaque, hwaddr addr, unsigned size) 90 | { 91 | PCIHelloDevState *d = (PCIHelloDevState *) opaque; 92 | printf("Read Ordered, addr =%x, size=%d\n", (unsigned) addr, size); 93 | 94 | switch (addr) { 95 | case 0: 96 | /* irq status */ 97 | return d->threw_irq; 98 | break; 99 | default: 100 | printf("Io not used\n"); 101 | return 0x0; 102 | 103 | } 104 | } 105 | 106 | static uint64_t hello_mmioread(void *opaque, hwaddr addr, unsigned size) 107 | { 108 | PCIHelloDevState *d = (PCIHelloDevState *) opaque; 109 | printf("MMIO Read Ordered, addr =%x, size=%d\n",(unsigned) addr, size); 110 | 111 | switch (addr) { 112 | case 0: 113 | /* also irq status */ 114 | printf("irq_status\n"); 115 | return d->threw_irq; 116 | break; 117 | case 4: 118 | /* Id of the device */ 119 | printf("id\n"); 120 | return d->id; 121 | break; 122 | default: 123 | printf("MMIO not used\n"); 124 | return 0x0; 125 | 126 | } 127 | } 128 | 129 | static void hello_mmiowrite(void *opaque, hwaddr addr, uint64_t value, unsigned size) 130 | { 131 | PCIHelloDevState *d = (PCIHelloDevState *) opaque; 132 | printf("MMIO write Ordered, addr=%x, value=%lu, size=%d\n",(unsigned) addr, value, size); 133 | 134 | switch (addr) { 135 | case 4: 136 | /* change the id */ 137 | d->id = value; 138 | break; 139 | default: 140 | printf("MMIO not writable or not used\n"); 141 | 142 | } 143 | } 144 | 145 | 146 | 147 | /* 148 | * Callbacks called when the Memory Region 149 | * representing the MMIO space is 150 | * accessed. 151 | */ 152 | static const MemoryRegionOps hello_mmio_ops = { 153 | .read = hello_mmioread, 154 | .write = hello_mmiowrite, 155 | .endianness = DEVICE_NATIVE_ENDIAN, 156 | .valid = { 157 | .min_access_size = 4, 158 | .max_access_size = 4, 159 | }, 160 | }; 161 | 162 | /* 163 | * Callbacks called when the Memory Region 164 | * representing the PIO space is 165 | * accessed. 166 | */ 167 | static const MemoryRegionOps hello_io_ops = { 168 | .read = hello_ioread, 169 | .write = hello_iowrite, 170 | .endianness = DEVICE_NATIVE_ENDIAN, 171 | .valid = { 172 | .min_access_size = 4, 173 | .max_access_size = 4, 174 | }, 175 | }; 176 | 177 | /* Callbacks for MMIO and PIO regions are registered here */ 178 | static void hello_io_setup(PCIHelloDevState *d) 179 | { 180 | memory_region_init_io(&d->mmio, OBJECT(d), &hello_mmio_ops, d, "hello_mmio", HELLO_MMIO_SIZE); 181 | memory_region_init_io(&d->io, OBJECT(d), &hello_io_ops, d, "hello_io", HELLO_IO_SIZE); 182 | } 183 | 184 | /* When device is loaded */ 185 | static int pci_hellodev_init(PCIDevice *pci_dev) 186 | { 187 | /* init the internal state of the device */ 188 | PCIHelloDevState *d = PCI_HELLO_DEV(pci_dev); 189 | printf("d=%lu\n", (unsigned long) &d); 190 | d->dma_size = 0x1ffff * sizeof(char); 191 | d->dma_buf = malloc(d->dma_size); 192 | d->id = 0x1337; 193 | d->threw_irq = 0; 194 | uint8_t *pci_conf; 195 | 196 | /* create the memory region representing the MMIO and PIO 197 | * of the device 198 | */ 199 | hello_io_setup(d); 200 | /* 201 | * See linux device driver (Edition 3) for the definition of a bar 202 | * in the PCI bus. 203 | */ 204 | pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->io); 205 | pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); 206 | 207 | pci_conf = pci_dev->config; 208 | /* also in ldd, a pci device has 4 pin for interrupt 209 | * here we use pin B. 210 | */ 211 | pci_conf[PCI_INTERRUPT_PIN] = 0x02; 212 | 213 | /* this device support interrupt */ 214 | //d->irq = pci_allocate_irq(pci_dev); 215 | 216 | printf("Hello World loaded\n"); 217 | return 0; 218 | } 219 | 220 | /* When device is unloaded 221 | * Can be useful for hot(un)plugging 222 | */ 223 | static void pci_hellodev_uninit(PCIDevice *dev) 224 | { 225 | PCIHelloDevState *d = (PCIHelloDevState *) dev; 226 | free(d->dma_buf); 227 | printf("Good bye World unloaded\n"); 228 | } 229 | 230 | static void qdev_pci_hellodev_reset(DeviceState *dev) 231 | { 232 | printf("Reset World\n"); 233 | } 234 | 235 | /* 236 | * TODO 237 | */ 238 | static Property hello_properties[] = { 239 | 240 | DEFINE_PROP_END_OF_LIST(), 241 | }; 242 | 243 | /* Called when the device is defined 244 | * PCI configuration is defined here 245 | * We inherit from PCIDeviceClass 246 | * Also see ldd for the meaning of the different args 247 | */ 248 | static void pci_hellodev_class_init(ObjectClass *klass, void *data) 249 | { 250 | DeviceClass *dc = DEVICE_CLASS(klass); 251 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); 252 | k->init = pci_hellodev_init; 253 | k->exit = pci_hellodev_uninit; 254 | /* this identify our device */ 255 | k->vendor_id = 0x1337; 256 | k->device_id = 0x0001; 257 | k->class_id = PCI_CLASS_OTHERS; 258 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); 259 | 260 | k->revision = 0x00; 261 | dc->desc = "PCI Hello World"; 262 | /* qemu user things */ 263 | dc->props = hello_properties; 264 | dc->reset = qdev_pci_hellodev_reset; 265 | } 266 | 267 | /* Contains all the informations of the device 268 | * we are creating. 269 | * class_init will be called when we are defining 270 | * our device. 271 | */ 272 | static const TypeInfo pci_hello_info = { 273 | .name = TYPE_PCI_HELLO_DEV, 274 | .parent = TYPE_PCI_DEVICE, 275 | .instance_size = sizeof(PCIHelloDevState), 276 | .class_init = pci_hellodev_class_init, 277 | }; 278 | 279 | /* function called before the qemu main 280 | * it will define our device 281 | */ 282 | static void pci_hello_register_types(void) 283 | { 284 | type_register_static(&pci_hello_info); 285 | } 286 | 287 | /* macro actually defining our device and registering it in qemu*/ 288 | type_init(pci_hello_register_types); 289 | -------------------------------------------------------------------------------- /hello_tic_sysbus.c: -------------------------------------------------------------------------------- 1 | /* HelloWorld sysbus device 2 | * Copyright (C) 2015 Kevin Grandemange 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | 19 | #include 20 | #include 21 | #include 22 | #define TYPE_DUMMY "test_tic" 23 | #define DUMMY_SIZE (1 << 10) 24 | #define DUMMY(obj)\ 25 | OBJECT_CHECK(DummyState, (obj), TYPE_DUMMY) 26 | 27 | 28 | typedef struct { 29 | SysBusDevice parent_obj; 30 | MemoryRegion iomem; 31 | qemu_irq irq; 32 | 33 | } DummyState; 34 | 35 | static void dummy_reset(DeviceState *d) 36 | { 37 | return; 38 | } 39 | 40 | static void dummy_write(void *opaque, hwaddr addr, 41 | uint64_t val, unsigned size) 42 | { 43 | 44 | DummyState *dummy = DUMMY(opaque); 45 | printf("DUMMY. MMIO write Ordered, addr=%x, value=%lu, size=%d\n",(unsigned) addr, val, size); 46 | /* let's say writing somewhere trigger a pulse irq */ 47 | printf("pulsing irq\n"); 48 | qemu_irq_pulse(dummy->irq); 49 | printf("irq up\n"); 50 | qemu_irq_raise(dummy->irq); 51 | 52 | } 53 | 54 | static uint64_t dummy_read(void *opaque, hwaddr addr, 55 | unsigned size) 56 | { 57 | DummyState *dummy = DUMMY(opaque); 58 | printf("DUMMY. MMIO Read Ordered, addr =%x, size=%d\n",(unsigned) addr, size); 59 | printf("irq down\n"); 60 | qemu_irq_lower(dummy->irq); 61 | return 0; 62 | } 63 | 64 | 65 | static const MemoryRegionOps dummy_mem_ops = { 66 | .read = dummy_read, 67 | .write = dummy_write, 68 | .endianness = DEVICE_NATIVE_ENDIAN, 69 | }; 70 | 71 | /* for migration */ 72 | static const VMStateDescription dummy_vmstate = { 73 | .name = "test_tic", 74 | .version_id = 1, 75 | .minimum_version_id = 1, 76 | .fields = (VMStateField[]) { 77 | VMSTATE_END_OF_LIST(), 78 | } 79 | }; 80 | 81 | static void dummy_init(Object *obj) 82 | { 83 | SysBusDevice *dev = SYS_BUS_DEVICE(obj); 84 | DummyState *s = DUMMY(obj); 85 | fprintf(stderr, "INIT\n"); 86 | memory_region_init_io(&s->iomem, OBJECT(s), &dummy_mem_ops, s, "dummy", 87 | DUMMY_SIZE); 88 | sysbus_init_mmio(dev, &s->iomem); 89 | sysbus_init_irq(dev, &s->irq); 90 | 91 | } 92 | 93 | 94 | static Property dummy_properties[] = { 95 | DEFINE_PROP_END_OF_LIST(), 96 | }; 97 | 98 | 99 | static void dummy_realize(DeviceState *dev, Error **errp) 100 | { 101 | printf("Do you realize ?\n"); 102 | } 103 | 104 | static void dummy_class_init(ObjectClass *klass, void *data) 105 | { 106 | DeviceClass *dc = DEVICE_CLASS(klass); 107 | 108 | dc->realize = dummy_realize; 109 | dc->reset = dummy_reset; 110 | dc->vmsd = &dummy_vmstate; 111 | dc->props = dummy_properties; 112 | 113 | } 114 | 115 | 116 | 117 | static const TypeInfo dummy_info = { 118 | .name = TYPE_DUMMY, 119 | .parent = TYPE_SYS_BUS_DEVICE, 120 | .instance_size = sizeof(DummyState), 121 | .instance_init = dummy_init, 122 | .class_init = dummy_class_init, 123 | 124 | }; 125 | 126 | 127 | static void dummy_register_types(void) 128 | { 129 | type_register_static(&dummy_info); 130 | } 131 | 132 | 133 | type_init(dummy_register_types); 134 | -------------------------------------------------------------------------------- /tic3.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | /* 3 | * Skeleton device tree; the bare minimum needed to boot; just include and 4 | * add a compatible value. The bootloader will typically populate the memory 5 | * node. 6 | */ 7 | 8 | / { 9 | #address-cells = <1>; 10 | #size-cells = <1>; 11 | chosen { }; 12 | aliases { }; 13 | memory { device_type = "memory"; reg = <0 0>; }; 14 | }; 15 | 16 | / { 17 | model = "ARM Versatile AB"; 18 | compatible = "arm,versatile-ab"; 19 | #address-cells = <1>; 20 | #size-cells = <1>; 21 | interrupt-parent = <&vic>; 22 | 23 | aliases { 24 | serial0 = &uart0; 25 | serial1 = &uart1; 26 | serial2 = &uart2; 27 | i2c0 = &i2c0; 28 | }; 29 | 30 | chosen { 31 | stdout-path = &uart0; 32 | }; 33 | 34 | memory { 35 | reg = <0x0 0x08000000>; 36 | }; 37 | 38 | xtal24mhz: xtal24mhz@24M { 39 | #clock-cells = <0>; 40 | compatible = "fixed-clock"; 41 | clock-frequency = <24000000>; 42 | }; 43 | 44 | core-module@10000000 { 45 | compatible = "arm,core-module-versatile", "syscon"; 46 | reg = <0x10000000 0x200>; 47 | 48 | /* OSC1 on AB, OSC4 on PB */ 49 | osc1: cm_aux_osc@24M { 50 | #clock-cells = <0>; 51 | compatible = "arm,versatile-cm-auxosc"; 52 | clocks = <&xtal24mhz>; 53 | }; 54 | 55 | /* The timer clock is the 24 MHz oscillator divided to 1MHz */ 56 | timclk: timclk@1M { 57 | #clock-cells = <0>; 58 | compatible = "fixed-factor-clock"; 59 | clock-div = <24>; 60 | clock-mult = <1>; 61 | clocks = <&xtal24mhz>; 62 | }; 63 | 64 | pclk: pclk@24M { 65 | #clock-cells = <0>; 66 | compatible = "fixed-factor-clock"; 67 | clock-div = <1>; 68 | clock-mult = <1>; 69 | clocks = <&xtal24mhz>; 70 | }; 71 | }; 72 | 73 | flash@34000000 { 74 | compatible = "arm,versatile-flash"; 75 | reg = <0x34000000 0x4000000>; 76 | bank-width = <4>; 77 | }; 78 | 79 | i2c0: i2c@10002000 { 80 | #address-cells = <1>; 81 | #size-cells = <0>; 82 | compatible = "arm,versatile-i2c"; 83 | reg = <0x10002000 0x1000>; 84 | 85 | rtc@68 { 86 | compatible = "dallas,ds1338"; 87 | reg = <0x68>; 88 | }; 89 | }; 90 | 91 | net@10010000 { 92 | compatible = "smsc,lan91c111"; 93 | reg = <0x10010000 0x10000>; 94 | interrupts = <25>; 95 | }; 96 | 97 | lcd@10008000 { 98 | compatible = "arm,versatile-lcd"; 99 | reg = <0x10008000 0x1000>; 100 | }; 101 | 102 | amba { 103 | compatible = "arm,amba-bus"; 104 | #address-cells = <1>; 105 | #size-cells = <1>; 106 | ranges; 107 | 108 | vic: intc@10140000 { 109 | compatible = "arm,versatile-vic"; 110 | interrupt-controller; 111 | #interrupt-cells = <1>; 112 | reg = <0x10140000 0x1000>; 113 | clear-mask = <0xffffffff>; 114 | valid-mask = <0xffffffff>; 115 | }; 116 | 117 | sic: intc@10003000 { 118 | compatible = "arm,versatile-sic"; 119 | interrupt-controller; 120 | #interrupt-cells = <1>; 121 | reg = <0x10003000 0x1000>; 122 | interrupt-parent = <&vic>; 123 | interrupts = <31>; /* Cascaded to vic */ 124 | clear-mask = <0xffffffff>; 125 | valid-mask = <0xffc203f8>; 126 | }; 127 | 128 | dma@10130000 { 129 | compatible = "arm,pl081", "arm,primecell"; 130 | reg = <0x10130000 0x1000>; 131 | interrupts = <17>; 132 | clocks = <&pclk>; 133 | clock-names = "apb_pclk"; 134 | }; 135 | 136 | uart0: uart@101f1000 { 137 | compatible = "arm,pl011", "arm,primecell"; 138 | reg = <0x101f1000 0x1000>; 139 | interrupts = <12>; 140 | clocks = <&xtal24mhz>, <&pclk>; 141 | clock-names = "uartclk", "apb_pclk"; 142 | }; 143 | 144 | uart1: uart@101f2000 { 145 | compatible = "arm,pl011", "arm,primecell"; 146 | reg = <0x101f2000 0x1000>; 147 | interrupts = <13>; 148 | clocks = <&xtal24mhz>, <&pclk>; 149 | clock-names = "uartclk", "apb_pclk"; 150 | }; 151 | 152 | uart2: uart@101f3000 { 153 | compatible = "arm,pl011", "arm,primecell"; 154 | reg = <0x101f3000 0x1000>; 155 | interrupts = <14>; 156 | clocks = <&xtal24mhz>, <&pclk>; 157 | clock-names = "uartclk", "apb_pclk"; 158 | }; 159 | 160 | smc@10100000 { 161 | compatible = "arm,primecell"; 162 | reg = <0x10100000 0x1000>; 163 | clocks = <&pclk>; 164 | clock-names = "apb_pclk"; 165 | }; 166 | 167 | mpmc@10110000 { 168 | compatible = "arm,primecell"; 169 | reg = <0x10110000 0x1000>; 170 | clocks = <&pclk>; 171 | clock-names = "apb_pclk"; 172 | }; 173 | 174 | display@10120000 { 175 | compatible = "arm,pl110", "arm,primecell"; 176 | reg = <0x10120000 0x1000>; 177 | interrupts = <16>; 178 | clocks = <&osc1>, <&pclk>; 179 | clock-names = "clcd", "apb_pclk"; 180 | }; 181 | 182 | sctl@101e0000 { 183 | compatible = "arm,primecell"; 184 | reg = <0x101e0000 0x1000>; 185 | clocks = <&pclk>; 186 | clock-names = "apb_pclk"; 187 | }; 188 | 189 | watchdog@101e1000 { 190 | compatible = "arm,primecell"; 191 | reg = <0x101e1000 0x1000>; 192 | interrupts = <0>; 193 | clocks = <&pclk>; 194 | clock-names = "apb_pclk"; 195 | }; 196 | 197 | timer@101e2000 { 198 | compatible = "arm,sp804", "arm,primecell"; 199 | reg = <0x101e2000 0x1000>; 200 | interrupts = <4>; 201 | clocks = <&timclk>, <&timclk>, <&pclk>; 202 | clock-names = "timer0", "timer1", "apb_pclk"; 203 | }; 204 | 205 | timer@101e3000 { 206 | compatible = "arm,sp804", "arm,primecell"; 207 | reg = <0x101e3000 0x1000>; 208 | interrupts = <5>; 209 | clocks = <&timclk>, <&timclk>, <&pclk>; 210 | clock-names = "timer0", "timer1", "apb_pclk"; 211 | }; 212 | 213 | gpio0: gpio@101e4000 { 214 | compatible = "arm,pl061", "arm,primecell"; 215 | reg = <0x101e4000 0x1000>; 216 | gpio-controller; 217 | interrupts = <6>; 218 | #gpio-cells = <2>; 219 | interrupt-controller; 220 | #interrupt-cells = <2>; 221 | clocks = <&pclk>; 222 | clock-names = "apb_pclk"; 223 | }; 224 | 225 | gpio1: gpio@101e5000 { 226 | compatible = "arm,pl061", "arm,primecell"; 227 | reg = <0x101e5000 0x1000>; 228 | interrupts = <7>; 229 | gpio-controller; 230 | #gpio-cells = <2>; 231 | interrupt-controller; 232 | #interrupt-cells = <2>; 233 | clocks = <&pclk>; 234 | clock-names = "apb_pclk"; 235 | }; 236 | 237 | rtc@101e8000 { 238 | compatible = "arm,pl030", "arm,primecell"; 239 | reg = <0x101e8000 0x1000>; 240 | interrupts = <10>; 241 | clocks = <&pclk>; 242 | clock-names = "apb_pclk"; 243 | }; 244 | 245 | sci@101f0000 { 246 | compatible = "arm,primecell"; 247 | reg = <0x101f0000 0x1000>; 248 | interrupts = <15>; 249 | clocks = <&pclk>; 250 | clock-names = "apb_pclk"; 251 | }; 252 | 253 | ssp@101f4000 { 254 | compatible = "arm,pl022", "arm,primecell"; 255 | reg = <0x101f4000 0x1000>; 256 | interrupts = <11>; 257 | clocks = <&xtal24mhz>, <&pclk>; 258 | clock-names = "SSPCLK", "apb_pclk"; 259 | }; 260 | 261 | fpga { 262 | compatible = "arm,versatile-fpga", "simple-bus"; 263 | #address-cells = <1>; 264 | #size-cells = <1>; 265 | ranges = <0 0x10000000 0x10000>; 266 | 267 | aaci@4000 { 268 | compatible = "arm,primecell"; 269 | reg = <0x4000 0x1000>; 270 | interrupts = <24>; 271 | clocks = <&pclk>; 272 | clock-names = "apb_pclk"; 273 | }; 274 | mmc@5000 { 275 | compatible = "arm,pl180", "arm,primecell"; 276 | reg = < 0x5000 0x1000>; 277 | interrupts-extended = <&vic 22 &sic 2>; 278 | clocks = <&xtal24mhz>, <&pclk>; 279 | clock-names = "mclk", "apb_pclk"; 280 | }; 281 | kmi@6000 { 282 | compatible = "arm,pl050", "arm,primecell"; 283 | reg = <0x6000 0x1000>; 284 | interrupt-parent = <&sic>; 285 | interrupts = <3>; 286 | clocks = <&xtal24mhz>, <&pclk>; 287 | clock-names = "KMIREFCLK", "apb_pclk"; 288 | }; 289 | kmi@7000 { 290 | compatible = "arm,pl050", "arm,primecell"; 291 | reg = <0x7000 0x1000>; 292 | interrupt-parent = <&sic>; 293 | interrupts = <4>; 294 | clocks = <&xtal24mhz>, <&pclk>; 295 | clock-names = "KMIREFCLK", "apb_pclk"; 296 | }; 297 | }; 298 | }; 299 | }; 300 | 301 | / { 302 | model = "ARM Versatile PB"; 303 | compatible = "arm,versatile-pb"; 304 | 305 | amba { 306 | test_tic: allegroIP@101f5000 { 307 | compatible = "test_tic"; 308 | reg = <0x101f5000 0x1000>; 309 | interrupts = <19>; 310 | interrupt-controller; 311 | clocks = <&pclk>; 312 | clock-names = "apb_pclk"; 313 | }; 314 | }; 315 | }; 316 | -------------------------------------------------------------------------------- /versatilepb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ARM Versatile Platform/Application Baseboard System emulation. 3 | * 4 | * Copyright (c) 2005-2007 CodeSourcery. 5 | * Written by Paul Brook 6 | * 7 | * This code is licensed under the GPL. 8 | */ 9 | 10 | #include "hw/sysbus.h" 11 | #include "hw/arm/arm.h" 12 | #include "hw/devices.h" 13 | #include "net/net.h" 14 | #include "sysemu/sysemu.h" 15 | #include "hw/pci/pci.h" 16 | #include "hw/i2c/i2c.h" 17 | #include "hw/boards.h" 18 | #include "sysemu/block-backend.h" 19 | #include "exec/address-spaces.h" 20 | #include "hw/block/flash.h" 21 | #include "qemu/error-report.h" 22 | 23 | #define VERSATILE_FLASH_ADDR 0x34000000 24 | #define VERSATILE_FLASH_SIZE (64 * 1024 * 1024) 25 | #define VERSATILE_FLASH_SECT_SIZE (256 * 1024) 26 | 27 | /* Primary interrupt controller. */ 28 | 29 | #define TYPE_VERSATILE_PB_SIC "versatilepb_sic" 30 | #define VERSATILE_PB_SIC(obj) \ 31 | OBJECT_CHECK(vpb_sic_state, (obj), TYPE_VERSATILE_PB_SIC) 32 | 33 | typedef struct vpb_sic_state { 34 | SysBusDevice parent_obj; 35 | 36 | MemoryRegion iomem; 37 | uint32_t level; 38 | uint32_t mask; 39 | uint32_t pic_enable; 40 | qemu_irq parent[32]; 41 | int irq; 42 | } vpb_sic_state; 43 | 44 | static const VMStateDescription vmstate_vpb_sic = { 45 | .name = "versatilepb_sic", 46 | .version_id = 1, 47 | .minimum_version_id = 1, 48 | .fields = (VMStateField[]) { 49 | VMSTATE_UINT32(level, vpb_sic_state), 50 | VMSTATE_UINT32(mask, vpb_sic_state), 51 | VMSTATE_UINT32(pic_enable, vpb_sic_state), 52 | VMSTATE_END_OF_LIST() 53 | } 54 | }; 55 | 56 | static void vpb_sic_update(vpb_sic_state *s) 57 | { 58 | uint32_t flags; 59 | 60 | flags = s->level & s->mask; 61 | qemu_set_irq(s->parent[s->irq], flags != 0); 62 | } 63 | 64 | static void vpb_sic_update_pic(vpb_sic_state *s) 65 | { 66 | int i; 67 | uint32_t mask; 68 | 69 | for (i = 21; i <= 30; i++) { 70 | mask = 1u << i; 71 | if (!(s->pic_enable & mask)) 72 | continue; 73 | qemu_set_irq(s->parent[i], (s->level & mask) != 0); 74 | } 75 | } 76 | 77 | static void vpb_sic_set_irq(void *opaque, int irq, int level) 78 | { 79 | vpb_sic_state *s = (vpb_sic_state *)opaque; 80 | if (level) 81 | s->level |= 1u << irq; 82 | else 83 | s->level &= ~(1u << irq); 84 | if (s->pic_enable & (1u << irq)) 85 | qemu_set_irq(s->parent[irq], level); 86 | vpb_sic_update(s); 87 | } 88 | 89 | static uint64_t vpb_sic_read(void *opaque, hwaddr offset, 90 | unsigned size) 91 | { 92 | vpb_sic_state *s = (vpb_sic_state *)opaque; 93 | 94 | switch (offset >> 2) { 95 | case 0: /* STATUS */ 96 | return s->level & s->mask; 97 | case 1: /* RAWSTAT */ 98 | return s->level; 99 | case 2: /* ENABLE */ 100 | return s->mask; 101 | case 4: /* SOFTINT */ 102 | return s->level & 1; 103 | case 8: /* PICENABLE */ 104 | return s->pic_enable; 105 | default: 106 | printf ("vpb_sic_read: Bad register offset 0x%x\n", (int)offset); 107 | return 0; 108 | } 109 | } 110 | 111 | static void vpb_sic_write(void *opaque, hwaddr offset, 112 | uint64_t value, unsigned size) 113 | { 114 | vpb_sic_state *s = (vpb_sic_state *)opaque; 115 | 116 | switch (offset >> 2) { 117 | case 2: /* ENSET */ 118 | s->mask |= value; 119 | break; 120 | case 3: /* ENCLR */ 121 | s->mask &= ~value; 122 | break; 123 | case 4: /* SOFTINTSET */ 124 | if (value) 125 | s->mask |= 1; 126 | break; 127 | case 5: /* SOFTINTCLR */ 128 | if (value) 129 | s->mask &= ~1u; 130 | break; 131 | case 8: /* PICENSET */ 132 | s->pic_enable |= (value & 0x7fe00000); 133 | vpb_sic_update_pic(s); 134 | break; 135 | case 9: /* PICENCLR */ 136 | s->pic_enable &= ~value; 137 | vpb_sic_update_pic(s); 138 | break; 139 | default: 140 | printf ("vpb_sic_write: Bad register offset 0x%x\n", (int)offset); 141 | return; 142 | } 143 | vpb_sic_update(s); 144 | } 145 | 146 | static const MemoryRegionOps vpb_sic_ops = { 147 | .read = vpb_sic_read, 148 | .write = vpb_sic_write, 149 | .endianness = DEVICE_NATIVE_ENDIAN, 150 | }; 151 | 152 | static int vpb_sic_init(SysBusDevice *sbd) 153 | { 154 | DeviceState *dev = DEVICE(sbd); 155 | vpb_sic_state *s = VERSATILE_PB_SIC(dev); 156 | int i; 157 | 158 | qdev_init_gpio_in(dev, vpb_sic_set_irq, 32); 159 | for (i = 0; i < 32; i++) { 160 | sysbus_init_irq(sbd, &s->parent[i]); 161 | } 162 | s->irq = 31; 163 | memory_region_init_io(&s->iomem, OBJECT(s), &vpb_sic_ops, s, 164 | "vpb-sic", 0x1000); 165 | sysbus_init_mmio(sbd, &s->iomem); 166 | return 0; 167 | } 168 | 169 | /* Board init. */ 170 | 171 | /* The AB and PB boards both use the same core, just with different 172 | peripherals and expansion busses. For now we emulate a subset of the 173 | PB peripherals and just change the board ID. */ 174 | 175 | static struct arm_boot_info versatile_binfo; 176 | 177 | static void versatile_init(MachineState *machine, int board_id) 178 | { 179 | ObjectClass *cpu_oc; 180 | Object *cpuobj; 181 | ARMCPU *cpu; 182 | MemoryRegion *sysmem = get_system_memory(); 183 | MemoryRegion *ram = g_new(MemoryRegion, 1); 184 | qemu_irq pic[32]; 185 | qemu_irq sic[32]; 186 | DeviceState *dev, *sysctl; 187 | SysBusDevice *busdev; 188 | DeviceState *pl041; 189 | PCIBus *pci_bus; 190 | NICInfo *nd; 191 | I2CBus *i2c; 192 | int n; 193 | int done_smc = 0; 194 | DriveInfo *dinfo; 195 | Error *err = NULL; 196 | 197 | if (!machine->cpu_model) { 198 | machine->cpu_model = "arm926"; 199 | } 200 | 201 | cpu_oc = cpu_class_by_name(TYPE_ARM_CPU, machine->cpu_model); 202 | if (!cpu_oc) { 203 | fprintf(stderr, "Unable to find CPU definition\n"); 204 | exit(1); 205 | } 206 | 207 | cpuobj = object_new(object_class_get_name(cpu_oc)); 208 | 209 | /* By default ARM1176 CPUs have EL3 enabled. This board does not 210 | * currently support EL3 so the CPU EL3 property is disabled before 211 | * realization. 212 | */ 213 | if (object_property_find(cpuobj, "has_el3", NULL)) { 214 | object_property_set_bool(cpuobj, false, "has_el3", &err); 215 | if (err) { 216 | error_report("%s", error_get_pretty(err)); 217 | exit(1); 218 | } 219 | } 220 | 221 | object_property_set_bool(cpuobj, true, "realized", &err); 222 | if (err) { 223 | error_report("%s", error_get_pretty(err)); 224 | exit(1); 225 | } 226 | 227 | cpu = ARM_CPU(cpuobj); 228 | 229 | memory_region_init_ram(ram, NULL, "versatile.ram", machine->ram_size, 230 | &error_abort); 231 | vmstate_register_ram_global(ram); 232 | /* ??? RAM should repeat to fill physical memory space. */ 233 | /* SDRAM at address zero. */ 234 | memory_region_add_subregion(sysmem, 0, ram); 235 | 236 | sysctl = qdev_create(NULL, "realview_sysctl"); 237 | qdev_prop_set_uint32(sysctl, "sys_id", 0x41007004); 238 | qdev_prop_set_uint32(sysctl, "proc_id", 0x02000000); 239 | qdev_init_nofail(sysctl); 240 | sysbus_mmio_map(SYS_BUS_DEVICE(sysctl), 0, 0x10000000); 241 | 242 | dev = sysbus_create_varargs("pl190", 0x10140000, 243 | qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_IRQ), 244 | qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_FIQ), 245 | NULL); 246 | for (n = 0; n < 32; n++) { 247 | pic[n] = qdev_get_gpio_in(dev, n); 248 | } 249 | dev = sysbus_create_simple(TYPE_VERSATILE_PB_SIC, 0x10003000, NULL); 250 | for (n = 0; n < 32; n++) { 251 | sysbus_connect_irq(SYS_BUS_DEVICE(dev), n, pic[n]); 252 | sic[n] = qdev_get_gpio_in(dev, n); 253 | } 254 | 255 | sysbus_create_simple("pl050_keyboard", 0x10006000, sic[3]); 256 | sysbus_create_simple("pl050_mouse", 0x10007000, sic[4]); 257 | 258 | dev = qdev_create(NULL, "versatile_pci"); 259 | busdev = SYS_BUS_DEVICE(dev); 260 | qdev_init_nofail(dev); 261 | sysbus_mmio_map(busdev, 0, 0x10001000); /* PCI controller regs */ 262 | sysbus_mmio_map(busdev, 1, 0x41000000); /* PCI self-config */ 263 | sysbus_mmio_map(busdev, 2, 0x42000000); /* PCI config */ 264 | sysbus_mmio_map(busdev, 3, 0x43000000); /* PCI I/O */ 265 | sysbus_mmio_map(busdev, 4, 0x44000000); /* PCI memory window 1 */ 266 | sysbus_mmio_map(busdev, 5, 0x50000000); /* PCI memory window 2 */ 267 | sysbus_mmio_map(busdev, 6, 0x60000000); /* PCI memory window 3 */ 268 | sysbus_connect_irq(busdev, 0, sic[27]); 269 | sysbus_connect_irq(busdev, 1, sic[28]); 270 | sysbus_connect_irq(busdev, 2, sic[29]); 271 | sysbus_connect_irq(busdev, 3, sic[30]); 272 | pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci"); 273 | 274 | for(n = 0; n < nb_nics; n++) { 275 | nd = &nd_table[n]; 276 | 277 | if (!done_smc && (!nd->model || strcmp(nd->model, "smc91c111") == 0)) { 278 | smc91c111_init(nd, 0x10010000, sic[25]); 279 | done_smc = 1; 280 | } else { 281 | pci_nic_init_nofail(nd, pci_bus, "rtl8139", NULL); 282 | } 283 | } 284 | if (usb_enabled()) { 285 | pci_create_simple(pci_bus, -1, "pci-ohci"); 286 | } 287 | n = drive_get_max_bus(IF_SCSI); 288 | while (n >= 0) { 289 | pci_create_simple(pci_bus, -1, "lsi53c895a"); 290 | n--; 291 | } 292 | 293 | sysbus_create_simple("pl011", 0x101f1000, pic[12]); 294 | sysbus_create_simple("pl011", 0x101f2000, pic[13]); 295 | sysbus_create_simple("pl011", 0x101f3000, pic[14]); 296 | sysbus_create_simple("pl011", 0x10009000, sic[6]); 297 | 298 | sysbus_create_simple("pl080", 0x10130000, pic[17]); 299 | sysbus_create_simple("sp804", 0x101e2000, pic[4]); 300 | sysbus_create_simple("sp804", 0x101e3000, pic[5]); 301 | 302 | sysbus_create_simple("pl061", 0x101e4000, pic[6]); 303 | sysbus_create_simple("pl061", 0x101e5000, pic[7]); 304 | sysbus_create_simple("pl061", 0x101e6000, pic[8]); 305 | sysbus_create_simple("pl061", 0x101e7000, pic[9]); 306 | 307 | 308 | sysbus_create_simple("test_tic", 0x101f5000, pic[15]); 309 | 310 | /* The versatile/PB actually has a modified Color LCD controller 311 | that includes hardware cursor support from the PL111. */ 312 | dev = sysbus_create_simple("pl110_versatile", 0x10120000, pic[16]); 313 | /* Wire up the mux control signals from the SYS_CLCD register */ 314 | qdev_connect_gpio_out(sysctl, 0, qdev_get_gpio_in(dev, 0)); 315 | 316 | sysbus_create_varargs("pl181", 0x10005000, sic[22], sic[1], NULL); 317 | sysbus_create_varargs("pl181", 0x1000b000, sic[23], sic[2], NULL); 318 | 319 | /* Add PL031 Real Time Clock. */ 320 | sysbus_create_simple("pl031", 0x101e8000, pic[10]); 321 | 322 | dev = sysbus_create_simple("versatile_i2c", 0x10002000, NULL); 323 | i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); 324 | i2c_create_slave(i2c, "ds1338", 0x68); 325 | 326 | /* Add PL041 AACI Interface to the LM4549 codec */ 327 | pl041 = qdev_create(NULL, "pl041"); 328 | qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512); 329 | qdev_init_nofail(pl041); 330 | sysbus_mmio_map(SYS_BUS_DEVICE(pl041), 0, 0x10004000); 331 | sysbus_connect_irq(SYS_BUS_DEVICE(pl041), 0, sic[24]); 332 | 333 | /* Memory map for Versatile/PB: */ 334 | /* 0x10000000 System registers. */ 335 | /* 0x10001000 PCI controller config registers. */ 336 | /* 0x10002000 Serial bus interface. */ 337 | /* 0x10003000 Secondary interrupt controller. */ 338 | /* 0x10004000 AACI (audio). */ 339 | /* 0x10005000 MMCI0. */ 340 | /* 0x10006000 KMI0 (keyboard). */ 341 | /* 0x10007000 KMI1 (mouse). */ 342 | /* 0x10008000 Character LCD Interface. */ 343 | /* 0x10009000 UART3. */ 344 | /* 0x1000a000 Smart card 1. */ 345 | /* 0x1000b000 MMCI1. */ 346 | /* 0x10010000 Ethernet. */ 347 | /* 0x10020000 USB. */ 348 | /* 0x10100000 SSMC. */ 349 | /* 0x10110000 MPMC. */ 350 | /* 0x10120000 CLCD Controller. */ 351 | /* 0x10130000 DMA Controller. */ 352 | /* 0x10140000 Vectored interrupt controller. */ 353 | /* 0x101d0000 AHB Monitor Interface. */ 354 | /* 0x101e0000 System Controller. */ 355 | /* 0x101e1000 Watchdog Interface. */ 356 | /* 0x101e2000 Timer 0/1. */ 357 | /* 0x101e3000 Timer 2/3. */ 358 | /* 0x101e4000 GPIO port 0. */ 359 | /* 0x101e5000 GPIO port 1. */ 360 | /* 0x101e6000 GPIO port 2. */ 361 | /* 0x101e7000 GPIO port 3. */ 362 | /* 0x101e8000 RTC. */ 363 | /* 0x101f0000 Smart card 0. */ 364 | /* 0x101f1000 UART0. */ 365 | /* 0x101f2000 UART1. */ 366 | /* 0x101f3000 UART2. */ 367 | /* 0x101f4000 SSPI. */ 368 | /* 0x101f5000 tic*/ 369 | /* 0x34000000 NOR Flash */ 370 | 371 | dinfo = drive_get(IF_PFLASH, 0, 0); 372 | if (!pflash_cfi01_register(VERSATILE_FLASH_ADDR, NULL, "versatile.flash", 373 | VERSATILE_FLASH_SIZE, 374 | dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, 375 | VERSATILE_FLASH_SECT_SIZE, 376 | VERSATILE_FLASH_SIZE / VERSATILE_FLASH_SECT_SIZE, 377 | 4, 0x0089, 0x0018, 0x0000, 0x0, 0)) { 378 | fprintf(stderr, "qemu: Error registering flash memory.\n"); 379 | } 380 | 381 | versatile_binfo.ram_size = machine->ram_size; 382 | versatile_binfo.kernel_filename = machine->kernel_filename; 383 | versatile_binfo.kernel_cmdline = machine->kernel_cmdline; 384 | versatile_binfo.initrd_filename = machine->initrd_filename; 385 | versatile_binfo.board_id = board_id; 386 | arm_load_kernel(cpu, &versatile_binfo); 387 | } 388 | 389 | static void vpb_init(MachineState *machine) 390 | { 391 | versatile_init(machine, 0x183); 392 | } 393 | 394 | static void vab_init(MachineState *machine) 395 | { 396 | versatile_init(machine, 0x25e); 397 | } 398 | 399 | static QEMUMachine versatilepb_machine = { 400 | .name = "versatilepb", 401 | .desc = "ARM Versatile/PB (ARM926EJ-S)", 402 | .init = vpb_init, 403 | .block_default_type = IF_SCSI, 404 | }; 405 | 406 | static QEMUMachine versatileab_machine = { 407 | .name = "versatileab", 408 | .desc = "ARM Versatile/AB (ARM926EJ-S)", 409 | .init = vab_init, 410 | .block_default_type = IF_SCSI, 411 | }; 412 | 413 | static void versatile_machine_init(void) 414 | { 415 | qemu_register_machine(&versatilepb_machine); 416 | qemu_register_machine(&versatileab_machine); 417 | } 418 | 419 | machine_init(versatile_machine_init); 420 | 421 | static void vpb_sic_class_init(ObjectClass *klass, void *data) 422 | { 423 | DeviceClass *dc = DEVICE_CLASS(klass); 424 | SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); 425 | 426 | k->init = vpb_sic_init; 427 | dc->vmsd = &vmstate_vpb_sic; 428 | } 429 | 430 | static const TypeInfo vpb_sic_info = { 431 | .name = TYPE_VERSATILE_PB_SIC, 432 | .parent = TYPE_SYS_BUS_DEVICE, 433 | .instance_size = sizeof(vpb_sic_state), 434 | .class_init = vpb_sic_class_init, 435 | }; 436 | 437 | static void versatilepb_register_types(void) 438 | { 439 | type_register_static(&vpb_sic_info); 440 | } 441 | 442 | type_init(versatilepb_register_types) 443 | --------------------------------------------------------------------------------