summaryrefslogtreecommitdiffstats
path: root/arch/x86/cpu/ivybridge/bd82x6x.c
blob: 1cb6cecda9ea0fb1d066b0fe9a2df5421d853bdb (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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2014 Google, Inc
 */
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <malloc.h>
#include <pch.h>
#include <asm/cpu.h>
#include <asm/intel_regs.h>
#include <asm/io.h>
#include <asm/lapic.h>
#include <asm/lpc_common.h>
#include <asm/pci.h>
#include <asm/arch/model_206ax.h>
#include <asm/arch/pch.h>
#include <asm/arch/sandybridge.h>

DECLARE_GLOBAL_DATA_PTR;

#define GPIO_BASE		0x48
#define BIOS_CTRL		0xdc

#define RCBA_AUDIO_CONFIG	0x2030
#define RCBA_AUDIO_CONFIG_HDA	BIT(31)
#define RCBA_AUDIO_CONFIG_MASK	0xfe

#ifndef CONFIG_HAVE_FSP
static int pch_revision_id = -1;
static int pch_type = -1;

/**
 * pch_silicon_revision() - Read silicon revision ID from the PCH
 *
 * @dev:	PCH device
 * @return silicon revision ID
 */
static int pch_silicon_revision(struct udevice *dev)
{
	u8 val;

	if (pch_revision_id < 0) {
		dm_pci_read_config8(dev, PCI_REVISION_ID, &val);
		pch_revision_id = val;
	}

	return pch_revision_id;
}

int pch_silicon_type(struct udevice *dev)
{
	u8 val;

	if (pch_type < 0) {
		dm_pci_read_config8(dev, PCI_DEVICE_ID + 1, &val);
		pch_type = val;
	}

	return pch_type;
}

/**
 * pch_silicon_supported() - Check if a certain revision is supported
 *
 * @dev:	PCH device
 * @type:	PCH type
 * @rev:	Minimum required resion
 * @return 0 if not supported, 1 if supported
 */
static int pch_silicon_supported(struct udevice *dev, int type, int rev)
{
	int cur_type = pch_silicon_type(dev);
	int cur_rev = pch_silicon_revision(dev);

	switch (type) {
	case PCH_TYPE_CPT:
		/* CougarPoint minimum revision */
		if (cur_type == PCH_TYPE_CPT && cur_rev >= rev)
			return 1;
		/* PantherPoint any revision */
		if (cur_type == PCH_TYPE_PPT)
			return 1;
		break;

	case PCH_TYPE_PPT:
		/* PantherPoint minimum revision */
		if (cur_type == PCH_TYPE_PPT && cur_rev >= rev)
			return 1;
		break;
	}

	return 0;
}

#define IOBP_RETRY 1000
static inline int iobp_poll(void)
{
	unsigned try = IOBP_RETRY;
	u32 data;

	while (try--) {
		data = readl(RCB_REG(IOBPS));
		if ((data & 1) == 0)
			return 1;
		udelay(10);
	}

	printf("IOBP timeout\n");
	return 0;
}

void pch_iobp_update(struct udevice *dev, u32 address, u32 andvalue,
		     u32 orvalue)
{
	u32 data;

	/* Set the address */
	writel(address, RCB_REG(IOBPIRI));

	/* READ OPCODE */
	if (pch_silicon_supported(dev, PCH_TYPE_CPT, PCH_STEP_B0))
		writel(IOBPS_RW_BX, RCB_REG(IOBPS));
	else
		writel(IOBPS_READ_AX, RCB_REG(IOBPS));
	if (!iobp_poll())
		return;

	/* Read IOBP data */
	data = readl(RCB_REG(IOBPD));
	if (!iobp_poll())
		return;

	/* Check for successful transaction */
	if ((readl(RCB_REG(IOBPS)) & 0x6) != 0) {
		printf("IOBP read 0x%08x failed\n", address);
		return;
	}

	/* Update the data */
	data &= andvalue;
	data |= orvalue;

	/* WRITE OPCODE */
	if (pch_silicon_supported(dev, PCH_TYPE_CPT, PCH_STEP_B0))
		writel(IOBPS_RW_BX, RCB_REG(IOBPS));
	else
		writel(IOBPS_WRITE_AX, RCB_REG(IOBPS));
	if (!iobp_poll())
		return;

	/* Write IOBP data */
	writel(data, RCB_REG(IOBPD));
	if (!iobp_poll())
		return;
}

static int bd82x6x_probe(struct udevice *dev)
{
	if (!(gd->flags & GD_FLG_RELOC))
		return 0;

	/* Cause the SATA device to do its init */
	uclass_first_device(UCLASS_AHCI, &dev);

	return 0;
}
#endif /* CONFIG_HAVE_FSP */

static int bd82x6x_pch_get_spi_base(struct udevice *dev, ulong *sbasep)
{
	u32 rcba;

	dm_pci_read_config32(dev, PCH_RCBA, &rcba);
	/* Bits 31-14 are the base address, 13-1 are reserved, 0 is enable */
	rcba = rcba & 0xffffc000;
	*sbasep = rcba + 0x3800;

	return 0;
}

static int bd82x6x_set_spi_protect(struct udevice *dev, bool protect)
{
	return lpc_set_spi_protect(dev, BIOS_CTRL, protect);
}

static int bd82x6x_get_gpio_base(struct udevice *dev, u32 *gbasep)
{
	u32 base;

	/*
	 * GPIO_BASE moved to its current offset with ICH6, but prior to
	 * that it was unused (or undocumented). Check that it looks
	 * okay: not all ones or zeros.
	 *
	 * Note we don't need check bit0 here, because the Tunnel Creek
	 * GPIO base address register bit0 is reserved (read returns 0),
	 * while on the Ivybridge the bit0 is used to indicate it is an
	 * I/O space.
	 */
	dm_pci_read_config32(dev, GPIO_BASE, &base);
	if (base == 0x00000000 || base == 0xffffffff) {
		debug("%s: unexpected BASE value\n", __func__);
		return -ENODEV;
	}

	/*
	 * Okay, I guess we're looking at the right device. The actual
	 * GPIO registers are in the PCI device's I/O space, starting
	 * at the offset that we just read. Bit 0 indicates that it's
	 * an I/O address, not a memory address, so mask that off.
	 */
	*gbasep = base & 1 ? base & ~3 : base & ~15;

	return 0;
}

static int bd82x6x_ioctl(struct udevice *dev, enum pch_req_t req, void *data,
			 int size)
{
	u32 rcba, val;

	switch (req) {
	case PCH_REQ_HDA_CONFIG:
		dm_pci_read_config32(dev, PCH_RCBA, &rcba);
		val = readl(rcba + RCBA_AUDIO_CONFIG);
		if (!(val & RCBA_AUDIO_CONFIG_HDA))
			return -ENOENT;

		return val & RCBA_AUDIO_CONFIG_MASK;
	case PCH_REQ_PMBASE_INFO: {
		struct pch_pmbase_info *pm = data;
		int ret;

		/* Find the base address of the powermanagement registers */
		ret = dm_pci_read_config16(dev, 0x40, &pm->base);
		if (ret)
			return ret;
		pm->base &= 0xfffe;
		pm->gpio0_en_ofs = GPE0_EN;
		pm->pm1_sts_ofs = PM1_STS;
		pm->pm1_cnt_ofs = PM1_CNT;

		return 0;
	}
	default:
		return -ENOSYS;
	}
}

static const struct pch_ops bd82x6x_pch_ops = {
	.get_spi_base	= bd82x6x_pch_get_spi_base,
	.set_spi_protect = bd82x6x_set_spi_protect,
	.get_gpio_base	= bd82x6x_get_gpio_base,
	.ioctl		= bd82x6x_ioctl,
};

static const struct udevice_id bd82x6x_ids[] = {
	{ .compatible = "intel,bd82x6x" },
	{ }
};

U_BOOT_DRIVER(bd82x6x_drv) = {
	.name		= "bd82x6x",
	.id		= UCLASS_PCH,
	.of_match	= bd82x6x_ids,
#ifndef CONFIG_HAVE_FSP
	.probe		= bd82x6x_probe,
#endif
	.ops		= &bd82x6x_pch_ops,
};