]>
Commit | Line | Data |
---|---|---|
c6631764 PA |
1 | /* |
2 | * dfu_nand.c -- DFU for NAND routines. | |
3 | * | |
4 | * Copyright (C) 2012-2013 Texas Instruments, Inc. | |
5 | * | |
6 | * Based on dfu_mmc.c which is: | |
7 | * Copyright (C) 2012 Samsung Electronics | |
8 | * author: Lukasz Majewski <l.majewski@samsung.com> | |
9 | * | |
1a459660 | 10 | * SPDX-License-Identifier: GPL-2.0+ |
c6631764 PA |
11 | */ |
12 | ||
13 | #include <common.h> | |
14 | #include <malloc.h> | |
15 | #include <errno.h> | |
16 | #include <div64.h> | |
17 | #include <dfu.h> | |
18 | #include <linux/mtd/mtd.h> | |
19 | #include <jffs2/load_kernel.h> | |
20 | #include <nand.h> | |
21 | ||
22 | enum dfu_nand_op { | |
23 | DFU_OP_READ = 1, | |
24 | DFU_OP_WRITE, | |
25 | }; | |
26 | ||
27 | static int nand_block_op(enum dfu_nand_op op, struct dfu_entity *dfu, | |
28 | u64 offset, void *buf, long *len) | |
29 | { | |
30 | loff_t start, lim; | |
31 | size_t count, actual; | |
32 | int ret; | |
33 | nand_info_t *nand; | |
34 | ||
35 | /* if buf == NULL return total size of the area */ | |
36 | if (buf == NULL) { | |
37 | *len = dfu->data.nand.size; | |
38 | return 0; | |
39 | } | |
40 | ||
41 | start = dfu->data.nand.start + offset + dfu->bad_skip; | |
42 | lim = dfu->data.nand.start + dfu->data.nand.size - start; | |
43 | count = *len; | |
44 | ||
45 | if (nand_curr_device < 0 || | |
46 | nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || | |
47 | !nand_info[nand_curr_device].name) { | |
48 | printf("%s: invalid nand device\n", __func__); | |
49 | return -1; | |
50 | } | |
51 | ||
52 | nand = &nand_info[nand_curr_device]; | |
53 | ||
a67cc37e | 54 | if (op == DFU_OP_READ) { |
c6631764 PA |
55 | ret = nand_read_skip_bad(nand, start, &count, &actual, |
56 | lim, buf); | |
a67cc37e HS |
57 | } else { |
58 | nand_erase_options_t opts; | |
59 | ||
60 | memset(&opts, 0, sizeof(opts)); | |
61 | opts.offset = start; | |
62 | opts.length = count; | |
63 | opts.spread = 1; | |
64 | opts.quiet = 1; | |
65 | opts.lim = lim; | |
66 | /* first erase */ | |
67 | ret = nand_erase_opts(nand, &opts); | |
68 | if (ret) | |
69 | return ret; | |
70 | /* then write */ | |
c6631764 PA |
71 | ret = nand_write_skip_bad(nand, start, &count, &actual, |
72 | lim, buf, 0); | |
a67cc37e | 73 | } |
c6631764 PA |
74 | |
75 | if (ret != 0) { | |
76 | printf("%s: nand_%s_skip_bad call failed at %llx!\n", | |
77 | __func__, op == DFU_OP_READ ? "read" : "write", | |
78 | start); | |
79 | return ret; | |
80 | } | |
81 | ||
82 | /* | |
83 | * Find out where we stopped writing data. This can be deeper into | |
84 | * the NAND than we expected due to having to skip bad blocks. So | |
85 | * we must take this into account for the next write, if any. | |
86 | */ | |
87 | if (actual > count) | |
88 | dfu->bad_skip += actual - count; | |
89 | ||
90 | return ret; | |
91 | } | |
92 | ||
93 | static inline int nand_block_write(struct dfu_entity *dfu, | |
94 | u64 offset, void *buf, long *len) | |
95 | { | |
96 | return nand_block_op(DFU_OP_WRITE, dfu, offset, buf, len); | |
97 | } | |
98 | ||
99 | static inline int nand_block_read(struct dfu_entity *dfu, | |
100 | u64 offset, void *buf, long *len) | |
101 | { | |
102 | return nand_block_op(DFU_OP_READ, dfu, offset, buf, len); | |
103 | } | |
104 | ||
105 | static int dfu_write_medium_nand(struct dfu_entity *dfu, | |
106 | u64 offset, void *buf, long *len) | |
107 | { | |
108 | int ret = -1; | |
109 | ||
110 | switch (dfu->layout) { | |
111 | case DFU_RAW_ADDR: | |
112 | ret = nand_block_write(dfu, offset, buf, len); | |
113 | break; | |
114 | default: | |
115 | printf("%s: Layout (%s) not (yet) supported!\n", __func__, | |
116 | dfu_get_layout(dfu->layout)); | |
117 | } | |
118 | ||
119 | return ret; | |
120 | } | |
121 | ||
122 | static int dfu_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf, | |
123 | long *len) | |
124 | { | |
125 | int ret = -1; | |
126 | ||
127 | switch (dfu->layout) { | |
128 | case DFU_RAW_ADDR: | |
129 | ret = nand_block_read(dfu, offset, buf, len); | |
130 | break; | |
131 | default: | |
132 | printf("%s: Layout (%s) not (yet) supported!\n", __func__, | |
133 | dfu_get_layout(dfu->layout)); | |
134 | } | |
135 | ||
136 | return ret; | |
137 | } | |
138 | ||
139 | int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s) | |
140 | { | |
141 | char *st; | |
142 | int ret, dev, part; | |
143 | ||
144 | dfu->dev_type = DFU_DEV_NAND; | |
145 | st = strsep(&s, " "); | |
146 | if (!strcmp(st, "raw")) { | |
147 | dfu->layout = DFU_RAW_ADDR; | |
148 | dfu->data.nand.start = simple_strtoul(s, &s, 16); | |
149 | s++; | |
150 | dfu->data.nand.size = simple_strtoul(s, &s, 16); | |
151 | } else if (!strcmp(st, "part")) { | |
152 | char mtd_id[32]; | |
153 | struct mtd_device *mtd_dev; | |
154 | u8 part_num; | |
155 | struct part_info *pi; | |
156 | ||
157 | dfu->layout = DFU_RAW_ADDR; | |
158 | ||
159 | dev = simple_strtoul(s, &s, 10); | |
160 | s++; | |
161 | part = simple_strtoul(s, &s, 10); | |
162 | ||
163 | sprintf(mtd_id, "%s%d,%d", "nand", dev, part - 1); | |
164 | printf("using id '%s'\n", mtd_id); | |
165 | ||
166 | mtdparts_init(); | |
167 | ||
168 | ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi); | |
169 | if (ret != 0) { | |
170 | printf("Could not locate '%s'\n", mtd_id); | |
171 | return -1; | |
172 | } | |
173 | ||
174 | dfu->data.nand.start = pi->offset; | |
175 | dfu->data.nand.size = pi->size; | |
176 | ||
177 | } else { | |
178 | printf("%s: Memory layout (%s) not supported!\n", __func__, st); | |
179 | return -1; | |
180 | } | |
181 | ||
182 | dfu->read_medium = dfu_read_medium_nand; | |
183 | dfu->write_medium = dfu_write_medium_nand; | |
184 | ||
185 | /* initial state */ | |
186 | dfu->inited = 0; | |
187 | ||
188 | return 0; | |
189 | } |