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 * 10 * SPDX-License-Identifier: GPL-2.0+ 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 static int nand_block_op(enum dfu_op op, struct dfu_entity *dfu, 23 u64 offset, void *buf, long *len) 24 { 25 loff_t start, lim; 26 size_t count, actual; 27 int ret; 28 struct mtd_info *mtd; 29 30 /* if buf == NULL return total size of the area */ 31 if (buf == NULL) { 32 *len = dfu->data.nand.size; 33 return 0; 34 } 35 36 start = dfu->data.nand.start + offset + dfu->bad_skip; 37 lim = dfu->data.nand.start + dfu->data.nand.size - start; 38 count = *len; 39 40 mtd = get_nand_dev_by_index(nand_curr_device); 41 42 if (nand_curr_device < 0 || 43 nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || 44 !mtd) { 45 printf("%s: invalid nand device\n", __func__); 46 return -1; 47 } 48 49 if (op == DFU_OP_READ) { 50 ret = nand_read_skip_bad(mtd, start, &count, &actual, 51 lim, buf); 52 } else { 53 nand_erase_options_t opts; 54 55 memset(&opts, 0, sizeof(opts)); 56 opts.offset = start; 57 opts.length = count; 58 opts.spread = 1; 59 opts.quiet = 1; 60 opts.lim = lim; 61 /* first erase */ 62 ret = nand_erase_opts(mtd, &opts); 63 if (ret) 64 return ret; 65 /* then write */ 66 ret = nand_write_skip_bad(mtd, start, &count, &actual, 67 lim, buf, WITH_WR_VERIFY); 68 } 69 70 if (ret != 0) { 71 printf("%s: nand_%s_skip_bad call failed at %llx!\n", 72 __func__, op == DFU_OP_READ ? "read" : "write", 73 start); 74 return ret; 75 } 76 77 /* 78 * Find out where we stopped writing data. This can be deeper into 79 * the NAND than we expected due to having to skip bad blocks. So 80 * we must take this into account for the next write, if any. 81 */ 82 if (actual > count) 83 dfu->bad_skip += actual - count; 84 85 return ret; 86 } 87 88 static inline int nand_block_write(struct dfu_entity *dfu, 89 u64 offset, void *buf, long *len) 90 { 91 return nand_block_op(DFU_OP_WRITE, dfu, offset, buf, len); 92 } 93 94 static inline int nand_block_read(struct dfu_entity *dfu, 95 u64 offset, void *buf, long *len) 96 { 97 return nand_block_op(DFU_OP_READ, dfu, offset, buf, len); 98 } 99 100 static int dfu_write_medium_nand(struct dfu_entity *dfu, 101 u64 offset, void *buf, long *len) 102 { 103 int ret = -1; 104 105 switch (dfu->layout) { 106 case DFU_RAW_ADDR: 107 ret = nand_block_write(dfu, offset, buf, len); 108 break; 109 default: 110 printf("%s: Layout (%s) not (yet) supported!\n", __func__, 111 dfu_get_layout(dfu->layout)); 112 } 113 114 return ret; 115 } 116 117 int dfu_get_medium_size_nand(struct dfu_entity *dfu, u64 *size) 118 { 119 *size = dfu->data.nand.size; 120 121 return 0; 122 } 123 124 static int dfu_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf, 125 long *len) 126 { 127 int ret = -1; 128 129 switch (dfu->layout) { 130 case DFU_RAW_ADDR: 131 ret = nand_block_read(dfu, offset, buf, len); 132 break; 133 default: 134 printf("%s: Layout (%s) not (yet) supported!\n", __func__, 135 dfu_get_layout(dfu->layout)); 136 } 137 138 return ret; 139 } 140 141 static int dfu_flush_medium_nand(struct dfu_entity *dfu) 142 { 143 int ret = 0; 144 u64 off; 145 146 /* in case of ubi partition, erase rest of the partition */ 147 if (dfu->data.nand.ubi) { 148 struct mtd_info *mtd = get_nand_dev_by_index(nand_curr_device); 149 nand_erase_options_t opts; 150 151 if (nand_curr_device < 0 || 152 nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || 153 !mtd) { 154 printf("%s: invalid nand device\n", __func__); 155 return -1; 156 } 157 158 memset(&opts, 0, sizeof(opts)); 159 off = dfu->offset; 160 if ((off & (mtd->erasesize - 1)) != 0) { 161 /* 162 * last write ended with unaligned length 163 * sector is erased, jump to next 164 */ 165 off = off & ~((mtd->erasesize - 1)); 166 off += mtd->erasesize; 167 } 168 opts.offset = dfu->data.nand.start + off + 169 dfu->bad_skip; 170 opts.length = dfu->data.nand.start + 171 dfu->data.nand.size - opts.offset; 172 ret = nand_erase_opts(mtd, &opts); 173 if (ret != 0) 174 printf("Failure erase: %d\n", ret); 175 } 176 177 return ret; 178 } 179 180 unsigned int dfu_polltimeout_nand(struct dfu_entity *dfu) 181 { 182 /* 183 * Currently, Poll Timeout != 0 is only needed on nand 184 * ubi partition, as the not used sectors need an erase 185 */ 186 if (dfu->data.nand.ubi) 187 return DFU_MANIFEST_POLL_TIMEOUT; 188 189 return DFU_DEFAULT_POLL_TIMEOUT; 190 } 191 192 int dfu_fill_entity_nand(struct dfu_entity *dfu, char *devstr, char *s) 193 { 194 char *st; 195 int ret, dev, part; 196 197 dfu->data.nand.ubi = 0; 198 dfu->dev_type = DFU_DEV_NAND; 199 st = strsep(&s, " "); 200 if (!strcmp(st, "raw")) { 201 dfu->layout = DFU_RAW_ADDR; 202 dfu->data.nand.start = simple_strtoul(s, &s, 16); 203 s++; 204 dfu->data.nand.size = simple_strtoul(s, &s, 16); 205 } else if ((!strcmp(st, "part")) || (!strcmp(st, "partubi"))) { 206 char mtd_id[32]; 207 struct mtd_device *mtd_dev; 208 u8 part_num; 209 struct part_info *pi; 210 211 dfu->layout = DFU_RAW_ADDR; 212 213 dev = simple_strtoul(s, &s, 10); 214 s++; 215 part = simple_strtoul(s, &s, 10); 216 217 sprintf(mtd_id, "%s%d,%d", "nand", dev, part - 1); 218 printf("using id '%s'\n", mtd_id); 219 220 mtdparts_init(); 221 222 ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi); 223 if (ret != 0) { 224 printf("Could not locate '%s'\n", mtd_id); 225 return -1; 226 } 227 228 dfu->data.nand.start = pi->offset; 229 dfu->data.nand.size = pi->size; 230 if (!strcmp(st, "partubi")) 231 dfu->data.nand.ubi = 1; 232 } else { 233 printf("%s: Memory layout (%s) not supported!\n", __func__, st); 234 return -1; 235 } 236 237 dfu->get_medium_size = dfu_get_medium_size_nand; 238 dfu->read_medium = dfu_read_medium_nand; 239 dfu->write_medium = dfu_write_medium_nand; 240 dfu->flush_medium = dfu_flush_medium_nand; 241 dfu->poll_timeout = dfu_polltimeout_nand; 242 243 /* initial state */ 244 dfu->inited = 0; 245 246 return 0; 247 } 248