highbank.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /*
  2. * Calxeda Highbank SoC emulation
  3. *
  4. * Copyright (c) 2010-2012 Calxeda
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms and conditions of the GNU General Public License,
  8. * version 2 or later, as published by the Free Software Foundation.
  9. *
  10. * This program is distributed in the hope it will be useful, but WITHOUT
  11. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13. * more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along with
  16. * this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. #include "sysbus.h"
  20. #include "arm-misc.h"
  21. #include "devices.h"
  22. #include "loader.h"
  23. #include "net/net.h"
  24. #include "sysemu/sysemu.h"
  25. #include "boards.h"
  26. #include "sysbus.h"
  27. #include "sysemu/blockdev.h"
  28. #include "exec/address-spaces.h"
  29. #define SMP_BOOT_ADDR 0x100
  30. #define SMP_BOOT_REG 0x40
  31. #define GIC_BASE_ADDR 0xfff10000
  32. #define NIRQ_GIC 160
  33. /* Board init. */
  34. static void hb_write_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
  35. {
  36. int n;
  37. uint32_t smpboot[] = {
  38. 0xee100fb0, /* mrc p15, 0, r0, c0, c0, 5 - read current core id */
  39. 0xe210000f, /* ands r0, r0, #0x0f */
  40. 0xe3a03040, /* mov r3, #0x40 - jump address is 0x40 + 0x10 * core id */
  41. 0xe0830200, /* add r0, r3, r0, lsl #4 */
  42. 0xe59f2024, /* ldr r2, privbase */
  43. 0xe3a01001, /* mov r1, #1 */
  44. 0xe5821100, /* str r1, [r2, #256] - set GICC_CTLR.Enable */
  45. 0xe3a010ff, /* mov r1, #0xff */
  46. 0xe5821104, /* str r1, [r2, #260] - set GICC_PMR.Priority to 0xff */
  47. 0xf57ff04f, /* dsb */
  48. 0xe320f003, /* wfi */
  49. 0xe5901000, /* ldr r1, [r0] */
  50. 0xe1110001, /* tst r1, r1 */
  51. 0x0afffffb, /* beq <wfi> */
  52. 0xe12fff11, /* bx r1 */
  53. GIC_BASE_ADDR /* privbase: gic address. */
  54. };
  55. for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
  56. smpboot[n] = tswap32(smpboot[n]);
  57. }
  58. rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot), SMP_BOOT_ADDR);
  59. }
  60. static void hb_reset_secondary(ARMCPU *cpu, const struct arm_boot_info *info)
  61. {
  62. CPUARMState *env = &cpu->env;
  63. switch (info->nb_cpus) {
  64. case 4:
  65. stl_phys_notdirty(SMP_BOOT_REG + 0x30, 0);
  66. case 3:
  67. stl_phys_notdirty(SMP_BOOT_REG + 0x20, 0);
  68. case 2:
  69. stl_phys_notdirty(SMP_BOOT_REG + 0x10, 0);
  70. env->regs[15] = SMP_BOOT_ADDR;
  71. break;
  72. default:
  73. break;
  74. }
  75. }
  76. #define NUM_REGS 0x200
  77. static void hb_regs_write(void *opaque, hwaddr offset,
  78. uint64_t value, unsigned size)
  79. {
  80. uint32_t *regs = opaque;
  81. if (offset == 0xf00) {
  82. if (value == 1 || value == 2) {
  83. qemu_system_reset_request();
  84. } else if (value == 3) {
  85. qemu_system_shutdown_request();
  86. }
  87. }
  88. regs[offset/4] = value;
  89. }
  90. static uint64_t hb_regs_read(void *opaque, hwaddr offset,
  91. unsigned size)
  92. {
  93. uint32_t *regs = opaque;
  94. uint32_t value = regs[offset/4];
  95. if ((offset == 0x100) || (offset == 0x108) || (offset == 0x10C)) {
  96. value |= 0x30000000;
  97. }
  98. return value;
  99. }
  100. static const MemoryRegionOps hb_mem_ops = {
  101. .read = hb_regs_read,
  102. .write = hb_regs_write,
  103. .endianness = DEVICE_NATIVE_ENDIAN,
  104. };
  105. typedef struct {
  106. SysBusDevice busdev;
  107. MemoryRegion *iomem;
  108. uint32_t regs[NUM_REGS];
  109. } HighbankRegsState;
  110. static VMStateDescription vmstate_highbank_regs = {
  111. .name = "highbank-regs",
  112. .version_id = 0,
  113. .minimum_version_id = 0,
  114. .minimum_version_id_old = 0,
  115. .fields = (VMStateField[]) {
  116. VMSTATE_UINT32_ARRAY(regs, HighbankRegsState, NUM_REGS),
  117. VMSTATE_END_OF_LIST(),
  118. },
  119. };
  120. static void highbank_regs_reset(DeviceState *dev)
  121. {
  122. SysBusDevice *sys_dev = SYS_BUS_DEVICE(dev);
  123. HighbankRegsState *s = FROM_SYSBUS(HighbankRegsState, sys_dev);
  124. s->regs[0x40] = 0x05F20121;
  125. s->regs[0x41] = 0x2;
  126. s->regs[0x42] = 0x05F30121;
  127. s->regs[0x43] = 0x05F40121;
  128. }
  129. static int highbank_regs_init(SysBusDevice *dev)
  130. {
  131. HighbankRegsState *s = FROM_SYSBUS(HighbankRegsState, dev);
  132. s->iomem = g_new(MemoryRegion, 1);
  133. memory_region_init_io(s->iomem, &hb_mem_ops, s->regs, "highbank_regs",
  134. 0x1000);
  135. sysbus_init_mmio(dev, s->iomem);
  136. return 0;
  137. }
  138. static void highbank_regs_class_init(ObjectClass *klass, void *data)
  139. {
  140. SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass);
  141. DeviceClass *dc = DEVICE_CLASS(klass);
  142. sbc->init = highbank_regs_init;
  143. dc->desc = "Calxeda Highbank registers";
  144. dc->vmsd = &vmstate_highbank_regs;
  145. dc->reset = highbank_regs_reset;
  146. }
  147. static const TypeInfo highbank_regs_info = {
  148. .name = "highbank-regs",
  149. .parent = TYPE_SYS_BUS_DEVICE,
  150. .instance_size = sizeof(HighbankRegsState),
  151. .class_init = highbank_regs_class_init,
  152. };
  153. static void highbank_regs_register_types(void)
  154. {
  155. type_register_static(&highbank_regs_info);
  156. }
  157. type_init(highbank_regs_register_types)
  158. static struct arm_boot_info highbank_binfo;
  159. /* ram_size must be set to match the upper bound of memory in the
  160. * device tree (linux/arch/arm/boot/dts/highbank.dts), which is
  161. * normally 0xff900000 or -m 4089. When running this board on a
  162. * 32-bit host, set the reg value of memory to 0xf7ff00000 in the
  163. * device tree and pass -m 2047 to QEMU.
  164. */
  165. static void highbank_init(QEMUMachineInitArgs *args)
  166. {
  167. ram_addr_t ram_size = args->ram_size;
  168. const char *cpu_model = args->cpu_model;
  169. const char *kernel_filename = args->kernel_filename;
  170. const char *kernel_cmdline = args->kernel_cmdline;
  171. const char *initrd_filename = args->initrd_filename;
  172. DeviceState *dev;
  173. SysBusDevice *busdev;
  174. qemu_irq *irqp;
  175. qemu_irq pic[128];
  176. int n;
  177. qemu_irq cpu_irq[4];
  178. MemoryRegion *sysram;
  179. MemoryRegion *dram;
  180. MemoryRegion *sysmem;
  181. char *sysboot_filename;
  182. if (!cpu_model) {
  183. cpu_model = "cortex-a9";
  184. }
  185. for (n = 0; n < smp_cpus; n++) {
  186. ARMCPU *cpu;
  187. cpu = cpu_arm_init(cpu_model);
  188. if (cpu == NULL) {
  189. fprintf(stderr, "Unable to find CPU definition\n");
  190. exit(1);
  191. }
  192. /* This will become a QOM property eventually */
  193. cpu->reset_cbar = GIC_BASE_ADDR;
  194. irqp = arm_pic_init_cpu(cpu);
  195. cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
  196. }
  197. sysmem = get_system_memory();
  198. dram = g_new(MemoryRegion, 1);
  199. memory_region_init_ram(dram, "highbank.dram", ram_size);
  200. /* SDRAM at address zero. */
  201. memory_region_add_subregion(sysmem, 0, dram);
  202. sysram = g_new(MemoryRegion, 1);
  203. memory_region_init_ram(sysram, "highbank.sysram", 0x8000);
  204. memory_region_add_subregion(sysmem, 0xfff88000, sysram);
  205. if (bios_name != NULL) {
  206. sysboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
  207. if (sysboot_filename != NULL) {
  208. uint32_t filesize = get_image_size(sysboot_filename);
  209. if (load_image_targphys("sysram.bin", 0xfff88000, filesize) < 0) {
  210. hw_error("Unable to load %s\n", bios_name);
  211. }
  212. } else {
  213. hw_error("Unable to find %s\n", bios_name);
  214. }
  215. }
  216. dev = qdev_create(NULL, "a9mpcore_priv");
  217. qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
  218. qdev_prop_set_uint32(dev, "num-irq", NIRQ_GIC);
  219. qdev_init_nofail(dev);
  220. busdev = SYS_BUS_DEVICE(dev);
  221. sysbus_mmio_map(busdev, 0, GIC_BASE_ADDR);
  222. for (n = 0; n < smp_cpus; n++) {
  223. sysbus_connect_irq(busdev, n, cpu_irq[n]);
  224. }
  225. for (n = 0; n < 128; n++) {
  226. pic[n] = qdev_get_gpio_in(dev, n);
  227. }
  228. dev = qdev_create(NULL, "l2x0");
  229. qdev_init_nofail(dev);
  230. busdev = SYS_BUS_DEVICE(dev);
  231. sysbus_mmio_map(busdev, 0, 0xfff12000);
  232. dev = qdev_create(NULL, "sp804");
  233. qdev_prop_set_uint32(dev, "freq0", 150000000);
  234. qdev_prop_set_uint32(dev, "freq1", 150000000);
  235. qdev_init_nofail(dev);
  236. busdev = SYS_BUS_DEVICE(dev);
  237. sysbus_mmio_map(busdev, 0, 0xfff34000);
  238. sysbus_connect_irq(busdev, 0, pic[18]);
  239. sysbus_create_simple("pl011", 0xfff36000, pic[20]);
  240. dev = qdev_create(NULL, "highbank-regs");
  241. qdev_init_nofail(dev);
  242. busdev = SYS_BUS_DEVICE(dev);
  243. sysbus_mmio_map(busdev, 0, 0xfff3c000);
  244. sysbus_create_simple("pl061", 0xfff30000, pic[14]);
  245. sysbus_create_simple("pl061", 0xfff31000, pic[15]);
  246. sysbus_create_simple("pl061", 0xfff32000, pic[16]);
  247. sysbus_create_simple("pl061", 0xfff33000, pic[17]);
  248. sysbus_create_simple("pl031", 0xfff35000, pic[19]);
  249. sysbus_create_simple("pl022", 0xfff39000, pic[23]);
  250. sysbus_create_simple("sysbus-ahci", 0xffe08000, pic[83]);
  251. if (nd_table[0].used) {
  252. qemu_check_nic_model(&nd_table[0], "xgmac");
  253. dev = qdev_create(NULL, "xgmac");
  254. qdev_set_nic_properties(dev, &nd_table[0]);
  255. qdev_init_nofail(dev);
  256. sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xfff50000);
  257. sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[77]);
  258. sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, pic[78]);
  259. sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, pic[79]);
  260. qemu_check_nic_model(&nd_table[1], "xgmac");
  261. dev = qdev_create(NULL, "xgmac");
  262. qdev_set_nic_properties(dev, &nd_table[1]);
  263. qdev_init_nofail(dev);
  264. sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xfff51000);
  265. sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[80]);
  266. sysbus_connect_irq(SYS_BUS_DEVICE(dev), 1, pic[81]);
  267. sysbus_connect_irq(SYS_BUS_DEVICE(dev), 2, pic[82]);
  268. }
  269. highbank_binfo.ram_size = ram_size;
  270. highbank_binfo.kernel_filename = kernel_filename;
  271. highbank_binfo.kernel_cmdline = kernel_cmdline;
  272. highbank_binfo.initrd_filename = initrd_filename;
  273. /* highbank requires a dtb in order to boot, and the dtb will override
  274. * the board ID. The following value is ignored, so set it to -1 to be
  275. * clear that the value is meaningless.
  276. */
  277. highbank_binfo.board_id = -1;
  278. highbank_binfo.nb_cpus = smp_cpus;
  279. highbank_binfo.loader_start = 0;
  280. highbank_binfo.write_secondary_boot = hb_write_secondary;
  281. highbank_binfo.secondary_cpu_reset_hook = hb_reset_secondary;
  282. arm_load_kernel(arm_env_get_cpu(first_cpu), &highbank_binfo);
  283. }
  284. static QEMUMachine highbank_machine = {
  285. .name = "highbank",
  286. .desc = "Calxeda Highbank (ECX-1000)",
  287. .init = highbank_init,
  288. .block_default_type = IF_SCSI,
  289. .max_cpus = 4,
  290. DEFAULT_MACHINE_OPTIONS,
  291. };
  292. static void highbank_machine_init(void)
  293. {
  294. qemu_register_machine(&highbank_machine);
  295. }
  296. machine_init(highbank_machine_init);