xref: /kvm-unit-tests/lib/arm/io.c (revision a8743efa3358d216f858dc8c8998ca012ed89b11)
15e61cba0SAndrew Jones /*
25e61cba0SAndrew Jones  * Each architecture must implement puts() and exit() with the I/O
35e61cba0SAndrew Jones  * devices exposed from QEMU, e.g. pl011 and chr-testdev. That's
45e61cba0SAndrew Jones  * what's done here, along with initialization functions for those
55e61cba0SAndrew Jones  * devices.
65e61cba0SAndrew Jones  *
75e61cba0SAndrew Jones  * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
85e61cba0SAndrew Jones  *
95e61cba0SAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
105e61cba0SAndrew Jones  */
118cca5668SAndrew Jones #include <libcflat.h>
128cca5668SAndrew Jones #include <devicetree.h>
138cca5668SAndrew Jones #include <chr-testdev.h>
148cca5668SAndrew Jones #include <asm/spinlock.h>
158cca5668SAndrew Jones #include <asm/io.h>
165e61cba0SAndrew Jones 
175e61cba0SAndrew Jones extern void halt(int code);
185e61cba0SAndrew Jones 
195e61cba0SAndrew Jones /*
205e61cba0SAndrew Jones  * Use this guess for the pl011 base in order to make an attempt at
215e61cba0SAndrew Jones  * having earlier printf support. We'll overwrite it with the real
22d70582c2SAndrew Jones  * base address that we read from the device tree later. This is
23d70582c2SAndrew Jones  * the address we expect QEMU's mach-virt machine type to put in
24d70582c2SAndrew Jones  * its generated device tree.
255e61cba0SAndrew Jones  */
26d70582c2SAndrew Jones #define UART_EARLY_BASE 0x09000000UL
275e61cba0SAndrew Jones 
285e61cba0SAndrew Jones static struct spinlock uart_lock;
29d70582c2SAndrew Jones static volatile u8 *uart0_base = (u8 *)UART_EARLY_BASE;
305e61cba0SAndrew Jones 
315e61cba0SAndrew Jones static void uart0_init(void)
325e61cba0SAndrew Jones {
335e61cba0SAndrew Jones 	const char *compatible = "arm,pl011";
345e61cba0SAndrew Jones 	struct dt_pbus_reg base;
355e61cba0SAndrew Jones 	int ret;
365e61cba0SAndrew Jones 
37d70582c2SAndrew Jones 	ret = dt_get_default_console_node();
38d70582c2SAndrew Jones 	assert(ret >= 0 || ret == -FDT_ERR_NOTFOUND);
39d70582c2SAndrew Jones 
40d70582c2SAndrew Jones 	if (ret == -FDT_ERR_NOTFOUND) {
41d70582c2SAndrew Jones 
425e61cba0SAndrew Jones 		ret = dt_pbus_get_base_compatible(compatible, &base);
435e61cba0SAndrew Jones 		assert(ret == 0 || ret == -FDT_ERR_NOTFOUND);
445e61cba0SAndrew Jones 
455e61cba0SAndrew Jones 		if (ret) {
46d70582c2SAndrew Jones 			printf("%s: %s not found in the device tree, "
47d70582c2SAndrew Jones 				"aborting...\n",
485e61cba0SAndrew Jones 				__func__, compatible);
495e61cba0SAndrew Jones 			abort();
505e61cba0SAndrew Jones 		}
515e61cba0SAndrew Jones 
52d70582c2SAndrew Jones 	} else {
53*a8743efaSAndrew Jones 		ret = dt_pbus_translate_node(ret, 0, &base);
54*a8743efaSAndrew Jones 		assert(ret == 0);
55d70582c2SAndrew Jones 	}
56d70582c2SAndrew Jones 
575e61cba0SAndrew Jones 	uart0_base = ioremap(base.addr, base.size);
58d70582c2SAndrew Jones 
59d70582c2SAndrew Jones 	if (uart0_base != (u8 *)UART_EARLY_BASE) {
60d70582c2SAndrew Jones 		printf("WARNING: early print support may not work. "
61d70582c2SAndrew Jones 		       "Found uart at %p, but early base is %p.\n",
62d70582c2SAndrew Jones 			uart0_base, (u8 *)UART_EARLY_BASE);
63d70582c2SAndrew Jones 	}
645e61cba0SAndrew Jones }
655e61cba0SAndrew Jones 
665e61cba0SAndrew Jones void io_init(void)
675e61cba0SAndrew Jones {
685e61cba0SAndrew Jones 	uart0_init();
695e61cba0SAndrew Jones 	chr_testdev_init();
705e61cba0SAndrew Jones }
715e61cba0SAndrew Jones 
725e61cba0SAndrew Jones void puts(const char *s)
735e61cba0SAndrew Jones {
745e61cba0SAndrew Jones 	spin_lock(&uart_lock);
755e61cba0SAndrew Jones 	while (*s)
765e61cba0SAndrew Jones 		writeb(*s++, uart0_base);
775e61cba0SAndrew Jones 	spin_unlock(&uart_lock);
785e61cba0SAndrew Jones }
795e61cba0SAndrew Jones 
805e61cba0SAndrew Jones void exit(int code)
815e61cba0SAndrew Jones {
825e61cba0SAndrew Jones 	chr_testdev_exit(code);
835e61cba0SAndrew Jones 	halt(code);
845e61cba0SAndrew Jones }
85