aboutsummaryrefslogtreecommitdiffstats
path: root/arch/m68k/virt/config.c
blob: 4ab22946ff68fec81b8f2fc45b029b99fa51b179 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// SPDX-License-Identifier: GPL-2.0

#include <linux/reboot.h>
#include <linux/serial_core.h>
#include <linux/random.h>
#include <clocksource/timer-goldfish.h>

#include <asm/bootinfo.h>
#include <asm/bootinfo-virt.h>
#include <asm/byteorder.h>
#include <asm/machdep.h>
#include <asm/virt.h>
#include <asm/config.h>

struct virt_booter_data virt_bi_data;

#define VIRT_CTRL_REG_FEATURES	0x00
#define VIRT_CTRL_REG_CMD	0x04

static struct resource ctrlres;

enum {
	CMD_NOOP,
	CMD_RESET,
	CMD_HALT,
	CMD_PANIC,
};

static void virt_get_model(char *str)
{
	/* str is 80 characters long */
	sprintf(str, "QEMU Virtual M68K Machine (%u.%u.%u)",
		(u8)(virt_bi_data.qemu_version >> 24),
		(u8)(virt_bi_data.qemu_version >> 16),
		(u8)(virt_bi_data.qemu_version >> 8));
}

static void virt_halt(void)
{
	void __iomem *base = (void __iomem *)virt_bi_data.ctrl.mmio;

	iowrite32be(CMD_HALT, base + VIRT_CTRL_REG_CMD);
	local_irq_disable();
	while (1)
		;
}

static void virt_reset(void)
{
	void __iomem *base = (void __iomem *)virt_bi_data.ctrl.mmio;

	iowrite32be(CMD_RESET, base + VIRT_CTRL_REG_CMD);
	local_irq_disable();
	while (1)
		;
}

/*
 * Parse a virtual-m68k-specific record in the bootinfo
 */

int __init virt_parse_bootinfo(const struct bi_record *record)
{
	int unknown = 0;
	const void *data = record->data;

	switch (be16_to_cpu(record->tag)) {
	case BI_VIRT_QEMU_VERSION:
		virt_bi_data.qemu_version = be32_to_cpup(data);
		break;
	case BI_VIRT_GF_PIC_BASE:
		virt_bi_data.pic.mmio = be32_to_cpup(data);
		data += 4;
		virt_bi_data.pic.irq = be32_to_cpup(data);
		break;
	case BI_VIRT_GF_RTC_BASE:
		virt_bi_data.rtc.mmio = be32_to_cpup(data);
		data += 4;
		virt_bi_data.rtc.irq = be32_to_cpup(data);
		break;
	case BI_VIRT_GF_TTY_BASE:
		virt_bi_data.tty.mmio = be32_to_cpup(data);
		data += 4;
		virt_bi_data.tty.irq = be32_to_cpup(data);
		break;
	case BI_VIRT_CTRL_BASE:
		virt_bi_data.ctrl.mmio = be32_to_cpup(data);
		data += 4;
		virt_bi_data.ctrl.irq = be32_to_cpup(data);
		break;
	case BI_VIRT_VIRTIO_BASE:
		virt_bi_data.virtio.mmio = be32_to_cpup(data);
		data += 4;
		virt_bi_data.virtio.irq = be32_to_cpup(data);
		break;
	case BI_VIRT_RNG_SEED: {
		u16 len = be16_to_cpup(data);
		add_bootloader_randomness(data + 2, len);
		/*
		 * Zero the data to preserve forward secrecy, and zero the
		 * length to prevent kexec from using it.
		 */
		memzero_explicit((void *)data, len + 2);
		break;
	}
	default:
		unknown = 1;
		break;
	}
	return unknown;
}

static void __init virt_sched_init(void)
{
	goldfish_timer_init(virt_bi_data.rtc.irq,
			    (void __iomem *)virt_bi_data.rtc.mmio);
}

void __init config_virt(void)
{
	char earlycon[24];

	snprintf(earlycon, sizeof(earlycon), "early_gf_tty,0x%08x",
		 virt_bi_data.tty.mmio);
	setup_earlycon(earlycon);

	ctrlres = (struct resource)
		   DEFINE_RES_MEM_NAMED(virt_bi_data.ctrl.mmio, 0x100,
					"virtctrl");

	if (request_resource(&iomem_resource, &ctrlres)) {
		pr_err("Cannot allocate virt controller resource\n");
		return;
	}

	mach_init_IRQ = virt_init_IRQ;
	mach_sched_init = virt_sched_init;
	mach_get_model = virt_get_model;
	mach_reset = virt_reset;
	mach_halt = virt_halt;

	register_platform_power_off(virt_halt);
}