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