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