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