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