xref: /rk3399_rockchip-uboot/drivers/mtd/spi/sf_mtd.c (revision 1ed65f0297874bd2af8ba83ac8425b97b338860b)
19fe6d871SDaniel Schwierzeck /*
29fe6d871SDaniel Schwierzeck  * Copyright (C) 2012-2014 Daniel Schwierzeck, daniel.schwierzeck@gmail.com
39fe6d871SDaniel Schwierzeck  *
49fe6d871SDaniel Schwierzeck  * SPDX-License-Identifier:	GPL-2.0+
59fe6d871SDaniel Schwierzeck  */
69fe6d871SDaniel Schwierzeck 
79fe6d871SDaniel Schwierzeck #include <common.h>
89fe6d871SDaniel Schwierzeck #include <malloc.h>
91221ce45SMasahiro Yamada #include <linux/errno.h>
109fe6d871SDaniel Schwierzeck #include <linux/mtd/mtd.h>
119fe6d871SDaniel Schwierzeck #include <spi_flash.h>
129fe6d871SDaniel Schwierzeck 
139fe6d871SDaniel Schwierzeck static struct mtd_info sf_mtd_info;
14c79c393fSBoris Brezillon static bool sf_mtd_registered;
159fe6d871SDaniel Schwierzeck static char sf_mtd_name[8];
169fe6d871SDaniel Schwierzeck 
spi_flash_mtd_erase(struct mtd_info * mtd,struct erase_info * instr)179fe6d871SDaniel Schwierzeck static int spi_flash_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
189fe6d871SDaniel Schwierzeck {
199fe6d871SDaniel Schwierzeck 	struct spi_flash *flash = mtd->priv;
209fe6d871SDaniel Schwierzeck 	int err;
219fe6d871SDaniel Schwierzeck 
22*1ed65f02SBoris Brezillon 	if (!flash)
23*1ed65f02SBoris Brezillon 		return -ENODEV;
24*1ed65f02SBoris Brezillon 
259fe6d871SDaniel Schwierzeck 	instr->state = MTD_ERASING;
269fe6d871SDaniel Schwierzeck 
279fe6d871SDaniel Schwierzeck 	err = spi_flash_erase(flash, instr->addr, instr->len);
289fe6d871SDaniel Schwierzeck 	if (err) {
299fe6d871SDaniel Schwierzeck 		instr->state = MTD_ERASE_FAILED;
309fe6d871SDaniel Schwierzeck 		instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
319fe6d871SDaniel Schwierzeck 		return -EIO;
329fe6d871SDaniel Schwierzeck 	}
339fe6d871SDaniel Schwierzeck 
349fe6d871SDaniel Schwierzeck 	instr->state = MTD_ERASE_DONE;
359fe6d871SDaniel Schwierzeck 	mtd_erase_callback(instr);
369fe6d871SDaniel Schwierzeck 
379fe6d871SDaniel Schwierzeck 	return 0;
389fe6d871SDaniel Schwierzeck }
399fe6d871SDaniel Schwierzeck 
spi_flash_mtd_read(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)409fe6d871SDaniel Schwierzeck static int spi_flash_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
419fe6d871SDaniel Schwierzeck 	size_t *retlen, u_char *buf)
429fe6d871SDaniel Schwierzeck {
439fe6d871SDaniel Schwierzeck 	struct spi_flash *flash = mtd->priv;
449fe6d871SDaniel Schwierzeck 	int err;
459fe6d871SDaniel Schwierzeck 
46*1ed65f02SBoris Brezillon 	if (!flash)
47*1ed65f02SBoris Brezillon 		return -ENODEV;
48*1ed65f02SBoris Brezillon 
499fe6d871SDaniel Schwierzeck 	err = spi_flash_read(flash, from, len, buf);
509fe6d871SDaniel Schwierzeck 	if (!err)
519fe6d871SDaniel Schwierzeck 		*retlen = len;
529fe6d871SDaniel Schwierzeck 
539fe6d871SDaniel Schwierzeck 	return err;
549fe6d871SDaniel Schwierzeck }
559fe6d871SDaniel Schwierzeck 
spi_flash_mtd_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u_char * buf)569fe6d871SDaniel Schwierzeck static int spi_flash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
579fe6d871SDaniel Schwierzeck 	size_t *retlen, const u_char *buf)
589fe6d871SDaniel Schwierzeck {
599fe6d871SDaniel Schwierzeck 	struct spi_flash *flash = mtd->priv;
609fe6d871SDaniel Schwierzeck 	int err;
619fe6d871SDaniel Schwierzeck 
62*1ed65f02SBoris Brezillon 	if (!flash)
63*1ed65f02SBoris Brezillon 		return -ENODEV;
64*1ed65f02SBoris Brezillon 
659fe6d871SDaniel Schwierzeck 	err = spi_flash_write(flash, to, len, buf);
669fe6d871SDaniel Schwierzeck 	if (!err)
679fe6d871SDaniel Schwierzeck 		*retlen = len;
689fe6d871SDaniel Schwierzeck 
699fe6d871SDaniel Schwierzeck 	return err;
709fe6d871SDaniel Schwierzeck }
719fe6d871SDaniel Schwierzeck 
spi_flash_mtd_sync(struct mtd_info * mtd)729fe6d871SDaniel Schwierzeck static void spi_flash_mtd_sync(struct mtd_info *mtd)
739fe6d871SDaniel Schwierzeck {
749fe6d871SDaniel Schwierzeck }
759fe6d871SDaniel Schwierzeck 
spi_flash_mtd_number(void)769fe6d871SDaniel Schwierzeck static int spi_flash_mtd_number(void)
779fe6d871SDaniel Schwierzeck {
789fe6d871SDaniel Schwierzeck #ifdef CONFIG_SYS_MAX_FLASH_BANKS
799fe6d871SDaniel Schwierzeck 	return CONFIG_SYS_MAX_FLASH_BANKS;
809fe6d871SDaniel Schwierzeck #else
819fe6d871SDaniel Schwierzeck 	return 0;
829fe6d871SDaniel Schwierzeck #endif
839fe6d871SDaniel Schwierzeck }
849fe6d871SDaniel Schwierzeck 
spi_flash_mtd_register(struct spi_flash * flash)859fe6d871SDaniel Schwierzeck int spi_flash_mtd_register(struct spi_flash *flash)
869fe6d871SDaniel Schwierzeck {
87c79c393fSBoris Brezillon 	int ret;
88c79c393fSBoris Brezillon 
89*1ed65f02SBoris Brezillon 	if (sf_mtd_registered) {
90*1ed65f02SBoris Brezillon 		ret = del_mtd_device(&sf_mtd_info);
91*1ed65f02SBoris Brezillon 		if (ret)
92*1ed65f02SBoris Brezillon 			return ret;
93*1ed65f02SBoris Brezillon 
94*1ed65f02SBoris Brezillon 		sf_mtd_registered = false;
95*1ed65f02SBoris Brezillon 	}
96c79c393fSBoris Brezillon 
97c79c393fSBoris Brezillon 	sf_mtd_registered = false;
989fe6d871SDaniel Schwierzeck 	memset(&sf_mtd_info, 0, sizeof(sf_mtd_info));
999fe6d871SDaniel Schwierzeck 	sprintf(sf_mtd_name, "nor%d", spi_flash_mtd_number());
1009fe6d871SDaniel Schwierzeck 
1019fe6d871SDaniel Schwierzeck 	sf_mtd_info.name = sf_mtd_name;
1029fe6d871SDaniel Schwierzeck 	sf_mtd_info.type = MTD_NORFLASH;
1039fe6d871SDaniel Schwierzeck 	sf_mtd_info.flags = MTD_CAP_NORFLASH;
1049fe6d871SDaniel Schwierzeck 	sf_mtd_info.writesize = 1;
1059fe6d871SDaniel Schwierzeck 	sf_mtd_info.writebufsize = flash->page_size;
1069fe6d871SDaniel Schwierzeck 
1079fe6d871SDaniel Schwierzeck 	sf_mtd_info._erase = spi_flash_mtd_erase;
1089fe6d871SDaniel Schwierzeck 	sf_mtd_info._read = spi_flash_mtd_read;
1099fe6d871SDaniel Schwierzeck 	sf_mtd_info._write = spi_flash_mtd_write;
1109fe6d871SDaniel Schwierzeck 	sf_mtd_info._sync = spi_flash_mtd_sync;
1119fe6d871SDaniel Schwierzeck 
1129fe6d871SDaniel Schwierzeck 	sf_mtd_info.size = flash->size;
1139fe6d871SDaniel Schwierzeck 	sf_mtd_info.priv = flash;
1149fe6d871SDaniel Schwierzeck 
1159fe6d871SDaniel Schwierzeck 	/* Only uniform flash devices for now */
1169fe6d871SDaniel Schwierzeck 	sf_mtd_info.numeraseregions = 0;
1179fe6d871SDaniel Schwierzeck 	sf_mtd_info.erasesize = flash->sector_size;
1189fe6d871SDaniel Schwierzeck 
119c79c393fSBoris Brezillon 	ret = add_mtd_device(&sf_mtd_info);
120c79c393fSBoris Brezillon 	if (!ret)
121c79c393fSBoris Brezillon 		sf_mtd_registered = true;
122c79c393fSBoris Brezillon 
123c79c393fSBoris Brezillon 	return ret;
1249fe6d871SDaniel Schwierzeck }
1259fe6d871SDaniel Schwierzeck 
spi_flash_mtd_unregister(void)1269fe6d871SDaniel Schwierzeck void spi_flash_mtd_unregister(void)
1279fe6d871SDaniel Schwierzeck {
128*1ed65f02SBoris Brezillon 	int ret;
129*1ed65f02SBoris Brezillon 
130*1ed65f02SBoris Brezillon 	if (!sf_mtd_registered)
131*1ed65f02SBoris Brezillon 		return;
132*1ed65f02SBoris Brezillon 
133*1ed65f02SBoris Brezillon 	ret = del_mtd_device(&sf_mtd_info);
134*1ed65f02SBoris Brezillon 	if (!ret) {
135*1ed65f02SBoris Brezillon 		sf_mtd_registered = false;
136*1ed65f02SBoris Brezillon 		return;
137*1ed65f02SBoris Brezillon 	}
138*1ed65f02SBoris Brezillon 
139*1ed65f02SBoris Brezillon 	/*
140*1ed65f02SBoris Brezillon 	 * Setting mtd->priv to NULL is the best we can do. Thanks to that,
141*1ed65f02SBoris Brezillon 	 * the MTD layer can still call mtd hooks without risking a
142*1ed65f02SBoris Brezillon 	 * use-after-free bug. Still, things should be fixed to prevent the
143*1ed65f02SBoris Brezillon 	 * spi_flash object from being destroyed when del_mtd_device() fails.
144*1ed65f02SBoris Brezillon 	 */
145*1ed65f02SBoris Brezillon 	sf_mtd_info.priv = NULL;
146*1ed65f02SBoris Brezillon 	printf("Failed to unregister MTD %s and the spi_flash object is going away: you're in deep trouble!",
147*1ed65f02SBoris Brezillon 	       sf_mtd_info.name);
1489fe6d871SDaniel Schwierzeck }
149