board: ge: bx50v3, imx53ppd: use DM I2C
[oweals/u-boot.git] / board / ge / common / vpd_reader.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2016 General Electric Company
4  */
5
6 #include "vpd_reader.h"
7
8 #include <i2c.h>
9 #include <linux/bch.h>
10 #include <stdlib.h>
11 #include <dm/uclass.h>
12 #include <i2c_eeprom.h>
13 #include <hexdump.h>
14
15 /* BCH configuration */
16
17 const struct {
18         int header_ecc_capability_bits;
19         int data_ecc_capability_bits;
20         unsigned int prim_poly;
21         struct {
22                 int min;
23                 int max;
24         } galois_field_order;
25 } bch_configuration = {
26         .header_ecc_capability_bits = 4,
27         .data_ecc_capability_bits = 16,
28         .prim_poly = 0,
29         .galois_field_order = {
30                 .min = 5,
31                 .max = 15,
32         },
33 };
34
35 static int calculate_galois_field_order(size_t source_length)
36 {
37         int gfo = bch_configuration.galois_field_order.min;
38
39         for (; gfo < bch_configuration.galois_field_order.max &&
40              ((((1 << gfo) - 1) - ((int)source_length * 8)) < 0);
41              gfo++) {
42         }
43
44         if (gfo == bch_configuration.galois_field_order.max)
45                 return -1;
46
47         return gfo + 1;
48 }
49
50 static int verify_bch(int ecc_bits, unsigned int prim_poly, u8 *data,
51                       size_t data_length, const u8 *ecc, size_t ecc_length)
52 {
53         int gfo = calculate_galois_field_order(data_length);
54
55         if (gfo < 0)
56                 return -1;
57
58         struct bch_control *bch = init_bch(gfo, ecc_bits, prim_poly);
59
60         if (!bch)
61                 return -1;
62
63         if (bch->ecc_bytes != ecc_length) {
64                 free_bch(bch);
65                 return -1;
66         }
67
68         unsigned int *errloc = (unsigned int *)calloc(data_length,
69                                                       sizeof(unsigned int));
70         int errors = decode_bch(bch, data, data_length, ecc, NULL, NULL,
71                                 errloc);
72
73         free_bch(bch);
74         if (errors < 0) {
75                 free(errloc);
76                 return -1;
77         }
78
79         if (errors > 0) {
80                 for (int n = 0; n < errors; n++) {
81                         if (errloc[n] >= 8 * data_length) {
82                                 /*
83                                  * n-th error located in ecc (no need for data
84                                  * correction)
85                                  */
86                         } else {
87                                 /* n-th error located in data */
88                                 data[errloc[n] / 8] ^= 1 << (errloc[n] % 8);
89                         }
90                 }
91         }
92
93         free(errloc);
94         return 0;
95 }
96
97 static const int ID;
98 static const int LEN = 1;
99 static const int VER = 2;
100 static const int TYP = 3;
101 static const int BLOCK_SIZE = 4;
102
103 static const u8 HEADER_BLOCK_ID;
104 static const u8 HEADER_BLOCK_LEN = 18;
105 static const u32 HEADER_BLOCK_MAGIC = 0xca53ca53;
106 static const size_t HEADER_BLOCK_VERIFY_LEN = 14;
107 static const size_t HEADER_BLOCK_ECC_OFF = 14;
108 static const size_t HEADER_BLOCK_ECC_LEN = 4;
109
110 static const u8 ECC_BLOCK_ID = 0xFF;
111
112 static int vpd_reader(size_t size, u8 *data, struct vpd_cache *userdata,
113                       int (*fn)(struct vpd_cache *, u8 id, u8 version, u8 type,
114                                 size_t size, u8 const *data))
115 {
116         if (size < HEADER_BLOCK_LEN || !data || !fn)
117                 return -EINVAL;
118
119         /*
120          * +--------------------+----------------+--//--+--------------------+
121          * | header block       | data block     | ...  | ecc block          |
122          * +--------------------+----------------+--//--+--------------------+
123          * :                    :                       :
124          * +------+-------+-----+                       +------+-------------+
125          * | id   | magic | ecc |                       | ...  | ecc         |
126          * | len  | off   |     |                       +------+-------------+
127          * | ver  | size  |     |                       :
128          * | type |       |     |                       :
129          * +------+-------+-----+                       :
130          * :              :     :                       :
131          * <----- [1] ---->     <--------- [2] --------->
132          *
133          * Repair (if necessary) the contents of header block [1] by using a
134          * 4 byte ECC located at the end of the header block.  A successful
135          * return value means that we can trust the header.
136          */
137         int ret = verify_bch(bch_configuration.header_ecc_capability_bits,
138                              bch_configuration.prim_poly, data,
139                              HEADER_BLOCK_VERIFY_LEN,
140                              &data[HEADER_BLOCK_ECC_OFF], HEADER_BLOCK_ECC_LEN);
141         if (ret < 0)
142                 return ret;
143
144         /* Validate header block { id, length, version, type }. */
145         if (data[ID] != HEADER_BLOCK_ID || data[LEN] != HEADER_BLOCK_LEN ||
146             data[VER] != 0 || data[TYP] != 0 ||
147             ntohl(*(u32 *)(&data[4])) != HEADER_BLOCK_MAGIC)
148                 return -EINVAL;
149
150         u32 offset = ntohl(*(u32 *)(&data[8]));
151         u16 size_bits = ntohs(*(u16 *)(&data[12]));
152
153         /* Check that ECC header fits. */
154         if (offset + 3 >= size)
155                 return -EINVAL;
156
157         /* Validate ECC block. */
158         u8 *ecc = &data[offset];
159
160         if (ecc[ID] != ECC_BLOCK_ID || ecc[LEN] < BLOCK_SIZE ||
161             ecc[LEN] + offset > size ||
162             ecc[LEN] - BLOCK_SIZE != size_bits / 8 || ecc[VER] != 1 ||
163             ecc[TYP] != 1)
164                 return -EINVAL;
165
166         /*
167          * Use the header block to locate the ECC block and verify the data
168          * blocks [2] against the ecc block ECC.
169          */
170         ret = verify_bch(bch_configuration.data_ecc_capability_bits,
171                          bch_configuration.prim_poly, &data[data[LEN]],
172                          offset - data[LEN], &data[offset + BLOCK_SIZE],
173                          ecc[LEN] - BLOCK_SIZE);
174         if (ret < 0)
175                 return ret;
176
177         /* Stop after ECC.  Ignore possible zero padding. */
178         size = offset;
179
180         for (;;) {
181                 /* Move to next block. */
182                 size -= data[LEN];
183                 data += data[LEN];
184
185                 if (size == 0) {
186                         /* Finished iterating through blocks. */
187                         return 0;
188                 }
189
190                 if (size < BLOCK_SIZE || data[LEN] < BLOCK_SIZE) {
191                         /* Not enough data for a header, or short header. */
192                         return -EINVAL;
193                 }
194
195                 ret = fn(userdata, data[ID], data[VER], data[TYP],
196                          data[LEN] - BLOCK_SIZE, &data[BLOCK_SIZE]);
197                 if (ret)
198                         return ret;
199         }
200 }
201
202 int read_vpd(struct vpd_cache *cache,
203              int (*process_block)(struct vpd_cache *, u8 id, u8 version,
204                                   u8 type, size_t size, u8 const *data))
205 {
206         struct udevice *dev;
207         int ret;
208         u8 *data;
209         int size;
210
211         ret = uclass_get_device_by_name(UCLASS_I2C_EEPROM, "vpd", &dev);
212         if (ret)
213                 return ret;
214
215         size = i2c_eeprom_size(dev);
216         if (size < 0) {
217                 printf("Unable to get size of eeprom: %d\n", ret);
218                 return ret;
219         }
220
221         data = malloc(size);
222         if (!data)
223                 return -ENOMEM;
224
225         ret = i2c_eeprom_read(dev, 0, data, size);
226         if (ret) {
227                 free(data);
228                 return ret;
229         }
230
231         ret = vpd_reader(size, data, cache, process_block);
232
233         free(data);
234
235         return ret;
236 }