blob: 302e6dec05e14a32688af09a22ca94acd209c5e2 [file] [log] [blame]
Andrey Petrov04a72c42017-03-01 15:51:57 -08001/*
2 * This file is part of the coreboot project.
3 *
praveen hodagatta praneshe26c4a42018-09-20 03:49:45 +08004 * Copyright 2017-2018 Intel Inc.
Andrey Petrov04a72c42017-03-01 15:51:57 -08005 *
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>
Subrata Banik05e06cd2017-11-09 15:04:09 +053017#include <assert.h>
Andrey Petrov04a72c42017-03-01 15:51:57 -080018#include <commonlib/helpers.h>
19#include <console/console.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +020020#include <device/mmio.h>
Andrey Petrov04a72c42017-03-01 15:51:57 -080021#include <delay.h>
22#include <device/pci.h>
23#include <device/pci_ids.h>
24#include <device/pci_ops.h>
25#include <intelblocks/cse.h>
Subrata Banik05e06cd2017-11-09 15:04:09 +053026#include <soc/iomap.h>
Andrey Petrov04a72c42017-03-01 15:51:57 -080027#include <soc/pci_devs.h>
Andrey Petrov04a72c42017-03-01 15:51:57 -080028#include <string.h>
29#include <timer.h>
30
Subrata Banik5c08c732017-11-13 14:54:37 +053031#define MAX_HECI_MESSAGE_RETRY_COUNT 5
32
Andrey Petrov04a72c42017-03-01 15:51:57 -080033/* Wait up to 15 sec for HECI to get ready */
34#define HECI_DELAY_READY (15 * 1000)
Jonathan Neuschäfer5268b762018-02-12 12:24:25 +010035/* Wait up to 100 usec between circular buffer polls */
Andrey Petrov04a72c42017-03-01 15:51:57 -080036#define HECI_DELAY 100
37/* Wait up to 5 sec for CSE to chew something we sent */
38#define HECI_SEND_TIMEOUT (5 * 1000)
39/* Wait up to 5 sec for CSE to blurp a reply */
40#define HECI_READ_TIMEOUT (5 * 1000)
41
42#define SLOT_SIZE sizeof(uint32_t)
43
44#define MMIO_CSE_CB_WW 0x00
45#define MMIO_HOST_CSR 0x04
46#define MMIO_CSE_CB_RW 0x08
47#define MMIO_CSE_CSR 0x0c
48
49#define CSR_IE (1 << 0)
50#define CSR_IS (1 << 1)
51#define CSR_IG (1 << 2)
52#define CSR_READY (1 << 3)
53#define CSR_RESET (1 << 4)
54#define CSR_RP_START 8
55#define CSR_RP (((1 << 8) - 1) << CSR_RP_START)
56#define CSR_WP_START 16
57#define CSR_WP (((1 << 8) - 1) << CSR_WP_START)
58#define CSR_CBD_START 24
59#define CSR_CBD (((1 << 8) - 1) << CSR_CBD_START)
60
61#define MEI_HDR_IS_COMPLETE (1 << 31)
62#define MEI_HDR_LENGTH_START 16
63#define MEI_HDR_LENGTH_SIZE 9
64#define MEI_HDR_LENGTH (((1 << MEI_HDR_LENGTH_SIZE) - 1) \
65 << MEI_HDR_LENGTH_START)
66#define MEI_HDR_HOST_ADDR_START 8
67#define MEI_HDR_HOST_ADDR (((1 << 8) - 1) << MEI_HDR_HOST_ADDR_START)
68#define MEI_HDR_CSE_ADDR_START 0
69#define MEI_HDR_CSE_ADDR (((1 << 8) - 1) << MEI_HDR_CSE_ADDR_START)
70
71
72struct cse_device {
73 uintptr_t sec_bar;
74} g_cse CAR_GLOBAL;
75
76/*
77 * Initialize the device with provided temporary BAR. If BAR is 0 use a
78 * default. This is intended for pre-mem usage only where BARs haven't been
79 * assigned yet and devices are not enabled.
80 */
81void heci_init(uintptr_t tempbar)
82{
83 struct cse_device *cse = car_get_var_ptr(&g_cse);
Elyes HAOUAS68c851b2018-06-12 22:06:09 +020084#if defined(__SIMPLE_DEVICE__)
85 pci_devfn_t dev = PCH_DEV_CSE;
86#else
87 struct device *dev = PCH_DEV_CSE;
88#endif
Andrey Petrov04a72c42017-03-01 15:51:57 -080089 u8 pcireg;
90
91 /* Assume it is already initialized, nothing else to do */
92 if (cse->sec_bar)
93 return;
94
95 /* Use default pre-ram bar */
96 if (!tempbar)
97 tempbar = HECI1_BASE_ADDRESS;
98
99 /* Assign Resources to HECI1 */
100 /* Clear BIT 1-2 of Command Register */
101 pcireg = pci_read_config8(dev, PCI_COMMAND);
102 pcireg &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
103 pci_write_config8(dev, PCI_COMMAND, pcireg);
104
105 /* Program Temporary BAR for HECI1 */
106 pci_write_config32(dev, PCI_BASE_ADDRESS_0, tempbar);
107 pci_write_config32(dev, PCI_BASE_ADDRESS_1, 0x0);
108
109 /* Enable Bus Master and MMIO Space */
110 pcireg = pci_read_config8(dev, PCI_COMMAND);
111 pcireg |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
112 pci_write_config8(dev, PCI_COMMAND, pcireg);
113
114 cse->sec_bar = tempbar;
115}
116
Subrata Banik05e06cd2017-11-09 15:04:09 +0530117/* Get HECI BAR 0 from PCI configuration space */
118static uint32_t get_cse_bar(void)
119{
120 uintptr_t bar;
121
122 bar = pci_read_config32(PCH_DEV_CSE, PCI_BASE_ADDRESS_0);
123 assert(bar != 0);
124 /*
125 * Bits 31-12 are the base address as per EDS for SPI,
126 * Don't care about 0-11 bit
127 */
128 return bar & ~PCI_BASE_ADDRESS_MEM_ATTR_MASK;
129}
130
Andrey Petrov04a72c42017-03-01 15:51:57 -0800131static uint32_t read_bar(uint32_t offset)
132{
133 struct cse_device *cse = car_get_var_ptr(&g_cse);
Jonathan Neuschäfer5268b762018-02-12 12:24:25 +0100134 /* Reach PCI config space to get BAR in case CAR global not available */
Subrata Banik05e06cd2017-11-09 15:04:09 +0530135 if (!cse->sec_bar)
136 cse->sec_bar = get_cse_bar();
Andrey Petrov04a72c42017-03-01 15:51:57 -0800137 return read32((void *)(cse->sec_bar + offset));
138}
139
140static void write_bar(uint32_t offset, uint32_t val)
141{
142 struct cse_device *cse = car_get_var_ptr(&g_cse);
Jonathan Neuschäfer5268b762018-02-12 12:24:25 +0100143 /* Reach PCI config space to get BAR in case CAR global not available */
Subrata Banik05e06cd2017-11-09 15:04:09 +0530144 if (!cse->sec_bar)
145 cse->sec_bar = get_cse_bar();
Andrey Petrov04a72c42017-03-01 15:51:57 -0800146 return write32((void *)(cse->sec_bar + offset), val);
147}
148
149static uint32_t read_cse_csr(void)
150{
151 return read_bar(MMIO_CSE_CSR);
152}
153
154static uint32_t read_host_csr(void)
155{
156 return read_bar(MMIO_HOST_CSR);
157}
158
159static void write_host_csr(uint32_t data)
160{
161 write_bar(MMIO_HOST_CSR, data);
162}
163
164static size_t filled_slots(uint32_t data)
165{
166 uint8_t wp, rp;
167 rp = data >> CSR_RP_START;
168 wp = data >> CSR_WP_START;
169 return (uint8_t) (wp - rp);
170}
171
172static size_t cse_filled_slots(void)
173{
174 return filled_slots(read_cse_csr());
175}
176
177static size_t host_empty_slots(void)
178{
179 uint32_t csr;
180 csr = read_host_csr();
181
182 return ((csr & CSR_CBD) >> CSR_CBD_START) - filled_slots(csr);
183}
184
185static void clear_int(void)
186{
187 uint32_t csr;
188 csr = read_host_csr();
189 csr |= CSR_IS;
190 write_host_csr(csr);
191}
192
193static uint32_t read_slot(void)
194{
195 return read_bar(MMIO_CSE_CB_RW);
196}
197
198static void write_slot(uint32_t val)
199{
200 write_bar(MMIO_CSE_CB_WW, val);
201}
202
203static int wait_write_slots(size_t cnt)
204{
205 struct stopwatch sw;
206
207 stopwatch_init_msecs_expire(&sw, HECI_SEND_TIMEOUT);
208 while (host_empty_slots() < cnt) {
209 udelay(HECI_DELAY);
210 if (stopwatch_expired(&sw)) {
211 printk(BIOS_ERR, "HECI: timeout, buffer not drained\n");
212 return 0;
213 }
214 }
215 return 1;
216}
217
218static int wait_read_slots(size_t cnt)
219{
220 struct stopwatch sw;
221
222 stopwatch_init_msecs_expire(&sw, HECI_READ_TIMEOUT);
223 while (cse_filled_slots() < cnt) {
224 udelay(HECI_DELAY);
225 if (stopwatch_expired(&sw)) {
226 printk(BIOS_ERR, "HECI: timed out reading answer!\n");
227 return 0;
228 }
229 }
230 return 1;
231}
232
233/* get number of full 4-byte slots */
234static size_t bytes_to_slots(size_t bytes)
235{
236 return ALIGN_UP(bytes, SLOT_SIZE) / SLOT_SIZE;
237}
238
239static int cse_ready(void)
240{
241 uint32_t csr;
242 csr = read_cse_csr();
243 return csr & CSR_READY;
244}
245
246static int wait_heci_ready(void)
247{
248 struct stopwatch sw;
249
250 stopwatch_init_msecs_expire(&sw, HECI_DELAY_READY);
251 while (!cse_ready()) {
252 udelay(HECI_DELAY);
253 if (stopwatch_expired(&sw))
254 return 0;
255 }
256
257 return 1;
258}
259
260static void host_gen_interrupt(void)
261{
262 uint32_t csr;
263 csr = read_host_csr();
264 csr |= CSR_IG;
265 write_host_csr(csr);
266}
267
268static size_t hdr_get_length(uint32_t hdr)
269{
270 return (hdr & MEI_HDR_LENGTH) >> MEI_HDR_LENGTH_START;
271}
272
273static int
274send_one_message(uint32_t hdr, const void *buff)
275{
276 size_t pend_len, pend_slots, remainder, i;
277 uint32_t tmp;
278 const uint32_t *p = buff;
279
280 /* Get space for the header */
281 if (!wait_write_slots(1))
282 return 0;
283
284 /* First, write header */
285 write_slot(hdr);
286
287 pend_len = hdr_get_length(hdr);
288 pend_slots = bytes_to_slots(pend_len);
289
290 if (!wait_write_slots(pend_slots))
291 return 0;
292
293 /* Write the body in whole slots */
294 i = 0;
295 while (i < ALIGN_DOWN(pend_len, SLOT_SIZE)) {
296 write_slot(*p++);
297 i += SLOT_SIZE;
298 }
299
300 remainder = pend_len % SLOT_SIZE;
301 /* Pad to 4 bytes not touching caller's buffer */
302 if (remainder) {
303 memcpy(&tmp, p, remainder);
304 write_slot(tmp);
305 }
306
307 host_gen_interrupt();
308
309 /* Make sure nothing bad happened during transmission */
310 if (!cse_ready())
311 return 0;
312
313 return pend_len;
314}
315
316int
317heci_send(const void *msg, size_t len, uint8_t host_addr, uint8_t client_addr)
318{
Subrata Banik5c08c732017-11-13 14:54:37 +0530319 uint8_t retry;
Andrey Petrov04a72c42017-03-01 15:51:57 -0800320 uint32_t csr, hdr;
Subrata Banik5c08c732017-11-13 14:54:37 +0530321 size_t sent, remaining, cb_size, max_length;
322 const uint8_t *p;
Andrey Petrov04a72c42017-03-01 15:51:57 -0800323
324 if (!msg || !len)
325 return 0;
326
327 clear_int();
328
Subrata Banik5c08c732017-11-13 14:54:37 +0530329 for (retry = 0; retry < MAX_HECI_MESSAGE_RETRY_COUNT; retry++) {
330 p = msg;
Andrey Petrov04a72c42017-03-01 15:51:57 -0800331
Subrata Banik5c08c732017-11-13 14:54:37 +0530332 if (!wait_heci_ready()) {
333 printk(BIOS_ERR, "HECI: not ready\n");
334 continue;
335 }
Andrey Petrov04a72c42017-03-01 15:51:57 -0800336
Subrata Banik4a722f52017-11-13 14:56:42 +0530337 csr = read_host_csr();
Subrata Banik5c08c732017-11-13 14:54:37 +0530338 cb_size = ((csr & CSR_CBD) >> CSR_CBD_START) * SLOT_SIZE;
339 /*
340 * Reserve one slot for the header. Limit max message
341 * length by 9 bits that are available in the header.
342 */
343 max_length = MIN(cb_size, (1 << MEI_HDR_LENGTH_SIZE) - 1)
344 - SLOT_SIZE;
345 remaining = len;
346
347 /*
348 * Fragment the message into smaller messages not exceeding
Jonathan Neuschäfer5268b762018-02-12 12:24:25 +0100349 * useful circular buffer length. Mark last message complete.
Subrata Banik5c08c732017-11-13 14:54:37 +0530350 */
351 do {
352 hdr = MIN(max_length, remaining)
353 << MEI_HDR_LENGTH_START;
354 hdr |= client_addr << MEI_HDR_CSE_ADDR_START;
355 hdr |= host_addr << MEI_HDR_HOST_ADDR_START;
356 hdr |= (MIN(max_length, remaining) == remaining) ?
Lee Leahy68ab0b52017-03-10 13:42:34 -0800357 MEI_HDR_IS_COMPLETE : 0;
Subrata Banik5c08c732017-11-13 14:54:37 +0530358 sent = send_one_message(hdr, p);
359 p += sent;
360 remaining -= sent;
361 } while (remaining > 0 && sent != 0);
Andrey Petrov04a72c42017-03-01 15:51:57 -0800362
Subrata Banik5c08c732017-11-13 14:54:37 +0530363 if (!remaining)
364 return 1;
365 }
366 return 0;
Andrey Petrov04a72c42017-03-01 15:51:57 -0800367}
368
369static size_t
370recv_one_message(uint32_t *hdr, void *buff, size_t maxlen)
371{
372 uint32_t reg, *p = buff;
373 size_t recv_slots, recv_len, remainder, i;
374
375 /* first get the header */
376 if (!wait_read_slots(1))
377 return 0;
378
379 *hdr = read_slot();
380 recv_len = hdr_get_length(*hdr);
381
382 if (!recv_len)
383 printk(BIOS_WARNING, "HECI: message is zero-sized\n");
384
385 recv_slots = bytes_to_slots(recv_len);
386
387 i = 0;
388 if (recv_len > maxlen) {
389 printk(BIOS_ERR, "HECI: response is too big\n");
390 return 0;
391 }
392
393 /* wait for the rest of messages to arrive */
394 wait_read_slots(recv_slots);
395
396 /* fetch whole slots first */
397 while (i < ALIGN_DOWN(recv_len, SLOT_SIZE)) {
398 *p++ = read_slot();
399 i += SLOT_SIZE;
400 }
401
Subrata Banik5c08c732017-11-13 14:54:37 +0530402 /*
403 * If ME is not ready, something went wrong and
404 * we received junk
405 */
406 if (!cse_ready())
407 return 0;
408
Andrey Petrov04a72c42017-03-01 15:51:57 -0800409 remainder = recv_len % SLOT_SIZE;
410
411 if (remainder) {
412 reg = read_slot();
413 memcpy(p, &reg, remainder);
414 }
415
416 return recv_len;
417}
418
419int heci_receive(void *buff, size_t *maxlen)
420{
Subrata Banik5c08c732017-11-13 14:54:37 +0530421 uint8_t retry;
Andrey Petrov04a72c42017-03-01 15:51:57 -0800422 size_t left, received;
423 uint32_t hdr = 0;
Subrata Banik5c08c732017-11-13 14:54:37 +0530424 uint8_t *p;
Andrey Petrov04a72c42017-03-01 15:51:57 -0800425
426 if (!buff || !maxlen || !*maxlen)
427 return 0;
428
Andrey Petrov04a72c42017-03-01 15:51:57 -0800429 clear_int();
430
Subrata Banik5c08c732017-11-13 14:54:37 +0530431 for (retry = 0; retry < MAX_HECI_MESSAGE_RETRY_COUNT; retry++) {
432 p = buff;
433 left = *maxlen;
434
435 if (!wait_heci_ready()) {
436 printk(BIOS_ERR, "HECI: not ready\n");
437 continue;
438 }
439
440 /*
441 * Receive multiple packets until we meet one marked
442 * complete or we run out of space in caller-provided buffer.
443 */
444 do {
445 received = recv_one_message(&hdr, p, left);
Lijian Zhaoc50296d2017-12-15 19:10:18 -0800446 if (!received) {
Elyes HAOUAS3d450002018-08-09 18:55:58 +0200447 printk(BIOS_ERR, "HECI: Failed to receive!\n");
Lijian Zhaoc50296d2017-12-15 19:10:18 -0800448 return 0;
449 }
Subrata Banik5c08c732017-11-13 14:54:37 +0530450 left -= received;
451 p += received;
452 /* If we read out everything ping to send more */
453 if (!(hdr & MEI_HDR_IS_COMPLETE) && !cse_filled_slots())
454 host_gen_interrupt();
455 } while (received && !(hdr & MEI_HDR_IS_COMPLETE) && left > 0);
456
457 if ((hdr & MEI_HDR_IS_COMPLETE) && received) {
458 *maxlen = p - (uint8_t *) buff;
459 return 1;
460 }
Andrey Petrov04a72c42017-03-01 15:51:57 -0800461 }
Subrata Banik5c08c732017-11-13 14:54:37 +0530462 return 0;
Andrey Petrov04a72c42017-03-01 15:51:57 -0800463}
464
465/*
466 * Attempt to reset the device. This is useful when host and ME are out
467 * of sync during transmission or ME didn't understand the message.
468 */
469int heci_reset(void)
470{
471 uint32_t csr;
472
473 /* Send reset request */
474 csr = read_host_csr();
475 csr |= CSR_RESET;
476 csr |= CSR_IG;
477 write_host_csr(csr);
478
479 if (wait_heci_ready()) {
480 /* Device is back on its imaginary feet, clear reset */
481 csr = read_host_csr();
482 csr &= ~CSR_RESET;
483 csr |= CSR_IG;
484 csr |= CSR_READY;
485 write_host_csr(csr);
486 return 1;
487 }
488
489 printk(BIOS_CRIT, "HECI: reset failed\n");
490
491 return 0;
492}
493
494#if ENV_RAMSTAGE
495
496static void update_sec_bar(struct device *dev)
497{
498 g_cse.sec_bar = find_resource(dev, PCI_BASE_ADDRESS_0)->base;
499}
500
501static void cse_set_resources(struct device *dev)
502{
Subrata Banik2ee54db2017-03-05 12:37:00 +0530503 if (dev->path.pci.devfn == PCH_DEVFN_CSE)
Andrey Petrov04a72c42017-03-01 15:51:57 -0800504 update_sec_bar(dev);
505
506 pci_dev_set_resources(dev);
507}
508
509static struct device_operations cse_ops = {
510 .set_resources = cse_set_resources,
511 .read_resources = pci_dev_read_resources,
512 .enable_resources = pci_dev_enable_resources,
513 .init = pci_dev_init,
Subrata Banik6bbc91a2017-12-07 14:55:51 +0530514 .ops_pci = &pci_dev_ops_pci,
Andrey Petrov04a72c42017-03-01 15:51:57 -0800515};
516
Hannah Williams63142152017-06-12 14:03:18 -0700517static const unsigned short pci_device_ids[] = {
518 PCI_DEVICE_ID_INTEL_APL_CSE0,
519 PCI_DEVICE_ID_INTEL_GLK_CSE0,
Andrey Petrov0405de92017-06-05 13:25:29 -0700520 PCI_DEVICE_ID_INTEL_CNL_CSE0,
Subrata Banikd0586d22017-11-27 13:28:41 +0530521 PCI_DEVICE_ID_INTEL_SKL_CSE0,
praveen hodagatta praneshe26c4a42018-09-20 03:49:45 +0800522 PCI_DEVICE_ID_INTEL_CNP_H_CSE0,
Aamir Bohra9eac0392018-06-30 12:07:04 +0530523 PCI_DEVICE_ID_INTEL_ICL_CSE0,
Ronak Kanabarda7ffb482019-02-05 01:51:13 +0530524 PCI_DEVICE_ID_INTEL_CMP_CSE0,
Hannah Williams63142152017-06-12 14:03:18 -0700525 0,
526};
527
Andrey Petrov04a72c42017-03-01 15:51:57 -0800528static const struct pci_driver cse_driver __pci_driver = {
529 .ops = &cse_ops,
530 .vendor = PCI_VENDOR_ID_INTEL,
531 /* SoC/chipset needs to provide PCI device ID */
Andrey Petrov0405de92017-06-05 13:25:29 -0700532 .devices = pci_device_ids
Andrey Petrov04a72c42017-03-01 15:51:57 -0800533};
534
535#endif