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