├── 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 |
--------------------------------------------------------------------------------