Home | History | Annotate | Download | only in mtd
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * (C) Copyright 2008 Semihalf
      4  *
      5  * Written by: Piotr Ziecik <kosmo (at) semihalf.com>
      6  */
      7 
      8 #include <common.h>
      9 #include <flash.h>
     10 #include <malloc.h>
     11 
     12 #include <linux/errno.h>
     13 #include <linux/mtd/mtd.h>
     14 #include <linux/mtd/concat.h>
     15 #include <mtd/cfi_flash.h>
     16 
     17 static struct mtd_info cfi_mtd_info[CFI_MAX_FLASH_BANKS];
     18 static char cfi_mtd_names[CFI_MAX_FLASH_BANKS][16];
     19 #ifdef CONFIG_MTD_CONCAT
     20 static char c_mtd_name[16];
     21 #endif
     22 
     23 static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
     24 {
     25 	flash_info_t *fi = mtd->priv;
     26 	size_t a_start = fi->start[0] + instr->addr;
     27 	size_t a_end = a_start + instr->len;
     28 	int s_first = -1;
     29 	int s_last = -1;
     30 	int error, sect;
     31 
     32 	for (sect = 0; sect < fi->sector_count; sect++) {
     33 		if (a_start == fi->start[sect])
     34 			s_first = sect;
     35 
     36 		if (sect < fi->sector_count - 1) {
     37 			if (a_end == fi->start[sect + 1]) {
     38 				s_last = sect;
     39 				break;
     40 			}
     41 		} else {
     42 			s_last = sect;
     43 			break;
     44 		}
     45 	}
     46 
     47 	if (s_first >= 0 && s_first <= s_last) {
     48 		instr->state = MTD_ERASING;
     49 
     50 		flash_set_verbose(0);
     51 		error = flash_erase(fi, s_first, s_last);
     52 		flash_set_verbose(1);
     53 
     54 		if (error) {
     55 			instr->state = MTD_ERASE_FAILED;
     56 			return -EIO;
     57 		}
     58 
     59 		instr->state = MTD_ERASE_DONE;
     60 		mtd_erase_callback(instr);
     61 		return 0;
     62 	}
     63 
     64 	return -EINVAL;
     65 }
     66 
     67 static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
     68 	size_t *retlen, u_char *buf)
     69 {
     70 	flash_info_t *fi = mtd->priv;
     71 	u_char *f = (u_char*)(fi->start[0]) + from;
     72 
     73 	memcpy(buf, f, len);
     74 	*retlen = len;
     75 
     76 	return 0;
     77 }
     78 
     79 static int cfi_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
     80 	size_t *retlen, const u_char *buf)
     81 {
     82 	flash_info_t *fi = mtd->priv;
     83 	u_long t = fi->start[0] + to;
     84 	int error;
     85 
     86 	flash_set_verbose(0);
     87 	error = write_buff(fi, (u_char*)buf, t, len);
     88 	flash_set_verbose(1);
     89 
     90 	if (!error) {
     91 		*retlen = len;
     92 		return 0;
     93 	}
     94 
     95 	return -EIO;
     96 }
     97 
     98 static void cfi_mtd_sync(struct mtd_info *mtd)
     99 {
    100 	/*
    101 	 * This function should wait until all pending operations
    102 	 * finish. However this driver is fully synchronous, so
    103 	 * this function returns immediately
    104 	 */
    105 }
    106 
    107 static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
    108 {
    109 	flash_info_t *fi = mtd->priv;
    110 
    111 	flash_set_verbose(0);
    112 	flash_protect(FLAG_PROTECT_SET, fi->start[0] + ofs,
    113 					fi->start[0] + ofs + len - 1, fi);
    114 	flash_set_verbose(1);
    115 
    116 	return 0;
    117 }
    118 
    119 static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
    120 {
    121 	flash_info_t *fi = mtd->priv;
    122 
    123 	flash_set_verbose(0);
    124 	flash_protect(FLAG_PROTECT_CLEAR, fi->start[0] + ofs,
    125 					fi->start[0] + ofs + len - 1, fi);
    126 	flash_set_verbose(1);
    127 
    128 	return 0;
    129 }
    130 
    131 static int cfi_mtd_set_erasesize(struct mtd_info *mtd, flash_info_t *fi)
    132 {
    133 	int sect_size = 0;
    134 	int sect_size_old = 0;
    135 	int sect;
    136 	int regions = 0;
    137 	int numblocks = 0;
    138 	ulong offset;
    139 	ulong base_addr;
    140 
    141 	/*
    142 	 * First detect the number of eraseregions so that we can allocate
    143 	 * the array of eraseregions correctly
    144 	 */
    145 	for (sect = 0; sect < fi->sector_count; sect++) {
    146 		if (sect_size_old != flash_sector_size(fi, sect))
    147 			regions++;
    148 		sect_size_old = flash_sector_size(fi, sect);
    149 	}
    150 
    151 	switch (regions) {
    152 	case 0:
    153 		return 1;
    154 	case 1:	/* flash has uniform erase size */
    155 		mtd->numeraseregions = 0;
    156 		mtd->erasesize = sect_size_old;
    157 		return 0;
    158 	}
    159 
    160 	mtd->numeraseregions = regions;
    161 	mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info) * regions);
    162 
    163 	/*
    164 	 * Now detect the largest sector and fill the eraseregions
    165 	 */
    166 	regions = 0;
    167 	base_addr = offset = fi->start[0];
    168 	sect_size_old = flash_sector_size(fi, 0);
    169 	for (sect = 0; sect < fi->sector_count; sect++) {
    170 		if (sect_size_old != flash_sector_size(fi, sect)) {
    171 			mtd->eraseregions[regions].offset = offset - base_addr;
    172 			mtd->eraseregions[regions].erasesize = sect_size_old;
    173 			mtd->eraseregions[regions].numblocks = numblocks;
    174 			/* Now start counting the next eraseregions */
    175 			numblocks = 0;
    176 			regions++;
    177 			offset = fi->start[sect];
    178 		}
    179 		numblocks++;
    180 
    181 		/*
    182 		 * Select the largest sector size as erasesize (e.g. for UBI)
    183 		 */
    184 		if (flash_sector_size(fi, sect) > sect_size)
    185 			sect_size = flash_sector_size(fi, sect);
    186 
    187 		sect_size_old = flash_sector_size(fi, sect);
    188 	}
    189 
    190 	/*
    191 	 * Set the last region
    192 	 */
    193 	mtd->eraseregions[regions].offset = offset - base_addr;
    194 	mtd->eraseregions[regions].erasesize = sect_size_old;
    195 	mtd->eraseregions[regions].numblocks = numblocks;
    196 
    197 	mtd->erasesize = sect_size;
    198 
    199 	return 0;
    200 }
    201 
    202 int cfi_mtd_init(void)
    203 {
    204 	struct mtd_info *mtd;
    205 	flash_info_t *fi;
    206 	int error, i;
    207 #ifdef CONFIG_MTD_CONCAT
    208 	int devices_found = 0;
    209 	struct mtd_info *mtd_list[CONFIG_SYS_MAX_FLASH_BANKS];
    210 #endif
    211 
    212 	for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
    213 		fi = &flash_info[i];
    214 		mtd = &cfi_mtd_info[i];
    215 
    216 		memset(mtd, 0, sizeof(struct mtd_info));
    217 
    218 		error = cfi_mtd_set_erasesize(mtd, fi);
    219 		if (error)
    220 			continue;
    221 
    222 		sprintf(cfi_mtd_names[i], "nor%d", i);
    223 		mtd->name		= cfi_mtd_names[i];
    224 		mtd->type		= MTD_NORFLASH;
    225 		mtd->flags		= MTD_CAP_NORFLASH;
    226 		mtd->size		= fi->size;
    227 		mtd->writesize		= 1;
    228 		mtd->writebufsize	= mtd->writesize;
    229 
    230 		mtd->_erase		= cfi_mtd_erase;
    231 		mtd->_read		= cfi_mtd_read;
    232 		mtd->_write		= cfi_mtd_write;
    233 		mtd->_sync		= cfi_mtd_sync;
    234 		mtd->_lock		= cfi_mtd_lock;
    235 		mtd->_unlock		= cfi_mtd_unlock;
    236 		mtd->priv		= fi;
    237 
    238 		if (add_mtd_device(mtd))
    239 			return -ENOMEM;
    240 
    241 #ifdef CONFIG_MTD_CONCAT
    242 		mtd_list[devices_found++] = mtd;
    243 #endif
    244 	}
    245 
    246 #ifdef CONFIG_MTD_CONCAT
    247 	if (devices_found > 1) {
    248 		/*
    249 		 * We detected multiple devices. Concatenate them together.
    250 		 */
    251 		sprintf(c_mtd_name, "nor%d", devices_found);
    252 		mtd = mtd_concat_create(mtd_list, devices_found, c_mtd_name);
    253 
    254 		if (mtd == NULL)
    255 			return -ENXIO;
    256 
    257 		if (add_mtd_device(mtd))
    258 			return -ENOMEM;
    259 	}
    260 #endif /* CONFIG_MTD_CONCAT */
    261 
    262 	return 0;
    263 }
    264