blob: 6c0459dc555b7b7cd5acda62a07aa0852df5bb00 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass6ca4ba02014-12-10 08:55:54 -07002/*
3 * Copyright (c) 2014 Google, Inc
Simon Glass6ca4ba02014-12-10 08:55:54 -07004 */
5
6#include <common.h>
Simon Glasseba6b8d2019-11-14 12:57:50 -07007#include <eeprom.h>
Masahiro Yamadaf9d52cc2014-12-18 20:00:26 +09008#include <linux/err.h>
Baruch Siach4e4c5ed2019-04-07 12:38:49 +03009#include <linux/kernel.h>
Simon Glass6ca4ba02014-12-10 08:55:54 -070010#include <dm.h>
Robert Beckettf030d6f2019-10-28 18:29:05 +000011#include <dm/device-internal.h>
Simon Glass6ca4ba02014-12-10 08:55:54 -070012#include <i2c.h>
13#include <i2c_eeprom.h>
14
Robert Beckett069ddc72019-10-28 18:29:06 +000015struct i2c_eeprom_drv_data {
16 u32 size; /* size in bytes */
17 u32 pagewidth; /* pagesize = 2^pagewidth */
Robert Beckett44cf4612020-01-31 15:07:52 +020018 u32 addr_offset_mask; /* bits in addr used for offset overflow */
19 u32 offset_len; /* size in bytes of offset */
Robert Beckett069ddc72019-10-28 18:29:06 +000020};
21
Jonas Karlman8427a242017-04-22 08:57:41 +000022int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf, int size)
23{
24 const struct i2c_eeprom_ops *ops = device_get_ops(dev);
25
26 if (!ops->read)
27 return -ENOSYS;
28
29 return ops->read(dev, offset, buf, size);
30}
31
32int i2c_eeprom_write(struct udevice *dev, int offset, uint8_t *buf, int size)
33{
34 const struct i2c_eeprom_ops *ops = device_get_ops(dev);
35
36 if (!ops->write)
37 return -ENOSYS;
38
39 return ops->write(dev, offset, buf, size);
40}
41
Robert Beckett069ddc72019-10-28 18:29:06 +000042int i2c_eeprom_size(struct udevice *dev)
43{
44 const struct i2c_eeprom_ops *ops = device_get_ops(dev);
45
46 if (!ops->size)
47 return -ENOSYS;
48
49 return ops->size(dev);
50}
51
Jonas Karlman8427a242017-04-22 08:57:41 +000052static int i2c_eeprom_std_read(struct udevice *dev, int offset, uint8_t *buf,
53 int size)
Simon Glass6ca4ba02014-12-10 08:55:54 -070054{
mario.six@gdsys.cc7559ac42016-06-22 15:14:16 +020055 return dm_i2c_read(dev, offset, buf, size);
Simon Glass6ca4ba02014-12-10 08:55:54 -070056}
57
Jonas Karlman8427a242017-04-22 08:57:41 +000058static int i2c_eeprom_std_write(struct udevice *dev, int offset,
59 const uint8_t *buf, int size)
Simon Glass6ca4ba02014-12-10 08:55:54 -070060{
Baruch Siach4e4c5ed2019-04-07 12:38:49 +030061 struct i2c_eeprom *priv = dev_get_priv(dev);
62 int ret;
63
64 while (size > 0) {
65 int write_size = min_t(int, size, priv->pagesize);
66
67 ret = dm_i2c_write(dev, offset, buf, write_size);
68 if (ret)
69 return ret;
70
71 offset += write_size;
72 buf += write_size;
73 size -= write_size;
74
75 udelay(10000);
76 }
77
78 return 0;
Simon Glass6ca4ba02014-12-10 08:55:54 -070079}
80
Robert Beckett069ddc72019-10-28 18:29:06 +000081static int i2c_eeprom_std_size(struct udevice *dev)
82{
83 struct i2c_eeprom *priv = dev_get_priv(dev);
84
85 return priv->size;
86}
87
Masahiro Yamadaa7cc93e82017-06-22 16:51:22 +090088static const struct i2c_eeprom_ops i2c_eeprom_std_ops = {
Jonas Karlman8427a242017-04-22 08:57:41 +000089 .read = i2c_eeprom_std_read,
90 .write = i2c_eeprom_std_write,
Robert Beckett069ddc72019-10-28 18:29:06 +000091 .size = i2c_eeprom_std_size,
Simon Glass6ca4ba02014-12-10 08:55:54 -070092};
93
mario.six@gdsys.cc7559ac42016-06-22 15:14:16 +020094static int i2c_eeprom_std_ofdata_to_platdata(struct udevice *dev)
95{
96 struct i2c_eeprom *priv = dev_get_priv(dev);
Robert Beckett069ddc72019-10-28 18:29:06 +000097 struct i2c_eeprom_drv_data *data =
98 (struct i2c_eeprom_drv_data *)dev_get_driver_data(dev);
Baruch Siach263b5b02019-04-07 12:38:48 +030099 u32 pagesize;
Robert Beckett069ddc72019-10-28 18:29:06 +0000100 u32 size;
Baruch Siach263b5b02019-04-07 12:38:48 +0300101
102 if (dev_read_u32(dev, "pagesize", &pagesize) == 0) {
103 priv->pagesize = pagesize;
Robert Beckett069ddc72019-10-28 18:29:06 +0000104 } else {
105 /* 6 bit -> page size of up to 2^63 (should be sufficient) */
106 priv->pagewidth = data->pagewidth;
107 priv->pagesize = (1 << priv->pagewidth);
Baruch Siach263b5b02019-04-07 12:38:48 +0300108 }
mario.six@gdsys.cc7559ac42016-06-22 15:14:16 +0200109
Robert Beckett069ddc72019-10-28 18:29:06 +0000110 if (dev_read_u32(dev, "size", &size) == 0)
111 priv->size = size;
112 else
113 priv->size = data->size;
mario.six@gdsys.cc7559ac42016-06-22 15:14:16 +0200114
115 return 0;
116}
117
Robert Beckettf030d6f2019-10-28 18:29:05 +0000118static 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
Masahiro Yamadaa7cc93e82017-06-22 16:51:22 +0900141static int i2c_eeprom_std_probe(struct udevice *dev)
Simon Glass6ca4ba02014-12-10 08:55:54 -0700142{
Baruch Siach38793e22019-08-05 09:03:30 +0300143 u8 test_byte;
144 int ret;
Robert Beckett44cf4612020-01-31 15:07:52 +0200145 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);
Baruch Siach38793e22019-08-05 09:03:30 +0300150
151 /* Verify that the chip is functional */
152 ret = i2c_eeprom_read(dev, 0, &test_byte, 1);
153 if (ret)
154 return -ENODEV;
155
Simon Glass6ca4ba02014-12-10 08:55:54 -0700156 return 0;
157}
158
Robert Beckett069ddc72019-10-28 18:29:06 +0000159static const struct i2c_eeprom_drv_data eeprom_data = {
160 .size = 0,
161 .pagewidth = 0,
Robert Beckett44cf4612020-01-31 15:07:52 +0200162 .addr_offset_mask = 0,
163 .offset_len = 1,
Robert Beckett069ddc72019-10-28 18:29:06 +0000164};
165
166static const struct i2c_eeprom_drv_data mc24aa02e48_data = {
167 .size = 256,
168 .pagewidth = 3,
Robert Beckett44cf4612020-01-31 15:07:52 +0200169 .addr_offset_mask = 0,
170 .offset_len = 1,
Robert Beckett069ddc72019-10-28 18:29:06 +0000171};
172
173static const struct i2c_eeprom_drv_data atmel24c01a_data = {
174 .size = 128,
175 .pagewidth = 3,
Robert Beckett44cf4612020-01-31 15:07:52 +0200176 .addr_offset_mask = 0,
177 .offset_len = 1,
Robert Beckett069ddc72019-10-28 18:29:06 +0000178};
179
180static const struct i2c_eeprom_drv_data atmel24c02_data = {
181 .size = 256,
182 .pagewidth = 3,
Robert Beckett44cf4612020-01-31 15:07:52 +0200183 .addr_offset_mask = 0,
184 .offset_len = 1,
Robert Beckett069ddc72019-10-28 18:29:06 +0000185};
186
187static const struct i2c_eeprom_drv_data atmel24c04_data = {
188 .size = 512,
189 .pagewidth = 4,
Robert Beckett44cf4612020-01-31 15:07:52 +0200190 .addr_offset_mask = 0x1,
191 .offset_len = 1,
Robert Beckett069ddc72019-10-28 18:29:06 +0000192};
193
194static const struct i2c_eeprom_drv_data atmel24c08_data = {
195 .size = 1024,
196 .pagewidth = 4,
Robert Beckett44cf4612020-01-31 15:07:52 +0200197 .addr_offset_mask = 0x3,
198 .offset_len = 1,
Robert Beckett069ddc72019-10-28 18:29:06 +0000199};
200
201static const struct i2c_eeprom_drv_data atmel24c08a_data = {
202 .size = 1024,
203 .pagewidth = 4,
Robert Beckett44cf4612020-01-31 15:07:52 +0200204 .addr_offset_mask = 0x3,
205 .offset_len = 1,
Robert Beckett069ddc72019-10-28 18:29:06 +0000206};
207
208static const struct i2c_eeprom_drv_data atmel24c16a_data = {
209 .size = 2048,
210 .pagewidth = 4,
Robert Beckett44cf4612020-01-31 15:07:52 +0200211 .addr_offset_mask = 0x7,
212 .offset_len = 1,
Robert Beckett069ddc72019-10-28 18:29:06 +0000213};
214
215static const struct i2c_eeprom_drv_data atmel24mac402_data = {
216 .size = 256,
217 .pagewidth = 4,
Robert Beckett44cf4612020-01-31 15:07:52 +0200218 .addr_offset_mask = 0,
219 .offset_len = 1,
Robert Beckett069ddc72019-10-28 18:29:06 +0000220};
221
222static const struct i2c_eeprom_drv_data atmel24c32_data = {
223 .size = 4096,
224 .pagewidth = 5,
Robert Beckett44cf4612020-01-31 15:07:52 +0200225 .addr_offset_mask = 0,
226 .offset_len = 2,
Robert Beckett069ddc72019-10-28 18:29:06 +0000227};
228
229static const struct i2c_eeprom_drv_data atmel24c64_data = {
230 .size = 8192,
231 .pagewidth = 5,
Robert Beckett44cf4612020-01-31 15:07:52 +0200232 .addr_offset_mask = 0,
233 .offset_len = 2,
Robert Beckett069ddc72019-10-28 18:29:06 +0000234};
235
236static const struct i2c_eeprom_drv_data atmel24c128_data = {
237 .size = 16384,
238 .pagewidth = 6,
Robert Beckett44cf4612020-01-31 15:07:52 +0200239 .addr_offset_mask = 0,
240 .offset_len = 2,
Robert Beckett069ddc72019-10-28 18:29:06 +0000241};
242
243static const struct i2c_eeprom_drv_data atmel24c256_data = {
244 .size = 32768,
245 .pagewidth = 6,
Robert Beckett44cf4612020-01-31 15:07:52 +0200246 .addr_offset_mask = 0,
247 .offset_len = 2,
Robert Beckett069ddc72019-10-28 18:29:06 +0000248};
249
250static const struct i2c_eeprom_drv_data atmel24c512_data = {
251 .size = 65536,
252 .pagewidth = 6,
Robert Beckett44cf4612020-01-31 15:07:52 +0200253 .addr_offset_mask = 0,
254 .offset_len = 2,
Robert Beckett069ddc72019-10-28 18:29:06 +0000255};
256
Simon Glass6ca4ba02014-12-10 08:55:54 -0700257static const struct udevice_id i2c_eeprom_std_ids[] = {
Robert Beckett069ddc72019-10-28 18:29:06 +0000258 { .compatible = "i2c-eeprom", (ulong)&eeprom_data },
259 { .compatible = "microchip,24aa02e48", (ulong)&mc24aa02e48_data },
260 { .compatible = "atmel,24c01a", (ulong)&atmel24c01a_data },
261 { .compatible = "atmel,24c02", (ulong)&atmel24c02_data },
262 { .compatible = "atmel,24c04", (ulong)&atmel24c04_data },
263 { .compatible = "atmel,24c08", (ulong)&atmel24c08_data },
264 { .compatible = "atmel,24c08a", (ulong)&atmel24c08a_data },
265 { .compatible = "atmel,24c16a", (ulong)&atmel24c16a_data },
266 { .compatible = "atmel,24mac402", (ulong)&atmel24mac402_data },
267 { .compatible = "atmel,24c32", (ulong)&atmel24c32_data },
268 { .compatible = "atmel,24c64", (ulong)&atmel24c64_data },
269 { .compatible = "atmel,24c128", (ulong)&atmel24c128_data },
270 { .compatible = "atmel,24c256", (ulong)&atmel24c256_data },
271 { .compatible = "atmel,24c512", (ulong)&atmel24c512_data },
Simon Glass6ca4ba02014-12-10 08:55:54 -0700272 { }
273};
274
275U_BOOT_DRIVER(i2c_eeprom_std) = {
mario.six@gdsys.cc7559ac42016-06-22 15:14:16 +0200276 .name = "i2c_eeprom",
277 .id = UCLASS_I2C_EEPROM,
278 .of_match = i2c_eeprom_std_ids,
Robert Beckettf030d6f2019-10-28 18:29:05 +0000279 .bind = i2c_eeprom_std_bind,
mario.six@gdsys.cc7559ac42016-06-22 15:14:16 +0200280 .probe = i2c_eeprom_std_probe,
281 .ofdata_to_platdata = i2c_eeprom_std_ofdata_to_platdata,
282 .priv_auto_alloc_size = sizeof(struct i2c_eeprom),
283 .ops = &i2c_eeprom_std_ops,
Simon Glass6ca4ba02014-12-10 08:55:54 -0700284};
285
Robert Beckettf030d6f2019-10-28 18:29:05 +0000286struct i2c_eeprom_partition {
287 u32 offset;
288 u32 size;
289};
290
291static int i2c_eeprom_partition_probe(struct udevice *dev)
292{
293 return 0;
294}
295
296static int i2c_eeprom_partition_ofdata_to_platdata(struct udevice *dev)
297{
298 struct i2c_eeprom_partition *priv = dev_get_priv(dev);
299 u32 offset, size;
300 int ret;
301
302 ret = dev_read_u32(dev, "offset", &offset);
303 if (ret)
304 return ret;
305
306 ret = dev_read_u32(dev, "size", &size);
307 if (ret)
308 return ret;
309
310 priv->offset = offset;
311 priv->size = size;
312
313 return 0;
314}
315
316static int i2c_eeprom_partition_read(struct udevice *dev, int offset,
317 u8 *buf, int size)
318{
319 struct i2c_eeprom_partition *priv = dev_get_priv(dev);
320 struct udevice *parent = dev_get_parent(dev);
321
322 if (!parent)
323 return -ENODEV;
324 if (offset + size > priv->size)
325 return -EINVAL;
326
327 return i2c_eeprom_read(parent, offset + priv->offset, buf, size);
328}
329
330static int i2c_eeprom_partition_write(struct udevice *dev, int offset,
331 const u8 *buf, int size)
332{
333 struct i2c_eeprom_partition *priv = dev_get_priv(dev);
334 struct udevice *parent = dev_get_parent(dev);
335
336 if (!parent)
337 return -ENODEV;
338 if (offset + size > priv->size)
339 return -EINVAL;
340
341 return i2c_eeprom_write(parent, offset + priv->offset, (uint8_t *)buf,
342 size);
343}
344
Robert Beckett069ddc72019-10-28 18:29:06 +0000345static int i2c_eeprom_partition_size(struct udevice *dev)
346{
347 struct i2c_eeprom_partition *priv = dev_get_priv(dev);
348
349 return priv->size;
350}
351
Robert Beckettf030d6f2019-10-28 18:29:05 +0000352static const struct i2c_eeprom_ops i2c_eeprom_partition_ops = {
353 .read = i2c_eeprom_partition_read,
354 .write = i2c_eeprom_partition_write,
Robert Beckett069ddc72019-10-28 18:29:06 +0000355 .size = i2c_eeprom_partition_size,
Robert Beckettf030d6f2019-10-28 18:29:05 +0000356};
357
358U_BOOT_DRIVER(i2c_eeprom_partition) = {
359 .name = "i2c_eeprom_partition",
360 .id = UCLASS_I2C_EEPROM,
361 .probe = i2c_eeprom_partition_probe,
362 .ofdata_to_platdata = i2c_eeprom_partition_ofdata_to_platdata,
363 .priv_auto_alloc_size = sizeof(struct i2c_eeprom_partition),
364 .ops = &i2c_eeprom_partition_ops,
365};
366
Simon Glass6ca4ba02014-12-10 08:55:54 -0700367UCLASS_DRIVER(i2c_eeprom) = {
368 .id = UCLASS_I2C_EEPROM,
369 .name = "i2c_eeprom",
370};