Angel Pons | 8a3453f | 2020-04-02 23:48:19 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 2 | |
Elyes HAOUAS | 361a935 | 2019-12-18 21:26:33 +0100 | [diff] [blame] | 3 | #include <commonlib/helpers.h> |
Arthur Heymans | c2a9f0c | 2018-03-28 18:48:24 +0200 | [diff] [blame] | 4 | #include <commonlib/region.h> |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 5 | #include <fmap.h> |
| 6 | #include <console/console.h> |
| 7 | #include <console/flash.h> |
Elyes HAOUAS | ede8dd0 | 2019-06-23 06:57:53 +0200 | [diff] [blame] | 8 | #include <types.h> |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 9 | |
| 10 | #define LINE_BUFFER_SIZE 128 |
| 11 | #define READ_BUFFER_SIZE 0x100 |
| 12 | |
Patrick Georgi | c9b1359 | 2019-11-29 11:47:47 +0100 | [diff] [blame] | 13 | static const struct region_device *rdev_ptr; |
| 14 | static struct region_device rdev; |
| 15 | static uint8_t line_buffer[LINE_BUFFER_SIZE]; |
| 16 | static size_t offset; |
| 17 | static size_t line_offset; |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 18 | |
| 19 | void flashconsole_init(void) |
| 20 | { |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 21 | uint8_t buffer[READ_BUFFER_SIZE]; |
| 22 | size_t size; |
Johanna Schander | 1c9746c | 2020-01-04 04:05:02 +0100 | [diff] [blame] | 23 | size_t initial_offset = 0; |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 24 | size_t len = READ_BUFFER_SIZE; |
| 25 | size_t i; |
| 26 | |
Patrick Georgi | c9b1359 | 2019-11-29 11:47:47 +0100 | [diff] [blame] | 27 | if (fmap_locate_area_as_rdev_rw("CONSOLE", &rdev)) { |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 28 | printk(BIOS_INFO, "Can't find 'CONSOLE' area in FMAP\n"); |
| 29 | return; |
| 30 | } |
Patrick Georgi | c9b1359 | 2019-11-29 11:47:47 +0100 | [diff] [blame] | 31 | size = region_device_sz(&rdev); |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 32 | |
| 33 | /* |
| 34 | * We need to check the region until we find a 0xff indicating |
| 35 | * the end of a previous log write. |
| 36 | * We can't erase the region because one stage would erase the |
| 37 | * data from the previous stage. Also, it looks like doing an |
| 38 | * erase could completely freeze the SPI controller and then |
| 39 | * we can't write anything anymore (apparently might happen if |
| 40 | * the sector is already erased, so we would need to read |
| 41 | * anyways to check if it's all 0xff). |
| 42 | */ |
Johanna Schander | 1c9746c | 2020-01-04 04:05:02 +0100 | [diff] [blame] | 43 | for (i = 0; i < len && initial_offset < size;) { |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 44 | // Fill the buffer on first iteration |
| 45 | if (i == 0) { |
Elyes HAOUAS | 361a935 | 2019-12-18 21:26:33 +0100 | [diff] [blame] | 46 | len = MIN(READ_BUFFER_SIZE, size - offset); |
Johanna Schander | 1c9746c | 2020-01-04 04:05:02 +0100 | [diff] [blame] | 47 | if (rdev_readat(&rdev, buffer, initial_offset, len) != len) |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 48 | return; |
| 49 | } |
| 50 | if (buffer[i] == 0xff) { |
Johanna Schander | 1c9746c | 2020-01-04 04:05:02 +0100 | [diff] [blame] | 51 | initial_offset += i; |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 52 | break; |
| 53 | } |
| 54 | // If we're done, repeat the process for the next sector |
| 55 | if (++i == READ_BUFFER_SIZE) { |
Johanna Schander | 1c9746c | 2020-01-04 04:05:02 +0100 | [diff] [blame] | 56 | initial_offset += len; |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 57 | i = 0; |
| 58 | } |
| 59 | } |
| 60 | // Make sure there is still space left on the console |
Johanna Schander | 1c9746c | 2020-01-04 04:05:02 +0100 | [diff] [blame] | 61 | if (initial_offset >= size) { |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 62 | printk(BIOS_INFO, "No space left on 'console' region in SPI flash\n"); |
| 63 | return; |
| 64 | } |
| 65 | |
Johanna Schander | 1c9746c | 2020-01-04 04:05:02 +0100 | [diff] [blame] | 66 | offset = initial_offset; |
Patrick Georgi | c9b1359 | 2019-11-29 11:47:47 +0100 | [diff] [blame] | 67 | rdev_ptr = &rdev; |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 68 | } |
| 69 | |
| 70 | void flashconsole_tx_byte(unsigned char c) |
| 71 | { |
Patrick Georgi | c9b1359 | 2019-11-29 11:47:47 +0100 | [diff] [blame] | 72 | if (!rdev_ptr) |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 73 | return; |
| 74 | |
Patrick Georgi | c9b1359 | 2019-11-29 11:47:47 +0100 | [diff] [blame] | 75 | size_t region_size = region_device_sz(rdev_ptr); |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 76 | |
Nico Huber | 361a5c0 | 2020-11-26 13:35:09 +0100 | [diff] [blame] | 77 | if (line_offset < LINE_BUFFER_SIZE) |
| 78 | line_buffer[line_offset++] = c; |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 79 | |
Patrick Georgi | c9b1359 | 2019-11-29 11:47:47 +0100 | [diff] [blame] | 80 | if (line_offset >= LINE_BUFFER_SIZE || |
| 81 | offset + line_offset >= region_size || c == '\n') { |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 82 | flashconsole_tx_flush(); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | void flashconsole_tx_flush(void) |
| 87 | { |
Patrick Georgi | c9b1359 | 2019-11-29 11:47:47 +0100 | [diff] [blame] | 88 | size_t len = line_offset; |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 89 | size_t region_size; |
Arthur Heymans | 6ea3a13 | 2019-11-20 21:50:54 +0100 | [diff] [blame] | 90 | static int busy; |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 91 | |
| 92 | /* Prevent any recursive loops in case the spi flash driver |
| 93 | * calls printk (in case of transaction timeout or |
| 94 | * any other error while writing) */ |
Arthur Heymans | 6ea3a13 | 2019-11-20 21:50:54 +0100 | [diff] [blame] | 95 | if (busy) |
| 96 | return; |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 97 | |
Patrick Georgi | c9b1359 | 2019-11-29 11:47:47 +0100 | [diff] [blame] | 98 | if (!rdev_ptr) |
Arthur Heymans | 6ea3a13 | 2019-11-20 21:50:54 +0100 | [diff] [blame] | 99 | return; |
| 100 | |
| 101 | busy = 1; |
Patrick Georgi | c9b1359 | 2019-11-29 11:47:47 +0100 | [diff] [blame] | 102 | region_size = region_device_sz(rdev_ptr); |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 103 | if (offset + len >= region_size) |
| 104 | len = region_size - offset; |
| 105 | |
Patrick Georgi | c9b1359 | 2019-11-29 11:47:47 +0100 | [diff] [blame] | 106 | if (rdev_writeat(&rdev, line_buffer, offset, len) != len) |
Arthur Heymans | 6ea3a13 | 2019-11-20 21:50:54 +0100 | [diff] [blame] | 107 | return; |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 108 | |
| 109 | // If the region is full, stop future write attempts |
| 110 | if (offset + len >= region_size) |
Arthur Heymans | 6ea3a13 | 2019-11-20 21:50:54 +0100 | [diff] [blame] | 111 | return; |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 112 | |
Johanna Schander | 1c9746c | 2020-01-04 04:05:02 +0100 | [diff] [blame] | 113 | offset += len; |
Patrick Georgi | c9b1359 | 2019-11-29 11:47:47 +0100 | [diff] [blame] | 114 | line_offset = 0; |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 115 | |
Arthur Heymans | 6ea3a13 | 2019-11-20 21:50:54 +0100 | [diff] [blame] | 116 | busy = 0; |
Youness Alaoui | c4b4ff3 | 2017-05-11 10:36:29 -0400 | [diff] [blame] | 117 | } |