1c6631764SPantelis Antoniou /* 2c6631764SPantelis Antoniou * dfu_nand.c -- DFU for NAND routines. 3c6631764SPantelis Antoniou * 4c6631764SPantelis Antoniou * Copyright (C) 2012-2013 Texas Instruments, Inc. 5c6631764SPantelis Antoniou * 6c6631764SPantelis Antoniou * Based on dfu_mmc.c which is: 7c6631764SPantelis Antoniou * Copyright (C) 2012 Samsung Electronics 8c6631764SPantelis Antoniou * author: Lukasz Majewski <l.majewski@samsung.com> 9c6631764SPantelis Antoniou * 101a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 11c6631764SPantelis Antoniou */ 12c6631764SPantelis Antoniou 13c6631764SPantelis Antoniou #include <common.h> 14c6631764SPantelis Antoniou #include <malloc.h> 15c6631764SPantelis Antoniou #include <errno.h> 16c6631764SPantelis Antoniou #include <div64.h> 17c6631764SPantelis Antoniou #include <dfu.h> 18c6631764SPantelis Antoniou #include <linux/mtd/mtd.h> 19c6631764SPantelis Antoniou #include <jffs2/load_kernel.h> 20c6631764SPantelis Antoniou #include <nand.h> 21c6631764SPantelis Antoniou 225a127c84SAfzal Mohammed static int nand_block_op(enum dfu_op op, struct dfu_entity *dfu, 23c6631764SPantelis Antoniou u64 offset, void *buf, long *len) 24c6631764SPantelis Antoniou { 25c6631764SPantelis Antoniou loff_t start, lim; 26c6631764SPantelis Antoniou size_t count, actual; 27c6631764SPantelis Antoniou int ret; 28c6631764SPantelis Antoniou nand_info_t *nand; 29c6631764SPantelis Antoniou 30c6631764SPantelis Antoniou /* if buf == NULL return total size of the area */ 31c6631764SPantelis Antoniou if (buf == NULL) { 32c6631764SPantelis Antoniou *len = dfu->data.nand.size; 33c6631764SPantelis Antoniou return 0; 34c6631764SPantelis Antoniou } 35c6631764SPantelis Antoniou 36c6631764SPantelis Antoniou start = dfu->data.nand.start + offset + dfu->bad_skip; 37c6631764SPantelis Antoniou lim = dfu->data.nand.start + dfu->data.nand.size - start; 38c6631764SPantelis Antoniou count = *len; 39c6631764SPantelis Antoniou 40c6631764SPantelis Antoniou if (nand_curr_device < 0 || 41c6631764SPantelis Antoniou nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || 42c6631764SPantelis Antoniou !nand_info[nand_curr_device].name) { 43c6631764SPantelis Antoniou printf("%s: invalid nand device\n", __func__); 44c6631764SPantelis Antoniou return -1; 45c6631764SPantelis Antoniou } 46c6631764SPantelis Antoniou 47c6631764SPantelis Antoniou nand = &nand_info[nand_curr_device]; 48c6631764SPantelis Antoniou 49a67cc37eSHeiko Schocher if (op == DFU_OP_READ) { 50c6631764SPantelis Antoniou ret = nand_read_skip_bad(nand, start, &count, &actual, 51c6631764SPantelis Antoniou lim, buf); 52a67cc37eSHeiko Schocher } else { 53a67cc37eSHeiko Schocher nand_erase_options_t opts; 54a67cc37eSHeiko Schocher 55a67cc37eSHeiko Schocher memset(&opts, 0, sizeof(opts)); 56a67cc37eSHeiko Schocher opts.offset = start; 57a67cc37eSHeiko Schocher opts.length = count; 58a67cc37eSHeiko Schocher opts.spread = 1; 59a67cc37eSHeiko Schocher opts.quiet = 1; 60a67cc37eSHeiko Schocher opts.lim = lim; 61a67cc37eSHeiko Schocher /* first erase */ 62a67cc37eSHeiko Schocher ret = nand_erase_opts(nand, &opts); 63a67cc37eSHeiko Schocher if (ret) 64a67cc37eSHeiko Schocher return ret; 65a67cc37eSHeiko Schocher /* then write */ 66c6631764SPantelis Antoniou ret = nand_write_skip_bad(nand, start, &count, &actual, 67*9ac71f11SPeter Tyser lim, buf, WITH_WR_VERIFY); 68a67cc37eSHeiko Schocher } 69c6631764SPantelis Antoniou 70c6631764SPantelis Antoniou if (ret != 0) { 71c6631764SPantelis Antoniou printf("%s: nand_%s_skip_bad call failed at %llx!\n", 72c6631764SPantelis Antoniou __func__, op == DFU_OP_READ ? "read" : "write", 73c6631764SPantelis Antoniou start); 74c6631764SPantelis Antoniou return ret; 75c6631764SPantelis Antoniou } 76c6631764SPantelis Antoniou 77c6631764SPantelis Antoniou /* 78c6631764SPantelis Antoniou * Find out where we stopped writing data. This can be deeper into 79c6631764SPantelis Antoniou * the NAND than we expected due to having to skip bad blocks. So 80c6631764SPantelis Antoniou * we must take this into account for the next write, if any. 81c6631764SPantelis Antoniou */ 82c6631764SPantelis Antoniou if (actual > count) 83c6631764SPantelis Antoniou dfu->bad_skip += actual - count; 84c6631764SPantelis Antoniou 85c6631764SPantelis Antoniou return ret; 86c6631764SPantelis Antoniou } 87c6631764SPantelis Antoniou 88c6631764SPantelis Antoniou static inline int nand_block_write(struct dfu_entity *dfu, 89c6631764SPantelis Antoniou u64 offset, void *buf, long *len) 90c6631764SPantelis Antoniou { 91c6631764SPantelis Antoniou return nand_block_op(DFU_OP_WRITE, dfu, offset, buf, len); 92c6631764SPantelis Antoniou } 93c6631764SPantelis Antoniou 94c6631764SPantelis Antoniou static inline int nand_block_read(struct dfu_entity *dfu, 95c6631764SPantelis Antoniou u64 offset, void *buf, long *len) 96c6631764SPantelis Antoniou { 97c6631764SPantelis Antoniou return nand_block_op(DFU_OP_READ, dfu, offset, buf, len); 98c6631764SPantelis Antoniou } 99c6631764SPantelis Antoniou 100c6631764SPantelis Antoniou static int dfu_write_medium_nand(struct dfu_entity *dfu, 101c6631764SPantelis Antoniou u64 offset, void *buf, long *len) 102c6631764SPantelis Antoniou { 103c6631764SPantelis Antoniou int ret = -1; 104c6631764SPantelis Antoniou 105c6631764SPantelis Antoniou switch (dfu->layout) { 106c6631764SPantelis Antoniou case DFU_RAW_ADDR: 107c6631764SPantelis Antoniou ret = nand_block_write(dfu, offset, buf, len); 108c6631764SPantelis Antoniou break; 109c6631764SPantelis Antoniou default: 110c6631764SPantelis Antoniou printf("%s: Layout (%s) not (yet) supported!\n", __func__, 111c6631764SPantelis Antoniou dfu_get_layout(dfu->layout)); 112c6631764SPantelis Antoniou } 113c6631764SPantelis Antoniou 114c6631764SPantelis Antoniou return ret; 115c6631764SPantelis Antoniou } 116c6631764SPantelis Antoniou 1170e285b50SStephen Warren long dfu_get_medium_size_nand(struct dfu_entity *dfu) 1180e285b50SStephen Warren { 1190e285b50SStephen Warren return dfu->data.nand.size; 1200e285b50SStephen Warren } 1210e285b50SStephen Warren 122c6631764SPantelis Antoniou static int dfu_read_medium_nand(struct dfu_entity *dfu, u64 offset, void *buf, 123c6631764SPantelis Antoniou long *len) 124c6631764SPantelis Antoniou { 125c6631764SPantelis Antoniou int ret = -1; 126c6631764SPantelis Antoniou 127c6631764SPantelis Antoniou switch (dfu->layout) { 128c6631764SPantelis Antoniou case DFU_RAW_ADDR: 129c6631764SPantelis Antoniou ret = nand_block_read(dfu, offset, buf, len); 130c6631764SPantelis Antoniou break; 131c6631764SPantelis Antoniou default: 132c6631764SPantelis Antoniou printf("%s: Layout (%s) not (yet) supported!\n", __func__, 133c6631764SPantelis Antoniou dfu_get_layout(dfu->layout)); 134c6631764SPantelis Antoniou } 135c6631764SPantelis Antoniou 136c6631764SPantelis Antoniou return ret; 137c6631764SPantelis Antoniou } 138c6631764SPantelis Antoniou 139815c30b2SHeiko Schocher static int dfu_flush_medium_nand(struct dfu_entity *dfu) 140815c30b2SHeiko Schocher { 141815c30b2SHeiko Schocher int ret = 0; 142815c30b2SHeiko Schocher 143815c30b2SHeiko Schocher /* in case of ubi partition, erase rest of the partition */ 144815c30b2SHeiko Schocher if (dfu->data.nand.ubi) { 145815c30b2SHeiko Schocher nand_info_t *nand; 146815c30b2SHeiko Schocher nand_erase_options_t opts; 147815c30b2SHeiko Schocher 148815c30b2SHeiko Schocher if (nand_curr_device < 0 || 149815c30b2SHeiko Schocher nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE || 150815c30b2SHeiko Schocher !nand_info[nand_curr_device].name) { 151815c30b2SHeiko Schocher printf("%s: invalid nand device\n", __func__); 152815c30b2SHeiko Schocher return -1; 153815c30b2SHeiko Schocher } 154815c30b2SHeiko Schocher 155815c30b2SHeiko Schocher nand = &nand_info[nand_curr_device]; 156815c30b2SHeiko Schocher 157815c30b2SHeiko Schocher memset(&opts, 0, sizeof(opts)); 158815c30b2SHeiko Schocher opts.offset = dfu->data.nand.start + dfu->offset + 159815c30b2SHeiko Schocher dfu->bad_skip; 160815c30b2SHeiko Schocher opts.length = dfu->data.nand.start + 161815c30b2SHeiko Schocher dfu->data.nand.size - opts.offset; 162815c30b2SHeiko Schocher ret = nand_erase_opts(nand, &opts); 163815c30b2SHeiko Schocher if (ret != 0) 164815c30b2SHeiko Schocher printf("Failure erase: %d\n", ret); 165815c30b2SHeiko Schocher } 166815c30b2SHeiko Schocher 167815c30b2SHeiko Schocher return ret; 168815c30b2SHeiko Schocher } 169815c30b2SHeiko Schocher 170fc25fa27SHeiko Schocher unsigned int dfu_polltimeout_nand(struct dfu_entity *dfu) 171fc25fa27SHeiko Schocher { 172fc25fa27SHeiko Schocher /* 173fc25fa27SHeiko Schocher * Currently, Poll Timeout != 0 is only needed on nand 174fc25fa27SHeiko Schocher * ubi partition, as the not used sectors need an erase 175fc25fa27SHeiko Schocher */ 176fc25fa27SHeiko Schocher if (dfu->data.nand.ubi) 177fc25fa27SHeiko Schocher return DFU_MANIFEST_POLL_TIMEOUT; 178fc25fa27SHeiko Schocher 179fc25fa27SHeiko Schocher return DFU_DEFAULT_POLL_TIMEOUT; 180fc25fa27SHeiko Schocher } 181fc25fa27SHeiko Schocher 182dd64827eSStephen Warren int dfu_fill_entity_nand(struct dfu_entity *dfu, char *devstr, char *s) 183c6631764SPantelis Antoniou { 184c6631764SPantelis Antoniou char *st; 185c6631764SPantelis Antoniou int ret, dev, part; 186c6631764SPantelis Antoniou 187815c30b2SHeiko Schocher dfu->data.nand.ubi = 0; 188c6631764SPantelis Antoniou dfu->dev_type = DFU_DEV_NAND; 189c6631764SPantelis Antoniou st = strsep(&s, " "); 190c6631764SPantelis Antoniou if (!strcmp(st, "raw")) { 191c6631764SPantelis Antoniou dfu->layout = DFU_RAW_ADDR; 192c6631764SPantelis Antoniou dfu->data.nand.start = simple_strtoul(s, &s, 16); 193c6631764SPantelis Antoniou s++; 194c6631764SPantelis Antoniou dfu->data.nand.size = simple_strtoul(s, &s, 16); 195815c30b2SHeiko Schocher } else if ((!strcmp(st, "part")) || (!strcmp(st, "partubi"))) { 196c6631764SPantelis Antoniou char mtd_id[32]; 197c6631764SPantelis Antoniou struct mtd_device *mtd_dev; 198c6631764SPantelis Antoniou u8 part_num; 199c6631764SPantelis Antoniou struct part_info *pi; 200c6631764SPantelis Antoniou 201c6631764SPantelis Antoniou dfu->layout = DFU_RAW_ADDR; 202c6631764SPantelis Antoniou 203c6631764SPantelis Antoniou dev = simple_strtoul(s, &s, 10); 204c6631764SPantelis Antoniou s++; 205c6631764SPantelis Antoniou part = simple_strtoul(s, &s, 10); 206c6631764SPantelis Antoniou 207c6631764SPantelis Antoniou sprintf(mtd_id, "%s%d,%d", "nand", dev, part - 1); 208c6631764SPantelis Antoniou printf("using id '%s'\n", mtd_id); 209c6631764SPantelis Antoniou 210c6631764SPantelis Antoniou mtdparts_init(); 211c6631764SPantelis Antoniou 212c6631764SPantelis Antoniou ret = find_dev_and_part(mtd_id, &mtd_dev, &part_num, &pi); 213c6631764SPantelis Antoniou if (ret != 0) { 214c6631764SPantelis Antoniou printf("Could not locate '%s'\n", mtd_id); 215c6631764SPantelis Antoniou return -1; 216c6631764SPantelis Antoniou } 217c6631764SPantelis Antoniou 218c6631764SPantelis Antoniou dfu->data.nand.start = pi->offset; 219c6631764SPantelis Antoniou dfu->data.nand.size = pi->size; 220815c30b2SHeiko Schocher if (!strcmp(st, "partubi")) 221815c30b2SHeiko Schocher dfu->data.nand.ubi = 1; 222c6631764SPantelis Antoniou } else { 223c6631764SPantelis Antoniou printf("%s: Memory layout (%s) not supported!\n", __func__, st); 224c6631764SPantelis Antoniou return -1; 225c6631764SPantelis Antoniou } 226c6631764SPantelis Antoniou 2270e285b50SStephen Warren dfu->get_medium_size = dfu_get_medium_size_nand; 228c6631764SPantelis Antoniou dfu->read_medium = dfu_read_medium_nand; 229c6631764SPantelis Antoniou dfu->write_medium = dfu_write_medium_nand; 230815c30b2SHeiko Schocher dfu->flush_medium = dfu_flush_medium_nand; 231fc25fa27SHeiko Schocher dfu->poll_timeout = dfu_polltimeout_nand; 232c6631764SPantelis Antoniou 233c6631764SPantelis Antoniou /* initial state */ 234c6631764SPantelis Antoniou dfu->inited = 0; 235c6631764SPantelis Antoniou 236c6631764SPantelis Antoniou return 0; 237c6631764SPantelis Antoniou } 238