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