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 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 */ 24 25 #include <common.h> 26 #include <malloc.h> 27 #include <errno.h> 28 #include <div64.h> 29 #include <dfu.h> 30 #include <linux/mtd/mtd.h> 31 #include <jffs2/load_kernel.h> 32 #include <nand.h> 33 34 enum dfu_nand_op { 35 DFU_OP_READ = 1, 36 DFU_OP_WRITE, 37 }; 38 39 static int nand_block_op(enum dfu_nand_op op, struct dfu_entity *dfu, 40 u64 offset, void *buf, long *len) 41 { 42 loff_t start, lim; 43 size_t count, actual; 44 int ret; 45 nand_info_t *nand; 46 47 /* if buf == NULL return total size of the area */ 48 if (buf == NULL) { 49 *len = dfu->data.nand.size; 50 return 0; 51 } 52 53 start = dfu->data.nand.start + offset + dfu->bad_skip; 54 lim = dfu->data.nand.start + dfu->data.nand.size - start; 55 count = *len; 56 57 if (nand_curr_device < 0 || 58 nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || 59 !nand_info[nand_curr_device].name) { 60 printf("%s: invalid nand device\n", __func__); 61 return -1; 62 } 63 64 nand = &nand_info[nand_curr_device]; 65 66 if (op == DFU_OP_READ) { 67 ret = nand_read_skip_bad(nand, start, &count, &actual, 68 lim, buf); 69 } else { 70 nand_erase_options_t opts; 71 72 memset(&opts, 0, sizeof(opts)); 73 opts.offset = start; 74 opts.length = count; 75 opts.spread = 1; 76 opts.quiet = 1; 77 opts.lim = lim; 78 /* first erase */ 79 ret = nand_erase_opts(nand, &opts); 80 if (ret) 81 return ret; 82 /* then write */ 83 ret = nand_write_skip_bad(nand, start, &count, &actual, 84 lim, buf, 0); 85 } 86 87 if (ret != 0) { 88 printf("%s: nand_%s_skip_bad call failed at %llx!\n", 89 __func__, op == DFU_OP_READ ? "read" : "write", 90 start); 91 return ret; 92 } 93 94 /* 95 * Find out where we stopped writing data. This can be deeper into 96 * the NAND than we expected due to having to skip bad blocks. So 97 * we must take this into account for the next write, if any. 98 */ 99 if (actual > count) 100 dfu->bad_skip += actual - count; 101 102 return ret; 103 } 104 105 static inline int nand_block_write(struct dfu_entity *dfu, 106 u64 offset, void *buf, long *len) 107 { 108 return nand_block_op(DFU_OP_WRITE, dfu, offset, buf, len); 109 } 110 111 static inline int nand_block_read(struct dfu_entity *dfu, 112 u64 offset, void *buf, long *len) 113 { 114 return nand_block_op(DFU_OP_READ, dfu, offset, buf, len); 115 } 116 117 static int dfu_write_medium_nand(struct dfu_entity *dfu, 118 u64 offset, void *buf, long *len) 119 { 120 int ret = -1; 121 122 switch (dfu->layout) { 123 case DFU_RAW_ADDR: 124 ret = nand_block_write(dfu, offset, buf, len); 125 break; 126 default: 127 printf("%s: Layout (%s) not (yet) supported!\n", __func__, 128 dfu_get_layout(dfu->layout)); 129 } 130 131 return ret; 132 } 133 134 static int dfu_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf, 135 long *len) 136 { 137 int ret = -1; 138 139 switch (dfu->layout) { 140 case DFU_RAW_ADDR: 141 ret = nand_block_read(dfu, offset, buf, len); 142 break; 143 default: 144 printf("%s: Layout (%s) not (yet) supported!\n", __func__, 145 dfu_get_layout(dfu->layout)); 146 } 147 148 return ret; 149 } 150 151 int dfu_fill_entity_nand(struct dfu_entity *dfu, char *s) 152 { 153 char *st; 154 int ret, dev, part; 155 156 dfu->dev_type = DFU_DEV_NAND; 157 st = strsep(&s, " "); 158 if (!strcmp(st, "raw")) { 159 dfu->layout = DFU_RAW_ADDR; 160 dfu->data.nand.start = simple_strtoul(s, &s, 16); 161 s++; 162 dfu->data.nand.size = simple_strtoul(s, &s, 16); 163 } else if (!strcmp(st, "part")) { 164 char mtd_id[32]; 165 struct mtd_device *mtd_dev; 166 u8 part_num; 167 struct part_info *pi; 168 169 dfu->layout = DFU_RAW_ADDR; 170 171 dev = simple_strtoul(s, &s, 10); 172 s++; 173 part = simple_strtoul(s, &s, 10); 174 175 sprintf(mtd_id, "%s%d,%d", "nand", dev, part - 1); 176 printf("using id '%s'\n", mtd_id); 177 178 mtdparts_init(); 179 180 ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi); 181 if (ret != 0) { 182 printf("Could not locate '%s'\n", mtd_id); 183 return -1; 184 } 185 186 dfu->data.nand.start = pi->offset; 187 dfu->data.nand.size = pi->size; 188 189 } else { 190 printf("%s: Memory layout (%s) not supported!\n", __func__, st); 191 return -1; 192 } 193 194 dfu->read_medium = dfu_read_medium_nand; 195 dfu->write_medium = dfu_write_medium_nand; 196 197 /* initial state */ 198 dfu->inited = 0; 199 200 return 0; 201 } 202