blob: 1346af4909d4138b568a3cae82a6e1d637181950 [file] [log] [blame]
Andrey Petrov04a72c42017-03-01 15:51:57 -08001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2017 Intel Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <arch/early_variables.h>
17#include <commonlib/helpers.h>
18#include <console/console.h>
19#include <delay.h>
20#include <device/pci.h>
21#include <device/pci_ids.h>
22#include <device/pci_ops.h>
23#include <intelblocks/cse.h>
24#include <soc/pci_devs.h>
Andrey Petrov04a72c42017-03-01 15:51:57 -080025#include <string.h>
26#include <timer.h>
27
28/* default window for early boot, must be at least 12 bytes in size */
29#define HECI1_BASE_ADDRESS 0xfed1a000
30
31/* Wait up to 15 sec for HECI to get ready */
32#define HECI_DELAY_READY (15 * 1000)
33/* Wait up to 100 usec between circullar buffer polls */
34#define HECI_DELAY 100
35/* Wait up to 5 sec for CSE to chew something we sent */
36#define HECI_SEND_TIMEOUT (5 * 1000)
37/* Wait up to 5 sec for CSE to blurp a reply */
38#define HECI_READ_TIMEOUT (5 * 1000)
39
40#define SLOT_SIZE sizeof(uint32_t)
41
42#define MMIO_CSE_CB_WW 0x00
43#define MMIO_HOST_CSR 0x04
44#define MMIO_CSE_CB_RW 0x08
45#define MMIO_CSE_CSR 0x0c
46
47#define CSR_IE (1 << 0)
48#define CSR_IS (1 << 1)
49#define CSR_IG (1 << 2)
50#define CSR_READY (1 << 3)
51#define CSR_RESET (1 << 4)
52#define CSR_RP_START 8
53#define CSR_RP (((1 << 8) - 1) << CSR_RP_START)
54#define CSR_WP_START 16
55#define CSR_WP (((1 << 8) - 1) << CSR_WP_START)
56#define CSR_CBD_START 24
57#define CSR_CBD (((1 << 8) - 1) << CSR_CBD_START)
58
59#define MEI_HDR_IS_COMPLETE (1 << 31)
60#define MEI_HDR_LENGTH_START 16
61#define MEI_HDR_LENGTH_SIZE 9
62#define MEI_HDR_LENGTH (((1 << MEI_HDR_LENGTH_SIZE) - 1) \
63 << MEI_HDR_LENGTH_START)
64#define MEI_HDR_HOST_ADDR_START 8
65#define MEI_HDR_HOST_ADDR (((1 << 8) - 1) << MEI_HDR_HOST_ADDR_START)
66#define MEI_HDR_CSE_ADDR_START 0
67#define MEI_HDR_CSE_ADDR (((1 << 8) - 1) << MEI_HDR_CSE_ADDR_START)
68
69
70struct cse_device {
71 uintptr_t sec_bar;
72} g_cse CAR_GLOBAL;
73
74/*
75 * Initialize the device with provided temporary BAR. If BAR is 0 use a
76 * default. This is intended for pre-mem usage only where BARs haven't been
77 * assigned yet and devices are not enabled.
78 */
79void heci_init(uintptr_t tempbar)
80{
81 struct cse_device *cse = car_get_var_ptr(&g_cse);
Subrata Banik2ee54db2017-03-05 12:37:00 +053082 device_t dev = PCH_DEV_CSE;
Andrey Petrov04a72c42017-03-01 15:51:57 -080083 u8 pcireg;
84
85 /* Assume it is already initialized, nothing else to do */
86 if (cse->sec_bar)
87 return;
88
89 /* Use default pre-ram bar */
90 if (!tempbar)
91 tempbar = HECI1_BASE_ADDRESS;
92
93 /* Assign Resources to HECI1 */
94 /* Clear BIT 1-2 of Command Register */
95 pcireg = pci_read_config8(dev, PCI_COMMAND);
96 pcireg &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
97 pci_write_config8(dev, PCI_COMMAND, pcireg);
98
99 /* Program Temporary BAR for HECI1 */
100 pci_write_config32(dev, PCI_BASE_ADDRESS_0, tempbar);
101 pci_write_config32(dev, PCI_BASE_ADDRESS_1, 0x0);
102
103 /* Enable Bus Master and MMIO Space */
104 pcireg = pci_read_config8(dev, PCI_COMMAND);
105 pcireg |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
106 pci_write_config8(dev, PCI_COMMAND, pcireg);
107
108 cse->sec_bar = tempbar;
109}
110
111static uint32_t read_bar(uint32_t offset)
112{
113 struct cse_device *cse = car_get_var_ptr(&g_cse);
114 return read32((void *)(cse->sec_bar + offset));
115}
116
117static void write_bar(uint32_t offset, uint32_t val)
118{
119 struct cse_device *cse = car_get_var_ptr(&g_cse);
120 return write32((void *)(cse->sec_bar + offset), val);
121}
122
123static uint32_t read_cse_csr(void)
124{
125 return read_bar(MMIO_CSE_CSR);
126}
127
128static uint32_t read_host_csr(void)
129{
130 return read_bar(MMIO_HOST_CSR);
131}
132
133static void write_host_csr(uint32_t data)
134{
135 write_bar(MMIO_HOST_CSR, data);
136}
137
138static size_t filled_slots(uint32_t data)
139{
140 uint8_t wp, rp;
141 rp = data >> CSR_RP_START;
142 wp = data >> CSR_WP_START;
143 return (uint8_t) (wp - rp);
144}
145
146static size_t cse_filled_slots(void)
147{
148 return filled_slots(read_cse_csr());
149}
150
151static size_t host_empty_slots(void)
152{
153 uint32_t csr;
154 csr = read_host_csr();
155
156 return ((csr & CSR_CBD) >> CSR_CBD_START) - filled_slots(csr);
157}
158
159static void clear_int(void)
160{
161 uint32_t csr;
162 csr = read_host_csr();
163 csr |= CSR_IS;
164 write_host_csr(csr);
165}
166
167static uint32_t read_slot(void)
168{
169 return read_bar(MMIO_CSE_CB_RW);
170}
171
172static void write_slot(uint32_t val)
173{
174 write_bar(MMIO_CSE_CB_WW, val);
175}
176
177static int wait_write_slots(size_t cnt)
178{
179 struct stopwatch sw;
180
181 stopwatch_init_msecs_expire(&sw, HECI_SEND_TIMEOUT);
182 while (host_empty_slots() < cnt) {
183 udelay(HECI_DELAY);
184 if (stopwatch_expired(&sw)) {
185 printk(BIOS_ERR, "HECI: timeout, buffer not drained\n");
186 return 0;
187 }
188 }
189 return 1;
190}
191
192static int wait_read_slots(size_t cnt)
193{
194 struct stopwatch sw;
195
196 stopwatch_init_msecs_expire(&sw, HECI_READ_TIMEOUT);
197 while (cse_filled_slots() < cnt) {
198 udelay(HECI_DELAY);
199 if (stopwatch_expired(&sw)) {
200 printk(BIOS_ERR, "HECI: timed out reading answer!\n");
201 return 0;
202 }
203 }
204 return 1;
205}
206
207/* get number of full 4-byte slots */
208static size_t bytes_to_slots(size_t bytes)
209{
210 return ALIGN_UP(bytes, SLOT_SIZE) / SLOT_SIZE;
211}
212
213static int cse_ready(void)
214{
215 uint32_t csr;
216 csr = read_cse_csr();
217 return csr & CSR_READY;
218}
219
220static int wait_heci_ready(void)
221{
222 struct stopwatch sw;
223
224 stopwatch_init_msecs_expire(&sw, HECI_DELAY_READY);
225 while (!cse_ready()) {
226 udelay(HECI_DELAY);
227 if (stopwatch_expired(&sw))
228 return 0;
229 }
230
231 return 1;
232}
233
234static void host_gen_interrupt(void)
235{
236 uint32_t csr;
237 csr = read_host_csr();
238 csr |= CSR_IG;
239 write_host_csr(csr);
240}
241
242static size_t hdr_get_length(uint32_t hdr)
243{
244 return (hdr & MEI_HDR_LENGTH) >> MEI_HDR_LENGTH_START;
245}
246
247static int
248send_one_message(uint32_t hdr, const void *buff)
249{
250 size_t pend_len, pend_slots, remainder, i;
251 uint32_t tmp;
252 const uint32_t *p = buff;
253
254 /* Get space for the header */
255 if (!wait_write_slots(1))
256 return 0;
257
258 /* First, write header */
259 write_slot(hdr);
260
261 pend_len = hdr_get_length(hdr);
262 pend_slots = bytes_to_slots(pend_len);
263
264 if (!wait_write_slots(pend_slots))
265 return 0;
266
267 /* Write the body in whole slots */
268 i = 0;
269 while (i < ALIGN_DOWN(pend_len, SLOT_SIZE)) {
270 write_slot(*p++);
271 i += SLOT_SIZE;
272 }
273
274 remainder = pend_len % SLOT_SIZE;
275 /* Pad to 4 bytes not touching caller's buffer */
276 if (remainder) {
277 memcpy(&tmp, p, remainder);
278 write_slot(tmp);
279 }
280
281 host_gen_interrupt();
282
283 /* Make sure nothing bad happened during transmission */
284 if (!cse_ready())
285 return 0;
286
287 return pend_len;
288}
289
290int
291heci_send(const void *msg, size_t len, uint8_t host_addr, uint8_t client_addr)
292{
293 uint32_t csr, hdr;
294 size_t sent = 0, remaining, cb_size, max_length;
295 uint8_t *p = (uint8_t *) msg;
296
297 if (!msg || !len)
298 return 0;
299
300 clear_int();
301
302 if (!wait_heci_ready()) {
303 printk(BIOS_ERR, "HECI: not ready\n");
304 return 0;
305 }
306
307 csr = read_cse_csr();
308 cb_size = ((csr & CSR_CBD) >> CSR_CBD_START) * SLOT_SIZE;
309 /*
310 * Reserve one slot for the header. Limit max message length by 9
311 * bits that are available in the header.
312 */
313 max_length = MIN(cb_size, (1 << MEI_HDR_LENGTH_SIZE) - 1) - SLOT_SIZE;
314 remaining = len;
315
316 /*
317 * Fragment the message into smaller messages not exceeding useful
318 * circullar buffer length. Mark last message complete.
319 */
320 do {
321 hdr = MIN(max_length, remaining) << MEI_HDR_LENGTH_START;
322 hdr |= client_addr << MEI_HDR_CSE_ADDR_START;
323 hdr |= host_addr << MEI_HDR_HOST_ADDR_START;
324 hdr |= (MIN(max_length, remaining) == remaining) ?
Lee Leahy68ab0b52017-03-10 13:42:34 -0800325 MEI_HDR_IS_COMPLETE : 0;
Andrey Petrov04a72c42017-03-01 15:51:57 -0800326 sent = send_one_message(hdr, p);
327 p += sent;
328 remaining -= sent;
329 } while (remaining > 0 && sent != 0);
330
331 return remaining == 0;
332}
333
334static size_t
335recv_one_message(uint32_t *hdr, void *buff, size_t maxlen)
336{
337 uint32_t reg, *p = buff;
338 size_t recv_slots, recv_len, remainder, i;
339
340 /* first get the header */
341 if (!wait_read_slots(1))
342 return 0;
343
344 *hdr = read_slot();
345 recv_len = hdr_get_length(*hdr);
346
347 if (!recv_len)
348 printk(BIOS_WARNING, "HECI: message is zero-sized\n");
349
350 recv_slots = bytes_to_slots(recv_len);
351
352 i = 0;
353 if (recv_len > maxlen) {
354 printk(BIOS_ERR, "HECI: response is too big\n");
355 return 0;
356 }
357
358 /* wait for the rest of messages to arrive */
359 wait_read_slots(recv_slots);
360
361 /* fetch whole slots first */
362 while (i < ALIGN_DOWN(recv_len, SLOT_SIZE)) {
363 *p++ = read_slot();
364 i += SLOT_SIZE;
365 }
366
367 remainder = recv_len % SLOT_SIZE;
368
369 if (remainder) {
370 reg = read_slot();
371 memcpy(p, &reg, remainder);
372 }
373
374 return recv_len;
375}
376
377int heci_receive(void *buff, size_t *maxlen)
378{
379 size_t left, received;
380 uint32_t hdr = 0;
381 uint8_t *p = buff;
382
383 if (!buff || !maxlen || !*maxlen)
384 return 0;
385
386 left = *maxlen;
387
388 clear_int();
389
390 if (!wait_heci_ready()) {
391 printk(BIOS_ERR, "HECI: not ready\n");
392 return 0;
393 }
394
395 /*
396 * Receive multiple packets until we meet one marked complete or we run
397 * out of space in caller-provided buffer.
398 */
399 do {
400 received = recv_one_message(&hdr, p, left);
401 left -= received;
402 p += received;
403 /* If we read out everything ping to send more */
404 if (!(hdr & MEI_HDR_IS_COMPLETE) && !cse_filled_slots())
405 host_gen_interrupt();
406 } while (received && !(hdr & MEI_HDR_IS_COMPLETE) && left > 0);
407
408 *maxlen = p - (uint8_t *) buff;
409
410 /* If ME is not ready, something went wrong and we received junk */
411 if (!cse_ready())
412 return 0;
413
414 return !!((hdr & MEI_HDR_IS_COMPLETE) && received);
415}
416
417/*
418 * Attempt to reset the device. This is useful when host and ME are out
419 * of sync during transmission or ME didn't understand the message.
420 */
421int heci_reset(void)
422{
423 uint32_t csr;
424
425 /* Send reset request */
426 csr = read_host_csr();
427 csr |= CSR_RESET;
428 csr |= CSR_IG;
429 write_host_csr(csr);
430
431 if (wait_heci_ready()) {
432 /* Device is back on its imaginary feet, clear reset */
433 csr = read_host_csr();
434 csr &= ~CSR_RESET;
435 csr |= CSR_IG;
436 csr |= CSR_READY;
437 write_host_csr(csr);
438 return 1;
439 }
440
441 printk(BIOS_CRIT, "HECI: reset failed\n");
442
443 return 0;
444}
445
446#if ENV_RAMSTAGE
447
448static void update_sec_bar(struct device *dev)
449{
450 g_cse.sec_bar = find_resource(dev, PCI_BASE_ADDRESS_0)->base;
451}
452
453static void cse_set_resources(struct device *dev)
454{
Subrata Banik2ee54db2017-03-05 12:37:00 +0530455 if (dev->path.pci.devfn == PCH_DEVFN_CSE)
Andrey Petrov04a72c42017-03-01 15:51:57 -0800456 update_sec_bar(dev);
457
458 pci_dev_set_resources(dev);
459}
460
461static struct device_operations cse_ops = {
462 .set_resources = cse_set_resources,
463 .read_resources = pci_dev_read_resources,
464 .enable_resources = pci_dev_enable_resources,
465 .init = pci_dev_init,
Andrey Petrov04a72c42017-03-01 15:51:57 -0800466};
467
Hannah Williams63142152017-06-12 14:03:18 -0700468static const unsigned short pci_device_ids[] = {
469 PCI_DEVICE_ID_INTEL_APL_CSE0,
470 PCI_DEVICE_ID_INTEL_GLK_CSE0,
471 0,
472};
473
Andrey Petrov04a72c42017-03-01 15:51:57 -0800474static const struct pci_driver cse_driver __pci_driver = {
475 .ops = &cse_ops,
476 .vendor = PCI_VENDOR_ID_INTEL,
477 /* SoC/chipset needs to provide PCI device ID */
Hannah Williams63142152017-06-12 14:03:18 -0700478 .devices = pci_device_ids,
Andrey Petrov04a72c42017-03-01 15:51:57 -0800479};
480
481#endif