191809ed5SPiotr Ziecik /* 291809ed5SPiotr Ziecik * (C) Copyright 2008 Semihalf 391809ed5SPiotr Ziecik * 491809ed5SPiotr Ziecik * Written by: Piotr Ziecik <kosmo@semihalf.com> 591809ed5SPiotr Ziecik * 691809ed5SPiotr Ziecik * See file CREDITS for list of people who contributed to this 791809ed5SPiotr Ziecik * project. 891809ed5SPiotr Ziecik * 991809ed5SPiotr Ziecik * This program is free software; you can redistribute it and/or 1091809ed5SPiotr Ziecik * modify it under the terms of the GNU General Public License as 1191809ed5SPiotr Ziecik * published by the Free Software Foundation; either version 2 of 1291809ed5SPiotr Ziecik * the License, or (at your option) any later version. 1391809ed5SPiotr Ziecik * 1491809ed5SPiotr Ziecik * This program is distributed in the hope that it will be useful, 1591809ed5SPiotr Ziecik * but WITHOUT ANY WARRANTY; without even the implied warranty of 1691809ed5SPiotr Ziecik * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1791809ed5SPiotr Ziecik * GNU General Public License for more details. 1891809ed5SPiotr Ziecik * 1991809ed5SPiotr Ziecik * You should have received a copy of the GNU General Public License 2091809ed5SPiotr Ziecik * along with this program; if not, write to the Free Software 2191809ed5SPiotr Ziecik * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 2291809ed5SPiotr Ziecik * MA 02111-1307 USA 2391809ed5SPiotr Ziecik * 2491809ed5SPiotr Ziecik */ 2591809ed5SPiotr Ziecik 2691809ed5SPiotr Ziecik #include <common.h> 2791809ed5SPiotr Ziecik #include <flash.h> 2891809ed5SPiotr Ziecik 2991809ed5SPiotr Ziecik #include <asm/errno.h> 3091809ed5SPiotr Ziecik #include <linux/mtd/mtd.h> 3191809ed5SPiotr Ziecik 3291809ed5SPiotr Ziecik extern flash_info_t flash_info[]; 3391809ed5SPiotr Ziecik 3491809ed5SPiotr Ziecik static struct mtd_info cfi_mtd_info[CONFIG_SYS_MAX_FLASH_BANKS]; 35c203ef5dSAndreas Huber static char cfi_mtd_names[CONFIG_SYS_MAX_FLASH_BANKS][16]; 3691809ed5SPiotr Ziecik 3791809ed5SPiotr Ziecik static int cfi_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) 3891809ed5SPiotr Ziecik { 3991809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 4091809ed5SPiotr Ziecik size_t a_start = fi->start[0] + instr->addr; 4191809ed5SPiotr Ziecik size_t a_end = a_start + instr->len; 4291809ed5SPiotr Ziecik int s_first = -1; 4391809ed5SPiotr Ziecik int s_last = -1; 4491809ed5SPiotr Ziecik int error, sect; 4591809ed5SPiotr Ziecik 4691809ed5SPiotr Ziecik for (sect = 0; sect < fi->sector_count - 1; sect++) { 4791809ed5SPiotr Ziecik if (a_start == fi->start[sect]) 4891809ed5SPiotr Ziecik s_first = sect; 4991809ed5SPiotr Ziecik 5091809ed5SPiotr Ziecik if (a_end == fi->start[sect + 1]) { 5191809ed5SPiotr Ziecik s_last = sect; 5291809ed5SPiotr Ziecik break; 5391809ed5SPiotr Ziecik } 5491809ed5SPiotr Ziecik } 5591809ed5SPiotr Ziecik 5691809ed5SPiotr Ziecik if (s_first >= 0 && s_first <= s_last) { 5791809ed5SPiotr Ziecik instr->state = MTD_ERASING; 5891809ed5SPiotr Ziecik 5991809ed5SPiotr Ziecik flash_set_verbose(0); 6091809ed5SPiotr Ziecik error = flash_erase(fi, s_first, s_last); 6191809ed5SPiotr Ziecik flash_set_verbose(1); 6291809ed5SPiotr Ziecik 6391809ed5SPiotr Ziecik if (error) { 6491809ed5SPiotr Ziecik instr->state = MTD_ERASE_FAILED; 6591809ed5SPiotr Ziecik return -EIO; 6691809ed5SPiotr Ziecik } 6791809ed5SPiotr Ziecik 6891809ed5SPiotr Ziecik instr->state = MTD_ERASE_DONE; 6991809ed5SPiotr Ziecik mtd_erase_callback(instr); 7091809ed5SPiotr Ziecik return 0; 7191809ed5SPiotr Ziecik } 7291809ed5SPiotr Ziecik 7391809ed5SPiotr Ziecik return -EINVAL; 7491809ed5SPiotr Ziecik } 7591809ed5SPiotr Ziecik 7691809ed5SPiotr Ziecik static int cfi_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, 7791809ed5SPiotr Ziecik size_t *retlen, u_char *buf) 7891809ed5SPiotr Ziecik { 7991809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 8091809ed5SPiotr Ziecik u_char *f = (u_char*)(fi->start[0]) + from; 8191809ed5SPiotr Ziecik 8291809ed5SPiotr Ziecik memcpy(buf, f, len); 8391809ed5SPiotr Ziecik *retlen = len; 8491809ed5SPiotr Ziecik 8591809ed5SPiotr Ziecik return 0; 8691809ed5SPiotr Ziecik } 8791809ed5SPiotr Ziecik 8891809ed5SPiotr Ziecik static int cfi_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, 8991809ed5SPiotr Ziecik size_t *retlen, const u_char *buf) 9091809ed5SPiotr Ziecik { 9191809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 9291809ed5SPiotr Ziecik u_long t = fi->start[0] + to; 9391809ed5SPiotr Ziecik int error; 9491809ed5SPiotr Ziecik 9591809ed5SPiotr Ziecik flash_set_verbose(0); 9691809ed5SPiotr Ziecik error = write_buff(fi, (u_char*)buf, t, len); 9791809ed5SPiotr Ziecik flash_set_verbose(1); 9891809ed5SPiotr Ziecik 9991809ed5SPiotr Ziecik if (!error) { 10091809ed5SPiotr Ziecik *retlen = len; 10191809ed5SPiotr Ziecik return 0; 10291809ed5SPiotr Ziecik } 10391809ed5SPiotr Ziecik 10491809ed5SPiotr Ziecik return -EIO; 10591809ed5SPiotr Ziecik } 10691809ed5SPiotr Ziecik 10791809ed5SPiotr Ziecik static void cfi_mtd_sync(struct mtd_info *mtd) 10891809ed5SPiotr Ziecik { 10991809ed5SPiotr Ziecik /* 11091809ed5SPiotr Ziecik * This function should wait until all pending operations 11191809ed5SPiotr Ziecik * finish. However this driver is fully synchronous, so 11291809ed5SPiotr Ziecik * this function returns immediately 11391809ed5SPiotr Ziecik */ 11491809ed5SPiotr Ziecik } 11591809ed5SPiotr Ziecik 11691809ed5SPiotr Ziecik static int cfi_mtd_lock(struct mtd_info *mtd, loff_t ofs, size_t len) 11791809ed5SPiotr Ziecik { 11891809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 11991809ed5SPiotr Ziecik 12091809ed5SPiotr Ziecik flash_set_verbose(0); 12191809ed5SPiotr Ziecik flash_protect(FLAG_PROTECT_SET, fi->start[0] + ofs, 12291809ed5SPiotr Ziecik fi->start[0] + ofs + len - 1, fi); 12391809ed5SPiotr Ziecik flash_set_verbose(1); 12491809ed5SPiotr Ziecik 12591809ed5SPiotr Ziecik return 0; 12691809ed5SPiotr Ziecik } 12791809ed5SPiotr Ziecik 12891809ed5SPiotr Ziecik static int cfi_mtd_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) 12991809ed5SPiotr Ziecik { 13091809ed5SPiotr Ziecik flash_info_t *fi = mtd->priv; 13191809ed5SPiotr Ziecik 13291809ed5SPiotr Ziecik flash_set_verbose(0); 13391809ed5SPiotr Ziecik flash_protect(FLAG_PROTECT_CLEAR, fi->start[0] + ofs, 13491809ed5SPiotr Ziecik fi->start[0] + ofs + len - 1, fi); 13591809ed5SPiotr Ziecik flash_set_verbose(1); 13691809ed5SPiotr Ziecik 13791809ed5SPiotr Ziecik return 0; 13891809ed5SPiotr Ziecik } 13991809ed5SPiotr Ziecik 14091809ed5SPiotr Ziecik static int cfi_mtd_set_erasesize(struct mtd_info *mtd, flash_info_t *fi) 14191809ed5SPiotr Ziecik { 14291809ed5SPiotr Ziecik int sect_size = 0; 14391809ed5SPiotr Ziecik int sect; 14491809ed5SPiotr Ziecik 145*f8e2b310SStefan Roese /* 146*f8e2b310SStefan Roese * Select the largest sector size as erasesize (e.g. for UBI) 147*f8e2b310SStefan Roese */ 14891809ed5SPiotr Ziecik for (sect = 0; sect < fi->sector_count; sect++) { 149*f8e2b310SStefan Roese if (flash_sector_size(fi, sect) > sect_size) 15091809ed5SPiotr Ziecik sect_size = flash_sector_size(fi, sect); 15191809ed5SPiotr Ziecik } 15291809ed5SPiotr Ziecik 15391809ed5SPiotr Ziecik mtd->erasesize = sect_size; 15491809ed5SPiotr Ziecik 15591809ed5SPiotr Ziecik return 0; 15691809ed5SPiotr Ziecik } 15791809ed5SPiotr Ziecik 15891809ed5SPiotr Ziecik int cfi_mtd_init(void) 15991809ed5SPiotr Ziecik { 16091809ed5SPiotr Ziecik struct mtd_info *mtd; 16191809ed5SPiotr Ziecik flash_info_t *fi; 16291809ed5SPiotr Ziecik int error, i; 16391809ed5SPiotr Ziecik 16491809ed5SPiotr Ziecik for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { 16591809ed5SPiotr Ziecik fi = &flash_info[i]; 16691809ed5SPiotr Ziecik mtd = &cfi_mtd_info[i]; 16791809ed5SPiotr Ziecik 16891809ed5SPiotr Ziecik memset(mtd, 0, sizeof(struct mtd_info)); 16991809ed5SPiotr Ziecik 17091809ed5SPiotr Ziecik error = cfi_mtd_set_erasesize(mtd, fi); 17191809ed5SPiotr Ziecik if (error) 17291809ed5SPiotr Ziecik continue; 17391809ed5SPiotr Ziecik 174c203ef5dSAndreas Huber sprintf(cfi_mtd_names[i], "nor%d", i); 175c203ef5dSAndreas Huber mtd->name = cfi_mtd_names[i]; 17691809ed5SPiotr Ziecik mtd->type = MTD_NORFLASH; 17791809ed5SPiotr Ziecik mtd->flags = MTD_CAP_NORFLASH; 17891809ed5SPiotr Ziecik mtd->size = fi->size; 17991809ed5SPiotr Ziecik mtd->writesize = 1; 18091809ed5SPiotr Ziecik 18191809ed5SPiotr Ziecik mtd->erase = cfi_mtd_erase; 18291809ed5SPiotr Ziecik mtd->read = cfi_mtd_read; 18391809ed5SPiotr Ziecik mtd->write = cfi_mtd_write; 18491809ed5SPiotr Ziecik mtd->sync = cfi_mtd_sync; 18591809ed5SPiotr Ziecik mtd->lock = cfi_mtd_lock; 18691809ed5SPiotr Ziecik mtd->unlock = cfi_mtd_unlock; 18791809ed5SPiotr Ziecik mtd->priv = fi; 18891809ed5SPiotr Ziecik 18991809ed5SPiotr Ziecik if (add_mtd_device(mtd)) 19091809ed5SPiotr Ziecik return -ENOMEM; 19191809ed5SPiotr Ziecik } 19291809ed5SPiotr Ziecik 19391809ed5SPiotr Ziecik return 0; 19491809ed5SPiotr Ziecik } 195