1 /* 2 * (C) Copyright 2008 Semihalf 3 * 4 * Written by: Piotr Ziecik <kosmo@semihalf.com> 5 * 6 * See file CREDITS for list of people who contributed to this 7 * project. 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License as 11 * published by the Free Software Foundation; either version 2 of 12 * the License, or (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 22 * MA 02111-1307 USA 23 * 24 */ 25 26 #include <common.h> 27 #include <flash.h> 28 29 #include <asm/errno.h> 30 #include <linux/mtd/mtd.h> 31 32 extern flash_info_t flash_info[]; 33 34 static struct mtd_info cfi_mtd_info[CONFIG_SYS_MAX_FLASH_BANKS]; 35 static char cfi_mtd_names[CONFIG_SYS_MAX_FLASH_BANKS][16]; 36 37 static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) 38 { 39 flash_info_t *fi = mtd->priv; 40 size_t a_start = fi->start[0] + instr->addr; 41 size_t a_end = a_start + instr->len; 42 int s_first = -1; 43 int s_last = -1; 44 int error, sect; 45 46 for (sect = 0; sect < fi->sector_count; sect++) { 47 if (a_start == fi->start[sect]) 48 s_first = sect; 49 50 if (sect < fi->sector_count - 1) { 51 if (a_end == fi->start[sect + 1]) { 52 s_last = sect; 53 break; 54 } 55 } else { 56 s_last = sect; 57 break; 58 } 59 } 60 61 if (s_first >= 0 && s_first <= s_last) { 62 instr->state = MTD_ERASING; 63 64 flash_set_verbose(0); 65 error = flash_erase(fi, s_first, s_last); 66 flash_set_verbose(1); 67 68 if (error) { 69 instr->state = MTD_ERASE_FAILED; 70 return -EIO; 71 } 72 73 instr->state = MTD_ERASE_DONE; 74 mtd_erase_callback(instr); 75 return 0; 76 } 77 78 return -EINVAL; 79 } 80 81 static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, 82 size_t *retlen, u_char *buf) 83 { 84 flash_info_t *fi = mtd->priv; 85 u_char *f = (u_char*)(fi->start[0]) + from; 86 87 memcpy(buf, f, len); 88 *retlen = len; 89 90 return 0; 91 } 92 93 static int cfi_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, 94 size_t *retlen, const u_char *buf) 95 { 96 flash_info_t *fi = mtd->priv; 97 u_long t = fi->start[0] + to; 98 int error; 99 100 flash_set_verbose(0); 101 error = write_buff(fi, (u_char*)buf, t, len); 102 flash_set_verbose(1); 103 104 if (!error) { 105 *retlen = len; 106 return 0; 107 } 108 109 return -EIO; 110 } 111 112 static void cfi_mtd_sync(struct mtd_info *mtd) 113 { 114 /* 115 * This function should wait until all pending operations 116 * finish. However this driver is fully synchronous, so 117 * this function returns immediately 118 */ 119 } 120 121 static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, size_t len) 122 { 123 flash_info_t *fi = mtd->priv; 124 125 flash_set_verbose(0); 126 flash_protect(FLAG_PROTECT_SET, fi->start[0] + ofs, 127 fi->start[0] + ofs + len - 1, fi); 128 flash_set_verbose(1); 129 130 return 0; 131 } 132 133 static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) 134 { 135 flash_info_t *fi = mtd->priv; 136 137 flash_set_verbose(0); 138 flash_protect(FLAG_PROTECT_CLEAR, fi->start[0] + ofs, 139 fi->start[0] + ofs + len - 1, fi); 140 flash_set_verbose(1); 141 142 return 0; 143 } 144 145 static int cfi_mtd_set_erasesize(struct mtd_info *mtd, flash_info_t *fi) 146 { 147 int sect_size = 0; 148 int sect; 149 150 /* 151 * Select the largest sector size as erasesize (e.g. for UBI) 152 */ 153 for (sect = 0; sect < fi->sector_count; sect++) { 154 if (flash_sector_size(fi, sect) > sect_size) 155 sect_size = flash_sector_size(fi, sect); 156 } 157 158 mtd->erasesize = sect_size; 159 160 return 0; 161 } 162 163 int cfi_mtd_init(void) 164 { 165 struct mtd_info *mtd; 166 flash_info_t *fi; 167 int error, i; 168 169 for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { 170 fi = &flash_info[i]; 171 mtd = &cfi_mtd_info[i]; 172 173 memset(mtd, 0, sizeof(struct mtd_info)); 174 175 error = cfi_mtd_set_erasesize(mtd, fi); 176 if (error) 177 continue; 178 179 sprintf(cfi_mtd_names[i], "nor%d", i); 180 mtd->name = cfi_mtd_names[i]; 181 mtd->type = MTD_NORFLASH; 182 mtd->flags = MTD_CAP_NORFLASH; 183 mtd->size = fi->size; 184 mtd->writesize = 1; 185 186 mtd->erase = cfi_mtd_erase; 187 mtd->read = cfi_mtd_read; 188 mtd->write = cfi_mtd_write; 189 mtd->sync = cfi_mtd_sync; 190 mtd->lock = cfi_mtd_lock; 191 mtd->unlock = cfi_mtd_unlock; 192 mtd->priv = fi; 193 194 if (add_mtd_device(mtd)) 195 return -ENOMEM; 196 } 197 198 return 0; 199 } 200