misc: i2c_eeprom: add fixed partitions support
[oweals/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/err.h>
9 #include <linux/kernel.h>
10 #include <dm.h>
11 #include <dm/device-internal.h>
12 #include <i2c.h>
13 #include <i2c_eeprom.h>
14
15 int i2c_eeprom_read(struct udevice *dev, int offset, uint8_t *buf, int size)
16 {
17         const struct i2c_eeprom_ops *ops = device_get_ops(dev);
18
19         if (!ops->read)
20                 return -ENOSYS;
21
22         return ops->read(dev, offset, buf, size);
23 }
24
25 int i2c_eeprom_write(struct udevice *dev, int offset, uint8_t *buf, int size)
26 {
27         const struct i2c_eeprom_ops *ops = device_get_ops(dev);
28
29         if (!ops->write)
30                 return -ENOSYS;
31
32         return ops->write(dev, offset, buf, size);
33 }
34
35 static int i2c_eeprom_std_read(struct udevice *dev, int offset, uint8_t *buf,
36                                int size)
37 {
38         return dm_i2c_read(dev, offset, buf, size);
39 }
40
41 static int i2c_eeprom_std_write(struct udevice *dev, int offset,
42                                 const uint8_t *buf, int size)
43 {
44         struct i2c_eeprom *priv = dev_get_priv(dev);
45         int ret;
46
47         while (size > 0) {
48                 int write_size = min_t(int, size, priv->pagesize);
49
50                 ret = dm_i2c_write(dev, offset, buf, write_size);
51                 if (ret)
52                         return ret;
53
54                 offset += write_size;
55                 buf += write_size;
56                 size -= write_size;
57
58                 udelay(10000);
59         }
60
61         return 0;
62 }
63
64 static const struct i2c_eeprom_ops i2c_eeprom_std_ops = {
65         .read   = i2c_eeprom_std_read,
66         .write  = i2c_eeprom_std_write,
67 };
68
69 static int i2c_eeprom_std_ofdata_to_platdata(struct udevice *dev)
70 {
71         struct i2c_eeprom *priv = dev_get_priv(dev);
72         u64 data = dev_get_driver_data(dev);
73         u32 pagesize;
74
75         if (dev_read_u32(dev, "pagesize", &pagesize) == 0) {
76                 priv->pagesize = pagesize;
77                 return 0;
78         }
79
80         /* 6 bit -> page size of up to 2^63 (should be sufficient) */
81         priv->pagewidth = data & 0x3F;
82         priv->pagesize = (1 << priv->pagewidth);
83
84         return 0;
85 }
86
87 static int i2c_eeprom_std_bind(struct udevice *dev)
88 {
89         ofnode partitions = ofnode_find_subnode(dev_ofnode(dev), "partitions");
90         ofnode partition;
91         const char *name;
92
93         if (!ofnode_valid(partitions))
94                 return 0;
95         if (!ofnode_device_is_compatible(partitions, "fixed-partitions"))
96                 return -ENOTSUPP;
97
98         ofnode_for_each_subnode(partition, partitions) {
99                 name = ofnode_get_name(partition);
100                 if (!name)
101                         continue;
102
103                 device_bind_ofnode(dev, DM_GET_DRIVER(i2c_eeprom_partition),
104                                    name, NULL, partition, NULL);
105         }
106
107         return 0;
108 }
109
110 static int i2c_eeprom_std_probe(struct udevice *dev)
111 {
112         u8 test_byte;
113         int ret;
114
115         /* Verify that the chip is functional */
116         ret = i2c_eeprom_read(dev, 0, &test_byte, 1);
117         if (ret)
118                 return -ENODEV;
119
120         return 0;
121 }
122
123 static const struct udevice_id i2c_eeprom_std_ids[] = {
124         { .compatible = "i2c-eeprom", .data = 0 },
125         { .compatible = "microchip,24aa02e48", .data = 3 },
126         { .compatible = "atmel,24c01a", .data = 3 },
127         { .compatible = "atmel,24c02", .data = 3 },
128         { .compatible = "atmel,24c04", .data = 4 },
129         { .compatible = "atmel,24c08", .data = 4 },
130         { .compatible = "atmel,24c08a", .data = 4 },
131         { .compatible = "atmel,24c16a", .data = 4 },
132         { .compatible = "atmel,24mac402", .data = 4 },
133         { .compatible = "atmel,24c32", .data = 5 },
134         { .compatible = "atmel,24c64", .data = 5 },
135         { .compatible = "atmel,24c128", .data = 6 },
136         { .compatible = "atmel,24c256", .data = 6 },
137         { .compatible = "atmel,24c512", .data = 6 },
138         { }
139 };
140
141 U_BOOT_DRIVER(i2c_eeprom_std) = {
142         .name                   = "i2c_eeprom",
143         .id                     = UCLASS_I2C_EEPROM,
144         .of_match               = i2c_eeprom_std_ids,
145         .bind                   = i2c_eeprom_std_bind,
146         .probe                  = i2c_eeprom_std_probe,
147         .ofdata_to_platdata     = i2c_eeprom_std_ofdata_to_platdata,
148         .priv_auto_alloc_size   = sizeof(struct i2c_eeprom),
149         .ops                    = &i2c_eeprom_std_ops,
150 };
151
152 struct i2c_eeprom_partition {
153         u32 offset;
154         u32 size;
155 };
156
157 static int i2c_eeprom_partition_probe(struct udevice *dev)
158 {
159         return 0;
160 }
161
162 static int i2c_eeprom_partition_ofdata_to_platdata(struct udevice *dev)
163 {
164         struct i2c_eeprom_partition *priv = dev_get_priv(dev);
165         u32 offset, size;
166         int ret;
167
168         ret = dev_read_u32(dev, "offset", &offset);
169         if (ret)
170                 return ret;
171
172         ret = dev_read_u32(dev, "size", &size);
173         if (ret)
174                 return ret;
175
176         priv->offset = offset;
177         priv->size = size;
178
179         return 0;
180 }
181
182 static int i2c_eeprom_partition_read(struct udevice *dev, int offset,
183                                      u8 *buf, int size)
184 {
185         struct i2c_eeprom_partition *priv = dev_get_priv(dev);
186         struct udevice *parent = dev_get_parent(dev);
187
188         if (!parent)
189                 return -ENODEV;
190         if (offset + size > priv->size)
191                 return -EINVAL;
192
193         return i2c_eeprom_read(parent, offset + priv->offset, buf, size);
194 }
195
196 static int i2c_eeprom_partition_write(struct udevice *dev, int offset,
197                                       const u8 *buf, int size)
198 {
199         struct i2c_eeprom_partition *priv = dev_get_priv(dev);
200         struct udevice *parent = dev_get_parent(dev);
201
202         if (!parent)
203                 return -ENODEV;
204         if (offset + size > priv->size)
205                 return -EINVAL;
206
207         return i2c_eeprom_write(parent, offset + priv->offset, (uint8_t *)buf,
208                                 size);
209 }
210
211 static const struct i2c_eeprom_ops i2c_eeprom_partition_ops = {
212         .read   = i2c_eeprom_partition_read,
213         .write  = i2c_eeprom_partition_write,
214 };
215
216 U_BOOT_DRIVER(i2c_eeprom_partition) = {
217         .name                   = "i2c_eeprom_partition",
218         .id                     = UCLASS_I2C_EEPROM,
219         .probe                  = i2c_eeprom_partition_probe,
220         .ofdata_to_platdata     = i2c_eeprom_partition_ofdata_to_platdata,
221         .priv_auto_alloc_size   = sizeof(struct i2c_eeprom_partition),
222         .ops                    = &i2c_eeprom_partition_ops,
223 };
224
225 UCLASS_DRIVER(i2c_eeprom) = {
226         .id             = UCLASS_I2C_EEPROM,
227         .name           = "i2c_eeprom",
228 };