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