]> git.sur5r.net Git - u-boot/blob - arch/x86/lib/mrccache.c
ec0d2cb18b29c38a8781f43e53e6f6c77337ae6d
[u-boot] / arch / x86 / lib / mrccache.c
1 /*
2  * From Coreboot src/southbridge/intel/bd82x6x/mrccache.c
3  *
4  * Copyright (C) 2014 Google Inc.
5  *
6  * SPDX-License-Identifier:     GPL-2.0
7  */
8
9 #include <common.h>
10 #include <errno.h>
11 #include <fdtdec.h>
12 #include <net.h>
13 #include <spi.h>
14 #include <spi_flash.h>
15 #include <asm/mrccache.h>
16
17 static struct mrc_data_container *next_mrc_block(
18         struct mrc_data_container *mrc_cache)
19 {
20         /* MRC data blocks are aligned within the region */
21         u32 mrc_size = sizeof(*mrc_cache) + mrc_cache->data_size;
22         if (mrc_size & (MRC_DATA_ALIGN - 1UL)) {
23                 mrc_size &= ~(MRC_DATA_ALIGN - 1UL);
24                 mrc_size += MRC_DATA_ALIGN;
25         }
26
27         u8 *region_ptr = (u8 *)mrc_cache;
28         region_ptr += mrc_size;
29         return (struct mrc_data_container *)region_ptr;
30 }
31
32 static int is_mrc_cache(struct mrc_data_container *cache)
33 {
34         return cache && (cache->signature == MRC_DATA_SIGNATURE);
35 }
36
37 /*
38  * Find the largest index block in the MRC cache. Return NULL if none is
39  * found.
40  */
41 struct mrc_data_container *mrccache_find_current(struct fmap_entry *entry)
42 {
43         struct mrc_data_container *cache, *next;
44         ulong base_addr, end_addr;
45         uint id;
46
47         base_addr = (1ULL << 32) - CONFIG_ROM_SIZE + entry->offset;
48         end_addr = base_addr + entry->length;
49         cache = NULL;
50
51         /* Search for the last filled entry in the region */
52         for (id = 0, next = (struct mrc_data_container *)base_addr;
53              is_mrc_cache(next);
54              id++) {
55                 cache = next;
56                 next = next_mrc_block(next);
57                 if ((ulong)next >= end_addr)
58                         break;
59         }
60
61         if (id-- == 0) {
62                 debug("%s: No valid MRC cache found.\n", __func__);
63                 return NULL;
64         }
65
66         /* Verify checksum */
67         if (cache->checksum != compute_ip_checksum(cache->data,
68                                                    cache->data_size)) {
69                 printf("%s: MRC cache checksum mismatch\n", __func__);
70                 return NULL;
71         }
72
73         debug("%s: picked entry %u from cache block\n", __func__, id);
74
75         return cache;
76 }
77
78 /**
79  * find_next_mrc_cache() - get next cache entry
80  *
81  * @entry:      MRC cache flash area
82  * @cache:      Entry to start from
83  *
84  * @return next cache entry if found, NULL if we got to the end
85  */
86 static struct mrc_data_container *find_next_mrc_cache(struct fmap_entry *entry,
87                 struct mrc_data_container *cache)
88 {
89         ulong base_addr, end_addr;
90
91         base_addr = (1ULL << 32) - CONFIG_ROM_SIZE + entry->offset;
92         end_addr = base_addr + entry->length;
93
94         cache = next_mrc_block(cache);
95         if ((ulong)cache >= end_addr) {
96                 /* Crossed the boundary */
97                 cache = NULL;
98                 debug("%s: no available entries found\n", __func__);
99         } else {
100                 debug("%s: picked next entry from cache block at %p\n",
101                       __func__, cache);
102         }
103
104         return cache;
105 }
106
107 int mrccache_update(struct udevice *sf, struct fmap_entry *entry,
108                     struct mrc_data_container *cur)
109 {
110         struct mrc_data_container *cache;
111         ulong offset;
112         ulong base_addr;
113         int ret;
114
115         /* Find the last used block */
116         base_addr = (1ULL << 32) - CONFIG_ROM_SIZE + entry->offset;
117         debug("Updating MRC cache data\n");
118         cache = mrccache_find_current(entry);
119         if (cache && (cache->data_size == cur->data_size) &&
120             (!memcmp(cache, cur, cache->data_size + sizeof(*cur)))) {
121                 debug("MRC data in flash is up to date. No update\n");
122                 return -EEXIST;
123         }
124
125         /* Move to the next block, which will be the first unused block */
126         if (cache)
127                 cache = find_next_mrc_cache(entry, cache);
128
129         /*
130          * If we have got to the end, erase the entire mrc-cache area and start
131          * again at block 0.
132          */
133         if (!cache) {
134                 debug("Erasing the MRC cache region of %x bytes at %x\n",
135                       entry->length, entry->offset);
136
137                 ret = spi_flash_erase_dm(sf, entry->offset, entry->length);
138                 if (ret) {
139                         debug("Failed to erase flash region\n");
140                         return ret;
141                 }
142                 cache = (struct mrc_data_container *)base_addr;
143         }
144
145         /* Write the data out */
146         offset = (ulong)cache - base_addr + entry->offset;
147         debug("Write MRC cache update to flash at %lx\n", offset);
148         ret = spi_flash_write_dm(sf, offset, cur->data_size + sizeof(*cur),
149                                  cur);
150         if (ret) {
151                 debug("Failed to write to SPI flash\n");
152                 return ret;
153         }
154
155         return 0;
156 }