1 /* 2 * Copyright 2014 Broadcom Corporation. 3 * Copyright 2015 Free Electrons. 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 8 #include <config.h> 9 #include <common.h> 10 11 #include <fastboot.h> 12 #include <image-sparse.h> 13 14 #include <linux/mtd/mtd.h> 15 #include <jffs2/jffs2.h> 16 #include <nand.h> 17 18 struct fb_nand_sparse { 19 struct mtd_info *mtd; 20 struct part_info *part; 21 }; 22 23 __weak int board_fastboot_erase_partition_setup(char *name) 24 { 25 return 0; 26 } 27 28 __weak int board_fastboot_write_partition_setup(char *name) 29 { 30 return 0; 31 } 32 33 static int fb_nand_lookup(const char *partname, 34 struct mtd_info **mtd, 35 struct part_info **part 36 char *response) 37 { 38 struct mtd_device *dev; 39 int ret; 40 u8 pnum; 41 42 ret = mtdparts_init(); 43 if (ret) { 44 error("Cannot initialize MTD partitions\n"); 45 fastboot_fail("cannot init mtdparts", response); 46 return ret; 47 } 48 49 ret = find_dev_and_part(partname, &dev, &pnum, part); 50 if (ret) { 51 error("cannot find partition: '%s'", partname); 52 fastboot_fail("cannot find partition", response); 53 return ret; 54 } 55 56 if (dev->id->type != MTD_DEV_TYPE_NAND) { 57 error("partition '%s' is not stored on a NAND device", 58 partname); 59 fastboot_fail("not a NAND device", response); 60 return -EINVAL; 61 } 62 63 *mtd = get_nand_dev_by_index(dev->id->num); 64 65 return 0; 66 } 67 68 static int _fb_nand_erase(struct mtd_info *mtd, struct part_info *part) 69 { 70 nand_erase_options_t opts; 71 int ret; 72 73 memset(&opts, 0, sizeof(opts)); 74 opts.offset = part->offset; 75 opts.length = part->size; 76 opts.quiet = 1; 77 78 printf("Erasing blocks 0x%llx to 0x%llx\n", 79 part->offset, part->offset + part->size); 80 81 ret = nand_erase_opts(mtd, &opts); 82 if (ret) 83 return ret; 84 85 printf("........ erased 0x%llx bytes from '%s'\n", 86 part->size, part->name); 87 88 return 0; 89 } 90 91 static int _fb_nand_write(struct mtd_info *mtd, struct part_info *part, 92 void *buffer, unsigned int offset, 93 unsigned int length, size_t *written) 94 { 95 int flags = WITH_WR_VERIFY; 96 97 #ifdef CONFIG_FASTBOOT_FLASH_NAND_TRIMFFS 98 flags |= WITH_DROP_FFS; 99 #endif 100 101 return nand_write_skip_bad(mtd, offset, &length, written, 102 part->size - (offset - part->offset), 103 buffer, flags); 104 } 105 106 static lbaint_t fb_nand_sparse_write(struct sparse_storage *info, 107 lbaint_t blk, lbaint_t blkcnt, const void *buffer) 108 { 109 struct fb_nand_sparse *sparse = info->priv; 110 size_t written; 111 int ret; 112 113 ret = _fb_nand_write(sparse->mtd, sparse->part, (void *)buffer, 114 blk * info->blksz, 115 blkcnt * info->blksz, &written); 116 if (ret < 0) { 117 printf("Failed to write sparse chunk\n"); 118 return ret; 119 } 120 121 /* TODO - verify that the value "written" includes the "bad-blocks" ... */ 122 123 /* 124 * the return value must be 'blkcnt' ("good-blocks") plus the 125 * number of "bad-blocks" encountered within this space... 126 */ 127 return written / info->blksz; 128 } 129 130 static lbaint_t fb_nand_sparse_reserve(struct sparse_storage *info, 131 lbaint_t blk, lbaint_t blkcnt) 132 { 133 int bad_blocks = 0; 134 135 /* 136 * TODO - implement a function to determine the total number 137 * of blocks which must be used in order to reserve the specified 138 * number ("blkcnt") of "good-blocks", starting at "blk"... 139 * ( possibly something like the "check_skip_len()" function ) 140 */ 141 142 /* 143 * the return value must be 'blkcnt' ("good-blocks") plus the 144 * number of "bad-blocks" encountered within this space... 145 */ 146 return blkcnt + bad_blocks; 147 } 148 149 void fb_nand_flash_write(const char *cmd, void *download_buffer, 150 unsigned int download_bytes, char *response) 151 { 152 struct part_info *part; 153 struct mtd_info *mtd = NULL; 154 int ret; 155 156 ret = fb_nand_lookup(cmd, &mtd, &part, response); 157 if (ret) { 158 error("invalid NAND device"); 159 fastboot_fail("invalid NAND device", response); 160 return; 161 } 162 163 ret = board_fastboot_write_partition_setup(part->name); 164 if (ret) 165 return; 166 167 if (is_sparse_image(download_buffer)) { 168 struct fb_nand_sparse sparse_priv; 169 struct sparse_storage sparse; 170 171 sparse_priv.mtd = mtd; 172 sparse_priv.part = part; 173 174 sparse.blksz = mtd->writesize; 175 sparse.start = part->offset / sparse.blksz; 176 sparse.size = part->size / sparse.blksz; 177 sparse.write = fb_nand_sparse_write; 178 sparse.reserve = fb_nand_sparse_reserve; 179 180 printf("Flashing sparse image at offset " LBAFU "\n", 181 sparse.start); 182 183 sparse.priv = &sparse_priv; 184 write_sparse_image(&sparse, cmd, download_buffer, 185 download_bytes, response); 186 } else { 187 printf("Flashing raw image at offset 0x%llx\n", 188 part->offset); 189 190 ret = _fb_nand_write(mtd, part, download_buffer, part->offset, 191 download_bytes, NULL); 192 193 printf("........ wrote %u bytes to '%s'\n", 194 download_bytes, part->name); 195 } 196 197 if (ret) { 198 fastboot_fail("error writing the image", response); 199 return; 200 } 201 202 fastboot_okay("", response); 203 } 204 205 void fb_nand_erase(const char *cmd, char *response) 206 { 207 struct part_info *part; 208 struct mtd_info *mtd = NULL; 209 int ret; 210 211 ret = fb_nand_lookup(cmd, &mtd, &part, response); 212 if (ret) { 213 error("invalid NAND device"); 214 fastboot_fail("invalid NAND device", response); 215 return; 216 } 217 218 ret = board_fastboot_erase_partition_setup(part->name); 219 if (ret) 220 return; 221 222 ret = _fb_nand_erase(mtd, part); 223 if (ret) { 224 error("failed erasing from device %s", mtd->name); 225 fastboot_fail("failed erasing from device", response); 226 return; 227 } 228 229 fastboot_okay("", response); 230 } 231