/* * $Id: nora.c,v 1.12 2000/07/13 10:32:33 dwmw2 Exp $ * * This is so simple I love it. * * * Added new flash partition schema for agenda * Copyright 2003 (C) Andreas Holzer * * Added noraPartWriteProc * Copyright 2003 (C) Paul Andreassen */ #include #include #include #include #include #include #include #define WINDOW_ADDR 0xbf000000 #define WINDOW_SIZE 0x01000000 #ifdef MTD_XIP #define MTD_ELF_SECTION __attribute__((__section__(".xir"))) #else #define MTD_ELF_SECTION #endif #define DISCO_START 0xc00000 #define DISCO_LEN 0x080000 #define DISCO_END (DISCO_START + DISCO_LEN) /* adjust an address for the discontinuity */ #define DISCO_ADJUST(a) ((aread(mymtd, from + (unsigned long)mtd->priv, len, retlen, buf); } MTD_ELF_SECTION static int nora_mtd_disco_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { unsigned long pos = from + (unsigned long)mtd->priv; if ( ((len + pos) <= DISCO_START) || ((pos) >= DISCO_START)){ return mymtd->read(mymtd, DISCO_ADJUST(pos), len, retlen, buf); }else{ printk("Discontinuity boundary crossed in nora_mtd_disco_read"); panic("Discontinuity boundary crossed in nora_mtd_disco_read"); } } MTD_ELF_SECTION static int nora_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { return mymtd->write(mymtd, to + (unsigned long)mtd->priv, len, retlen, buf); } MTD_ELF_SECTION static int nora_mtd_disco_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { unsigned long pos = to + (unsigned long)mtd->priv; if ( ((len + pos) <= DISCO_START) || ((pos) >= DISCO_START)){ return mymtd->write(mymtd, DISCO_ADJUST(pos), len, retlen, buf); }else{ printk("Discontinuity boundary crossed in nora_mtd_disco_write"); panic("Discontinuity boundary crossed in nora_mtd_disco_write"); } } MTD_ELF_SECTION static int nora_mtd_erase (struct mtd_info *mtd, struct erase_info *instr) { instr->addr += (unsigned long)mtd->priv; return mymtd->erase(mymtd, instr); } MTD_ELF_SECTION static int nora_mtd_disco_erase (struct mtd_info *mtd, struct erase_info *instr) { unsigned long pos = instr->addr + (unsigned long)mtd->priv; instr->addr = DISCO_ADJUST(pos); return mymtd->erase(mymtd, instr); } MTD_ELF_SECTION static void nora_mtd_sync (struct mtd_info *mtd) { mymtd->sync(mymtd); } MTD_ELF_SECTION static int nora_mtd_suspend (struct mtd_info *mtd) { return mymtd->suspend(mymtd); } MTD_ELF_SECTION static void nora_mtd_resume (struct mtd_info *mtd) { mymtd->resume(mymtd); } static struct mtd_info nora_mtds[2] = { /* boot, kernel, ramdisk, fs */ { type: MTD_NORFLASH, flags: MTD_CAP_NORFLASH, size: 0x380000, erasesize: 0x20000, name: "Agenda 3.5MB JFFS Partition", module: THIS_MODULE, erase: nora_mtd_erase, read: nora_mtd_read, write: nora_mtd_write, suspend: nora_mtd_suspend, resume: nora_mtd_resume, sync: nora_mtd_sync, priv: (void *)0xc80000 }, { type: MTD_NORFLASH, flags: MTD_CAP_NORFLASH | MTD_WRITEABLE, size: 0xf80000, // flash size - PMON size erasesize: 0x20000, name: "Agenda 12MB Flash", module: THIS_MODULE, erase: nora_mtd_disco_erase, // access routine bridge the hole inside read: nora_mtd_disco_read, write: nora_mtd_disco_write, suspend: nora_mtd_suspend, resume: nora_mtd_resume, sync: nora_mtd_sync, priv: (void *)0x0 }, /* only prepared not initialized in setup */ { type: MTD_NORFLASH, flags: MTD_CAP_NORFLASH, size: 0x080000, erasesize: 0x20000, name: "Pmon (very experimental)", module: THIS_MODULE, erase: nora_mtd_erase, read: nora_mtd_read, write: nora_mtd_write, suspend: nora_mtd_suspend, resume: nora_mtd_resume, sync: nora_mtd_sync, priv: (void *)0xC00000, } }; /* * MTD 'PARTITIONING' STUFF */ static unsigned long nrparts = 3; #define MTD_PNAME_MAX 30 static char np_text[][MTD_PNAME_MAX] = { "Kernel", "Root", "Flash", "", "" }; static struct mtd_partition nora_partitions[] = { { name: np_text[0], offset: 0, #ifdef CONFIG_XIP_ROM size: 0x200000 #else size: 0x100000 #endif }, { name: np_text[1], #ifdef CONFIG_XIP_ROM offset: 0x200000, size: 0xA00000 // 0xC80000 #else offset: 0x100000, size: 0xB00000 // 0xE80000 #endif }, { name: np_text[2], offset: 0xC00000, // dangerous stuff for PMON hole size: 0x380000, // simulates flash access via mtd[1] }, { name: np_text[3], offset: 0, size: 0 }, { name: np_text[4], offset: 0, size: 0 } }; static inline int noraProcInfo (char *buf, int i) { return sprintf(buf, "mtdblock%d: %08lx(%08lx) %08lx \"%s\" %04x\n", i+2, nora_partitions[i].offset, DISCO_ADJUST(nora_partitions[i].offset), nora_partitions[i].size, nora_partitions[i].name, nora_partitions[i].mask_flags); } static int noraPartReadProc ( char *page, char **start, off_t off,int count,int *eof, void *data_unused) { int len = 0, l, i; off_t begin = 0; for (i=0; i< nrparts; i++) { l = noraProcInfo(page + len, i); len += l; if (len+begin > off+count) goto done; if (len+begin < off) { begin += len; len = 0; } } *eof = 1; done: if (off >= len+begin) return 0; *start = page + (begin-off); return ((count < begin+len-off) ? count : begin+len-off); } /* output format: mtdblock2: 00000000 00200000 "Kernel" mtdblock3: 00200000 00700000 "Root-Cramfs" mtdblock4: 00a00000 00200000 "Tools-Cramfs" mtdblock5: 00900000 00100000 "Extra-Cramfs" input format: .*[0-9].* [0-F]* [0-F]* ".*" */ static int noraPartWriteProc(struct file *file, const char *buffer, unsigned long count, void *data) { const int maxBuffer=80; char szBuffer[maxBuffer+1]; int sizeBuffer, sizeName, mtdblock; char *pBuffer, *pName; u_long offset, size; if (!capable(CAP_SYS_ADMIN)) return -EACCES; // copy string (not sure why we do this, maybe to do with highmem?) sizeBuffer = (count < maxBuffer) ? count : maxBuffer; copy_from_user(szBuffer, buffer, sizeBuffer); szBuffer[sizeBuffer] = '\0'; pBuffer=szBuffer; // find a number while ((*pBuffer<'0') || (*pBuffer>'9')) if (++pBuffer >= (szBuffer+sizeBuffer)) return -EINVAL; mtdblock=*pBuffer-'0'; // only allowed mtdblocks be added in order upto 6 #ifdef CONFIG_CRAMFS_LINEAR if ((mtdblock<4) || ((mtdblock-2)>nrparts) || (mtdblock>6)) return -ERANGE; #else if ((mtdblock<3) || ((mtdblock-2)>nrparts) || (mtdblock>6)) return -ERANGE; #endif // skip until space or tab while ((*pBuffer!=' ') && (*pBuffer!= '\t')) if (++pBuffer >= (szBuffer+sizeBuffer)) return -EINVAL; // skip space or tab while ((*pBuffer==' ') || (*pBuffer== '\t')) if (++pBuffer >= (szBuffer+sizeBuffer)) return -EINVAL; // read offset offset=simple_strtoul(pBuffer, &pBuffer, 16); // skip space or tab while ((*pBuffer==' ') || (*pBuffer== '\t')) if (++pBuffer >= (szBuffer+sizeBuffer)) return -EINVAL; // read size size=simple_strtoul(pBuffer, &pBuffer, 16); // skip space or tab while ((*pBuffer==' ') || (*pBuffer== '\t')) if (++pBuffer >= (szBuffer+sizeBuffer)) return -EINVAL; // find name start if (*pBuffer!='\"') return -EINVAL; if (++pBuffer >= (szBuffer+sizeBuffer)) return -EINVAL; pName=pBuffer; // find name end while ((*pBuffer!='\"') && (++pBuffer < (szBuffer+sizeBuffer))); // update partitions if ((mtdblock-2)>=nrparts) nrparts++; sizeName = (pBuffer-pName); sizeName = (sizeName >= MTD_PNAME_MAX) ? MTD_PNAME_MAX - 1: sizeName; strncpy(nora_partitions[mtdblock-2].name, pName, sizeName); nora_partitions[mtdblock-2].name[sizeName]='\0'; nora_partitions[mtdblock-2].offset=offset; nora_partitions[mtdblock-2].size=((size-1)|(0x20000UL-1))+1; printk(KERN_NOTICE "added/updated mtdblock%d: %08lx %08lx \"%s\"\n", mtdblock, nora_partitions[mtdblock-2].offset, nora_partitions[mtdblock-2].size, nora_partitions[mtdblock-2].name); del_mtd_partitions(&(nora_mtds[1])); // skip testing because add_mtd_partitions does it add_mtd_partitions(&(nora_mtds[1]), nora_partitions, nrparts); return count; } #ifdef CONFIG_CRAMFS_LINEAR void add_cramfs_linear_partition(u_long offset, u_long size) { const char *name = "Root-Cramfs"; const char *name2 = "Root-Cramfs (size a guess)"; // assumes that linear cramfs are loaded very early in boot process if (nrparts == 1) nrparts = 2; offset-=WINDOW_ADDR; if (size != 0x10000) { size=((size-1)|(0x20000UL-1))+1; strncpy(nora_partitions[1].name, name, strlen(name)+1); printk(KERN_INFO "cramfsl: added linear partition mtdblock3: %08lx %08lx \"%s\"\n", offset, size, nora_partitions[1].name); } else { size = 0x700000; strncpy(nora_partitions[1].name, name2, strlen(name2)+1); printk(KERN_INFO "cramfsl: added linear partition mtdblock3: %08lx 00010000 \"%s\" size changed to %08lx\n", offset, nora_partitions[1].name, size); } nora_partitions[1].offset=offset; nora_partitions[1].size=size; del_mtd_partitions(&(nora_mtds[1])); add_mtd_partitions(&(nora_mtds[1]), nora_partitions, nrparts); } #endif #if LINUX_VERSION_CODE < 0x20300 #ifdef MODULE #define init_nora init_module #define cleanup_nora cleanup_module #endif #endif int __init init_nora(void) { struct proc_dir_entry *procNoraPart; int ret; printk(KERN_NOTICE "nora flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); mymtd = do_cfi_probe(&nora_map); if (mymtd) { #ifdef MODULE mymtd->module = &__this_module; #endif add_mtd_device(&nora_mtds[0]); add_mtd_device(&nora_mtds[1]); ret=add_mtd_partitions(&nora_mtds[1], nora_partitions, nrparts); if ((procNoraPart = create_proc_entry("noraPartitions", 0, 0))){ procNoraPart->read_proc = noraPartReadProc; procNoraPart->write_proc = noraPartWriteProc; } return ret; } return -ENXIO; } static void __exit cleanup_nora(void) { if (mymtd) { remove_proc_entry("noraPartitions", 0); del_mtd_partitions(&(nora_mtds[1])); del_mtd_device(&(nora_mtds[1])); del_mtd_device(&(nora_mtds[0])); map_destroy(mymtd); } } module_init(init_nora); module_exit(cleanup_nora); EXPORT_SYMBOL(nora_mtds); EXPORT_SYMBOL(nora_partitions); #ifdef CONFIG_CRAMFS_LINEAR EXPORT_SYMBOL(add_cramfs_linear_partition); #endif