2 * (C) Copyright 2008 Semihalf
4 * Written by: Piotr Ziecik <kosmo@semihalf.com>
6 * SPDX-License-Identifier: GPL-2.0+
13 #include <linux/errno.h>
14 #include <linux/mtd/mtd.h>
15 #include <linux/mtd/concat.h>
16 #include <mtd/cfi_flash.h>
18 static struct mtd_info cfi_mtd_info
[CFI_MAX_FLASH_BANKS
];
19 static char cfi_mtd_names
[CFI_MAX_FLASH_BANKS
][16];
20 #ifdef CONFIG_MTD_CONCAT
21 static char c_mtd_name
[16];
24 static int cfi_mtd_erase(struct mtd_info
*mtd
, struct erase_info
*instr
)
26 flash_info_t
*fi
= mtd
->priv
;
27 size_t a_start
= fi
->start
[0] + instr
->addr
;
28 size_t a_end
= a_start
+ instr
->len
;
33 for (sect
= 0; sect
< fi
->sector_count
; sect
++) {
34 if (a_start
== fi
->start
[sect
])
37 if (sect
< fi
->sector_count
- 1) {
38 if (a_end
== fi
->start
[sect
+ 1]) {
48 if (s_first
>= 0 && s_first
<= s_last
) {
49 instr
->state
= MTD_ERASING
;
52 error
= flash_erase(fi
, s_first
, s_last
);
56 instr
->state
= MTD_ERASE_FAILED
;
60 instr
->state
= MTD_ERASE_DONE
;
61 mtd_erase_callback(instr
);
68 static int cfi_mtd_read(struct mtd_info
*mtd
, loff_t from
, size_t len
,
69 size_t *retlen
, u_char
*buf
)
71 flash_info_t
*fi
= mtd
->priv
;
72 u_char
*f
= (u_char
*)(fi
->start
[0]) + from
;
80 static int cfi_mtd_write(struct mtd_info
*mtd
, loff_t to
, size_t len
,
81 size_t *retlen
, const u_char
*buf
)
83 flash_info_t
*fi
= mtd
->priv
;
84 u_long t
= fi
->start
[0] + to
;
88 error
= write_buff(fi
, (u_char
*)buf
, t
, len
);
99 static void cfi_mtd_sync(struct mtd_info
*mtd
)
102 * This function should wait until all pending operations
103 * finish. However this driver is fully synchronous, so
104 * this function returns immediately
108 static int cfi_mtd_lock(struct mtd_info
*mtd
, loff_t ofs
, uint64_t len
)
110 flash_info_t
*fi
= mtd
->priv
;
112 flash_set_verbose(0);
113 flash_protect(FLAG_PROTECT_SET
, fi
->start
[0] + ofs
,
114 fi
->start
[0] + ofs
+ len
- 1, fi
);
115 flash_set_verbose(1);
120 static int cfi_mtd_unlock(struct mtd_info
*mtd
, loff_t ofs
, uint64_t len
)
122 flash_info_t
*fi
= mtd
->priv
;
124 flash_set_verbose(0);
125 flash_protect(FLAG_PROTECT_CLEAR
, fi
->start
[0] + ofs
,
126 fi
->start
[0] + ofs
+ len
- 1, fi
);
127 flash_set_verbose(1);
132 static int cfi_mtd_set_erasesize(struct mtd_info
*mtd
, flash_info_t
*fi
)
135 int sect_size_old
= 0;
143 * First detect the number of eraseregions so that we can allocate
144 * the array of eraseregions correctly
146 for (sect
= 0; sect
< fi
->sector_count
; sect
++) {
147 if (sect_size_old
!= flash_sector_size(fi
, sect
))
149 sect_size_old
= flash_sector_size(fi
, sect
);
155 case 1: /* flash has uniform erase size */
156 mtd
->numeraseregions
= 0;
157 mtd
->erasesize
= sect_size_old
;
161 mtd
->numeraseregions
= regions
;
162 mtd
->eraseregions
= malloc(sizeof(struct mtd_erase_region_info
) * regions
);
165 * Now detect the largest sector and fill the eraseregions
168 base_addr
= offset
= fi
->start
[0];
169 sect_size_old
= flash_sector_size(fi
, 0);
170 for (sect
= 0; sect
< fi
->sector_count
; sect
++) {
171 if (sect_size_old
!= flash_sector_size(fi
, sect
)) {
172 mtd
->eraseregions
[regions
].offset
= offset
- base_addr
;
173 mtd
->eraseregions
[regions
].erasesize
= sect_size_old
;
174 mtd
->eraseregions
[regions
].numblocks
= numblocks
;
175 /* Now start counting the next eraseregions */
178 offset
= fi
->start
[sect
];
183 * Select the largest sector size as erasesize (e.g. for UBI)
185 if (flash_sector_size(fi
, sect
) > sect_size
)
186 sect_size
= flash_sector_size(fi
, sect
);
188 sect_size_old
= flash_sector_size(fi
, sect
);
192 * Set the last region
194 mtd
->eraseregions
[regions
].offset
= offset
- base_addr
;
195 mtd
->eraseregions
[regions
].erasesize
= sect_size_old
;
196 mtd
->eraseregions
[regions
].numblocks
= numblocks
;
198 mtd
->erasesize
= sect_size
;
203 int cfi_mtd_init(void)
205 struct mtd_info
*mtd
;
208 #ifdef CONFIG_MTD_CONCAT
209 int devices_found
= 0;
210 struct mtd_info
*mtd_list
[CONFIG_SYS_MAX_FLASH_BANKS
];
213 for (i
= 0; i
< CONFIG_SYS_MAX_FLASH_BANKS
; i
++) {
215 mtd
= &cfi_mtd_info
[i
];
217 memset(mtd
, 0, sizeof(struct mtd_info
));
219 error
= cfi_mtd_set_erasesize(mtd
, fi
);
223 sprintf(cfi_mtd_names
[i
], "nor%d", i
);
224 mtd
->name
= cfi_mtd_names
[i
];
225 mtd
->type
= MTD_NORFLASH
;
226 mtd
->flags
= MTD_CAP_NORFLASH
;
227 mtd
->size
= fi
->size
;
229 mtd
->writebufsize
= mtd
->writesize
;
231 mtd
->_erase
= cfi_mtd_erase
;
232 mtd
->_read
= cfi_mtd_read
;
233 mtd
->_write
= cfi_mtd_write
;
234 mtd
->_sync
= cfi_mtd_sync
;
235 mtd
->_lock
= cfi_mtd_lock
;
236 mtd
->_unlock
= cfi_mtd_unlock
;
239 if (add_mtd_device(mtd
))
242 #ifdef CONFIG_MTD_CONCAT
243 mtd_list
[devices_found
++] = mtd
;
247 #ifdef CONFIG_MTD_CONCAT
248 if (devices_found
> 1) {
250 * We detected multiple devices. Concatenate them together.
252 sprintf(c_mtd_name
, "nor%d", devices_found
);
253 mtd
= mtd_concat_create(mtd_list
, devices_found
, c_mtd_name
);
258 if (add_mtd_device(mtd
))
261 #endif /* CONFIG_MTD_CONCAT */