xref: /openbmc/u-boot/arch/x86/cpu/ivybridge/bd82x6x.c (revision 0edd82e2)
1 /*
2  * Copyright (C) 2014 Google, Inc
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 #include <common.h>
7 #include <dm.h>
8 #include <errno.h>
9 #include <fdtdec.h>
10 #include <malloc.h>
11 #include <pch.h>
12 #include <syscon.h>
13 #include <asm/cpu.h>
14 #include <asm/io.h>
15 #include <asm/lapic.h>
16 #include <asm/pci.h>
17 #include <asm/arch/bd82x6x.h>
18 #include <asm/arch/model_206ax.h>
19 #include <asm/arch/pch.h>
20 #include <asm/arch/sandybridge.h>
21 
22 #define GPIO_BASE	0x48
23 #define BIOS_CTRL	0xdc
24 
25 #ifndef CONFIG_HAVE_FSP
26 static int pch_revision_id = -1;
27 static int pch_type = -1;
28 
29 /**
30  * pch_silicon_revision() - Read silicon revision ID from the PCH
31  *
32  * @dev:	PCH device
33  * @return silicon revision ID
34  */
35 static int pch_silicon_revision(struct udevice *dev)
36 {
37 	u8 val;
38 
39 	if (pch_revision_id < 0) {
40 		dm_pci_read_config8(dev, PCI_REVISION_ID, &val);
41 		pch_revision_id = val;
42 	}
43 
44 	return pch_revision_id;
45 }
46 
47 int pch_silicon_type(struct udevice *dev)
48 {
49 	u8 val;
50 
51 	if (pch_type < 0) {
52 		dm_pci_read_config8(dev, PCI_DEVICE_ID + 1, &val);
53 		pch_type = val;
54 	}
55 
56 	return pch_type;
57 }
58 
59 /**
60  * pch_silicon_supported() - Check if a certain revision is supported
61  *
62  * @dev:	PCH device
63  * @type:	PCH type
64  * @rev:	Minimum required resion
65  * @return 0 if not supported, 1 if supported
66  */
67 static int pch_silicon_supported(struct udevice *dev, int type, int rev)
68 {
69 	int cur_type = pch_silicon_type(dev);
70 	int cur_rev = pch_silicon_revision(dev);
71 
72 	switch (type) {
73 	case PCH_TYPE_CPT:
74 		/* CougarPoint minimum revision */
75 		if (cur_type == PCH_TYPE_CPT && cur_rev >= rev)
76 			return 1;
77 		/* PantherPoint any revision */
78 		if (cur_type == PCH_TYPE_PPT)
79 			return 1;
80 		break;
81 
82 	case PCH_TYPE_PPT:
83 		/* PantherPoint minimum revision */
84 		if (cur_type == PCH_TYPE_PPT && cur_rev >= rev)
85 			return 1;
86 		break;
87 	}
88 
89 	return 0;
90 }
91 
92 #define IOBP_RETRY 1000
93 static inline int iobp_poll(void)
94 {
95 	unsigned try = IOBP_RETRY;
96 	u32 data;
97 
98 	while (try--) {
99 		data = readl(RCB_REG(IOBPS));
100 		if ((data & 1) == 0)
101 			return 1;
102 		udelay(10);
103 	}
104 
105 	printf("IOBP timeout\n");
106 	return 0;
107 }
108 
109 void pch_iobp_update(struct udevice *dev, u32 address, u32 andvalue,
110 		     u32 orvalue)
111 {
112 	u32 data;
113 
114 	/* Set the address */
115 	writel(address, RCB_REG(IOBPIRI));
116 
117 	/* READ OPCODE */
118 	if (pch_silicon_supported(dev, PCH_TYPE_CPT, PCH_STEP_B0))
119 		writel(IOBPS_RW_BX, RCB_REG(IOBPS));
120 	else
121 		writel(IOBPS_READ_AX, RCB_REG(IOBPS));
122 	if (!iobp_poll())
123 		return;
124 
125 	/* Read IOBP data */
126 	data = readl(RCB_REG(IOBPD));
127 	if (!iobp_poll())
128 		return;
129 
130 	/* Check for successful transaction */
131 	if ((readl(RCB_REG(IOBPS)) & 0x6) != 0) {
132 		printf("IOBP read 0x%08x failed\n", address);
133 		return;
134 	}
135 
136 	/* Update the data */
137 	data &= andvalue;
138 	data |= orvalue;
139 
140 	/* WRITE OPCODE */
141 	if (pch_silicon_supported(dev, PCH_TYPE_CPT, PCH_STEP_B0))
142 		writel(IOBPS_RW_BX, RCB_REG(IOBPS));
143 	else
144 		writel(IOBPS_WRITE_AX, RCB_REG(IOBPS));
145 	if (!iobp_poll())
146 		return;
147 
148 	/* Write IOBP data */
149 	writel(data, RCB_REG(IOBPD));
150 	if (!iobp_poll())
151 		return;
152 }
153 
154 static int bd82x6x_probe(struct udevice *dev)
155 {
156 	struct udevice *gma_dev;
157 	int ret;
158 
159 	if (!(gd->flags & GD_FLG_RELOC))
160 		return 0;
161 
162 	/* Cause the SATA device to do its init */
163 	uclass_first_device(UCLASS_DISK, &dev);
164 
165 	ret = syscon_get_by_driver_data(X86_SYSCON_GMA, &gma_dev);
166 	if (ret)
167 		return ret;
168 	ret = gma_func0_init(gma_dev);
169 	if (ret)
170 		return ret;
171 
172 	return 0;
173 }
174 #endif /* CONFIG_HAVE_FSP */
175 
176 static int bd82x6x_pch_get_spi_base(struct udevice *dev, ulong *sbasep)
177 {
178 	u32 rcba;
179 
180 	dm_pci_read_config32(dev, PCH_RCBA, &rcba);
181 	/* Bits 31-14 are the base address, 13-1 are reserved, 0 is enable */
182 	rcba = rcba & 0xffffc000;
183 	*sbasep = rcba + 0x3800;
184 
185 	return 0;
186 }
187 
188 static int bd82x6x_set_spi_protect(struct udevice *dev, bool protect)
189 {
190 	uint8_t bios_cntl;
191 
192 	/* Adjust the BIOS write protect and SMM BIOS Write Protect Disable */
193 	dm_pci_read_config8(dev, BIOS_CTRL, &bios_cntl);
194 	if (protect) {
195 		bios_cntl &= ~BIOS_CTRL_BIOSWE;
196 		bios_cntl |= BIT(5);
197 	} else {
198 		bios_cntl |= BIOS_CTRL_BIOSWE;
199 		bios_cntl &= ~BIT(5);
200 	}
201 	dm_pci_write_config8(dev, BIOS_CTRL, bios_cntl);
202 
203 	return 0;
204 }
205 
206 static int bd82x6x_get_gpio_base(struct udevice *dev, u32 *gbasep)
207 {
208 	u32 base;
209 
210 	/*
211 	 * GPIO_BASE moved to its current offset with ICH6, but prior to
212 	 * that it was unused (or undocumented). Check that it looks
213 	 * okay: not all ones or zeros.
214 	 *
215 	 * Note we don't need check bit0 here, because the Tunnel Creek
216 	 * GPIO base address register bit0 is reserved (read returns 0),
217 	 * while on the Ivybridge the bit0 is used to indicate it is an
218 	 * I/O space.
219 	 */
220 	dm_pci_read_config32(dev, GPIO_BASE, &base);
221 	if (base == 0x00000000 || base == 0xffffffff) {
222 		debug("%s: unexpected BASE value\n", __func__);
223 		return -ENODEV;
224 	}
225 
226 	/*
227 	 * Okay, I guess we're looking at the right device. The actual
228 	 * GPIO registers are in the PCI device's I/O space, starting
229 	 * at the offset that we just read. Bit 0 indicates that it's
230 	 * an I/O address, not a memory address, so mask that off.
231 	 */
232 	*gbasep = base & 1 ? base & ~3 : base & ~15;
233 
234 	return 0;
235 }
236 
237 static const struct pch_ops bd82x6x_pch_ops = {
238 	.get_spi_base	= bd82x6x_pch_get_spi_base,
239 	.set_spi_protect = bd82x6x_set_spi_protect,
240 	.get_gpio_base	= bd82x6x_get_gpio_base,
241 };
242 
243 static const struct udevice_id bd82x6x_ids[] = {
244 	{ .compatible = "intel,bd82x6x" },
245 	{ }
246 };
247 
248 U_BOOT_DRIVER(bd82x6x_drv) = {
249 	.name		= "bd82x6x",
250 	.id		= UCLASS_PCH,
251 	.of_match	= bd82x6x_ids,
252 #ifndef CONFIG_HAVE_FSP
253 	.probe		= bd82x6x_probe,
254 #endif
255 	.ops		= &bd82x6x_pch_ops,
256 };
257