blob: 78a709f766c8b4d1915f2ac6fea049d1dd957c3a [file] [log] [blame]
Masami Hiramatsu1f52b642023-05-31 00:29:14 -05001// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2023, Linaro Limited
4 */
5
6#define LOG_CATEGORY UCLASS_FWU_MDATA
7
8#include <fwu.h>
9#include <fwu_mdata.h>
10#include <memalign.h>
11
12#include <linux/errno.h>
13#include <linux/types.h>
14
Masami Hiramatsu1f52b642023-05-31 00:29:14 -050015enum fwu_mtd_op {
16 FWU_MTD_READ,
17 FWU_MTD_WRITE,
18};
19
Masami Hiramatsu1f52b642023-05-31 00:29:14 -050020static bool mtd_is_aligned_with_block_size(struct mtd_info *mtd, u64 size)
21{
22 return !do_div(size, mtd->erasesize);
23}
24
25static int mtd_io_data(struct mtd_info *mtd, u32 offs, u32 size, void *data,
26 enum fwu_mtd_op op)
27{
28 struct mtd_oob_ops io_op = {};
29 u64 lock_len;
30 size_t len;
31 void *buf;
32 int ret;
33
34 if (!mtd_is_aligned_with_block_size(mtd, offs)) {
35 log_err("Offset unaligned with a block (0x%x)\n", mtd->erasesize);
36 return -EINVAL;
37 }
38
39 /* This will expand erase size to align with the block size */
40 lock_len = round_up(size, mtd->erasesize);
41
42 ret = mtd_unlock(mtd, offs, lock_len);
43 if (ret && ret != -EOPNOTSUPP)
44 return ret;
45
46 if (op == FWU_MTD_WRITE) {
47 struct erase_info erase_op = {};
48
49 erase_op.mtd = mtd;
50 erase_op.addr = offs;
51 erase_op.len = lock_len;
52 erase_op.scrub = 0;
53
54 ret = mtd_erase(mtd, &erase_op);
55 if (ret)
56 goto lock;
57 }
58
59 /* Also, expand the write size to align with the write size */
60 len = round_up(size, mtd->writesize);
61
62 buf = memalign(ARCH_DMA_MINALIGN, len);
63 if (!buf) {
64 ret = -ENOMEM;
65 goto lock;
66 }
67 memset(buf, 0xff, len);
68
69 io_op.mode = MTD_OPS_AUTO_OOB;
70 io_op.len = len;
71 io_op.datbuf = buf;
72
73 if (op == FWU_MTD_WRITE) {
74 memcpy(buf, data, size);
75 ret = mtd_write_oob(mtd, offs, &io_op);
76 } else {
77 ret = mtd_read_oob(mtd, offs, &io_op);
78 if (!ret)
79 memcpy(data, buf, size);
80 }
81 free(buf);
82
83lock:
84 mtd_lock(mtd, offs, lock_len);
85
86 return ret;
87}
88
Sughosh Ganu15665c52024-03-22 16:27:16 +053089static int fwu_mtd_read_mdata(struct udevice *dev, struct fwu_mdata *mdata,
90 bool primary, u32 size)
Masami Hiramatsu1f52b642023-05-31 00:29:14 -050091{
92 struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
93 struct mtd_info *mtd = mtd_priv->mtd;
94 u32 offs = primary ? mtd_priv->pri_offset : mtd_priv->sec_offset;
95
Sughosh Ganu15665c52024-03-22 16:27:16 +053096 return mtd_io_data(mtd, offs, size, mdata, FWU_MTD_READ);
Masami Hiramatsu1f52b642023-05-31 00:29:14 -050097}
98
Sughosh Ganu15665c52024-03-22 16:27:16 +053099static int fwu_mtd_write_mdata(struct udevice *dev, struct fwu_mdata *mdata,
100 bool primary, u32 size)
Masami Hiramatsu1f52b642023-05-31 00:29:14 -0500101{
102 struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
103 struct mtd_info *mtd = mtd_priv->mtd;
104 u32 offs = primary ? mtd_priv->pri_offset : mtd_priv->sec_offset;
105
Sughosh Ganu15665c52024-03-22 16:27:16 +0530106 return mtd_io_data(mtd, offs, size, mdata, FWU_MTD_WRITE);
Masami Hiramatsu1f52b642023-05-31 00:29:14 -0500107}
108
109static int flash_partition_offset(struct udevice *dev, const char *part_name, fdt_addr_t *offset)
110{
111 ofnode node, parts_node;
112 fdt_addr_t size = 0;
113
114 parts_node = ofnode_by_compatible(dev_ofnode(dev), "fixed-partitions");
115 node = ofnode_by_prop_value(parts_node, "label", part_name, strlen(part_name) + 1);
116 if (!ofnode_valid(node)) {
117 log_err("Warning: Failed to find partition by label <%s>\n", part_name);
118 return -ENOENT;
119 }
120
121 *offset = ofnode_get_addr_size_index_notrans(node, 0, &size);
122
123 return (int)size;
124}
125
Sughosh Ganub05d6a42024-03-22 16:27:17 +0530126static int get_fwu_mdata_dev(struct udevice *dev)
Masami Hiramatsu1f52b642023-05-31 00:29:14 -0500127{
128 struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
129 const fdt32_t *phandle_p = NULL;
130 struct udevice *mtd_dev;
131 struct mtd_info *mtd;
132 const char *label;
133 fdt_addr_t offset;
134 int ret, size;
135 u32 phandle;
Masami Hiramatsu1f52b642023-05-31 00:29:14 -0500136
137 /* Find the FWU mdata storage device */
138 phandle_p = ofnode_get_property(dev_ofnode(dev),
139 "fwu-mdata-store", &size);
140 if (!phandle_p) {
141 log_err("FWU meta data store not defined in device-tree\n");
142 return -ENOENT;
143 }
144
145 phandle = fdt32_to_cpu(*phandle_p);
146
147 ret = device_get_global_by_ofnode(ofnode_get_by_phandle(phandle),
148 &mtd_dev);
149 if (ret) {
150 log_err("FWU: failed to get mtd device\n");
151 return ret;
152 }
153
154 mtd_probe_devices();
155
156 mtd_for_each_device(mtd) {
157 if (mtd->dev == mtd_dev) {
158 mtd_priv->mtd = mtd;
159 log_debug("Found the FWU mdata mtd device %s\n", mtd->name);
160 break;
161 }
162 }
163 if (!mtd_priv->mtd) {
164 log_err("Failed to find mtd device by fwu-mdata-store\n");
165 return -ENODEV;
166 }
167
168 /* Get the offset of primary and secondary mdata */
169 ret = ofnode_read_string_index(dev_ofnode(dev), "mdata-parts", 0, &label);
170 if (ret)
171 return ret;
172 strncpy(mtd_priv->pri_label, label, 50);
173
174 ret = flash_partition_offset(mtd_dev, mtd_priv->pri_label, &offset);
175 if (ret <= 0)
176 return ret;
177 mtd_priv->pri_offset = offset;
178
179 ret = ofnode_read_string_index(dev_ofnode(dev), "mdata-parts", 1, &label);
180 if (ret)
181 return ret;
182 strncpy(mtd_priv->sec_label, label, 50);
183
184 ret = flash_partition_offset(mtd_dev, mtd_priv->sec_label, &offset);
185 if (ret <= 0)
186 return ret;
187 mtd_priv->sec_offset = offset;
188
Sughosh Ganub05d6a42024-03-22 16:27:17 +0530189 return 0;
190}
191
192static int fwu_mtd_image_info_populate(struct udevice *dev, u8 nbanks,
193 u16 nimages)
194{
195 struct fwu_mtd_image_info *mtd_images;
196 struct fwu_mdata_mtd_priv *mtd_priv = dev_get_priv(dev);
197 struct udevice *mtd_dev = mtd_priv->mtd->dev;
198 fdt_addr_t offset;
199 ofnode bank;
200 int off_img;
201 u32 total_images;
Masami Hiramatsu1f52b642023-05-31 00:29:14 -0500202
Sughosh Ganub05d6a42024-03-22 16:27:17 +0530203 total_images = nbanks * nimages;
204 mtd_priv->fwu_mtd_images = malloc(sizeof(struct fwu_mtd_image_info) *
205 total_images);
206 if (!mtd_priv->fwu_mtd_images)
207 return -ENOMEM;
208
209 off_img = 0;
210 mtd_images = mtd_priv->fwu_mtd_images;
Masami Hiramatsu1f52b642023-05-31 00:29:14 -0500211 ofnode_for_each_subnode(bank, dev_ofnode(dev)) {
212 int bank_num, bank_offset, bank_size;
213 const char *bank_name;
214 ofnode image;
215
216 ofnode_read_u32(bank, "id", &bank_num);
217 bank_name = ofnode_read_string(bank, "label");
218 bank_size = flash_partition_offset(mtd_dev, bank_name, &offset);
219 if (bank_size <= 0)
220 return bank_size;
221 bank_offset = offset;
222 log_debug("Bank%d: %s [0x%x - 0x%x]\n",
223 bank_num, bank_name, bank_offset, bank_offset + bank_size);
224
225 ofnode_for_each_subnode(image, bank) {
226 int image_num, image_offset, image_size;
227 const char *uuid;
228
Sughosh Ganub05d6a42024-03-22 16:27:17 +0530229 if (off_img == total_images) {
Masami Hiramatsu1f52b642023-05-31 00:29:14 -0500230 log_err("DT provides more images than configured!\n");
231 break;
232 }
233
234 uuid = ofnode_read_string(image, "uuid");
235 ofnode_read_u32(image, "id", &image_num);
236 ofnode_read_u32(image, "offset", &image_offset);
237 ofnode_read_u32(image, "size", &image_size);
238
Sughosh Ganub05d6a42024-03-22 16:27:17 +0530239 mtd_images[off_img].start = bank_offset + image_offset;
240 mtd_images[off_img].size = image_size;
241 mtd_images[off_img].bank_num = bank_num;
242 mtd_images[off_img].image_num = image_num;
243 strcpy(mtd_images[off_img].uuidbuf, uuid);
Masami Hiramatsu1f52b642023-05-31 00:29:14 -0500244 log_debug("\tImage%d: %s @0x%x\n\n",
245 image_num, uuid, bank_offset + image_offset);
246 off_img++;
247 }
248 }
249
250 return 0;
251}
252
253static int fwu_mdata_mtd_probe(struct udevice *dev)
254{
Sughosh Ganub05d6a42024-03-22 16:27:17 +0530255 u8 nbanks;
256 u16 nimages;
257 int ret;
258
259 ret = get_fwu_mdata_dev(dev);
260 if (ret)
261 return ret;
262
263 nbanks = CONFIG_FWU_NUM_BANKS;
264 nimages = CONFIG_FWU_NUM_IMAGES_PER_BANK;
265 ret = fwu_mtd_image_info_populate(dev, nbanks, nimages);
266 if (ret)
267 return ret;
268
269 return 0;
Masami Hiramatsu1f52b642023-05-31 00:29:14 -0500270}
271
272static struct fwu_mdata_ops fwu_mtd_ops = {
273 .read_mdata = fwu_mtd_read_mdata,
274 .write_mdata = fwu_mtd_write_mdata,
275};
276
277static const struct udevice_id fwu_mdata_ids[] = {
278 { .compatible = "u-boot,fwu-mdata-mtd" },
279 { }
280};
281
282U_BOOT_DRIVER(fwu_mdata_mtd) = {
283 .name = "fwu-mdata-mtd",
284 .id = UCLASS_FWU_MDATA,
285 .of_match = fwu_mdata_ids,
286 .ops = &fwu_mtd_ops,
287 .probe = fwu_mdata_mtd_probe,
Masami Hiramatsu1f52b642023-05-31 00:29:14 -0500288 .priv_auto = sizeof(struct fwu_mdata_mtd_priv),
289};