Merge tag 'u-boot-imx-20200825' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx
[platform/kernel/u-boot.git] / drivers / misc / i2c_eeprom.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2014 Google, Inc
4  */
5
6 #include <common.h>
7 #include <eeprom.h>
8 #include <linux/delay.h>
9 #include <linux/err.h>
10 #include <linux/kernel.h>
11 #include <dm.h>
12 #include <dm/device-internal.h>
13 #include <i2c.h>
14 #include <i2c_eeprom.h>
15
16 struct i2c_eeprom_drv_data {
17         u32 size; /* size in bytes */
18         u32 pagesize; /* page size in bytes */
19         u32 addr_offset_mask; /* bits in addr used for offset overflow */
20         u32 offset_len; /* size in bytes of offset */
21         u32 start_offset; /* valid start offset inside memory, by default 0 */
22 };
23
24 int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf, int size)
25 {
26         const struct i2c_eeprom_ops *ops = device_get_ops(dev);
27
28         if (!ops->read)
29                 return -ENOSYS;
30
31         return ops->read(dev, offset, buf, size);
32 }
33
34 int i2c_eeprom_write(struct udevice *dev, int offset, uint8_t *buf, int size)
35 {
36         const struct i2c_eeprom_ops *ops = device_get_ops(dev);
37
38         if (!ops->write)
39                 return -ENOSYS;
40
41         return ops->write(dev, offset, buf, size);
42 }
43
44 int i2c_eeprom_size(struct udevice *dev)
45 {
46         const struct i2c_eeprom_ops *ops = device_get_ops(dev);
47
48         if (!ops->size)
49                 return -ENOSYS;
50
51         return ops->size(dev);
52 }
53
54 static int i2c_eeprom_std_read(struct udevice *dev, int offset, uint8_t *buf,
55                                int size)
56 {
57         return dm_i2c_read(dev, offset, buf, size);
58 }
59
60 static int i2c_eeprom_std_write(struct udevice *dev, int offset,
61                                 const uint8_t *buf, int size)
62 {
63         struct i2c_eeprom *priv = dev_get_priv(dev);
64         int ret;
65
66         while (size > 0) {
67                 int write_size = min_t(int, size, priv->pagesize);
68
69                 ret = dm_i2c_write(dev, offset, buf, write_size);
70                 if (ret)
71                         return ret;
72
73                 offset += write_size;
74                 buf += write_size;
75                 size -= write_size;
76
77                 udelay(10000);
78         }
79
80         return 0;
81 }
82
83 static int i2c_eeprom_std_size(struct udevice *dev)
84 {
85         struct i2c_eeprom *priv = dev_get_priv(dev);
86
87         return priv->size;
88 }
89
90 static const struct i2c_eeprom_ops i2c_eeprom_std_ops = {
91         .read   = i2c_eeprom_std_read,
92         .write  = i2c_eeprom_std_write,
93         .size   = i2c_eeprom_std_size,
94 };
95
96 static int i2c_eeprom_std_ofdata_to_platdata(struct udevice *dev)
97 {
98         struct i2c_eeprom *priv = dev_get_priv(dev);
99         struct i2c_eeprom_drv_data *data =
100                 (struct i2c_eeprom_drv_data *)dev_get_driver_data(dev);
101         u32 pagesize;
102         u32 size;
103
104         if (dev_read_u32(dev, "pagesize", &pagesize) == 0)
105                 priv->pagesize = pagesize;
106         else
107                 /* 6 bit -> page size of up to 2^63 (should be sufficient) */
108                 priv->pagesize = data->pagesize;
109
110         if (dev_read_u32(dev, "size", &size) == 0)
111                 priv->size = size;
112         else
113                 priv->size = data->size;
114
115         return 0;
116 }
117
118 static int i2c_eeprom_std_bind(struct udevice *dev)
119 {
120         ofnode partitions = ofnode_find_subnode(dev_ofnode(dev), "partitions");
121         ofnode partition;
122         const char *name;
123
124         if (!ofnode_valid(partitions))
125                 return 0;
126         if (!ofnode_device_is_compatible(partitions, "fixed-partitions"))
127                 return -ENOTSUPP;
128
129         ofnode_for_each_subnode(partition, partitions) {
130                 name = ofnode_get_name(partition);
131                 if (!name)
132                         continue;
133
134                 device_bind_ofnode(dev, DM_GET_DRIVER(i2c_eeprom_partition),
135                                    name, NULL, partition, NULL);
136         }
137
138         return 0;
139 }
140
141 static int i2c_eeprom_std_probe(struct udevice *dev)
142 {
143         u8 test_byte;
144         int ret;
145         struct i2c_eeprom_drv_data *data =
146                 (struct i2c_eeprom_drv_data *)dev_get_driver_data(dev);
147
148         i2c_set_chip_offset_len(dev, data->offset_len);
149         i2c_set_chip_addr_offset_mask(dev, data->addr_offset_mask);
150
151         /* Verify that the chip is functional */
152         /*
153          * Not all eeproms start from offset 0. Valid offset is available
154          * in the platform data struct.
155          */
156         ret = i2c_eeprom_read(dev, data->start_offset, &test_byte, 1);
157         if (ret)
158                 return -ENODEV;
159
160         return 0;
161 }
162
163 static const struct i2c_eeprom_drv_data eeprom_data = {
164         .size = 0,
165         .pagesize = 1,
166         .addr_offset_mask = 0,
167         .offset_len = 1,
168 };
169
170 static const struct i2c_eeprom_drv_data mc24aa02e48_data = {
171         .size = 256,
172         .pagesize = 8,
173         .addr_offset_mask = 0,
174         .offset_len = 1,
175 };
176
177 static const struct i2c_eeprom_drv_data atmel24c01a_data = {
178         .size = 128,
179         .pagesize = 8,
180         .addr_offset_mask = 0,
181         .offset_len = 1,
182 };
183
184 static const struct i2c_eeprom_drv_data atmel24c02_data = {
185         .size = 256,
186         .pagesize = 8,
187         .addr_offset_mask = 0,
188         .offset_len = 1,
189 };
190
191 static const struct i2c_eeprom_drv_data atmel24c04_data = {
192         .size = 512,
193         .pagesize = 16,
194         .addr_offset_mask = 0x1,
195         .offset_len = 1,
196 };
197
198 static const struct i2c_eeprom_drv_data atmel24c08_data = {
199         .size = 1024,
200         .pagesize = 16,
201         .addr_offset_mask = 0x3,
202         .offset_len = 1,
203 };
204
205 static const struct i2c_eeprom_drv_data atmel24c08a_data = {
206         .size = 1024,
207         .pagesize = 16,
208         .addr_offset_mask = 0x3,
209         .offset_len = 1,
210 };
211
212 static const struct i2c_eeprom_drv_data atmel24c16a_data = {
213         .size = 2048,
214         .pagesize = 16,
215         .addr_offset_mask = 0x7,
216         .offset_len = 1,
217 };
218
219 static const struct i2c_eeprom_drv_data atmel24mac402_data = {
220         .size = 256,
221         .pagesize = 16,
222         .addr_offset_mask = 0,
223         .offset_len = 1,
224         .start_offset = 0x80,
225 };
226
227 static const struct i2c_eeprom_drv_data atmel24c32_data = {
228         .size = 4096,
229         .pagesize = 32,
230         .addr_offset_mask = 0,
231         .offset_len = 2,
232 };
233
234 static const struct i2c_eeprom_drv_data atmel24c64_data = {
235         .size = 8192,
236         .pagesize = 32,
237         .addr_offset_mask = 0,
238         .offset_len = 2,
239 };
240
241 static const struct i2c_eeprom_drv_data atmel24c128_data = {
242         .size = 16384,
243         .pagesize = 64,
244         .addr_offset_mask = 0,
245         .offset_len = 2,
246 };
247
248 static const struct i2c_eeprom_drv_data atmel24c256_data = {
249         .size = 32768,
250         .pagesize = 64,
251         .addr_offset_mask = 0,
252         .offset_len = 2,
253 };
254
255 static const struct i2c_eeprom_drv_data atmel24c512_data = {
256         .size = 65536,
257         .pagesize = 64,
258         .addr_offset_mask = 0,
259         .offset_len = 2,
260 };
261
262 static const struct udevice_id i2c_eeprom_std_ids[] = {
263         { .compatible = "i2c-eeprom", (ulong)&eeprom_data },
264         { .compatible = "microchip,24aa02e48", (ulong)&mc24aa02e48_data },
265         { .compatible = "atmel,24c01a", (ulong)&atmel24c01a_data },
266         { .compatible = "atmel,24c02", (ulong)&atmel24c02_data },
267         { .compatible = "atmel,24c04", (ulong)&atmel24c04_data },
268         { .compatible = "atmel,24c08", (ulong)&atmel24c08_data },
269         { .compatible = "atmel,24c08a", (ulong)&atmel24c08a_data },
270         { .compatible = "atmel,24c16a", (ulong)&atmel24c16a_data },
271         { .compatible = "atmel,24mac402", (ulong)&atmel24mac402_data },
272         { .compatible = "atmel,24c32", (ulong)&atmel24c32_data },
273         { .compatible = "atmel,24c64", (ulong)&atmel24c64_data },
274         { .compatible = "atmel,24c128", (ulong)&atmel24c128_data },
275         { .compatible = "atmel,24c256", (ulong)&atmel24c256_data },
276         { .compatible = "atmel,24c512", (ulong)&atmel24c512_data },
277         { }
278 };
279
280 U_BOOT_DRIVER(i2c_eeprom_std) = {
281         .name                   = "i2c_eeprom",
282         .id                     = UCLASS_I2C_EEPROM,
283         .of_match               = i2c_eeprom_std_ids,
284         .bind                   = i2c_eeprom_std_bind,
285         .probe                  = i2c_eeprom_std_probe,
286         .ofdata_to_platdata     = i2c_eeprom_std_ofdata_to_platdata,
287         .priv_auto_alloc_size   = sizeof(struct i2c_eeprom),
288         .ops                    = &i2c_eeprom_std_ops,
289 };
290
291 struct i2c_eeprom_partition {
292         u32 offset;
293         u32 size;
294 };
295
296 static int i2c_eeprom_partition_probe(struct udevice *dev)
297 {
298         return 0;
299 }
300
301 static int i2c_eeprom_partition_ofdata_to_platdata(struct udevice *dev)
302 {
303         struct i2c_eeprom_partition *priv = dev_get_priv(dev);
304         u32 reg[2];
305         int ret;
306
307         ret = dev_read_u32_array(dev, "reg", reg, 2);
308         if (ret)
309                 return ret;
310
311         if (!reg[1])
312                 return -EINVAL;
313
314         priv->offset = reg[0];
315         priv->size = reg[1];
316
317         debug("%s: base %x, size %x\n", __func__, priv->offset, priv->size);
318
319         return 0;
320 }
321
322 static int i2c_eeprom_partition_read(struct udevice *dev, int offset,
323                                      u8 *buf, int size)
324 {
325         struct i2c_eeprom_partition *priv = dev_get_priv(dev);
326         struct udevice *parent = dev_get_parent(dev);
327
328         if (!parent)
329                 return -ENODEV;
330         if (offset + size > priv->size)
331                 return -EINVAL;
332
333         return i2c_eeprom_read(parent, offset + priv->offset, buf, size);
334 }
335
336 static int i2c_eeprom_partition_write(struct udevice *dev, int offset,
337                                       const u8 *buf, int size)
338 {
339         struct i2c_eeprom_partition *priv = dev_get_priv(dev);
340         struct udevice *parent = dev_get_parent(dev);
341
342         if (!parent)
343                 return -ENODEV;
344         if (offset + size > priv->size)
345                 return -EINVAL;
346
347         return i2c_eeprom_write(parent, offset + priv->offset, (uint8_t *)buf,
348                                 size);
349 }
350
351 static int i2c_eeprom_partition_size(struct udevice *dev)
352 {
353         struct i2c_eeprom_partition *priv = dev_get_priv(dev);
354
355         return priv->size;
356 }
357
358 static const struct i2c_eeprom_ops i2c_eeprom_partition_ops = {
359         .read   = i2c_eeprom_partition_read,
360         .write  = i2c_eeprom_partition_write,
361         .size   = i2c_eeprom_partition_size,
362 };
363
364 U_BOOT_DRIVER(i2c_eeprom_partition) = {
365         .name                   = "i2c_eeprom_partition",
366         .id                     = UCLASS_I2C_EEPROM,
367         .probe                  = i2c_eeprom_partition_probe,
368         .ofdata_to_platdata     = i2c_eeprom_partition_ofdata_to_platdata,
369         .priv_auto_alloc_size   = sizeof(struct i2c_eeprom_partition),
370         .ops                    = &i2c_eeprom_partition_ops,
371 };
372
373 UCLASS_DRIVER(i2c_eeprom) = {
374         .id             = UCLASS_I2C_EEPROM,
375         .name           = "i2c_eeprom",
376 };