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