blob: 9e0b8a78397622e652b08663c016ff718b41ac44 [file] [log] [blame]
Stefan Reinauer3b921082019-11-26 16:56:26 -08001/*
2 * Copyright 2019 Google LLC
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <stdint.h>
17#include "em100.h"
18
19enum ifd_version {
20 IFD_VERSION_1,
21 IFD_VERSION_2,
22};
23
24enum platform {
25 PLATFORM_APL,
26 PLATFORM_CNL,
27 PLATFORM_GLK,
28 PLATFORM_ICL,
29 PLATFORM_SKLKBL,
30 PLATFORM_TGL,
31};
32
33enum spi_frequency {
34 SPI_FREQUENCY_20MHZ = 0,
35 SPI_FREQUENCY_33MHZ = 1,
36 SPI_FREQUENCY_48MHZ = 2,
37 SPI_FREQUENCY_50MHZ_30MHZ = 4,
38 SPI_FREQUENCY_17MHZ = 6,
39};
40
41/* flash descriptor */
42typedef struct {
43 uint32_t flvalsig;
44 uint32_t flmap0;
45 uint32_t flmap1;
46 uint32_t flmap2;
47} __attribute__((packed)) fdbar_t;
48
49/* component section */
50typedef struct {
51 uint32_t flcomp;
52 uint32_t flill;
53 uint32_t flpb;
54} __attribute__((packed)) fcba_t;
55
56/**
57 * valid_pointer - examine whether a pointer falls in [base, base + limit)
58 * @param ptr: the non-void* pointer to a single arbitrary-sized object.
59 * @param base: base address represented with char* type.
60 * @param limit: upper limit of the legal address.
61 * @return: ptr if valid, otherwise NULL.
62 */
63static void *valid_pointer(char *ptr, char *base, size_t limit)
64{
65 return ((char *)(ptr) >= (base) &&
66 (char *)&(ptr)[1] <= (base) + (limit)) ? ptr : NULL;
67}
68
69/**
70 * find_fd - Find firmware flash descriptor
71 * @param image: pointer to image in ram
72 * @param size: size of image
73 * @return: pointer to flash descriptor or NULL
74 */
75static fdbar_t *find_fd(char *image, int size)
76{
77#define FD_SIGNATURE 0x0FF0A55A
78 int i, found = 0;
79
80 /* Scan for FD signature */
81 for (i = 0; i < (size - 4); i += 4) {
82 if (*(uint32_t *) (image + i) == FD_SIGNATURE) {
83 found = 1;
84 break; /* signature found. */
85 }
86 }
87
88 if (!found) {
89 printf("No Flash Descriptor found in this image\n");
90 return NULL;
91 }
92
93 return (fdbar_t *)valid_pointer(image + i, image, size);
94}
95
96
97/**
98 * find_fcba - Find FCBA block in image
99 * @param image: pointer to image in ram
100 * @param size: size of image
101 * @return: pointer to FCBA or NULL
102 */
103static fcba_t *find_fcba(fdbar_t *fdb, char *image, int size)
104{
105 return (fcba_t *)valid_pointer(image + ((fdb->flmap0 & 0xff) << 4), image, size);
106}
107
108/*
109 * There is no version field in the descriptor so to determine
110 * if this is a new descriptor format we check the hardcoded SPI
111 * read frequency to see if it is fixed at 20MHz or 17MHz.
112 */
113static int get_ifd_version(fcba_t *fcba, int platform)
114{
115 int read_freq;
116 unsigned int i;
117
118 /* Some newer platforms have re-defined the FCBA field that was
119 * used to distinguish IFD v1 v/s v2. Define a list of platforms
120 * that we know do not have the required FCBA field, but are IFD
121 * v2 and return true if current platform is one of them.
122 */
123 static const int ifd_2_platforms[] = {
124 PLATFORM_GLK,
125 PLATFORM_CNL,
126 PLATFORM_ICL,
127 PLATFORM_TGL,
128 };
129
130 /* Unfortunately we do not have a way to determine the
131 * platform of an image any more than we have to determine
132 * its IFD version, but leaving this code in to elaborate
133 * a possible fix.
134 */
135 for (i = 0; i < ARRAY_SIZE(ifd_2_platforms); i++) {
136 if (platform == ifd_2_platforms[i])
137 return IFD_VERSION_2;
138 }
139
140 read_freq = (fcba->flcomp >> 17) & 7;
141
142 switch (read_freq) {
143 case SPI_FREQUENCY_20MHZ:
144 return IFD_VERSION_1;
145 case SPI_FREQUENCY_17MHZ:
146 case SPI_FREQUENCY_50MHZ_30MHZ:
147 return IFD_VERSION_2;
148 default:
149 fprintf(stderr, "Unknown descriptor version: %d\n",
150 read_freq);
151 exit(EXIT_FAILURE);
152 }
153}
154
155static void ifd_set_spi_frequency(fcba_t *fcba, enum spi_frequency freq)
156{
157 /* clear bits 21-30 */
158 fcba->flcomp &= ~0x7fe00000;
159 /* Read ID and Read Status Clock Frequency */
160 fcba->flcomp |= freq << 27;
161 /* Write and Erase Clock Frequency */
162 fcba->flcomp |= freq << 24;
163 /* Fast Read Clock Frequency */
164 fcba->flcomp |= freq << 21;
165}
166
167static void ifd_set_em100_mode(fcba_t *fcba, struct em100 *em100)
168{
169 int freq;
170
171 if (em100->hwversion == HWVERSION_EM100PRO_G2) {
172 printf("Warning: EM100Pro-G2 can run at full speed.\n");
173 }
174
175 /* Auto-detect IFD version. Right now we don't support
176 * hard-coding the IFD version, hence passing -1 for platform.
177 */
178 int ifd_version = get_ifd_version(fcba, -1);
179 switch (ifd_version) {
180 case IFD_VERSION_1:
181 freq = SPI_FREQUENCY_20MHZ;
182 printf("Limit SPI frequency to 20MHz.\n");
183 break;
184 case IFD_VERSION_2:
185 freq = SPI_FREQUENCY_17MHZ;
186 printf("Limit SPI frequency to 17MHz.\n");
187 break;
188 default:
189 freq = SPI_FREQUENCY_17MHZ;
190 printf("Limit SPI frequency to 17MHz.\n");
191 break;
192 }
193
194 ifd_set_spi_frequency(fcba, freq);
195}
196
197/**
198 * autocorrect_image: Modify image to work with EM100
199 * @param em100: initialized em100 device structure
200 * @param image: pointer to emulated image in RAM
201 * @param size: size of emulated image
202 *
203 * @return: 0: image type known and image patched
204 * @return: 1: image type not detected (unpatched)
205 */
206
207int autocorrect_image(struct em100 *em100, char *image, size_t size)
208{
209 printf("Auto-detecting image type ... ");
210 fdbar_t *fdb = find_fd(image, size);
211 if (fdb) {
212 printf("IFD\n");
213
214 fcba_t *fcba = find_fcba(fdb, image, size);
215 if (!fcba) {
216 printf("Inconsistent image.\n");
217 return 1;
218 }
219
220 /* Set EM100 mode */
221 ifd_set_em100_mode(fcba, em100);
222 } else {
223 printf("<unknown>\n");
224 return 1; /* No support for other image types (yet). */
225 }
226
227 return 0;
228}