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 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 54 if (op == DFU_OP_READ) { 55 ret = nand_read_skip_bad(nand, start, &count, &actual, 56 lim, buf); 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 */ 71 ret = nand_write_skip_bad(nand, start, &count, &actual, 72 lim, buf, 0); 73 } 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 } 190