Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
praveen hodagatta pranesh | e26c4a4 | 2018-09-20 03:49:45 +0800 | [diff] [blame] | 4 | * Copyright 2017-2018 Intel Inc. |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 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 | |
Subrata Banik | 05e06cd | 2017-11-09 15:04:09 +0530 | [diff] [blame] | 16 | #include <assert.h> |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 17 | #include <commonlib/helpers.h> |
| 18 | #include <console/console.h> |
Kyösti Mälkki | 13f6650 | 2019-03-03 08:01:05 +0200 | [diff] [blame] | 19 | #include <device/mmio.h> |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 20 | #include <delay.h> |
| 21 | #include <device/pci.h> |
| 22 | #include <device/pci_ids.h> |
| 23 | #include <device/pci_ops.h> |
| 24 | #include <intelblocks/cse.h> |
Subrata Banik | 05e06cd | 2017-11-09 15:04:09 +0530 | [diff] [blame] | 25 | #include <soc/iomap.h> |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 26 | #include <soc/pci_devs.h> |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 27 | #include <string.h> |
| 28 | #include <timer.h> |
| 29 | |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 30 | #define MAX_HECI_MESSAGE_RETRY_COUNT 5 |
| 31 | |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 32 | /* Wait up to 15 sec for HECI to get ready */ |
| 33 | #define HECI_DELAY_READY (15 * 1000) |
Jonathan Neuschäfer | 5268b76 | 2018-02-12 12:24:25 +0100 | [diff] [blame] | 34 | /* Wait up to 100 usec between circular buffer polls */ |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 35 | #define HECI_DELAY 100 |
| 36 | /* Wait up to 5 sec for CSE to chew something we sent */ |
| 37 | #define HECI_SEND_TIMEOUT (5 * 1000) |
| 38 | /* Wait up to 5 sec for CSE to blurp a reply */ |
| 39 | #define HECI_READ_TIMEOUT (5 * 1000) |
| 40 | |
| 41 | #define SLOT_SIZE sizeof(uint32_t) |
| 42 | |
| 43 | #define MMIO_CSE_CB_WW 0x00 |
| 44 | #define MMIO_HOST_CSR 0x04 |
| 45 | #define MMIO_CSE_CB_RW 0x08 |
| 46 | #define MMIO_CSE_CSR 0x0c |
| 47 | |
| 48 | #define CSR_IE (1 << 0) |
| 49 | #define CSR_IS (1 << 1) |
| 50 | #define CSR_IG (1 << 2) |
| 51 | #define CSR_READY (1 << 3) |
| 52 | #define CSR_RESET (1 << 4) |
| 53 | #define CSR_RP_START 8 |
| 54 | #define CSR_RP (((1 << 8) - 1) << CSR_RP_START) |
| 55 | #define CSR_WP_START 16 |
| 56 | #define CSR_WP (((1 << 8) - 1) << CSR_WP_START) |
| 57 | #define CSR_CBD_START 24 |
| 58 | #define CSR_CBD (((1 << 8) - 1) << CSR_CBD_START) |
| 59 | |
| 60 | #define MEI_HDR_IS_COMPLETE (1 << 31) |
| 61 | #define MEI_HDR_LENGTH_START 16 |
| 62 | #define MEI_HDR_LENGTH_SIZE 9 |
| 63 | #define MEI_HDR_LENGTH (((1 << MEI_HDR_LENGTH_SIZE) - 1) \ |
| 64 | << MEI_HDR_LENGTH_START) |
| 65 | #define MEI_HDR_HOST_ADDR_START 8 |
| 66 | #define MEI_HDR_HOST_ADDR (((1 << 8) - 1) << MEI_HDR_HOST_ADDR_START) |
| 67 | #define MEI_HDR_CSE_ADDR_START 0 |
| 68 | #define MEI_HDR_CSE_ADDR (((1 << 8) - 1) << MEI_HDR_CSE_ADDR_START) |
| 69 | |
Sridhar Siricilla | b9d075b | 2019-08-31 11:38:33 +0530 | [diff] [blame] | 70 | #define HECI_OP_MODE_SEC_OVERRIDE 5 |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 71 | |
Sridhar Siricilla | d415c20 | 2019-08-31 14:54:57 +0530 | [diff] [blame] | 72 | /* Global Reset Command ID */ |
| 73 | #define MKHI_GLOBAL_RESET_REQ 0xb |
| 74 | #define MKHI_GROUP_ID_CBM 0 |
| 75 | |
| 76 | /* RST Origin */ |
| 77 | #define GR_ORIGIN_BIOS_POST 2 |
| 78 | |
Sridhar Siricilla | e30a0e6 | 2019-08-31 16:12:21 +0530 | [diff] [blame^] | 79 | #define MKHI_HMRFPO_GROUP_ID 5 |
| 80 | |
| 81 | /* HMRFPO Command Ids */ |
| 82 | #define MKHI_HMRFPO_ENABLE 1 |
| 83 | #define MKHI_HMRFPO_GET_STATUS 3 |
| 84 | |
| 85 | #define ME_HFS_CWS_NORMAL 5 |
| 86 | #define ME_HFS_MODE_NORMAL 0 |
| 87 | #define ME_HFS_TEMP_DISABLE 3 |
| 88 | |
Arthur Heymans | 3d6ccd0 | 2019-05-27 17:25:23 +0200 | [diff] [blame] | 89 | static struct cse_device { |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 90 | uintptr_t sec_bar; |
Arthur Heymans | a5eed80 | 2019-05-25 10:28:11 +0200 | [diff] [blame] | 91 | } g_cse; |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 92 | |
Sridhar Siricilla | d415c20 | 2019-08-31 14:54:57 +0530 | [diff] [blame] | 93 | /* HECI Message Header */ |
| 94 | struct mkhi_hdr { |
| 95 | uint8_t group_id; |
| 96 | uint8_t command:7; |
| 97 | uint8_t is_resp:1; |
| 98 | uint8_t rsvd; |
| 99 | uint8_t result; |
| 100 | } __packed; |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 101 | /* |
| 102 | * Initialize the device with provided temporary BAR. If BAR is 0 use a |
| 103 | * default. This is intended for pre-mem usage only where BARs haven't been |
| 104 | * assigned yet and devices are not enabled. |
| 105 | */ |
| 106 | void heci_init(uintptr_t tempbar) |
| 107 | { |
Elyes HAOUAS | 68c851b | 2018-06-12 22:06:09 +0200 | [diff] [blame] | 108 | #if defined(__SIMPLE_DEVICE__) |
| 109 | pci_devfn_t dev = PCH_DEV_CSE; |
| 110 | #else |
| 111 | struct device *dev = PCH_DEV_CSE; |
| 112 | #endif |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 113 | u8 pcireg; |
| 114 | |
| 115 | /* Assume it is already initialized, nothing else to do */ |
Arthur Heymans | a5eed80 | 2019-05-25 10:28:11 +0200 | [diff] [blame] | 116 | if (g_cse.sec_bar) |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 117 | return; |
| 118 | |
| 119 | /* Use default pre-ram bar */ |
| 120 | if (!tempbar) |
| 121 | tempbar = HECI1_BASE_ADDRESS; |
| 122 | |
| 123 | /* Assign Resources to HECI1 */ |
| 124 | /* Clear BIT 1-2 of Command Register */ |
| 125 | pcireg = pci_read_config8(dev, PCI_COMMAND); |
| 126 | pcireg &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); |
| 127 | pci_write_config8(dev, PCI_COMMAND, pcireg); |
| 128 | |
| 129 | /* Program Temporary BAR for HECI1 */ |
| 130 | pci_write_config32(dev, PCI_BASE_ADDRESS_0, tempbar); |
| 131 | pci_write_config32(dev, PCI_BASE_ADDRESS_1, 0x0); |
| 132 | |
| 133 | /* Enable Bus Master and MMIO Space */ |
| 134 | pcireg = pci_read_config8(dev, PCI_COMMAND); |
| 135 | pcireg |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; |
| 136 | pci_write_config8(dev, PCI_COMMAND, pcireg); |
| 137 | |
Arthur Heymans | a5eed80 | 2019-05-25 10:28:11 +0200 | [diff] [blame] | 138 | g_cse.sec_bar = tempbar; |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 139 | } |
| 140 | |
Subrata Banik | 05e06cd | 2017-11-09 15:04:09 +0530 | [diff] [blame] | 141 | /* Get HECI BAR 0 from PCI configuration space */ |
| 142 | static uint32_t get_cse_bar(void) |
| 143 | { |
| 144 | uintptr_t bar; |
| 145 | |
| 146 | bar = pci_read_config32(PCH_DEV_CSE, PCI_BASE_ADDRESS_0); |
| 147 | assert(bar != 0); |
| 148 | /* |
| 149 | * Bits 31-12 are the base address as per EDS for SPI, |
| 150 | * Don't care about 0-11 bit |
| 151 | */ |
| 152 | return bar & ~PCI_BASE_ADDRESS_MEM_ATTR_MASK; |
| 153 | } |
| 154 | |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 155 | static uint32_t read_bar(uint32_t offset) |
| 156 | { |
Jonathan Neuschäfer | 5268b76 | 2018-02-12 12:24:25 +0100 | [diff] [blame] | 157 | /* Reach PCI config space to get BAR in case CAR global not available */ |
Arthur Heymans | a5eed80 | 2019-05-25 10:28:11 +0200 | [diff] [blame] | 158 | if (!g_cse.sec_bar) |
| 159 | g_cse.sec_bar = get_cse_bar(); |
| 160 | return read32((void *)(g_cse.sec_bar + offset)); |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | static void write_bar(uint32_t offset, uint32_t val) |
| 164 | { |
Jonathan Neuschäfer | 5268b76 | 2018-02-12 12:24:25 +0100 | [diff] [blame] | 165 | /* Reach PCI config space to get BAR in case CAR global not available */ |
Arthur Heymans | a5eed80 | 2019-05-25 10:28:11 +0200 | [diff] [blame] | 166 | if (!g_cse.sec_bar) |
| 167 | g_cse.sec_bar = get_cse_bar(); |
| 168 | return write32((void *)(g_cse.sec_bar + offset), val); |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 169 | } |
| 170 | |
| 171 | static uint32_t read_cse_csr(void) |
| 172 | { |
| 173 | return read_bar(MMIO_CSE_CSR); |
| 174 | } |
| 175 | |
| 176 | static uint32_t read_host_csr(void) |
| 177 | { |
| 178 | return read_bar(MMIO_HOST_CSR); |
| 179 | } |
| 180 | |
| 181 | static void write_host_csr(uint32_t data) |
| 182 | { |
| 183 | write_bar(MMIO_HOST_CSR, data); |
| 184 | } |
| 185 | |
| 186 | static size_t filled_slots(uint32_t data) |
| 187 | { |
| 188 | uint8_t wp, rp; |
| 189 | rp = data >> CSR_RP_START; |
| 190 | wp = data >> CSR_WP_START; |
| 191 | return (uint8_t) (wp - rp); |
| 192 | } |
| 193 | |
| 194 | static size_t cse_filled_slots(void) |
| 195 | { |
| 196 | return filled_slots(read_cse_csr()); |
| 197 | } |
| 198 | |
| 199 | static size_t host_empty_slots(void) |
| 200 | { |
| 201 | uint32_t csr; |
| 202 | csr = read_host_csr(); |
| 203 | |
| 204 | return ((csr & CSR_CBD) >> CSR_CBD_START) - filled_slots(csr); |
| 205 | } |
| 206 | |
| 207 | static void clear_int(void) |
| 208 | { |
| 209 | uint32_t csr; |
| 210 | csr = read_host_csr(); |
| 211 | csr |= CSR_IS; |
| 212 | write_host_csr(csr); |
| 213 | } |
| 214 | |
| 215 | static uint32_t read_slot(void) |
| 216 | { |
| 217 | return read_bar(MMIO_CSE_CB_RW); |
| 218 | } |
| 219 | |
| 220 | static void write_slot(uint32_t val) |
| 221 | { |
| 222 | write_bar(MMIO_CSE_CB_WW, val); |
| 223 | } |
| 224 | |
| 225 | static int wait_write_slots(size_t cnt) |
| 226 | { |
| 227 | struct stopwatch sw; |
| 228 | |
| 229 | stopwatch_init_msecs_expire(&sw, HECI_SEND_TIMEOUT); |
| 230 | while (host_empty_slots() < cnt) { |
| 231 | udelay(HECI_DELAY); |
| 232 | if (stopwatch_expired(&sw)) { |
| 233 | printk(BIOS_ERR, "HECI: timeout, buffer not drained\n"); |
| 234 | return 0; |
| 235 | } |
| 236 | } |
| 237 | return 1; |
| 238 | } |
| 239 | |
| 240 | static int wait_read_slots(size_t cnt) |
| 241 | { |
| 242 | struct stopwatch sw; |
| 243 | |
| 244 | stopwatch_init_msecs_expire(&sw, HECI_READ_TIMEOUT); |
| 245 | while (cse_filled_slots() < cnt) { |
| 246 | udelay(HECI_DELAY); |
| 247 | if (stopwatch_expired(&sw)) { |
| 248 | printk(BIOS_ERR, "HECI: timed out reading answer!\n"); |
| 249 | return 0; |
| 250 | } |
| 251 | } |
| 252 | return 1; |
| 253 | } |
| 254 | |
| 255 | /* get number of full 4-byte slots */ |
| 256 | static size_t bytes_to_slots(size_t bytes) |
| 257 | { |
| 258 | return ALIGN_UP(bytes, SLOT_SIZE) / SLOT_SIZE; |
| 259 | } |
| 260 | |
| 261 | static int cse_ready(void) |
| 262 | { |
| 263 | uint32_t csr; |
| 264 | csr = read_cse_csr(); |
| 265 | return csr & CSR_READY; |
| 266 | } |
| 267 | |
Sridhar Siricilla | b9d075b | 2019-08-31 11:38:33 +0530 | [diff] [blame] | 268 | /* |
| 269 | * Checks if CSE is in SEC_OVERRIDE operation mode. This is the mode where |
| 270 | * CSE will allow reflashing of CSE region. |
| 271 | */ |
| 272 | static uint8_t check_cse_sec_override_mode(void) |
| 273 | { |
| 274 | union me_hfsts1 hfs1; |
| 275 | hfs1.data = me_read_config32(PCI_ME_HFSTS1); |
| 276 | if (hfs1.fields.operation_mode == HECI_OP_MODE_SEC_OVERRIDE) |
| 277 | return 1; |
| 278 | return 0; |
| 279 | } |
| 280 | |
| 281 | /* Makes the host ready to communicate with CSE */ |
| 282 | void set_host_ready(void) |
| 283 | { |
| 284 | uint32_t csr; |
| 285 | csr = read_host_csr(); |
| 286 | csr &= ~CSR_RESET; |
| 287 | csr |= (CSR_IG | CSR_READY); |
| 288 | write_host_csr(csr); |
| 289 | } |
| 290 | |
| 291 | /* Polls for ME state 'HECI_OP_MODE_SEC_OVERRIDE' for 15 seconds */ |
| 292 | uint8_t wait_cse_sec_override_mode(void) |
| 293 | { |
| 294 | struct stopwatch sw; |
| 295 | stopwatch_init_msecs_expire(&sw, HECI_DELAY_READY); |
| 296 | while (!check_cse_sec_override_mode()) { |
| 297 | udelay(HECI_DELAY); |
| 298 | if (stopwatch_expired(&sw)) |
| 299 | return 0; |
| 300 | } |
| 301 | |
| 302 | return 1; |
| 303 | } |
| 304 | |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 305 | static int wait_heci_ready(void) |
| 306 | { |
| 307 | struct stopwatch sw; |
| 308 | |
| 309 | stopwatch_init_msecs_expire(&sw, HECI_DELAY_READY); |
| 310 | while (!cse_ready()) { |
| 311 | udelay(HECI_DELAY); |
| 312 | if (stopwatch_expired(&sw)) |
| 313 | return 0; |
| 314 | } |
| 315 | |
| 316 | return 1; |
| 317 | } |
| 318 | |
| 319 | static void host_gen_interrupt(void) |
| 320 | { |
| 321 | uint32_t csr; |
| 322 | csr = read_host_csr(); |
| 323 | csr |= CSR_IG; |
| 324 | write_host_csr(csr); |
| 325 | } |
| 326 | |
| 327 | static size_t hdr_get_length(uint32_t hdr) |
| 328 | { |
| 329 | return (hdr & MEI_HDR_LENGTH) >> MEI_HDR_LENGTH_START; |
| 330 | } |
| 331 | |
| 332 | static int |
| 333 | send_one_message(uint32_t hdr, const void *buff) |
| 334 | { |
| 335 | size_t pend_len, pend_slots, remainder, i; |
| 336 | uint32_t tmp; |
| 337 | const uint32_t *p = buff; |
| 338 | |
| 339 | /* Get space for the header */ |
| 340 | if (!wait_write_slots(1)) |
| 341 | return 0; |
| 342 | |
| 343 | /* First, write header */ |
| 344 | write_slot(hdr); |
| 345 | |
| 346 | pend_len = hdr_get_length(hdr); |
| 347 | pend_slots = bytes_to_slots(pend_len); |
| 348 | |
| 349 | if (!wait_write_slots(pend_slots)) |
| 350 | return 0; |
| 351 | |
| 352 | /* Write the body in whole slots */ |
| 353 | i = 0; |
| 354 | while (i < ALIGN_DOWN(pend_len, SLOT_SIZE)) { |
| 355 | write_slot(*p++); |
| 356 | i += SLOT_SIZE; |
| 357 | } |
| 358 | |
| 359 | remainder = pend_len % SLOT_SIZE; |
| 360 | /* Pad to 4 bytes not touching caller's buffer */ |
| 361 | if (remainder) { |
| 362 | memcpy(&tmp, p, remainder); |
| 363 | write_slot(tmp); |
| 364 | } |
| 365 | |
| 366 | host_gen_interrupt(); |
| 367 | |
| 368 | /* Make sure nothing bad happened during transmission */ |
| 369 | if (!cse_ready()) |
| 370 | return 0; |
| 371 | |
| 372 | return pend_len; |
| 373 | } |
| 374 | |
| 375 | int |
| 376 | heci_send(const void *msg, size_t len, uint8_t host_addr, uint8_t client_addr) |
| 377 | { |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 378 | uint8_t retry; |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 379 | uint32_t csr, hdr; |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 380 | size_t sent, remaining, cb_size, max_length; |
| 381 | const uint8_t *p; |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 382 | |
| 383 | if (!msg || !len) |
| 384 | return 0; |
| 385 | |
| 386 | clear_int(); |
| 387 | |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 388 | for (retry = 0; retry < MAX_HECI_MESSAGE_RETRY_COUNT; retry++) { |
| 389 | p = msg; |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 390 | |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 391 | if (!wait_heci_ready()) { |
| 392 | printk(BIOS_ERR, "HECI: not ready\n"); |
| 393 | continue; |
| 394 | } |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 395 | |
Subrata Banik | 4a722f5 | 2017-11-13 14:56:42 +0530 | [diff] [blame] | 396 | csr = read_host_csr(); |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 397 | cb_size = ((csr & CSR_CBD) >> CSR_CBD_START) * SLOT_SIZE; |
| 398 | /* |
| 399 | * Reserve one slot for the header. Limit max message |
| 400 | * length by 9 bits that are available in the header. |
| 401 | */ |
| 402 | max_length = MIN(cb_size, (1 << MEI_HDR_LENGTH_SIZE) - 1) |
| 403 | - SLOT_SIZE; |
| 404 | remaining = len; |
| 405 | |
| 406 | /* |
| 407 | * Fragment the message into smaller messages not exceeding |
Jonathan Neuschäfer | 5268b76 | 2018-02-12 12:24:25 +0100 | [diff] [blame] | 408 | * useful circular buffer length. Mark last message complete. |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 409 | */ |
| 410 | do { |
| 411 | hdr = MIN(max_length, remaining) |
| 412 | << MEI_HDR_LENGTH_START; |
| 413 | hdr |= client_addr << MEI_HDR_CSE_ADDR_START; |
| 414 | hdr |= host_addr << MEI_HDR_HOST_ADDR_START; |
| 415 | hdr |= (MIN(max_length, remaining) == remaining) ? |
Lee Leahy | 68ab0b5 | 2017-03-10 13:42:34 -0800 | [diff] [blame] | 416 | MEI_HDR_IS_COMPLETE : 0; |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 417 | sent = send_one_message(hdr, p); |
| 418 | p += sent; |
| 419 | remaining -= sent; |
| 420 | } while (remaining > 0 && sent != 0); |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 421 | |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 422 | if (!remaining) |
| 423 | return 1; |
| 424 | } |
| 425 | return 0; |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 426 | } |
| 427 | |
| 428 | static size_t |
| 429 | recv_one_message(uint32_t *hdr, void *buff, size_t maxlen) |
| 430 | { |
| 431 | uint32_t reg, *p = buff; |
| 432 | size_t recv_slots, recv_len, remainder, i; |
| 433 | |
| 434 | /* first get the header */ |
| 435 | if (!wait_read_slots(1)) |
| 436 | return 0; |
| 437 | |
| 438 | *hdr = read_slot(); |
| 439 | recv_len = hdr_get_length(*hdr); |
| 440 | |
| 441 | if (!recv_len) |
| 442 | printk(BIOS_WARNING, "HECI: message is zero-sized\n"); |
| 443 | |
| 444 | recv_slots = bytes_to_slots(recv_len); |
| 445 | |
| 446 | i = 0; |
| 447 | if (recv_len > maxlen) { |
| 448 | printk(BIOS_ERR, "HECI: response is too big\n"); |
| 449 | return 0; |
| 450 | } |
| 451 | |
| 452 | /* wait for the rest of messages to arrive */ |
| 453 | wait_read_slots(recv_slots); |
| 454 | |
| 455 | /* fetch whole slots first */ |
| 456 | while (i < ALIGN_DOWN(recv_len, SLOT_SIZE)) { |
| 457 | *p++ = read_slot(); |
| 458 | i += SLOT_SIZE; |
| 459 | } |
| 460 | |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 461 | /* |
| 462 | * If ME is not ready, something went wrong and |
| 463 | * we received junk |
| 464 | */ |
| 465 | if (!cse_ready()) |
| 466 | return 0; |
| 467 | |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 468 | remainder = recv_len % SLOT_SIZE; |
| 469 | |
| 470 | if (remainder) { |
| 471 | reg = read_slot(); |
| 472 | memcpy(p, ®, remainder); |
| 473 | } |
| 474 | |
| 475 | return recv_len; |
| 476 | } |
| 477 | |
| 478 | int heci_receive(void *buff, size_t *maxlen) |
| 479 | { |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 480 | uint8_t retry; |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 481 | size_t left, received; |
| 482 | uint32_t hdr = 0; |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 483 | uint8_t *p; |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 484 | |
| 485 | if (!buff || !maxlen || !*maxlen) |
| 486 | return 0; |
| 487 | |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 488 | clear_int(); |
| 489 | |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 490 | for (retry = 0; retry < MAX_HECI_MESSAGE_RETRY_COUNT; retry++) { |
| 491 | p = buff; |
| 492 | left = *maxlen; |
| 493 | |
| 494 | if (!wait_heci_ready()) { |
| 495 | printk(BIOS_ERR, "HECI: not ready\n"); |
| 496 | continue; |
| 497 | } |
| 498 | |
| 499 | /* |
| 500 | * Receive multiple packets until we meet one marked |
| 501 | * complete or we run out of space in caller-provided buffer. |
| 502 | */ |
| 503 | do { |
| 504 | received = recv_one_message(&hdr, p, left); |
Lijian Zhao | c50296d | 2017-12-15 19:10:18 -0800 | [diff] [blame] | 505 | if (!received) { |
Elyes HAOUAS | 3d45000 | 2018-08-09 18:55:58 +0200 | [diff] [blame] | 506 | printk(BIOS_ERR, "HECI: Failed to receive!\n"); |
Lijian Zhao | c50296d | 2017-12-15 19:10:18 -0800 | [diff] [blame] | 507 | return 0; |
| 508 | } |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 509 | left -= received; |
| 510 | p += received; |
| 511 | /* If we read out everything ping to send more */ |
| 512 | if (!(hdr & MEI_HDR_IS_COMPLETE) && !cse_filled_slots()) |
| 513 | host_gen_interrupt(); |
| 514 | } while (received && !(hdr & MEI_HDR_IS_COMPLETE) && left > 0); |
| 515 | |
| 516 | if ((hdr & MEI_HDR_IS_COMPLETE) && received) { |
| 517 | *maxlen = p - (uint8_t *) buff; |
| 518 | return 1; |
| 519 | } |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 520 | } |
Subrata Banik | 5c08c73 | 2017-11-13 14:54:37 +0530 | [diff] [blame] | 521 | return 0; |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 522 | } |
| 523 | |
Sridhar Siricilla | a5208f5 | 2019-08-30 17:10:24 +0530 | [diff] [blame] | 524 | int heci_send_receive(const void *snd_msg, size_t snd_sz, void *rcv_msg, size_t *rcv_sz) |
| 525 | { |
| 526 | if (!heci_send(snd_msg, snd_sz, BIOS_HOST_ADDR, HECI_MKHI_ADDR)) { |
| 527 | printk(BIOS_ERR, "HECI: send Failed\n"); |
| 528 | return 0; |
| 529 | } |
| 530 | |
| 531 | if (rcv_msg != NULL) { |
| 532 | if (!heci_receive(rcv_msg, rcv_sz)) { |
| 533 | printk(BIOS_ERR, "HECI: receive Failed\n"); |
| 534 | return 0; |
| 535 | } |
| 536 | } |
| 537 | return 1; |
| 538 | } |
| 539 | |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 540 | /* |
| 541 | * Attempt to reset the device. This is useful when host and ME are out |
| 542 | * of sync during transmission or ME didn't understand the message. |
| 543 | */ |
| 544 | int heci_reset(void) |
| 545 | { |
| 546 | uint32_t csr; |
| 547 | |
| 548 | /* Send reset request */ |
| 549 | csr = read_host_csr(); |
Sridhar Siricilla | b9d075b | 2019-08-31 11:38:33 +0530 | [diff] [blame] | 550 | csr |= (CSR_RESET | CSR_IG); |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 551 | write_host_csr(csr); |
| 552 | |
| 553 | if (wait_heci_ready()) { |
| 554 | /* Device is back on its imaginary feet, clear reset */ |
Sridhar Siricilla | b9d075b | 2019-08-31 11:38:33 +0530 | [diff] [blame] | 555 | set_host_ready(); |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 556 | return 1; |
| 557 | } |
| 558 | |
| 559 | printk(BIOS_CRIT, "HECI: reset failed\n"); |
| 560 | |
| 561 | return 0; |
| 562 | } |
| 563 | |
Sridhar Siricilla | 2cc6691 | 2019-08-31 11:20:34 +0530 | [diff] [blame] | 564 | bool is_cse_enabled(void) |
| 565 | { |
| 566 | const struct device *cse_dev = pcidev_path_on_root(PCH_DEVFN_CSE); |
| 567 | |
| 568 | if (!cse_dev || !cse_dev->enabled) { |
| 569 | printk(BIOS_WARNING, "HECI: No CSE device\n"); |
| 570 | return false; |
| 571 | } |
| 572 | |
| 573 | if (pci_read_config16(PCH_DEV_CSE, PCI_VENDOR_ID) == 0xFFFF) { |
| 574 | printk(BIOS_WARNING, "HECI: CSE device is hidden\n"); |
| 575 | return false; |
| 576 | } |
| 577 | |
| 578 | return true; |
| 579 | } |
| 580 | |
| 581 | uint32_t me_read_config32(int offset) |
| 582 | { |
| 583 | return pci_read_config32(PCH_DEV_CSE, offset); |
| 584 | } |
| 585 | |
Sridhar Siricilla | d415c20 | 2019-08-31 14:54:57 +0530 | [diff] [blame] | 586 | /* |
| 587 | * Sends GLOBAL_RESET_REQ cmd to CSE.The reset type can be GLOBAL_RESET/ |
| 588 | * HOST_RESET_ONLY/CSE_RESET_ONLY. |
| 589 | */ |
| 590 | int send_heci_reset_req_message(uint8_t rst_type) |
| 591 | { |
| 592 | int status; |
| 593 | struct mkhi_hdr reply; |
| 594 | struct reset_message { |
| 595 | struct mkhi_hdr hdr; |
| 596 | uint8_t req_origin; |
| 597 | uint8_t reset_type; |
| 598 | } __packed; |
| 599 | struct reset_message msg = { |
| 600 | .hdr = { |
| 601 | .group_id = MKHI_GROUP_ID_CBM, |
| 602 | .command = MKHI_GLOBAL_RESET_REQ, |
| 603 | }, |
| 604 | .req_origin = GR_ORIGIN_BIOS_POST, |
| 605 | .reset_type = rst_type |
| 606 | }; |
| 607 | size_t reply_size; |
| 608 | |
| 609 | if (!((rst_type == GLOBAL_RESET) || |
| 610 | (rst_type == HOST_RESET_ONLY) || (rst_type == CSE_RESET_ONLY))) |
| 611 | return -1; |
| 612 | |
| 613 | heci_reset(); |
| 614 | |
| 615 | reply_size = sizeof(reply); |
| 616 | memset(&reply, 0, reply_size); |
| 617 | |
| 618 | printk(BIOS_DEBUG, "HECI: Global Reset(Type:%d) Command\n", rst_type); |
| 619 | if (rst_type == CSE_RESET_ONLY) |
| 620 | status = heci_send_receive(&msg, sizeof(msg), NULL, 0); |
| 621 | else |
| 622 | status = heci_send_receive(&msg, sizeof(msg), &reply, |
| 623 | &reply_size); |
| 624 | |
| 625 | if (status != 1) |
| 626 | return -1; |
| 627 | |
| 628 | printk(BIOS_DEBUG, "HECI: Global Reset success!\n"); |
| 629 | return 0; |
| 630 | } |
| 631 | |
Sridhar Siricilla | e30a0e6 | 2019-08-31 16:12:21 +0530 | [diff] [blame^] | 632 | /* Sends HMRFPO Enable command to CSE */ |
| 633 | int send_hmrfpo_enable_msg(void) |
| 634 | { |
| 635 | struct hmrfpo_enable_msg { |
| 636 | struct mkhi_hdr hdr; |
| 637 | uint32_t nonce[2]; |
| 638 | } __packed; |
| 639 | |
| 640 | /* HMRFPO Enable message */ |
| 641 | struct hmrfpo_enable_msg msg = { |
| 642 | .hdr = { |
| 643 | .group_id = MKHI_HMRFPO_GROUP_ID, |
| 644 | .command = MKHI_HMRFPO_ENABLE, |
| 645 | }, |
| 646 | .nonce = {0}, |
| 647 | }; |
| 648 | |
| 649 | /* HMRFPO Enable response */ |
| 650 | struct hmrfpo_enable_resp { |
| 651 | struct mkhi_hdr hdr; |
| 652 | uint32_t fct_base; |
| 653 | uint32_t fct_limit; |
| 654 | uint8_t status; |
| 655 | uint8_t padding[3]; |
| 656 | } __packed; |
| 657 | |
| 658 | struct hmrfpo_enable_resp resp; |
| 659 | size_t resp_size = sizeof(struct hmrfpo_enable_resp); |
| 660 | union me_hfsts1 hfs1; |
| 661 | |
| 662 | printk(BIOS_DEBUG, "HECI: Send HMRFPO Enable Command\n"); |
| 663 | hfs1.data = me_read_config32(PCI_ME_HFSTS1); |
| 664 | /* |
| 665 | * This command can be run only if: |
| 666 | * - Working state is normal and |
| 667 | * - Operation mode is normal or temporary disable mode. |
| 668 | */ |
| 669 | if (hfs1.fields.working_state != ME_HFS_CWS_NORMAL || |
| 670 | (hfs1.fields.operation_mode != ME_HFS_MODE_NORMAL && |
| 671 | hfs1.fields.operation_mode != ME_HFS_TEMP_DISABLE)) { |
| 672 | printk(BIOS_ERR, "HECI: ME not in required Mode\n"); |
| 673 | goto failed; |
| 674 | } |
| 675 | |
| 676 | if (!heci_send_receive(&msg, sizeof(struct hmrfpo_enable_msg), |
| 677 | &resp, &resp_size)) |
| 678 | goto failed; |
| 679 | |
| 680 | if (resp.hdr.result) { |
| 681 | printk(BIOS_ERR, "HECI: Resp Failed:%d\n", resp.hdr.result); |
| 682 | goto failed; |
| 683 | } |
| 684 | return 1; |
| 685 | |
| 686 | failed: |
| 687 | return 0; |
| 688 | } |
| 689 | |
| 690 | /* |
| 691 | * Sends HMRFPO Get Status command to CSE to get the HMRFPO status. |
| 692 | * The status can be DISABLES/LOCKED/ENABLED |
| 693 | */ |
| 694 | int send_hmrfpo_get_status_msg(void) |
| 695 | { |
| 696 | struct hmrfpo_get_status_msg { |
| 697 | struct mkhi_hdr hdr; |
| 698 | } __packed; |
| 699 | |
| 700 | struct hmrfpo_get_status_resp { |
| 701 | struct mkhi_hdr hdr; |
| 702 | uint8_t status; |
| 703 | uint8_t padding[3]; |
| 704 | } __packed; |
| 705 | |
| 706 | struct hmrfpo_get_status_msg msg = { |
| 707 | .hdr = { |
| 708 | .group_id = MKHI_HMRFPO_GROUP_ID, |
| 709 | .command = MKHI_HMRFPO_GET_STATUS, |
| 710 | }, |
| 711 | }; |
| 712 | struct hmrfpo_get_status_resp resp; |
| 713 | size_t resp_size = sizeof(struct hmrfpo_get_status_resp); |
| 714 | |
| 715 | printk(BIOS_INFO, "HECI: Sending Get HMRFPO Status Command\n"); |
| 716 | |
| 717 | if (!heci_send_receive(&msg, sizeof(struct hmrfpo_get_status_msg), |
| 718 | &resp, &resp_size)) { |
| 719 | printk(BIOS_ERR, "HECI: HMRFPO send/receive fail\n"); |
| 720 | return -1; |
| 721 | } |
| 722 | |
| 723 | if (resp.hdr.result) { |
| 724 | printk(BIOS_ERR, "HECI: HMRFPO Resp Failed:%d\n", |
| 725 | resp.hdr.result); |
| 726 | return -1; |
| 727 | } |
| 728 | |
| 729 | return resp.status; |
| 730 | } |
| 731 | |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 732 | #if ENV_RAMSTAGE |
| 733 | |
| 734 | static void update_sec_bar(struct device *dev) |
| 735 | { |
| 736 | g_cse.sec_bar = find_resource(dev, PCI_BASE_ADDRESS_0)->base; |
| 737 | } |
| 738 | |
| 739 | static void cse_set_resources(struct device *dev) |
| 740 | { |
Subrata Banik | 2ee54db | 2017-03-05 12:37:00 +0530 | [diff] [blame] | 741 | if (dev->path.pci.devfn == PCH_DEVFN_CSE) |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 742 | update_sec_bar(dev); |
| 743 | |
| 744 | pci_dev_set_resources(dev); |
| 745 | } |
| 746 | |
| 747 | static struct device_operations cse_ops = { |
| 748 | .set_resources = cse_set_resources, |
| 749 | .read_resources = pci_dev_read_resources, |
| 750 | .enable_resources = pci_dev_enable_resources, |
| 751 | .init = pci_dev_init, |
Subrata Banik | 6bbc91a | 2017-12-07 14:55:51 +0530 | [diff] [blame] | 752 | .ops_pci = &pci_dev_ops_pci, |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 753 | }; |
| 754 | |
Hannah Williams | 6314215 | 2017-06-12 14:03:18 -0700 | [diff] [blame] | 755 | static const unsigned short pci_device_ids[] = { |
| 756 | PCI_DEVICE_ID_INTEL_APL_CSE0, |
| 757 | PCI_DEVICE_ID_INTEL_GLK_CSE0, |
Andrey Petrov | 0405de9 | 2017-06-05 13:25:29 -0700 | [diff] [blame] | 758 | PCI_DEVICE_ID_INTEL_CNL_CSE0, |
Subrata Banik | d0586d2 | 2017-11-27 13:28:41 +0530 | [diff] [blame] | 759 | PCI_DEVICE_ID_INTEL_SKL_CSE0, |
Maxim Polyakov | 571d07d | 2019-08-22 13:11:32 +0300 | [diff] [blame] | 760 | PCI_DEVICE_ID_INTEL_LWB_CSE0, |
| 761 | PCI_DEVICE_ID_INTEL_LWB_CSE0_SUPER, |
praveen hodagatta pranesh | e26c4a4 | 2018-09-20 03:49:45 +0800 | [diff] [blame] | 762 | PCI_DEVICE_ID_INTEL_CNP_H_CSE0, |
Aamir Bohra | 9eac039 | 2018-06-30 12:07:04 +0530 | [diff] [blame] | 763 | PCI_DEVICE_ID_INTEL_ICL_CSE0, |
Ronak Kanabar | da7ffb48 | 2019-02-05 01:51:13 +0530 | [diff] [blame] | 764 | PCI_DEVICE_ID_INTEL_CMP_CSE0, |
Hannah Williams | 6314215 | 2017-06-12 14:03:18 -0700 | [diff] [blame] | 765 | 0, |
| 766 | }; |
| 767 | |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 768 | static const struct pci_driver cse_driver __pci_driver = { |
| 769 | .ops = &cse_ops, |
| 770 | .vendor = PCI_VENDOR_ID_INTEL, |
| 771 | /* SoC/chipset needs to provide PCI device ID */ |
Andrey Petrov | 0405de9 | 2017-06-05 13:25:29 -0700 | [diff] [blame] | 772 | .devices = pci_device_ids |
Andrey Petrov | 04a72c4 | 2017-03-01 15:51:57 -0800 | [diff] [blame] | 773 | }; |
| 774 | |
| 775 | #endif |