summaryrefslogtreecommitdiffstats
path: root/arch/x86/cpu/qemu/qemu.c
blob: c29add39b26bfb1fa7bd3e15508a275410aa11cc (plain)
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
 * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <common.h>
#include <pci.h>
#include <qemu_fw_cfg.h>
#include <asm/irq.h>
#include <asm/post.h>
#include <asm/processor.h>
#include <asm/arch/device.h>
#include <asm/arch/qemu.h>

static bool i440fx;

#ifdef CONFIG_QFW

/* on x86, the qfw registers are all IO ports */
#define FW_CONTROL_PORT	0x510
#define FW_DATA_PORT		0x511
#define FW_DMA_PORT_LOW	0x514
#define FW_DMA_PORT_HIGH	0x518

static void qemu_x86_fwcfg_read_entry_pio(uint16_t entry,
		uint32_t size, void *address)
{
	uint32_t i = 0;
	uint8_t *data = address;

	/*
	 * writting FW_CFG_INVALID will cause read operation to resume at
	 * last offset, otherwise read will start at offset 0
	 *
	 * Note: on platform where the control register is IO port, the
	 * endianness is little endian.
	 */
	if (entry != FW_CFG_INVALID)
		outw(cpu_to_le16(entry), FW_CONTROL_PORT);

	/* the endianness of data register is string-preserving */
	while (size--)
		data[i++] = inb(FW_DATA_PORT);
}

static void qemu_x86_fwcfg_read_entry_dma(struct fw_cfg_dma_access *dma)
{
	/* the DMA address register is big endian */
	outl(cpu_to_be32((uint32_t)dma), FW_DMA_PORT_HIGH);

	while (be32_to_cpu(dma->control) & ~FW_CFG_DMA_ERROR)
		__asm__ __volatile__ ("pause");
}

static struct fw_cfg_arch_ops fwcfg_x86_ops = {
	.arch_read_pio = qemu_x86_fwcfg_read_entry_pio,
	.arch_read_dma = qemu_x86_fwcfg_read_entry_dma
};
#endif

static void enable_pm_piix(void)
{
	u8 en;
	u16 cmd;

	/* Set the PM I/O base */
	pci_write_config32(PIIX_PM, PMBA, CONFIG_ACPI_PM1_BASE | 1);

	/* Enable access to the PM I/O space */
	pci_read_config16(PIIX_PM, PCI_COMMAND, &cmd);
	cmd |= PCI_COMMAND_IO;
	pci_write_config16(PIIX_PM, PCI_COMMAND, cmd);

	/* PM I/O Space Enable (PMIOSE) */
	pci_read_config8(PIIX_PM, PMREGMISC, &en);
	en |= PMIOSE;
	pci_write_config8(PIIX_PM, PMREGMISC, en);
}

static void enable_pm_ich9(void)
{
	/* Set the PM I/O base */
	pci_write_config32(ICH9_PM, PMBA, CONFIG_ACPI_PM1_BASE | 1);
}

static void qemu_chipset_init(void)
{
	u16 device, xbcs;
	int pam, i;

	/*
	 * i440FX and Q35 chipset have different PAM register offset, but with
	 * the same bitfield layout. Here we determine the offset based on its
	 * PCI device ID.
	 */
	pci_read_config16(PCI_BDF(0, 0, 0), PCI_DEVICE_ID, &device);
	i440fx = (device == PCI_DEVICE_ID_INTEL_82441);
	pam = i440fx ? I440FX_PAM : Q35_PAM;

	/*
	 * Initialize Programmable Attribute Map (PAM) Registers
	 *
	 * Configure legacy segments C/D/E/F to system RAM
	 */
	for (i = 0; i < PAM_NUM; i++)
		pci_write_config8(PCI_BDF(0, 0, 0), pam + i, PAM_RW);

	if (i440fx) {
		/*
		 * Enable legacy IDE I/O ports decode
		 *
		 * Note: QEMU always decode legacy IDE I/O port on PIIX chipset.
		 * However Linux ata_piix driver does sanity check on these two
		 * registers to see whether legacy ports decode is turned on.
		 * This is to make Linux ata_piix driver happy.
		 */
		pci_write_config16(PIIX_IDE, IDE0_TIM, IDE_DECODE_EN);
		pci_write_config16(PIIX_IDE, IDE1_TIM, IDE_DECODE_EN);

		/* Enable I/O APIC */
		pci_read_config16(PIIX_ISA, XBCS, &xbcs);
		xbcs |= APIC_EN;
		pci_write_config16(PIIX_ISA, XBCS, xbcs);

		enable_pm_piix();
	} else {
		/* Configure PCIe ECAM base address */
		pci_write_config32(PCI_BDF(0, 0, 0), PCIEX_BAR,
				   CONFIG_PCIE_ECAM_BASE | BAR_EN);

		enable_pm_ich9();
	}

#ifdef CONFIG_QFW
	qemu_fwcfg_init(&fwcfg_x86_ops);
#endif
}

int arch_cpu_init(void)
{
	int ret;

	post_code(POST_CPU_INIT);

	ret = x86_cpu_init_f();
	if (ret)
		return ret;

	return 0;
}

#ifndef CONFIG_EFI_STUB
int print_cpuinfo(void)
{
	post_code(POST_CPU_INFO);
	return default_print_cpuinfo();
}
#endif

void reset_cpu(ulong addr)
{
	/* cold reset */
	x86_full_reset();
}

int arch_early_init_r(void)
{
	qemu_chipset_init();

	return 0;
}

#ifdef CONFIG_GENERATE_MP_TABLE
int mp_determine_pci_dstirq(int bus, int dev, int func, int pirq)
{
	u8 irq;

	if (i440fx) {
		/*
		 * Not like most x86 platforms, the PIRQ[A-D] on PIIX3 are not
		 * connected to I/O APIC INTPIN#16-19. Instead they are routed
		 * to an irq number controled by the PIRQ routing register.
		 */
		pci_read_config8(PCI_BDF(bus, dev, func),
				 PCI_INTERRUPT_LINE, &irq);
	} else {
		/*
		 * ICH9's PIRQ[A-H] are not consecutive numbers from 0 to 7.
		 * PIRQ[A-D] still maps to [0-3] but PIRQ[E-H] maps to [8-11].
		 */
		irq = pirq < 8 ? pirq + 16 : pirq + 12;
	}

	return irq;
}
#endif
OpenPOWER on IntegriCloud