1 /* 2 * Copyright (C) 2012-2014 Daniel Schwierzeck, daniel.schwierzeck@gmail.com 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <common.h> 8 #include <malloc.h> 9 #include <linux/errno.h> 10 #include <linux/mtd/mtd.h> 11 #include <spi_flash.h> 12 13 static struct mtd_info sf_mtd_info; 14 static bool sf_mtd_registered; 15 static char sf_mtd_name[8]; 16 17 static int spi_flash_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) 18 { 19 struct spi_flash *flash = mtd->priv; 20 int err; 21 22 if (!flash) 23 return -ENODEV; 24 25 instr->state = MTD_ERASING; 26 27 err = spi_flash_erase(flash, instr->addr, instr->len); 28 if (err) { 29 instr->state = MTD_ERASE_FAILED; 30 instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; 31 return -EIO; 32 } 33 34 instr->state = MTD_ERASE_DONE; 35 mtd_erase_callback(instr); 36 37 return 0; 38 } 39 40 static int spi_flash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, 41 size_t *retlen, u_char *buf) 42 { 43 struct spi_flash *flash = mtd->priv; 44 int err; 45 46 if (!flash) 47 return -ENODEV; 48 49 err = spi_flash_read(flash, from, len, buf); 50 if (!err) 51 *retlen = len; 52 53 return err; 54 } 55 56 static int spi_flash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, 57 size_t *retlen, const u_char *buf) 58 { 59 struct spi_flash *flash = mtd->priv; 60 int err; 61 62 if (!flash) 63 return -ENODEV; 64 65 err = spi_flash_write(flash, to, len, buf); 66 if (!err) 67 *retlen = len; 68 69 return err; 70 } 71 72 static void spi_flash_mtd_sync(struct mtd_info *mtd) 73 { 74 } 75 76 static int spi_flash_mtd_number(void) 77 { 78 #ifdef CONFIG_SYS_MAX_FLASH_BANKS 79 return CONFIG_SYS_MAX_FLASH_BANKS; 80 #else 81 return 0; 82 #endif 83 } 84 85 int spi_flash_mtd_register(struct spi_flash *flash) 86 { 87 int ret; 88 89 if (sf_mtd_registered) { 90 ret = del_mtd_device(&sf_mtd_info); 91 if (ret) 92 return ret; 93 94 sf_mtd_registered = false; 95 } 96 97 sf_mtd_registered = false; 98 memset(&sf_mtd_info, 0, sizeof(sf_mtd_info)); 99 sprintf(sf_mtd_name, "nor%d", spi_flash_mtd_number()); 100 101 sf_mtd_info.name = sf_mtd_name; 102 sf_mtd_info.type = MTD_NORFLASH; 103 sf_mtd_info.flags = MTD_CAP_NORFLASH; 104 sf_mtd_info.writesize = 1; 105 sf_mtd_info.writebufsize = flash->page_size; 106 107 sf_mtd_info._erase = spi_flash_mtd_erase; 108 sf_mtd_info._read = spi_flash_mtd_read; 109 sf_mtd_info._write = spi_flash_mtd_write; 110 sf_mtd_info._sync = spi_flash_mtd_sync; 111 112 sf_mtd_info.size = flash->size; 113 sf_mtd_info.priv = flash; 114 115 /* Only uniform flash devices for now */ 116 sf_mtd_info.numeraseregions = 0; 117 sf_mtd_info.erasesize = flash->sector_size; 118 119 ret = add_mtd_device(&sf_mtd_info); 120 if (!ret) 121 sf_mtd_registered = true; 122 123 return ret; 124 } 125 126 void spi_flash_mtd_unregister(void) 127 { 128 int ret; 129 130 if (!sf_mtd_registered) 131 return; 132 133 ret = del_mtd_device(&sf_mtd_info); 134 if (!ret) { 135 sf_mtd_registered = false; 136 return; 137 } 138 139 /* 140 * Setting mtd->priv to NULL is the best we can do. Thanks to that, 141 * the MTD layer can still call mtd hooks without risking a 142 * use-after-free bug. Still, things should be fixed to prevent the 143 * spi_flash object from being destroyed when del_mtd_device() fails. 144 */ 145 sf_mtd_info.priv = NULL; 146 printf("Failed to unregister MTD %s and the spi_flash object is going away: you're in deep trouble!", 147 sf_mtd_info.name); 148 } 149