From 9c37f3329ae098d4c17e8bec589a589bcbf0acff Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 28 Oct 2007 21:56:39 -0400 Subject: [MTD] [NAND] Check for RedBoot partitions on CAFÉ NAND MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe_nand.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 1e811715211..da6ceaa80ba 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -11,6 +11,7 @@ #undef DEBUG #include #include +#include #include #include #include @@ -52,6 +53,7 @@ struct cafe_priv { struct nand_chip nand; + struct mtd_partition *parts; struct pci_dev *pdev; void __iomem *mmio; struct rs_control *rs; @@ -84,6 +86,10 @@ static unsigned int numtimings; static int timing[3]; module_param_array(timing, int, &numtimings, 0644); +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "RedBoot", NULL }; +#endif + /* Hrm. Why isn't this already conditional on something in the struct device? */ #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) @@ -620,7 +626,9 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, { struct mtd_info *mtd; struct cafe_priv *cafe; + struct mtd_partition *parts; uint32_t ctrl; + int nr_parts; int err = 0; /* Very old versions shared the same PCI ident for all three @@ -787,7 +795,18 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, goto out_irq; pci_set_drvdata(pdev, mtd); + + /* We register the whole device first, separate from the partitions */ add_mtd_device(mtd); + +#ifdef CONFIG_MTD_PARTITIONS + nr_parts = parse_mtd_partitions(mtd, part_probes, &parts, 0); + if (nr_parts > 0) { + cafe->parts = parts; + dev_info(&cafe->pdev->dev, "%d RedBoot partitions found\n", nr_parts); + add_mtd_partitions(mtd, parts, nr_parts); + } +#endif goto out; out_irq: -- cgit v1.2.3 From 3c441baa0365ea7c3be9ee79f03e944289dd37e1 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 28 Oct 2007 21:57:02 -0400 Subject: [MTD] Skip bad blocks when checking for RedBoot partition table Signed-off-by: David Woodhouse --- drivers/mtd/redboot.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index a61351f88ec..47474903263 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -59,16 +59,31 @@ static int parse_redboot_partitions(struct mtd_info *master, static char nullstring[] = "unallocated"; #endif + if ( directory < 0 ) { + offset = master->size + directory * master->erasesize; + while (master->block_isbad && + master->block_isbad(master, offset)) { + if (!offset) { + nogood: + printk(KERN_NOTICE "Failed to find a non-bad block to check for RedBoot partition table\n"); + return -EIO; + } + offset -= master->erasesize; + } + } else { + offset = directory * master->erasesize; + while (master->block_isbad && + master->block_isbad(master, offset)) { + offset += master->erasesize; + if (offset == master->size) + goto nogood; + } + } buf = vmalloc(master->erasesize); if (!buf) return -ENOMEM; - if ( directory < 0 ) - offset = master->size + directory*master->erasesize; - else - offset = directory*master->erasesize; - printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n", master->name, offset); -- cgit v1.2.3 From a25b7fee537ab4dbc6eb301bd455ee8d01b707f6 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 30 Oct 2007 17:08:29 +0800 Subject: [MTD] [NAND] Add Blackfin BF52x support in bf5xx_nand driver Signed-off-by: Michael Hennerich Signed-off-by: Bryan Wu Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 2 +- drivers/mtd/nand/bf5xx_nand.c | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 246d4512f64..bf0e9b083d1 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -93,7 +93,7 @@ config MTD_NAND_AU1550 config MTD_NAND_BF5XX tristate "Blackfin on-chip NAND Flash Controller driver" - depends on BF54x && MTD_NAND + depends on (BF54x || BF52x) && MTD_NAND help This enables the Blackfin on-chip NAND flash controller diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 1657ecd7488..542850cd4c3 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -74,7 +74,22 @@ static int hardware_ecc = 1; static int hardware_ecc; #endif -static unsigned short bfin_nfc_pin_req[] = {P_NAND_CE, P_NAND_RB, 0}; +static unsigned short bfin_nfc_pin_req[] = + {P_NAND_CE, + P_NAND_RB, + P_NAND_D0, + P_NAND_D1, + P_NAND_D2, + P_NAND_D3, + P_NAND_D4, + P_NAND_D5, + P_NAND_D6, + P_NAND_D7, + P_NAND_WE, + P_NAND_RE, + P_NAND_CLE, + P_NAND_ALE, + 0}; /* * Data structures for bf5xx nand flash controller driver @@ -507,12 +522,13 @@ static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) init_completion(&info->dma_completion); +#ifdef CONFIG_BF54x /* Setup DMAC1 channel mux for NFC which shared with SDH */ val = bfin_read_DMAC1_PERIMUX(); val &= 0xFFFE; bfin_write_DMAC1_PERIMUX(val); SSYNC(); - +#endif /* Request NFC DMA channel */ ret = request_dma(CH_NFC, "BF5XX NFC driver"); if (ret < 0) { -- cgit v1.2.3 From 4edaf56e0f8a6f71e3361bf74e3dc835811761e6 Mon Sep 17 00:00:00 2001 From: Valentine Barshak Date: Mon, 29 Oct 2007 23:29:02 +0300 Subject: MTD: small physmap_of partition parsing fixes Use of_get_next_child for proper ref counting as suggested by Stephen Rothwell and remove add_mtd_partitions from parse_partitions to avoid duplicate mtd device registration for RedBoot partitions. Signed-off-by: Valentine Barshak Acked-by: David Gibson Heckled-for-on-IRC-by: Josh Boyer Signed-off-by: David Woodhouse --- drivers/mtd/maps/physmap_of.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index aeed9ea7971..d4bcd3f8c57 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -94,14 +94,13 @@ static int __devinit parse_partitions(struct of_flash *info, * line, these take precedence over device tree information */ nr_parts = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0); - if (nr_parts > 0) { - add_mtd_partitions(info->mtd, info->parts, nr_parts); - return 0; - } + if (nr_parts > 0) + return nr_parts; /* First count the subnodes */ nr_parts = 0; - for (pp = dp->child; pp; pp = pp->sibling) + for (pp = of_get_next_child(dp, NULL); pp; + pp = of_get_next_child(dp, pp)) nr_parts++; if (nr_parts == 0) @@ -112,12 +111,14 @@ static int __devinit parse_partitions(struct of_flash *info, if (!info->parts) return -ENOMEM; - for (pp = dp->child, i = 0; pp; pp = pp->sibling, i++) { + for (pp = of_get_next_child(dp, NULL), i = 0; pp; + pp = of_get_next_child(dp, pp), i++) { const u32 *reg; int len; reg = of_get_property(pp, "reg", &len); if (!reg || (len != 2*sizeof(u32))) { + of_node_put(pp); dev_err(&dev->dev, "Invalid 'reg' on %s\n", dp->full_name); kfree(info->parts); -- cgit v1.2.3 From d10a39d1a580db005d206fb6527a60fd9800c9fd Mon Sep 17 00:00:00 2001 From: Hans-Christian Egtvedt Date: Tue, 30 Oct 2007 16:33:07 +0100 Subject: [MTD] [NOR] More CFI fixups for Atmel chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert CFI tables from Atmel cmdset_0001 chips to Intel format and set BufWrite timeouts to 0 for Atmel cmdset_0001 and cmdset_0002 chips. Some chips may indicate support for buffered writes even though they only support dual-word writes. The CFI fixup must run before fixup_use_write_buffers for this to work. Signed-off-by: Håvard Skinnemoen Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 43 +++++++++++++++++++++++++++++++++++++ drivers/mtd/chips/cfi_cmdset_0002.c | 6 +++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index a9eb1c51624..da851c217fc 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -50,6 +50,7 @@ #define I82802AC 0x00ac #define MANUFACTURER_ST 0x0020 #define M50LPW080 0x002F +#define AT49BV640D 0x02de static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); @@ -157,6 +158,47 @@ static void cfi_tell_features(struct cfi_pri_intelext *extp) } #endif +/* Atmel chips don't use the same PRI format as Intel chips */ +static void fixup_convert_atmel_pri(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; + struct cfi_pri_atmel atmel_pri; + uint32_t features = 0; + + /* Reverse byteswapping */ + extp->FeatureSupport = cpu_to_le32(extp->FeatureSupport); + extp->BlkStatusRegMask = cpu_to_le16(extp->BlkStatusRegMask); + extp->ProtRegAddr = cpu_to_le16(extp->ProtRegAddr); + + memcpy(&atmel_pri, extp, sizeof(atmel_pri)); + memset((char *)extp + 5, 0, sizeof(*extp) - 5); + + printk(KERN_ERR "atmel Features: %02x\n", atmel_pri.Features); + + if (atmel_pri.Features & 0x01) /* chip erase supported */ + features |= (1<<0); + if (atmel_pri.Features & 0x02) /* erase suspend supported */ + features |= (1<<1); + if (atmel_pri.Features & 0x04) /* program suspend supported */ + features |= (1<<2); + if (atmel_pri.Features & 0x08) /* simultaneous operations supported */ + features |= (1<<9); + if (atmel_pri.Features & 0x20) /* page mode read supported */ + features |= (1<<7); + if (atmel_pri.Features & 0x40) /* queued erase supported */ + features |= (1<<4); + if (atmel_pri.Features & 0x80) /* Protection bits supported */ + features |= (1<<6); + + extp->FeatureSupport = features; + + /* burst write mode not supported */ + cfi->cfiq->BufWriteTimeoutTyp = 0; + cfi->cfiq->BufWriteTimeoutMax = 0; +} + #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE /* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ static void fixup_intel_strataflash(struct mtd_info *mtd, void* param) @@ -234,6 +276,7 @@ static void fixup_use_powerup_lock(struct mtd_info *mtd, void *param) } static struct cfi_fixup cfi_fixup_table[] = { + { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, #endif diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 389acc600f5..571226eefeb 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -185,6 +185,10 @@ static void fixup_convert_atmel_pri(struct mtd_info *mtd, void *param) extp->TopBottom = 2; else extp->TopBottom = 3; + + /* burst write mode not supported */ + cfi->cfiq->BufWriteTimeoutTyp = 0; + cfi->cfiq->BufWriteTimeoutMax = 0; } static void fixup_use_secsi(struct mtd_info *mtd, void *param) @@ -217,6 +221,7 @@ static void fixup_use_atmel_lock(struct mtd_info *mtd, void *param) } static struct cfi_fixup cfi_fixup_table[] = { + { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, #ifdef AMD_BOOTLOC_BUG { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL }, #endif @@ -229,7 +234,6 @@ static struct cfi_fixup cfi_fixup_table[] = { #if !FORCE_WORD_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, #endif - { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, { 0, 0, NULL, NULL } }; static struct cfi_fixup jedec_fixup_table[] = { -- cgit v1.2.3 From 15953580e79b58caefb107e77f218e009b9992e6 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 1 Nov 2007 16:25:56 -0400 Subject: [JFFS2] Improve getdents vs. f_pos handling on NOR flash. Commit a491486a2087ac3dfc00efb4f838c8d684afaf54 started obliterating dirents directly on the medium, when jffs2_can_mark_obsolete(). Removing them immediately from the f->dents list, however, screws up handling of f_pos within a directory -- because the offset is equivalent to the number of entries through the list we are, and the existence of deletion dirents served to provide 'placeholders' for unlinked entries. Now, 'rm -r' doesn't even manage to unlink everything in the directory. Revert to keeping 'deletion' dirents in the list, at least in memory even though we no longer write anything to the medium. Spotted, debugged and mostly fixed by Joakim Tjernlund Signed-off-by: David Woodhouse --- fs/jffs2/nodelist.c | 9 ++++++--- fs/jffs2/write.c | 27 +++++++++++++++------------ 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index 4bf86088b3a..87c6f555e1a 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -32,15 +32,18 @@ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new if ((*prev)->nhash == new->nhash && !strcmp((*prev)->name, new->name)) { /* Duplicate. Free one */ if (new->version < (*prev)->version) { - dbg_dentlist("Eep! Marking new dirent node is obsolete, old is \"%s\", ino #%u\n", + dbg_dentlist("Eep! Marking new dirent node obsolete, old is \"%s\", ino #%u\n", (*prev)->name, (*prev)->ino); jffs2_mark_node_obsolete(c, new->raw); jffs2_free_full_dirent(new); } else { - dbg_dentlist("marking old dirent \"%s\", ino #%u bsolete\n", + dbg_dentlist("marking old dirent \"%s\", ino #%u obsolete\n", (*prev)->name, (*prev)->ino); new->next = (*prev)->next; - jffs2_mark_node_obsolete(c, ((*prev)->raw)); + /* It may have been a 'placeholder' deletion dirent, + if jffs2_can_mark_obsolete() (see jffs2_do_unlink()) */ + if ((*prev)->raw) + jffs2_mark_node_obsolete(c, ((*prev)->raw)); jffs2_free_full_dirent(*prev); *prev = new; } diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 147e2cbee9e..611012f7c8a 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -582,7 +582,7 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, jffs2_add_fd_to_list(c, fd, &dir_f->dents); up(&dir_f->sem); } else { - struct jffs2_full_dirent **prev = &dir_f->dents; + struct jffs2_full_dirent *fd = dir_f->dents; uint32_t nhash = full_name_hash(name, namelen); /* We don't actually want to reserve any space, but we do @@ -590,18 +590,20 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, down(&c->alloc_sem); down(&dir_f->sem); - while ((*prev) && (*prev)->nhash <= nhash) { - if ((*prev)->nhash == nhash && - !memcmp((*prev)->name, name, namelen) && - !(*prev)->name[namelen]) { - struct jffs2_full_dirent *this = *prev; + for (fd = dir_f->dents; fd; fd = fd->next) { + if (fd->nhash == nhash && + !memcmp(fd->name, name, namelen) && + !fd->name[namelen]) { D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n", - this->ino, ref_offset(this->raw))); - - *prev = this->next; - jffs2_mark_node_obsolete(c, (this->raw)); - jffs2_free_full_dirent(this); + fd->ino, ref_offset(fd->raw))); + jffs2_mark_node_obsolete(c, fd->raw); + /* We don't want to remove it from the list immediately, + because that screws up getdents()/seek() semantics even + more than they're screwed already. Turn it into a + node-less deletion dirent instead -- a placeholder */ + fd->raw = NULL; + fd->ino = 0; break; } prev = &((*prev)->next); @@ -630,7 +632,8 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino)); } - jffs2_mark_node_obsolete(c, fd->raw); + if (fd->raw) + jffs2_mark_node_obsolete(c, fd->raw); jffs2_free_full_dirent(fd); } } -- cgit v1.2.3 From 857013b87b6e0fea776c1f0b365dbce3d6eba8c6 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 1 Nov 2007 16:27:38 -0400 Subject: [JFFS2] Don't strip sgid bit from inode permissions dwmw2: anyway, removing sgid from directories or from files without S_IXGRP is a plain and simple bug these days you don't need that logics at all - simply remove it Signed-off-by: David Woodhouse --- fs/jffs2/fs.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index d2e06f7ea96..ee192af0b8b 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -97,11 +97,7 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) ri->gid = cpu_to_je16((ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid); if (ivalid & ATTR_MODE) - if (iattr->ia_mode & S_ISGID && - !in_group_p(je16_to_cpu(ri->gid)) && !capable(CAP_FSETID)) - ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID); - else - ri->mode = cpu_to_jemode(iattr->ia_mode); + ri->mode = cpu_to_jemode(iattr->ia_mode); else ri->mode = cpu_to_jemode(inode->i_mode); -- cgit v1.2.3 From 6d88202e3985afc5ac62733b7673c7e815cda698 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 6 Nov 2007 08:29:59 +0000 Subject: [JFFS2] Fix misapplied patch causing compile breakage Somehow, the patch in commit 15953580e79b58caefb107e77f218e009b9992e6 was misapplied and part of the old list-traversal remained. Remove it. Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- fs/jffs2/write.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 611012f7c8a..ecdf18d0486 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -606,7 +606,6 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, fd->ino = 0; break; } - prev = &((*prev)->next); } up(&dir_f->sem); } -- cgit v1.2.3 From 050416e93354158b025360387746fb7257d7ce07 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 6 Nov 2007 08:36:49 +0000 Subject: [JFFS2] make jffs2_get_acl() static jffs2_get_acl() can now become static again. Signed-off-by: Adrian Bunk Acked-by: KaiGai Kohei Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- fs/jffs2/acl.c | 2 +- fs/jffs2/acl.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 77fc5838609..993ddfce031 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -176,7 +176,7 @@ static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct spin_unlock(&inode->i_lock); } -struct posix_acl *jffs2_get_acl(struct inode *inode, int type) +static struct posix_acl *jffs2_get_acl(struct inode *inode, int type) { struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); struct posix_acl *acl; diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h index 76c6ebd1acd..0bb7f003fd8 100644 --- a/fs/jffs2/acl.h +++ b/fs/jffs2/acl.h @@ -28,7 +28,6 @@ struct jffs2_acl_header { #define JFFS2_ACL_NOT_CACHED ((void *)-1) -extern struct posix_acl *jffs2_get_acl(struct inode *inode, int type); extern int jffs2_permission(struct inode *, int, struct nameidata *); extern int jffs2_acl_chmod(struct inode *); extern int jffs2_init_acl_pre(struct inode *, struct inode *, int *); @@ -40,7 +39,6 @@ extern struct xattr_handler jffs2_acl_default_xattr_handler; #else -#define jffs2_get_acl(inode, type) (NULL) #define jffs2_permission (NULL) #define jffs2_acl_chmod(inode) (0) #define jffs2_init_acl_pre(dir_i,inode,mode) (0) -- cgit v1.2.3 From a66f66c44d53a4bab4b6b2903fd271f13ce4101b Mon Sep 17 00:00:00 2001 From: Jesper Nilsson Date: Tue, 6 Nov 2007 08:40:24 +0000 Subject: [MTD] Provide mtdram.h with mtdram_init_device() prototype This is used by axisflashmap.c to boot from ram. Signed-off-by: Jesper Nilsson Acked-by: Mikael Starvik Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- include/linux/mtd/mtdram.h | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 include/linux/mtd/mtdram.h diff --git a/include/linux/mtd/mtdram.h b/include/linux/mtd/mtdram.h new file mode 100644 index 00000000000..04fdc07b735 --- /dev/null +++ b/include/linux/mtd/mtdram.h @@ -0,0 +1,8 @@ +#ifndef __MTD_MTDRAM_H__ +#define __MTD_MTDRAM_H__ + +#include +int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, + unsigned long size, char *name); + +#endif /* __MTD_MTDRAM_H__ */ -- cgit v1.2.3 From 8547e583a1140698cab41bc3f687efe8f8b2bb41 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 9 Nov 2007 15:34:42 +0000 Subject: [MTD] [NOR] Add support for the SST 39VF1601 flash chip Add support for the SST 39VF1601 flash chip. Signed-off-by: David Howells Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index a67b23b87fc..5074b5e8393 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1527,6 +1527,21 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256), ERASEINFO(0x1000,256) } + }, { + .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .dev_id = SST39VF1601, + .name = "SST 39VF1601", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ + [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } }, { .mfr_id = MANUFACTURER_ST, -- cgit v1.2.3 From 92525726df0c30e080b0fce9b0eb699c622d261e Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 21 Nov 2007 12:08:16 -0500 Subject: [JFFS2] Fix data CRC checking on NOR flash. We were failing to check the data CRC on data nodes on non-writebuffered flash, which led to "interesting" behaviour on unclean shutdowns. Signed-off-by: David Woodhouse --- fs/jffs2/readinode.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 2eae5d2dbeb..da22da95459 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -37,23 +37,24 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info BUG_ON(tn->csize == 0); - if (!jffs2_is_writebuffered(c)) - goto adj_acc; - /* Calculate how many bytes were already checked */ ofs = ref_offset(ref) + sizeof(struct jffs2_raw_inode); - len = ofs % c->wbuf_pagesize; - if (likely(len)) - len = c->wbuf_pagesize - len; - - if (len >= tn->csize) { - dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n", - ref_offset(ref), tn->csize, ofs); - goto adj_acc; - } + len = tn->csize; + + if (jffs2_is_writebuffered(c)) { + int adj = ofs % c->wbuf_pagesize; + if (likely(adj)) + adj = c->wbuf_pagesize - adj; + + if (adj >= tn->csize) { + dbg_readinode("no need to check node at %#08x, data length %u, data starts at %#08x - it has already been checked.\n", + ref_offset(ref), tn->csize, ofs); + goto adj_acc; + } - ofs += len; - len = tn->csize - len; + ofs += adj; + len -= adj; + } dbg_readinode("check node at %#08x, data length %u, partial CRC %#08x, correct CRC %#08x, data starts at %#08x, start checking from %#08x - %u bytes.\n", ref_offset(ref), tn->csize, tn->partial_crc, tn->data_crc, ofs - len, ofs, len); -- cgit v1.2.3 From b1c9c9be6da010510459aca93f5754efb19695ff Mon Sep 17 00:00:00 2001 From: Alexey Korolev Date: Fri, 23 Nov 2007 09:31:56 +0000 Subject: [MTD] [NOR] Support Intel P3x flash support with CFI version 1.5 Signed-off-by: Alexey Korolev Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index da851c217fc..ed5ce41d137 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -320,7 +320,7 @@ read_pri_intelext(struct map_info *map, __u16 adr) return NULL; if (extp->MajorVersion != '1' || - (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { + (extp->MinorVersion < '0' || extp->MinorVersion > '5')) { printk(KERN_ERR " Unknown Intel/Sharp Extended Query " "version %c.%c.\n", extp->MajorVersion, extp->MinorVersion); -- cgit v1.2.3 From 5f4d47d5d1060a93be83e33a167a53a7f8c08b20 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 6 Nov 2007 09:17:25 +0200 Subject: [MTD] [OneNAND] Do not stop reading for ECC errors When an ECC error occurs, the read should be completed anyway before returning -EBADMSG. Returning -EBADMSG straight away is incorrect. Signed-off-by: Adrian Hunter Acked-by: Kyungmin Park Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 1b0b3201141..ed9f9c061ac 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -855,6 +855,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, this->command(mtd, ONENAND_CMD_READ, from, writesize); ret = this->wait(mtd, FL_READING); onenand_update_bufferram(mtd, from, !ret); + if (ret == -EBADMSG) + ret = 0; } } @@ -913,6 +915,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, /* Now wait for load */ ret = this->wait(mtd, FL_READING); onenand_update_bufferram(mtd, from, !ret); + if (ret == -EBADMSG) + ret = 0; } /* @@ -923,12 +927,12 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, ops->retlen = read; ops->oobretlen = oobread; - if (mtd->ecc_stats.failed - stats.failed) - return -EBADMSG; - if (ret) return ret; + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; } @@ -944,6 +948,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { struct onenand_chip *this = mtd->priv; + struct mtd_ecc_stats stats; int read = 0, thislen, column, oobsize; size_t len = ops->ooblen; mtd_oob_mode_t mode = ops->mode; @@ -977,6 +982,8 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, return -EINVAL; } + stats = mtd->ecc_stats; + while (read < len) { cond_resched(); @@ -988,18 +995,16 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, onenand_update_bufferram(mtd, from, 0); ret = this->wait(mtd, FL_READING); - /* First copy data and check return value for ECC handling */ + if (ret && ret != -EBADMSG) { + printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); + break; + } if (mode == MTD_OOB_AUTO) onenand_transfer_auto_oob(mtd, buf, column, thislen); else this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); - if (ret) { - printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); - break; - } - read += thislen; if (read == len) @@ -1016,7 +1021,14 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, } ops->oobretlen = read; - return ret; + + if (ret) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return 0; } /** -- cgit v1.2.3 From 6c24e4161e80a5c03e9d969b5db73d8553846037 Mon Sep 17 00:00:00 2001 From: Alexander Belyakov Date: Wed, 7 Nov 2007 11:58:07 +0300 Subject: [MTD] [NOR] Prevent erase command invocation on suspended chip while running stress tests we have met cfi_cmdset_0001.c driver issue. Working on multipartitional devices with erase suspend on write feature enabled it is possible to get erase operation invoked on chip with suspended erase. get_chip() looses information about earlier suspended erase and new erase operation gets issued. New erase operations report successful completion, but blocks remain dirty causing, for example, JFFS2 error messages like: ... Newly-erased block contained word 0x20031985 at offset 0x00200000 Newly-erased block contained word 0x20031985 at offset 0x00280000 Newly-erased block contained word 0x20031985 at offset 0x00240000 ... The patch below fixes that issue. Signed-off-by: Alexander Belyakov Acked-by: Nicolas Pitre Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index ed5ce41d137..350671ec522 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -795,6 +795,7 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) { int ret; + DECLARE_WAITQUEUE(wait, current); retry: if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING @@ -851,6 +852,20 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr spin_unlock(contender->mutex); } + /* Check if we already have suspended erase + * on this chip. Sleep. */ + if (mode == FL_ERASING && shared->erasing + && shared->erasing->oldstate == FL_ERASING) { + spin_unlock(&shared->lock); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + spin_lock(chip->mutex); + goto retry; + } + /* We now own it */ shared->writing = chip; if (mode == FL_ERASING) -- cgit v1.2.3 From c2056e1e1ddcca8d43e89543e1795e4457f5d1e9 Mon Sep 17 00:00:00 2001 From: Alexey Korolev Date: Tue, 27 Nov 2007 11:25:10 +0000 Subject: [JFFS2] Fix return value check for mtd->point() in check_node_data() If we ask it to map 'len' bytes of the device, don't compare against some other number and whine that it's different. That's a little silly. Signed-off-by: Alexey Korolev Signed-off-by: David Woodhouse --- fs/jffs2/readinode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index da22da95459..fb89ab5e1d5 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -64,7 +64,7 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info * adding and jffs2_flash_read_end() interface. */ if (c->mtd->point) { err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer); - if (!err && retlen < tn->csize) { + if (!err && retlen < len) { JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize); c->mtd->unpoint(c->mtd, buffer, ofs, retlen); } else if (err) -- cgit v1.2.3 From cccb45d4b34728d33638085435f8fdc0a83e0c00 Mon Sep 17 00:00:00 2001 From: Andrew Victor Date: Mon, 19 Nov 2007 15:37:23 +0200 Subject: [MTD] mtd_dataflash: Incorrect compare-after-write check After writing to a Dataflash page, the built-in compare operation is used to check that the page was successfully written. A logic bug in checking the results of the comparison currently causes the compare to never fail. This bug was originally in the legacy at91_dataflash.c driver. Signed-off-by: Andrew Victor Acked-by: David Brownell Signed-off-by: David Woodhouse --- drivers/mtd/devices/mtd_dataflash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index a5ed6d232c3..b35e4813a3a 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -420,7 +420,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, status = dataflash_waitready(priv->spi); /* Check result of the compare operation */ - if ((status & (1 << 6)) == 1) { + if (status & (1 << 6)) { printk(KERN_ERR "%s: compare page %u, err %d\n", spi->dev.bus_id, pageaddr, status); remaining = 0; -- cgit v1.2.3 From 73061e4c2dcfba17c6a0137a1199d3e00d03b14c Mon Sep 17 00:00:00 2001 From: Stanislav Brabec Date: Mon, 19 Nov 2007 22:33:02 +0100 Subject: [MTD] fix CONFIG_MTD_SHARP_SL if CONFIG_MTD=m Sharp Zaurus SL-C3200 with CONFIG_MTD=m and CONFIG_MTD_SHARP_SL=y (as it is bool) lost support for the ROM flash. With CONFIG_MTD=y it has no problems. It is caused by losing of compiled code of drivers/mtd/maps/sharpsl-flash.o. It was linked to drivers/mtd/maps/built-in.o and drivers/mtd/built-in.o, but lost and not linked to drivers/built-in.o (because CONFIG_MTD!=y). Patch below fixes this problem by creating sharpsl-flash.ko (and the code works correctly as a module). Signed-off-by: Stanislav Brabec Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index a592fc04cf7..93dcb780db4 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -576,7 +576,7 @@ config MTD_BAST_MAXSIZE default "4" config MTD_SHARP_SL - bool "ROM mapped on Sharp SL Series" + tristate "ROM mapped on Sharp SL Series" depends on ARCH_PXA help This enables access to the flash chip on the Sharp SL Series of PDAs. -- cgit v1.2.3 From 03680b1e00d146df718c8a4eac34438566b70c85 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Mon, 19 Nov 2007 23:28:07 +0000 Subject: [MTD] [NAND] S3C2410 correctly set nFCE over resume Ensure the nFCE line is de-asserted over suspend and then re-initialised when the system resumes. This is to ensure that the NAND is kept in lowest power mode over suspend (power settings are only specified for nFCE inactive) as well as fixing the Simtec Osiris which relies on nFCE being inactive. Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse --- drivers/mtd/nand/s3c2410.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 66f76e9618d..512acfc8901 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -120,6 +120,8 @@ struct s3c2410_nand_info { int sel_bit; int mtd_count; + unsigned long save_nfconf; + enum s3c_cpu_type cpu_type; }; @@ -810,6 +812,16 @@ static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) struct s3c2410_nand_info *info = platform_get_drvdata(dev); if (info) { + info->save_nfconf = readl(info->regs + S3C2410_NFCONF); + + /* For the moment, we must ensure nFCE is high during + * the time we are suspended. This really should be + * handled by suspending the MTDs we are using, but + * that is currently not the case. */ + + writel(info->save_nfconf | info->sel_bit, + info->regs + S3C2410_NFCONF); + if (!allow_clk_stop(info)) clk_disable(info->clk); } @@ -820,11 +832,19 @@ static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) static int s3c24xx_nand_resume(struct platform_device *dev) { struct s3c2410_nand_info *info = platform_get_drvdata(dev); + unsigned long nfconf; if (info) { clk_enable(info->clk); s3c2410_nand_inithw(info, dev); + /* Restore the state of the nFCE line. */ + + nfconf = readl(info->regs + S3C2410_NFCONF); + nfconf &= ~info->sel_bit; + nfconf |= info->save_nfconf & info->sel_bit; + writel(nfconf, info->regs + S3C2410_NFCONF); + if (allow_clk_stop(info)) clk_disable(info->clk); } -- cgit v1.2.3 From 846fc31d06e54ad94026da11da0668c050fe777e Mon Sep 17 00:00:00 2001 From: Egor Martovetsky Date: Wed, 28 Nov 2007 18:37:31 -0600 Subject: [MTD] [NAND] pasemi_nand driver Plumbing for NAND connected via localbus on PA Semi PWRficient-based boards. From: Egor Martovetsky Signed-off-by: Olof Johansson Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 6 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/pasemi_nand.c | 243 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 drivers/mtd/nand/pasemi_nand.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index bf0e9b083d1..f8db6251209 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -283,6 +283,12 @@ config MTD_NAND_CM_X270 tristate "Support for NAND Flash on CM-X270 modules" depends on MTD_NAND && MACH_ARMCORE +config MTD_NAND_PASEMI + tristate "NAND support for PA Semi PWRficient" + depends on MTD_NAND && PPC_PASEMI + help + Enables support for NAND Flash interface on PA Semi PWRficient + based boards config MTD_NAND_NANDSIM tristate "Support for NAND Flash Simulator" diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 3ad6c0165da..fd5004795ec 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -29,5 +29,6 @@ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_ALAUDA) += alauda.o +obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c new file mode 100644 index 00000000000..75c89903902 --- /dev/null +++ b/drivers/mtd/nand/pasemi_nand.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2006-2007 PA Semi, Inc + * + * Author: Egor Martovetsky + * Maintained by: Olof Johansson + * + * Driver for the PWRficient onchip NAND flash interface + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define LBICTRL_LPCCTL_NR 0x00004000 +#define CLE_PIN_CTL 15 +#define ALE_PIN_CTL 14 + +static unsigned int lpcctl; +static struct mtd_info *pasemi_nand_mtd; +static const char driver_name[] = "pasemi-nand"; + +static void pasemi_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + + while (len > 0x800) { + memcpy_fromio(buf, chip->IO_ADDR_R, 0x800); + buf += 0x800; + len -= 0x800; + } + memcpy_fromio(buf, chip->IO_ADDR_R, len); +} + +static void pasemi_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + + while (len > 0x800) { + memcpy_toio(chip->IO_ADDR_R, buf, 0x800); + buf += 0x800; + len -= 0x800; + } + memcpy_toio(chip->IO_ADDR_R, buf, len); +} + +static void pasemi_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + out_8(chip->IO_ADDR_W + (1 << CLE_PIN_CTL), cmd); + else + out_8(chip->IO_ADDR_W + (1 << ALE_PIN_CTL), cmd); + + /* Push out posted writes */ + eieio(); + inl(lpcctl); +} + +int pasemi_device_ready(struct mtd_info *mtd) +{ + return !!(inl(lpcctl) & LBICTRL_LPCCTL_NR); +} + +static int __devinit pasemi_nand_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct pci_dev *pdev; + struct device_node *np = ofdev->node; + struct resource res; + struct nand_chip *chip; + int err = 0; + + err = of_address_to_resource(np, 0, &res); + + if (err) + return -EINVAL; + + /* We only support one device at the moment */ + if (pasemi_nand_mtd) + return -ENODEV; + + pr_debug("pasemi_nand at %lx-%lx\n", res.start, res.end); + + /* Allocate memory for MTD device structure and private data */ + pasemi_nand_mtd = kzalloc(sizeof(struct mtd_info) + + sizeof(struct nand_chip), GFP_KERNEL); + if (!pasemi_nand_mtd) { + printk(KERN_WARNING + "Unable to allocate PASEMI NAND MTD device structure\n"); + err = -ENOMEM; + goto out; + } + + /* Get pointer to private data */ + chip = (struct nand_chip *)&pasemi_nand_mtd[1]; + + /* Link the private data with the MTD structure */ + pasemi_nand_mtd->priv = chip; + pasemi_nand_mtd->owner = THIS_MODULE; + + chip->IO_ADDR_R = of_iomap(np, 0); + chip->IO_ADDR_W = chip->IO_ADDR_R; + + if (!chip->IO_ADDR_R) { + err = -EIO; + goto out_mtd; + } + + pdev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa008, NULL); + if (!pdev) { + err = -ENODEV; + goto out_ior; + } + + lpcctl = pci_resource_start(pdev, 0); + + if (!request_region(lpcctl, 4, driver_name)) { + err = -EBUSY; + goto out_ior; + } + + chip->cmd_ctrl = pasemi_hwcontrol; + chip->dev_ready = pasemi_device_ready; + chip->read_buf = pasemi_read_buf; + chip->write_buf = pasemi_write_buf; + chip->chip_delay = 0; + chip->ecc.mode = NAND_ECC_SOFT; + + /* Enable the following for a flash based bad block table */ + chip->options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR; + + /* Scan to find existance of the device */ + if (nand_scan(pasemi_nand_mtd, 1)) { + err = -ENXIO; + goto out_lpc; + } + + if (add_mtd_device(pasemi_nand_mtd)) { + printk(KERN_ERR "pasemi_nand: Unable to register MTD device\n"); + err = -ENODEV; + goto out_lpc; + } + + printk(KERN_INFO "PA Semi NAND flash at %08lx, control at I/O %x\n", + res.start, lpcctl); + + return 0; + + out_lpc: + release_region(lpcctl, 4); + out_ior: + iounmap(chip->IO_ADDR_R); + out_mtd: + kfree(pasemi_nand_mtd); + out: + return err; +} + +static int __devexit pasemi_nand_remove(struct of_device *ofdev) +{ + struct nand_chip *chip; + + if (!pasemi_nand_mtd) + return 0; + + chip = pasemi_nand_mtd->priv; + + /* Release resources, unregister device */ + nand_release(pasemi_nand_mtd); + + release_region(lpcctl, 4); + + iounmap(chip->IO_ADDR_R); + + /* Free the MTD device structure */ + kfree(pasemi_nand_mtd); + + pasemi_nand_mtd = NULL; + + return 0; +} + +static struct of_device_id pasemi_nand_match[] = +{ + { + .compatible = "pasemi,localbus-nand", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, pasemi_nand_match); + +static struct of_platform_driver pasemi_nand_driver = +{ + .name = (char*)driver_name, + .match_table = pasemi_nand_match, + .probe = pasemi_nand_probe, + .remove = pasemi_nand_remove, +}; + +static int __init pasemi_nand_init(void) +{ + return of_register_platform_driver(&pasemi_nand_driver); +} +module_init(pasemi_nand_init); + +static void __exit pasemi_nand_exit(void) +{ + of_unregister_platform_driver(&pasemi_nand_driver); +} +module_exit(pasemi_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Egor Martovetsky "); +MODULE_DESCRIPTION("NAND flash interface driver for PA Semi PWRficient"); -- cgit v1.2.3 From ce37ab42ad8b38ef2f36c31c6b4c39b87f36b792 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 3 Dec 2007 12:46:12 +0000 Subject: [MTD] Always initialise mutex in new mtd_blktrans_dev. We were only initialising the mutex in the case where the new device was automatically allocated the highest minor number. If the caller specified a minor number, or if it filled in a free slot which was made by a previous device deregistering, the mutex wouldn't get initialised when we jumped out of the loop. Reported by Monte Copeland Signed-off-by: David Woodhouse --- drivers/mtd/mtd_blkdevs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 74d9d30edab..839eed8430a 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -248,9 +248,9 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) return -EBUSY; } - mutex_init(&new->lock); list_add_tail(&new->list, &tr->devs); added: + mutex_init(&new->lock); if (!tr->writesect) new->readonly = 1; -- cgit v1.2.3 From 5d3cce3b8ef45317c59487f4d83dc43c355ae40a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 3 Dec 2007 12:48:57 +0000 Subject: [MTD] [NOR] Clean up jedec_probe, remove unlock address arrays This should have no functional effects -- we've been ignoring all but the first address in the array for a long time, and using it only to indicate which device types are supported. Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 1401 +++++++++++++++++---------------------- 1 file changed, 624 insertions(+), 777 deletions(-) diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 5074b5e8393..cb8c34da360 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -194,8 +194,8 @@ enum uaddr { struct unlock_addr { - u32 addr1; - u32 addr2; + uint32_t addr1; + uint32_t addr2; }; @@ -246,16 +246,16 @@ static const struct unlock_addr unlock_addrs[] = { } }; - struct amd_flash_info { - const __u16 mfr_id; - const __u16 dev_id; const char *name; - const int DevSize; - const int NumEraseRegions; - const int CmdSet; - const __u8 uaddr[4]; /* unlock addrs for 8, 16, 32, 64 */ - const ulong regions[6]; + const uint16_t mfr_id; + const uint16_t dev_id; + const uint8_t dev_size; + const uint8_t nr_regions; + const uint16_t cmd_set; + const uint32_t regions[6]; + const uint8_t devtypes; /* Bitmask for x8, x16 etc. */ + const uint8_t uaddr; /* unlock addrs for 8, 16, 32, 64 */ }; #define ERASEINFO(size,blocks) (size<<8)|(blocks-1) @@ -280,12 +280,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29F032B, .name = "AMD AM29F032B", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ - }, - .DevSize = SIZE_4MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .devtypes = CFI_DEVICETYPE_X8, + .dev_size = SIZE_4MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,64) } @@ -293,13 +292,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29LV160DT, .name = "AMD AM29LV160DT", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,31), ERASEINFO(0x08000,1), @@ -310,13 +308,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29LV160DB, .name = "AMD AM29LV160DB", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -327,13 +324,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29LV400BB, .name = "AMD AM29LV400BB", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -344,13 +340,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29LV400BT, .name = "AMD AM29LV400BT", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,7), ERASEINFO(0x08000,1), @@ -361,13 +356,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29LV800BB, .name = "AMD AM29LV800BB", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -379,13 +373,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29DL800BB, .name = "AMD AM29DL800BB", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 6, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 6, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x08000,1), @@ -398,13 +391,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29DL800BT, .name = "AMD AM29DL800BT", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 6, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 6, .regions = { ERASEINFO(0x10000,14), ERASEINFO(0x04000,1), @@ -417,13 +409,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29F800BB, .name = "AMD AM29F800BB", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -434,13 +425,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29LV800BT, .name = "AMD AM29LV800BT", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,15), ERASEINFO(0x08000,1), @@ -451,13 +441,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29F800BT, .name = "AMD AM29F800BT", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,15), ERASEINFO(0x08000,1), @@ -468,12 +457,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29F017D, .name = "AMD AM29F017D", - .uaddr = { - [0] = MTD_UADDR_DONT_CARE /* x8 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_DONT_CARE, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,32), } @@ -481,12 +469,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29F016D, .name = "AMD AM29F016D", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,32), } @@ -494,12 +481,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29F080, .name = "AMD AM29F080", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,16), } @@ -507,12 +493,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29F040, .name = "AMD AM29F040", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,8), } @@ -520,12 +505,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29LV040B, .name = "AMD AM29LV040B", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,8), } @@ -533,12 +517,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_AMD, .dev_id = AM29F002T, .name = "AMD AM29F002T", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ - }, - .DevSize = SIZE_256KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .dev_size = SIZE_256KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,3), ERASEINFO(0x08000,1), @@ -549,12 +532,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ATMEL, .dev_id = AT49BV512, .name = "Atmel AT49BV512", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_64KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_64KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,1) } @@ -562,12 +544,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ATMEL, .dev_id = AT29LV512, .name = "Atmel AT29LV512", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_64KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_64KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x80,256), ERASEINFO(0x80,256) @@ -576,13 +557,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ATMEL, .dev_id = AT49BV16X, .name = "Atmel AT49BV16X", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ - [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x0AAA, + /* XX: Maybe MTD_UADDR_0x0555_0x0AAA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x02000,8), ERASEINFO(0x10000,31) @@ -591,13 +571,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ATMEL, .dev_id = AT49BV16XT, .name = "Atmel AT49BV16XT", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ - [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x0AAA, + /* XX: Maybe MTD_UADDR_0x0555_0x0AAA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x10000,31), ERASEINFO(0x02000,8) @@ -606,13 +585,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ATMEL, .dev_id = AT49BV32X, .name = "Atmel AT49BV32X", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ - [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ - }, - .DevSize = SIZE_4MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x0AAA, + /* XX: Maybe MTD_UADDR_0x0555_0x0AAA ? */ + .dev_size = SIZE_4MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x02000,8), ERASEINFO(0x10000,63) @@ -621,13 +599,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ATMEL, .dev_id = AT49BV32XT, .name = "Atmel AT49BV32XT", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ - [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ - }, - .DevSize = SIZE_4MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x0AAA, + /* XX: Maybe MTD_UADDR_0x0555_0x0AAA ? */ + .dev_size = SIZE_4MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x10000,63), ERASEINFO(0x02000,8) @@ -636,12 +613,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29F040C, .name = "Fujitsu MBM29F040C", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,8) } @@ -649,13 +625,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29F800BA, .name = "Fujitsu MBM29F800BA", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -666,12 +641,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29LV650UE, .name = "Fujitsu MBM29LV650UE", - .uaddr = { - [0] = MTD_UADDR_DONT_CARE /* x16 */ - }, - .DevSize = SIZE_8MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_DONT_CARE, + .dev_size = SIZE_8MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,128) } @@ -679,13 +653,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29LV320TE, .name = "Fujitsu MBM29LV320TE", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_4MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_4MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x10000,63), ERASEINFO(0x02000,8) @@ -694,13 +667,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29LV320BE, .name = "Fujitsu MBM29LV320BE", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_4MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_4MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x02000,8), ERASEINFO(0x10000,63) @@ -709,13 +681,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29LV160TE, .name = "Fujitsu MBM29LV160TE", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,31), ERASEINFO(0x08000,1), @@ -726,13 +697,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29LV160BE, .name = "Fujitsu MBM29LV160BE", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -743,13 +713,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29LV800BA, .name = "Fujitsu MBM29LV800BA", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -760,13 +729,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29LV800TA, .name = "Fujitsu MBM29LV800TA", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,15), ERASEINFO(0x08000,1), @@ -777,13 +745,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29LV400BC, .name = "Fujitsu MBM29LV400BC", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -794,13 +761,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_FUJITSU, .dev_id = MBM29LV400TC, .name = "Fujitsu MBM29LV400TC", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,7), ERASEINFO(0x08000,1), @@ -811,12 +777,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_HYUNDAI, .dev_id = HY29F002T, .name = "Hyundai HY29F002T", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ - }, - .DevSize = SIZE_256KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .dev_size = SIZE_256KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,3), ERASEINFO(0x08000,1), @@ -827,12 +792,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F004B3B, .name = "Intel 28F004B3B", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 7), @@ -841,12 +805,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F004B3T, .name = "Intel 28F004B3T", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x10000, 7), ERASEINFO(0x02000, 8), @@ -855,13 +818,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F400B3B, .name = "Intel 28F400B3B", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - [1] = MTD_UADDR_UNNECESSARY, /* x16 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + /* XX: Maybe MTD_UADDR_UNNECESSARY ? */ + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 7), @@ -870,13 +832,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F400B3T, .name = "Intel 28F400B3T", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - [1] = MTD_UADDR_UNNECESSARY, /* x16 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + /* XX: Maybe MTD_UADDR_UNNECESSARY ? */ + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x10000, 7), ERASEINFO(0x02000, 8), @@ -885,12 +846,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F008B3B, .name = "Intel 28F008B3B", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 15), @@ -899,12 +859,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F008B3T, .name = "Intel 28F008B3T", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x10000, 15), ERASEINFO(0x02000, 8), @@ -913,12 +872,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F008S5, .name = "Intel 28F008S5", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_INTEL_EXT, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_INTEL_EXT, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,16), } @@ -926,12 +884,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F016S5, .name = "Intel 28F016S5", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_INTEL_EXT, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_INTEL_EXT, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,32), } @@ -939,12 +896,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F008SA, .name = "Intel 28F008SA", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000, 16), } @@ -952,12 +908,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F800B3B, .name = "Intel 28F800B3B", - .uaddr = { - [1] = MTD_UADDR_UNNECESSARY, /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 15), @@ -966,12 +921,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F800B3T, .name = "Intel 28F800B3T", - .uaddr = { - [1] = MTD_UADDR_UNNECESSARY, /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x10000, 15), ERASEINFO(0x02000, 8), @@ -980,12 +934,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F016B3B, .name = "Intel 28F016B3B", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 31), @@ -994,12 +947,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F016S3, .name = "Intel I28F016S3", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000, 32), } @@ -1007,12 +959,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F016B3T, .name = "Intel 28F016B3T", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x10000, 31), ERASEINFO(0x02000, 8), @@ -1021,12 +972,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F160B3B, .name = "Intel 28F160B3B", - .uaddr = { - [1] = MTD_UADDR_UNNECESSARY, /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 31), @@ -1035,12 +985,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F160B3T, .name = "Intel 28F160B3T", - .uaddr = { - [1] = MTD_UADDR_UNNECESSARY, /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x10000, 31), ERASEINFO(0x02000, 8), @@ -1049,12 +998,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F320B3B, .name = "Intel 28F320B3B", - .uaddr = { - [1] = MTD_UADDR_UNNECESSARY, /* x16 */ - }, - .DevSize = SIZE_4MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_4MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 63), @@ -1063,12 +1011,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F320B3T, .name = "Intel 28F320B3T", - .uaddr = { - [1] = MTD_UADDR_UNNECESSARY, /* x16 */ - }, - .DevSize = SIZE_4MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_4MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x10000, 63), ERASEINFO(0x02000, 8), @@ -1077,12 +1024,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F640B3B, .name = "Intel 28F640B3B", - .uaddr = { - [1] = MTD_UADDR_UNNECESSARY, /* x16 */ - }, - .DevSize = SIZE_8MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_8MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x02000, 8), ERASEINFO(0x10000, 127), @@ -1091,12 +1037,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I28F640B3T, .name = "Intel 28F640B3T", - .uaddr = { - [1] = MTD_UADDR_UNNECESSARY, /* x16 */ - }, - .DevSize = SIZE_8MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_8MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x10000, 127), ERASEINFO(0x02000, 8), @@ -1105,12 +1050,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I82802AB, .name = "Intel 82802AB", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_INTEL_EXT, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_INTEL_EXT, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,8), } @@ -1118,12 +1062,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_INTEL, .dev_id = I82802AC, .name = "Intel 82802AC", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_INTEL_EXT, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_INTEL_EXT, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,16), } @@ -1131,12 +1074,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29LV040C, .name = "Macronix MX29LV040C", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,8), } @@ -1144,13 +1086,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29LV160T, .name = "MXIC MX29LV160T", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,31), ERASEINFO(0x08000,1), @@ -1161,13 +1102,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_NEC, .dev_id = UPD29F064115, .name = "NEC uPD29F064115", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_8MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 3, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_8MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 3, .regions = { ERASEINFO(0x2000,8), ERASEINFO(0x10000,126), @@ -1177,13 +1117,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29LV160B, .name = "MXIC MX29LV160B", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -1194,12 +1133,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29F040, .name = "Macronix MX29F040", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,8), } @@ -1207,12 +1145,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29F016, .name = "Macronix MX29F016", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,32), } @@ -1220,12 +1157,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29F004T, .name = "Macronix MX29F004T", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,7), ERASEINFO(0x08000,1), @@ -1236,12 +1172,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29F004B, .name = "Macronix MX29F004B", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -1252,12 +1187,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_MACRONIX, .dev_id = MX29F002T, .name = "Macronix MX29F002T", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ - }, - .DevSize = SIZE_256KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .dev_size = SIZE_256KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,3), ERASEINFO(0x08000,1), @@ -1268,12 +1202,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_PMC, .dev_id = PM49FL002, .name = "PMC Pm49FL002", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_256KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_256KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO( 0x01000, 64 ) } @@ -1281,12 +1214,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_PMC, .dev_id = PM49FL004, .name = "PMC Pm49FL004", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO( 0x01000, 128 ) } @@ -1294,12 +1226,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_PMC, .dev_id = PM49FL008, .name = "PMC Pm49FL008", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO( 0x01000, 256 ) } @@ -1307,25 +1238,23 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_SHARP, .dev_id = LH28F640BF, .name = "LH28F640BF", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_4MiB, - .CmdSet = P_ID_INTEL_STD, - .NumEraseRegions= 1, - .regions = { + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_4MiB, + .cmd_set = P_ID_INTEL_STD, + .nr_regions = 1, + .regions = { ERASEINFO(0x40000,16), } }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF512, .name = "SST 39LF512", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_64KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_64KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x01000,16), } @@ -1333,12 +1262,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF010, .name = "SST 39LF010", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_128KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_128KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x01000,32), } @@ -1346,36 +1274,33 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_SST, .dev_id = SST29EE020, .name = "SST 29EE020", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_256KiB, - .CmdSet = P_ID_SST_PAGE, - .NumEraseRegions= 1, - .regions = {ERASEINFO(0x01000,64), - } - }, { + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_256KiB, + .cmd_set = P_ID_SST_PAGE, + .nr_regions = 1, + .regions = {ERASEINFO(0x01000,64), + } + }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST29LE020, .name = "SST 29LE020", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_256KiB, - .CmdSet = P_ID_SST_PAGE, - .NumEraseRegions= 1, - .regions = {ERASEINFO(0x01000,64), - } + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_256KiB, + .cmd_set = P_ID_SST_PAGE, + .nr_regions = 1, + .regions = {ERASEINFO(0x01000,64), + } }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF020, .name = "SST 39LF020", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_256KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_256KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x01000,64), } @@ -1383,12 +1308,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF040, .name = "SST 39LF040", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x01000,128), } @@ -1396,12 +1320,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_SST, .dev_id = SST39SF010A, .name = "SST 39SF010A", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_128KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_128KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x01000,32), } @@ -1409,26 +1332,24 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_SST, .dev_id = SST39SF020A, .name = "SST 39SF020A", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_256KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_256KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x01000,64), } }, { .mfr_id = MANUFACTURER_SST, - .dev_id = SST49LF040B, - .name = "SST 49LF040B", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, - .regions = { + .dev_id = SST49LF040B, + .name = "SST 49LF040B", + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, + .regions = { ERASEINFO(0x01000,128), } }, { @@ -1436,12 +1357,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_SST, .dev_id = SST49LF004B, .name = "SST 49LF004B", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x01000,128), } @@ -1449,12 +1369,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_SST, .dev_id = SST49LF008A, .name = "SST 49LF008A", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x01000,256), } @@ -1462,12 +1381,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_SST, .dev_id = SST49LF030A, .name = "SST 49LF030A", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x01000,96), } @@ -1475,12 +1393,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_SST, .dev_id = SST49LF040A, .name = "SST 49LF040A", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x01000,128), } @@ -1488,72 +1405,52 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_SST, .dev_id = SST49LF080A, .name = "SST 49LF080A", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x01000,256), } }, { - .mfr_id = MANUFACTURER_SST, /* should be CFI */ - .dev_id = SST39LF160, - .name = "SST 39LF160", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ - [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 2, - .regions = { - ERASEINFO(0x1000,256), - ERASEINFO(0x1000,256) - } - }, { - .mfr_id = MANUFACTURER_SST, /* should be CFI */ - .dev_id = SST39VF1601, - .name = "SST 39VF1601", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ - [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 2, - .regions = { - ERASEINFO(0x1000,256), - ERASEINFO(0x1000,256) - } - }, { - .mfr_id = MANUFACTURER_SST, /* should be CFI */ - .dev_id = SST39VF1601, - .name = "SST 39VF1601", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ - [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 2, - .regions = { - ERASEINFO(0x1000,256), - ERASEINFO(0x1000,256) - } - + .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .dev_id = SST39LF160, + .name = "SST 39LF160", + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + /* XX: Maybe MTD_UADDR_0x5555_0x2AAA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } + }, { + .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .dev_id = SST39VF1601, + .name = "SST 39VF1601", + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + /* XX: Maybe MTD_UADDR_0x5555_0x2AAA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } }, { .mfr_id = MANUFACTURER_ST, .dev_id = M29F800AB, .name = "ST M29F800AB", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -1564,13 +1461,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ .dev_id = M29W800DT, .name = "ST M29W800DT", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ - [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + /* XX: Maybe MTD_UADDR_0x5555_0x2AAA ? */ + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,15), ERASEINFO(0x08000,1), @@ -1581,13 +1477,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ .dev_id = M29W800DB, .name = "ST M29W800DB", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ - [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + /* XX: Maybe MTD_UADDR_0x5555_0x2AAA ? */ + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -1598,13 +1493,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ .dev_id = M29W160DT, .name = "ST M29W160DT", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,31), ERASEINFO(0x08000,1), @@ -1615,13 +1509,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ .dev_id = M29W160DB, .name = "ST M29W160DB", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -1632,12 +1525,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ST, .dev_id = M29W040B, .name = "ST M29W040B", - .uaddr = { - [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0555_0x02AA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,8), } @@ -1645,12 +1537,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ST, .dev_id = M50FW040, .name = "ST M50FW040", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_512KiB, - .CmdSet = P_ID_INTEL_EXT, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_INTEL_EXT, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,8), } @@ -1658,12 +1549,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ST, .dev_id = M50FW080, .name = "ST M50FW080", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_INTEL_EXT, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_INTEL_EXT, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,16), } @@ -1671,12 +1561,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ST, .dev_id = M50FW016, .name = "ST M50FW016", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_INTEL_EXT, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_INTEL_EXT, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,32), } @@ -1684,12 +1573,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_ST, .dev_id = M50LPW080, .name = "ST M50LPW080", - .uaddr = { - [0] = MTD_UADDR_UNNECESSARY, /* x8 */ - }, - .DevSize = SIZE_1MiB, - .CmdSet = P_ID_INTEL_EXT, - .NumEraseRegions= 1, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_UNNECESSARY, + .dev_size = SIZE_1MiB, + .cmd_set = P_ID_INTEL_EXT, + .nr_regions = 1, .regions = { ERASEINFO(0x10000,16), } @@ -1697,13 +1585,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_TOSHIBA, .dev_id = TC58FVT160, .name = "Toshiba TC58FVT160", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000,31), ERASEINFO(0x08000,1), @@ -1714,13 +1601,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_TOSHIBA, .dev_id = TC58FVB160, .name = "Toshiba TC58FVB160", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ - }, - .DevSize = SIZE_2MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -1731,13 +1617,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_TOSHIBA, .dev_id = TC58FVB321, .name = "Toshiba TC58FVB321", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ - }, - .DevSize = SIZE_4MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_4MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x02000,8), ERASEINFO(0x10000,63) @@ -1746,13 +1631,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_TOSHIBA, .dev_id = TC58FVT321, .name = "Toshiba TC58FVT321", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ - }, - .DevSize = SIZE_4MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_4MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x10000,63), ERASEINFO(0x02000,8) @@ -1761,13 +1645,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_TOSHIBA, .dev_id = TC58FVB641, .name = "Toshiba TC58FVB641", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_8MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_8MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x02000,8), ERASEINFO(0x10000,127) @@ -1776,13 +1659,12 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_TOSHIBA, .dev_id = TC58FVT641, .name = "Toshiba TC58FVT641", - .uaddr = { - [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ - [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ - }, - .DevSize = SIZE_8MiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 2, + .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x0AAA_0x0555, + /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .dev_size = SIZE_8MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, .regions = { ERASEINFO(0x10000,127), ERASEINFO(0x02000,8) @@ -1791,12 +1673,11 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = MANUFACTURER_WINBOND, .dev_id = W49V002A, .name = "Winbond W49V002A", - .uaddr = { - [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ - }, - .DevSize = SIZE_256KiB, - .CmdSet = P_ID_AMD_STD, - .NumEraseRegions= 4, + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_256KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 4, .regions = { ERASEINFO(0x10000, 3), ERASEINFO(0x08000, 1), @@ -1809,12 +1690,12 @@ static const struct amd_flash_info jedec_table[] = { static int cfi_jedec_setup(struct cfi_private *p_cfi, int index); -static int jedec_probe_chip(struct map_info *map, __u32 base, +static int jedec_probe_chip(struct map_info *map, uint32_t base, unsigned long *chip_map, struct cfi_private *cfi); static struct mtd_info *jedec_probe(struct map_info *map); -static inline u32 jedec_read_mfr(struct map_info *map, __u32 base, +static inline u32 jedec_read_mfr(struct map_info *map, uint32_t base, struct cfi_private *cfi) { map_word result; @@ -1825,7 +1706,7 @@ static inline u32 jedec_read_mfr(struct map_info *map, __u32 base, return result.x[0] & mask; } -static inline u32 jedec_read_id(struct map_info *map, __u32 base, +static inline u32 jedec_read_id(struct map_info *map, uint32_t base, struct cfi_private *cfi) { map_word result; @@ -1866,42 +1747,20 @@ static inline void jedec_reset(u32 base, struct map_info *map, } -static inline __u8 finfo_uaddr(const struct amd_flash_info *finfo, int device_type) -{ - int uaddr_idx; - __u8 uaddr = MTD_UADDR_NOT_SUPPORTED; - - switch ( device_type ) { - case CFI_DEVICETYPE_X8: uaddr_idx = 0; break; - case CFI_DEVICETYPE_X16: uaddr_idx = 1; break; - case CFI_DEVICETYPE_X32: uaddr_idx = 2; break; - default: - printk(KERN_NOTICE "MTD: %s(): unknown device_type %d\n", - __func__, device_type); - goto uaddr_done; - } - - uaddr = finfo->uaddr[uaddr_idx]; - - if (uaddr != MTD_UADDR_NOT_SUPPORTED ) { - /* ASSERT("The unlock addresses for non-8-bit mode - are bollocks. We don't really need an array."); */ - uaddr = finfo->uaddr[0]; - } - - uaddr_done: - return uaddr; -} - - static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) { int i,num_erase_regions; - __u8 uaddr; + uint8_t uaddr; + + if (! (jedec_table[index].devtypes & p_cfi->device_type)) { + DEBUG(MTD_DEBUG_LEVEL1, "Rejecting potential %s with incompatible %d-bit device type\n", + jedec_table[index].name, 4 * (1<device_type)); + return 0; + } - printk("Found: %s\n",jedec_table[index].name); + printk(KERN_INFO "Found: %s\n",jedec_table[index].name); - num_erase_regions = jedec_table[index].NumEraseRegions; + num_erase_regions = jedec_table[index].nr_regions; p_cfi->cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL); if (!p_cfi->cfiq) { @@ -1911,9 +1770,9 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) memset(p_cfi->cfiq,0,sizeof(struct cfi_ident)); - p_cfi->cfiq->P_ID = jedec_table[index].CmdSet; - p_cfi->cfiq->NumEraseRegions = jedec_table[index].NumEraseRegions; - p_cfi->cfiq->DevSize = jedec_table[index].DevSize; + p_cfi->cfiq->P_ID = jedec_table[index].cmd_set; + p_cfi->cfiq->NumEraseRegions = jedec_table[index].nr_regions; + p_cfi->cfiq->DevSize = jedec_table[index].dev_size; p_cfi->cfi_mode = CFI_MODE_JEDEC; for (i=0; imfr = jedec_table[index].mfr_id; p_cfi->id = jedec_table[index].dev_id; - uaddr = finfo_uaddr(&jedec_table[index], p_cfi->device_type); - if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) { - kfree( p_cfi->cfiq ); - return 0; - } + uaddr = jedec_table[index].uaddr; p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1; p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2; @@ -1945,14 +1800,14 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) * be perfect - consequently there should be some module parameters that * could be manually specified to force the chip info. */ -static inline int jedec_match( __u32 base, +static inline int jedec_match( uint32_t base, struct map_info *map, struct cfi_private *cfi, const struct amd_flash_info *finfo ) { int rc = 0; /* failure until all tests pass */ u32 mfr, id; - __u8 uaddr; + uint8_t uaddr; /* * The IDs must match. For X16 and X32 devices operating in @@ -1965,8 +1820,8 @@ static inline int jedec_match( __u32 base, */ switch (cfi->device_type) { case CFI_DEVICETYPE_X8: - mfr = (__u8)finfo->mfr_id; - id = (__u8)finfo->dev_id; + mfr = (uint8_t)finfo->mfr_id; + id = (uint8_t)finfo->dev_id; /* bjd: it seems that if we do this, we can end up * detecting 16bit flashes as an 8bit device, even though @@ -1979,12 +1834,12 @@ static inline int jedec_match( __u32 base, } break; case CFI_DEVICETYPE_X16: - mfr = (__u16)finfo->mfr_id; - id = (__u16)finfo->dev_id; + mfr = (uint16_t)finfo->mfr_id; + id = (uint16_t)finfo->dev_id; break; case CFI_DEVICETYPE_X32: - mfr = (__u16)finfo->mfr_id; - id = (__u32)finfo->dev_id; + mfr = (uint16_t)finfo->mfr_id; + id = (uint32_t)finfo->dev_id; break; default: printk(KERN_WARNING @@ -1999,19 +1854,19 @@ static inline int jedec_match( __u32 base, /* the part size must fit in the memory window */ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check fit 0x%.8x + 0x%.8x = 0x%.8x\n", - __func__, base, 1 << finfo->DevSize, base + (1 << finfo->DevSize) ); - if ( base + cfi_interleave(cfi) * ( 1 << finfo->DevSize ) > map->size ) { + __func__, base, 1 << finfo->dev_size, base + (1 << finfo->dev_size) ); + if ( base + cfi_interleave(cfi) * ( 1 << finfo->dev_size ) > map->size ) { DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): 0x%.4x 0x%.4x %dKiB doesn't fit\n", __func__, finfo->mfr_id, finfo->dev_id, - 1 << finfo->DevSize ); + 1 << finfo->dev_size ); goto match_done; } - uaddr = finfo_uaddr(finfo, cfi->device_type); - if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) { + if (! (finfo->devtypes & cfi->device_type)) goto match_done; - } + + uaddr = finfo->uaddr; DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n", __func__, cfi->addr_unlock1, cfi->addr_unlock2 ); @@ -2096,19 +1951,11 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, } /* Ensure the unlock addresses we try stay inside the map */ - probe_offset1 = cfi_build_cmd_addr( - cfi->addr_unlock1, - cfi_interleave(cfi), - cfi->device_type); - probe_offset2 = cfi_build_cmd_addr( - cfi->addr_unlock1, - cfi_interleave(cfi), - cfi->device_type); + probe_offset1 = cfi_build_cmd_addr(cfi->addr_unlock1, cfi_interleave(cfi), cfi->device_type); + probe_offset2 = cfi_build_cmd_addr(cfi->addr_unlock1, cfi_interleave(cfi), cfi->device_type); if ( ((base + probe_offset1 + map_bankwidth(map)) >= map->size) || ((base + probe_offset2 + map_bankwidth(map)) >= map->size)) - { goto retry; - } /* Reset */ jedec_reset(base, map, cfi); @@ -2143,8 +1990,8 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, } goto retry; } else { - __u16 mfr; - __u16 id; + uint16_t mfr; + uint16_t id; /* Make sure it is a chip of the same manufacturer and id */ mfr = jedec_read_mfr(map, base, cfi); -- cgit v1.2.3 From f6f0f81895ad8272905bf3d637b7c99a62238d79 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 30 Nov 2007 16:24:52 +0000 Subject: [MTD] [NOR] Fix overflow check in jedec_probe Having laid the code out so that it's easier to read instead of sticking to the 80-column guideline even when it doesn't make sense, a bug is immediately spotted... we were only checking _one_ of the unlock addresses to see if it runs off the end of the map. Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index cb8c34da360..6041ce8908e 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1952,7 +1952,7 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, } /* Ensure the unlock addresses we try stay inside the map */ probe_offset1 = cfi_build_cmd_addr(cfi->addr_unlock1, cfi_interleave(cfi), cfi->device_type); - probe_offset2 = cfi_build_cmd_addr(cfi->addr_unlock1, cfi_interleave(cfi), cfi->device_type); + probe_offset2 = cfi_build_cmd_addr(cfi->addr_unlock2, cfi_interleave(cfi), cfi->device_type); if ( ((base + probe_offset1 + map_bankwidth(map)) >= map->size) || ((base + probe_offset2 + map_bankwidth(map)) >= map->size)) goto retry; -- cgit v1.2.3 From cec80bf2cc5283f2f00c34f474be857e5c9f6f65 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 3 Dec 2007 13:01:21 +0000 Subject: [MTD] [NOR] Attempt to clean up the JEDEC unlock address confusion Use a single unlock address, adjust it for the device type in the knowledge that it'll be adjusted back again. This has the desirable effect of masking out the least significant bit of the address for x16 devices. Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 85 ++++++++++++----------------------------- 1 file changed, 24 insertions(+), 61 deletions(-) diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 6041ce8908e..64059384521 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -294,7 +294,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "AMD AM29LV160DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -310,7 +309,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "AMD AM29LV160DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -326,7 +324,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "AMD AM29LV400BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_512KiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -342,7 +339,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "AMD AM29LV400BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_512KiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -358,7 +354,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "AMD AM29LV800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -375,7 +370,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "AMD AM29DL800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 6, @@ -393,7 +387,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "AMD AM29DL800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 6, @@ -411,7 +404,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "AMD AM29F800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -427,7 +419,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "AMD AM29LV800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -443,7 +434,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "AMD AM29F800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -558,8 +548,7 @@ static const struct amd_flash_info jedec_table[] = { .dev_id = AT49BV16X, .name = "Atmel AT49BV16X", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x0555_0x0AAA, - /* XX: Maybe MTD_UADDR_0x0555_0x0AAA ? */ + .uaddr = MTD_UADDR_0x0555_0x0AAA, /* ???? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -572,8 +561,7 @@ static const struct amd_flash_info jedec_table[] = { .dev_id = AT49BV16XT, .name = "Atmel AT49BV16XT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x0555_0x0AAA, - /* XX: Maybe MTD_UADDR_0x0555_0x0AAA ? */ + .uaddr = MTD_UADDR_0x0555_0x0AAA, /* ???? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -586,8 +574,7 @@ static const struct amd_flash_info jedec_table[] = { .dev_id = AT49BV32X, .name = "Atmel AT49BV32X", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x0555_0x0AAA, - /* XX: Maybe MTD_UADDR_0x0555_0x0AAA ? */ + .uaddr = MTD_UADDR_0x0555_0x0AAA, /* ???? */ .dev_size = SIZE_4MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -600,8 +587,7 @@ static const struct amd_flash_info jedec_table[] = { .dev_id = AT49BV32XT, .name = "Atmel AT49BV32XT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x0555_0x0AAA, - /* XX: Maybe MTD_UADDR_0x0555_0x0AAA ? */ + .uaddr = MTD_UADDR_0x0555_0x0AAA, /* ???? */ .dev_size = SIZE_4MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -627,7 +613,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Fujitsu MBM29F800BA", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -655,7 +640,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Fujitsu MBM29LV320TE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_4MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -669,7 +653,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Fujitsu MBM29LV320BE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_4MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -683,7 +666,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Fujitsu MBM29LV160TE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -699,7 +681,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Fujitsu MBM29LV160BE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -715,7 +696,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Fujitsu MBM29LV800BA", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -731,7 +711,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Fujitsu MBM29LV800TA", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -747,7 +726,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Fujitsu MBM29LV400BC", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_512KiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -763,7 +741,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Fujitsu MBM29LV400TC", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_512KiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -820,7 +797,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Intel 28F400B3B", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_UNNECESSARY, - /* XX: Maybe MTD_UADDR_UNNECESSARY ? */ .dev_size = SIZE_512KiB, .cmd_set = P_ID_INTEL_STD, .nr_regions = 2, @@ -834,7 +810,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Intel 28F400B3T", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_UNNECESSARY, - /* XX: Maybe MTD_UADDR_UNNECESSARY ? */ .dev_size = SIZE_512KiB, .cmd_set = P_ID_INTEL_STD, .nr_regions = 2, @@ -1088,7 +1063,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "MXIC MX29LV160T", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -1103,8 +1077,7 @@ static const struct amd_flash_info jedec_table[] = { .dev_id = UPD29F064115, .name = "NEC uPD29F064115", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x0555_0x02AA, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .uaddr = MTD_UADDR_0x0555_0x02AA, /* ???? */ .dev_size = SIZE_8MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 3, @@ -1119,7 +1092,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "MXIC MX29LV160B", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -1418,8 +1390,7 @@ static const struct amd_flash_info jedec_table[] = { .dev_id = SST39LF160, .name = "SST 39LF160", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x5555_0x2AAA, - /* XX: Maybe MTD_UADDR_0x5555_0x2AAA ? */ + .uaddr = MTD_UADDR_0x5555_0x2AAA, /* ???? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -1432,8 +1403,7 @@ static const struct amd_flash_info jedec_table[] = { .dev_id = SST39VF1601, .name = "SST 39VF1601", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x5555_0x2AAA, - /* XX: Maybe MTD_UADDR_0x5555_0x2AAA ? */ + .uaddr = MTD_UADDR_0x5555_0x2AAA, /* ???? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -1447,7 +1417,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "ST M29F800AB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -1462,8 +1431,7 @@ static const struct amd_flash_info jedec_table[] = { .dev_id = M29W800DT, .name = "ST M29W800DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x5555_0x2AAA, - /* XX: Maybe MTD_UADDR_0x5555_0x2AAA ? */ + .uaddr = MTD_UADDR_0x5555_0x2AAA, /* ???? */ .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -1478,8 +1446,7 @@ static const struct amd_flash_info jedec_table[] = { .dev_id = M29W800DB, .name = "ST M29W800DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x5555_0x2AAA, - /* XX: Maybe MTD_UADDR_0x5555_0x2AAA ? */ + .uaddr = MTD_UADDR_0x5555_0x2AAA, /* ???? */ .dev_size = SIZE_1MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -1494,8 +1461,7 @@ static const struct amd_flash_info jedec_table[] = { .dev_id = M29W160DT, .name = "ST M29W160DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x0555_0x02AA, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .uaddr = MTD_UADDR_0x0555_0x02AA, /* ???? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -1510,8 +1476,7 @@ static const struct amd_flash_info jedec_table[] = { .dev_id = M29W160DB, .name = "ST M29W160DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, - .uaddr = MTD_UADDR_0x0555_0x02AA, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ + .uaddr = MTD_UADDR_0x0555_0x02AA, /* ???? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -1587,7 +1552,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Toshiba TC58FVT160", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -1603,7 +1567,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Toshiba TC58FVB160", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_2MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 4, @@ -1619,7 +1582,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Toshiba TC58FVB321", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_4MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -1633,7 +1595,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Toshiba TC58FVT321", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_4MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -1647,7 +1608,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Toshiba TC58FVB641", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_8MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -1661,7 +1621,6 @@ static const struct amd_flash_info jedec_table[] = { .name = "Toshiba TC58FVT641", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x0AAA_0x0555, - /* XX: Maybe MTD_UADDR_0x0555_0x02AA ? */ .dev_size = SIZE_8MiB, .cmd_set = P_ID_AMD_STD, .nr_regions = 2, @@ -1728,7 +1687,7 @@ static inline void jedec_reset(u32 base, struct map_info *map, * 0x2aaa, 0xF0 at 0x5555 this will not affect the AMD chips * as they will ignore the writes and dont care what address * the F0 is written to */ - if(cfi->addr_unlock1) { + if (cfi->addr_unlock1) { DEBUG( MTD_DEBUG_LEVEL3, "reset unlock called %x %x \n", cfi->addr_unlock1,cfi->addr_unlock2); @@ -1737,7 +1696,7 @@ static inline void jedec_reset(u32 base, struct map_info *map, } cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); - /* Some misdesigned intel chips do not respond for 0xF0 for a reset, + /* Some misdesigned Intel chips do not respond for 0xF0 for a reset, * so ensure we're in read mode. Send both the Intel and the AMD command * for this. Intel uses 0xff for this, AMD uses 0xff for NOP, so * this should be safe. @@ -1786,8 +1745,12 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) uaddr = jedec_table[index].uaddr; - p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1; - p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2; + /* The table has unlock addresses in _bytes_, and we try not to let + our brains explode when we see the datasheets talking about address + lines numbered from A-1 to A18. The CFI table has unlock addresses + in device-words according to the mode the device is connected in */ + p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 / p_cfi->device_type; + p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 / p_cfi->device_type; return 1; /* ok */ } @@ -1871,8 +1834,8 @@ static inline int jedec_match( uint32_t base, DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n", __func__, cfi->addr_unlock1, cfi->addr_unlock2 ); if ( MTD_UADDR_UNNECESSARY != uaddr && MTD_UADDR_DONT_CARE != uaddr - && ( unlock_addrs[uaddr].addr1 != cfi->addr_unlock1 || - unlock_addrs[uaddr].addr2 != cfi->addr_unlock2 ) ) { + && ( unlock_addrs[uaddr].addr1 / cfi->device_type != cfi->addr_unlock1 || + unlock_addrs[uaddr].addr2 / cfi->device_type != cfi->addr_unlock2 ) ) { DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): 0x%.4x 0x%.4x did not match\n", __func__, @@ -1912,7 +1875,7 @@ static inline int jedec_match( uint32_t base, * were truly frobbing a real device. */ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): return to ID mode\n", __func__ ); - if(cfi->addr_unlock1) { + if (cfi->addr_unlock1) { cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL); } @@ -1938,8 +1901,8 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, if (MTD_UADDR_UNNECESSARY == uaddr_idx) return 0; - cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1; - cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2; + cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1 / cfi->device_type; + cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2 / cfi->device_type; } /* Make certain we aren't probing past the end of map */ -- cgit v1.2.3 From 2a1dba2931dc14a2a202eef435ab24cf6bc6dbd4 Mon Sep 17 00:00:00 2001 From: Tzachi Perelstein Date: Wed, 17 Oct 2007 01:10:40 +0200 Subject: [MTD] [NAND] Marvell Orion device bus NAND controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Driver for the device bus NAND controller in the Marvell Orion family of ARM SoCs. Signed-off-by: Tzachi Perelstein Signed-off-by: Lennert Buytenhek Acked-by: Jörn Engel Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 9 +++ drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/orion_nand.c | 171 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 drivers/mtd/nand/orion_nand.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f8db6251209..0a840d5d75a 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -312,4 +312,13 @@ config MTD_ALAUDA These two (and possibly other) Alauda-based cardreaders for SmartMedia and xD allow raw flash access. +config MTD_NAND_ORION + tristate "NAND Flash support for Marvell Orion SoC" + depends on ARCH_ORION && MTD_NAND + help + This enables the NAND flash controller on Orion machines. + + No board specific support is done by this driver, each board + must advertise a platform_device for the driver to attach. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index fd5004795ec..e35f5ea3a7a 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -30,5 +30,6 @@ obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_ALAUDA) += alauda.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o +obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c new file mode 100644 index 00000000000..9162cca0182 --- /dev/null +++ b/drivers/mtd/nand/orion_nand.c @@ -0,0 +1,171 @@ +/* + * drivers/mtd/nand/orion_nand.c + * + * NAND support for Marvell Orion SoC platforms + * + * Tzachi Perelstein + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTD_CMDLINE_PARTS +static const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + +static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct nand_chip *nc = mtd->priv; + struct orion_nand_data *board = nc->priv; + u32 offs; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + offs = (1 << board->cle); + else if (ctrl & NAND_ALE) + offs = (1 << board->ale); + else + return; + + if (nc->options & NAND_BUSWIDTH_16) + offs <<= 1; + + writeb(cmd, nc->IO_ADDR_W + offs); +} + +static int __init orion_nand_probe(struct platform_device *pdev) +{ + struct mtd_info *mtd; + struct nand_chip *nc; + struct orion_nand_data *board; + void __iomem *io_base; + int ret = 0; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *partitions = NULL; + int num_part = 0; +#endif + + nc = kzalloc(sizeof(struct nand_chip) + sizeof(struct mtd_info), GFP_KERNEL); + if (!nc) { + printk(KERN_ERR "orion_nand: failed to allocate device structure.\n"); + ret = -ENOMEM; + goto no_res; + } + mtd = (struct mtd_info *)(nc + 1); + + io_base = ioremap(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); + if (!io_base) { + printk(KERN_ERR "orion_nand: ioremap failed\n"); + ret = -EIO; + goto no_res; + } + + board = pdev->dev.platform_data; + + mtd->priv = nc; + mtd->owner = THIS_MODULE; + + nc->priv = board; + nc->IO_ADDR_R = nc->IO_ADDR_W = io_base; + nc->cmd_ctrl = orion_nand_cmd_ctrl; + nc->ecc.mode = NAND_ECC_SOFT; + + if (board->width == 16) + nc->options |= NAND_BUSWIDTH_16; + + platform_set_drvdata(pdev, mtd); + + if (nand_scan(mtd, 1)) { + ret = -ENXIO; + goto no_dev; + } + +#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + mtd->name = "orion_nand"; + num_part = parse_mtd_partitions(mtd, part_probes, &partitions, 0); +#endif + /* If cmdline partitions have been passed, let them be used */ + if (num_part <= 0) { + num_part = board->nr_parts; + partitions = board->parts; + } + + if (partitions && num_part > 0) + ret = add_mtd_partitions(mtd, partitions, num_part); + else + ret = add_mtd_device(mtd); +#else + ret = add_mtd_device(mtd); +#endif + + if (ret) { + nand_release(mtd); + goto no_dev; + } + + return 0; + +no_dev: + platform_set_drvdata(pdev, NULL); + iounmap(io_base); +no_res: + kfree(nc); + + return ret; +} + +static int __devexit orion_nand_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct nand_chip *nc = mtd->priv; + + nand_release(mtd); + + iounmap(nc->IO_ADDR_W); + + kfree(nc); + + return 0; +} + +static struct platform_driver orion_nand_driver = { + .probe = orion_nand_probe, + .remove = orion_nand_remove, + .driver = { + .name = "orion_nand", + .owner = THIS_MODULE, + }, +}; + +static int __init orion_nand_init(void) +{ + return platform_driver_register(&orion_nand_driver); +} + +static void __exit orion_nand_exit(void) +{ + platform_driver_unregister(&orion_nand_driver); +} + +module_init(orion_nand_init); +module_exit(orion_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tzachi Perelstein"); +MODULE_DESCRIPTION("NAND glue for Orion platforms"); -- cgit v1.2.3 From 256331d53a40f436cd0b16166621d819923145c8 Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Tue, 6 Nov 2007 11:55:00 +0100 Subject: [MTD] mtdoops: Document usage in Kconfig Add usage instructions to Kconfig for mtdoops driver. Signed-off-by: Peter Korsgaard Signed-off-by: David Woodhouse --- drivers/mtd/Kconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 8848e8ac705..661eac09f5c 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -286,6 +286,9 @@ config MTD_OOPS buffer in a flash partition where it can be read back at some later point. + To use, add console=ttyMTDx to the kernel command line, + where x is the MTD device number to use. + source "drivers/mtd/chips/Kconfig" source "drivers/mtd/maps/Kconfig" -- cgit v1.2.3 From 235d6200ea63372935e097cb82e6a8c133d51cad Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Tue, 6 Nov 2007 11:56:02 +0100 Subject: [MTD] mtdoops cleanup Use memcpy instead of open coding a copy loop. Signed-off-by: Peter Korsgaard Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index f8af627f0b9..20eaf294f62 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -286,7 +286,6 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) { struct mtdoops_context *cxt = co->data; struct mtd_info *mtd = cxt->mtd; - int i; if (!oops_in_progress) { mtdoops_console_sync(); @@ -305,10 +304,8 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) if ((count + cxt->writecount) > OOPS_PAGE_SIZE) count = OOPS_PAGE_SIZE - cxt->writecount; - for (i = 0; i < count; i++, s++) - *((char *)(cxt->oops_buf) + cxt->writecount + i) = *s; - - cxt->writecount = cxt->writecount + count; + memcpy(cxt->oops_buf + cxt->writecount, s, count); + cxt->writecount += count; } static int __init mtdoops_console_setup(struct console *co, char *options) -- cgit v1.2.3 From 2362a53ec59f286495307e0e0d8ef2401e8c5c49 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 18 Oct 2007 20:09:41 +0300 Subject: UBI: fix error code in ubi_io_read() When NAND detects an ECC error, it returns -EBADMSG. It does not stop reading requested data if one page has an ECC error, it keeps going and reads all the requested data. If it fails to read all the data, it does not return -EBADMSG, but returns the error code which reflects the reason of the failure. But some drivers may have bugs (e.g., OneNAND had) and stop reading after the first ECC error, so it returns -EBADMSG. In turn, UBI propagates this up to the caller. The caller will treat this as "all the requested data was read, but there was an ECC error". So we change the error code to -EIO if it is -EBADMSG and the read length is less then the requested length. We also add an assertion, so if UBI debugging is enabled, UBI will bug. Pointed-to-by: Adrian Hunter Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/io.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 7c304eec78b..db3efdef243 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -173,6 +173,16 @@ retry: ubi_err("error %d while reading %d bytes from PEB %d:%d, " "read %zd bytes", err, len, pnum, offset, read); ubi_dbg_dump_stack(); + + /* + * The driver should never return -EBADMSG if it failed to read + * all the requested data. But some buggy drivers might do + * this, so we change it to -EIO. + */ + if (read != len && err == -EBADMSG) { + ubi_assert(0); + err = -EIO; + } } else { ubi_assert(len == read); -- cgit v1.2.3 From 94780d4de2e9339ab93df63420db70f11882634d Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 4 Dec 2007 21:36:12 +0200 Subject: UBI: bugfix: allocate mandatory EBs first First allocate the necessary eraseblocks, then the optional ones. Otherwise it allocates all PEBs for bad EB handling, and fails on then following EBA LEB allocation. Reported-by: Adrian Hunter Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/eba.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 880fa369035..85f50c83cf4 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -1168,6 +1168,15 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) } } + if (ubi->avail_pebs < EBA_RESERVED_PEBS) { + ubi_err("no enough physical eraseblocks (%d, need %d)", + ubi->avail_pebs, EBA_RESERVED_PEBS); + err = -ENOSPC; + goto out_free; + } + ubi->avail_pebs -= EBA_RESERVED_PEBS; + ubi->rsvd_pebs += EBA_RESERVED_PEBS; + if (ubi->bad_allowed) { ubi_calculate_reserved(ubi); @@ -1184,15 +1193,6 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) ubi->rsvd_pebs += ubi->beb_rsvd_pebs; } - if (ubi->avail_pebs < EBA_RESERVED_PEBS) { - ubi_err("no enough physical eraseblocks (%d, need %d)", - ubi->avail_pebs, EBA_RESERVED_PEBS); - err = -ENOSPC; - goto out_free; - } - ubi->avail_pebs -= EBA_RESERVED_PEBS; - ubi->rsvd_pebs += EBA_RESERVED_PEBS; - dbg_eba("EBA unit is initialized"); return 0; -- cgit v1.2.3 From 393852ecfeec575ac78216b0eb58e4fd92f0816c Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 6 Dec 2007 18:47:30 +0200 Subject: UBI: add ubi_leb_map interface The idea of this interface belongs to Adrian Hunter. The interface is extremely useful when one has to have a guarantee that an LEB will contain all 0xFFs even in case of an unclean reboot. UBI does have an 'ubi_leb_erase()' call which may do this, but it is stupid and ineffecient, because it flushes whole queue. I should be re-worked to just be a pair of unmap, map calls. The user of the interfaci is UBIFS at the moment. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/eba.c | 13 ++++++++----- drivers/mtd/ubi/kapi.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/ubi.h | 1 + 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 85f50c83cf4..c87db07bcd0 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -656,11 +656,14 @@ retry: goto write_error; } - err = ubi_io_write_data(ubi, buf, pnum, offset, len); - if (err) { - ubi_warn("failed to write %d bytes at offset %d of LEB %d:%d, " - "PEB %d", len, offset, vol_id, lnum, pnum); - goto write_error; + if (len) { + err = ubi_io_write_data(ubi, buf, pnum, offset, len); + if (err) { + ubi_warn("failed to write %d bytes at offset %d of " + "LEB %d:%d, PEB %d", len, offset, vol_id, + lnum, pnum); + goto write_error; + } } vol->eba_tbl[lnum] = pnum; diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 03c774f4154..e1ef802a03a 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -546,6 +546,51 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) } EXPORT_SYMBOL_GPL(ubi_leb_unmap); +/** + * ubi_leb_map - map logical erasblock to a physical eraseblock. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * @dtype: expected data type + * + * This function maps an un-mapped logical eraseblock @lnum to a physical + * eraseblock. This means, that after a successfull invocation of this + * function the logical eraseblock @lnum will be empty (contain only %0xFF + * bytes) and be mapped to a physical eraseblock, even if an unclean reboot + * happens. + * + * This function returns zero in case of success, %-EBADF if the volume is + * damaged because of an interrupted update, %-EBADMSG if the logical + * eraseblock is already mapped, and other negative error codes in case of + * other failures. + */ +int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int vol_id = vol->vol_id; + + dbg_msg("unmap LEB %d:%d", vol_id, lnum); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && + dtype != UBI_UNKNOWN) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + if (vol->eba_tbl[lnum] >= 0) + return -EBADMSG; + + return ubi_eba_write_leb(ubi, vol_id, lnum, NULL, 0, 0, dtype); +} +EXPORT_SYMBOL_GPL(ubi_leb_map); + /** * ubi_is_mapped - check if logical eraseblock is mapped. * @desc: volume descriptor diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h index 3d967b6b120..c4abe035122 100644 --- a/include/linux/mtd/ubi.h +++ b/include/linux/mtd/ubi.h @@ -167,6 +167,7 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, int len, int dtype); int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum); int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum); +int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype); int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum); /* -- cgit v1.2.3 From 54b2c8f93d7c3ddc04b55666b878fec437d32983 Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Thu, 13 Dec 2007 23:53:08 +0100 Subject: UBI: silence a warning This patch silences the following warning : drivers/mtd/ubi/vmt.c:73: warning: 'ret' may be used uninitialized in this function gcc can't see that we always initialize ret in all situations where it is actually used. The one case where it's not initialized is when we BUG(), but gcc doesn't know that we won't then continue and use an uninitialized 'ret'. This patch results in code that does exactely the same as before, but it also makes gcc shut up, so we generate one less line of warning noise. Signed-off-by: Jesper Juhl Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/vmt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 88629a320c2..e44948de7dd 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -70,13 +70,14 @@ static struct device_attribute attr_vol_upd_marker = static ssize_t vol_attribute_show(struct device *dev, struct device_attribute *attr, char *buf) { - int ret; + int ret = -ENODEV; + struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); spin_lock(&vol->ubi->volumes_lock); if (vol->removed) { spin_unlock(&vol->ubi->volumes_lock); - return -ENODEV; + return ret; } if (attr == &attr_vol_reserved_ebs) ret = sprintf(buf, "%d\n", vol->reserved_pebs); -- cgit v1.2.3 From 732aeacff6b2fcf3750cad9018bdd663a21a6a12 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sat, 15 Dec 2007 15:09:07 +0200 Subject: UBI: minor tidy-ups Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/vmt.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index e44948de7dd..58d6abe50bf 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -71,7 +71,6 @@ static ssize_t vol_attribute_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret = -ENODEV; - struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); spin_lock(&vol->ubi->volumes_lock); @@ -79,6 +78,7 @@ static ssize_t vol_attribute_show(struct device *dev, spin_unlock(&vol->ubi->volumes_lock); return ret; } + if (attr == &attr_vol_reserved_ebs) ret = sprintf(buf, "%d\n", vol->reserved_pebs); else if (attr == &attr_vol_type) { @@ -95,9 +95,9 @@ static ssize_t vol_attribute_show(struct device *dev, ret = sprintf(buf, "%d\n", vol->corrupted); else if (attr == &attr_vol_alignment) ret = sprintf(buf, "%d\n", vol->alignment); - else if (attr == &attr_vol_usable_eb_size) { + else if (attr == &attr_vol_usable_eb_size) ret = sprintf(buf, "%d\n", vol->usable_leb_size); - } else if (attr == &attr_vol_data_bytes) + else if (attr == &attr_vol_data_bytes) ret = sprintf(buf, "%lld\n", vol->used_bytes); else if (attr == &attr_vol_upd_marker) ret = sprintf(buf, "%d\n", vol->upd_marker); @@ -111,6 +111,7 @@ static ssize_t vol_attribute_show(struct device *dev, static void vol_release(struct device *dev) { struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); + ubi_assert(vol->removed); kfree(vol); } -- cgit v1.2.3 From 49dfc299288fe183b62a3f679a40c91b482d6d73 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sat, 15 Dec 2007 18:13:56 +0200 Subject: UBI: remove redundant field Remove redundant ubi->major field - we have it in ubi->cdev.dev already. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 12 +++++------- drivers/mtd/ubi/cdev.c | 2 +- drivers/mtd/ubi/kapi.c | 4 ++-- drivers/mtd/ubi/ubi.h | 2 -- drivers/mtd/ubi/vmt.c | 12 ++++++++---- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 023653977a1..b0791f79505 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -150,7 +150,7 @@ static int ubi_sysfs_init(struct ubi_device *ubi) int err; ubi->dev.release = dev_release; - ubi->dev.devt = MKDEV(ubi->major, 0); + ubi->dev.devt = ubi->cdev.dev; ubi->dev.class = ubi_class; sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num); err = device_register(&ubi->dev); @@ -278,12 +278,11 @@ static int uif_init(struct ubi_device *ubi) return err; } + ubi_assert(MINOR(dev) == 0); cdev_init(&ubi->cdev, &ubi_cdev_operations); - ubi->major = MAJOR(dev); - dbg_msg("%s major is %u", ubi->ubi_name, ubi->major); + dbg_msg("%s major is %u", ubi->ubi_name, MAJOR(dev)); ubi->cdev.owner = THIS_MODULE; - dev = MKDEV(ubi->major, 0); err = cdev_add(&ubi->cdev, dev, 1); if (err) { ubi_err("cannot add character device %s", ubi->ubi_name); @@ -309,8 +308,7 @@ out_volumes: out_cdev: cdev_del(&ubi->cdev); out_unreg: - unregister_chrdev_region(MKDEV(ubi->major, 0), - ubi->vtbl_slots + 1); + unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); return err; } @@ -323,7 +321,7 @@ static void uif_close(struct ubi_device *ubi) kill_volumes(ubi); ubi_sysfs_close(ubi); cdev_del(&ubi->cdev); - unregister_chrdev_region(MKDEV(ubi->major, 0), ubi->vtbl_slots + 1); + unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); } /** diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index fe4da1e96c5..9771e7f410c 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -61,7 +61,7 @@ static struct ubi_device *major_to_device(int major) int i; for (i = 0; i < ubi_devices_cnt; i++) - if (ubi_devices[i] && ubi_devices[i]->major == major) + if (ubi_devices[i] && MAJOR(ubi_devices[i]->cdev.dev) == major) return ubi_devices[i]; BUG(); return NULL; diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index e1ef802a03a..3bf2c951c2e 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -46,7 +46,7 @@ int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) di->leb_size = ubi->leb_size; di->min_io_size = ubi->min_io_size; di->ro_mode = ubi->ro_mode; - di->cdev = MKDEV(ubi->major, 0); + di->cdev = ubi->cdev.dev; return 0; } EXPORT_SYMBOL_GPL(ubi_get_device_info); @@ -73,7 +73,7 @@ void ubi_get_volume_info(struct ubi_volume_desc *desc, vi->usable_leb_size = vol->usable_leb_size; vi->name_len = vol->name_len; vi->name = vol->name; - vi->cdev = MKDEV(ubi->major, vi->vol_id + 1); + vi->cdev = vol->cdev.dev; } EXPORT_SYMBOL_GPL(ubi_get_volume_info); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 5e941a63303..318ce2543fb 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -204,7 +204,6 @@ struct ubi_wl_entry; * @cdev: character device object to create character device * @ubi_num: UBI device number * @ubi_name: UBI device name - * @major: character device major number * @vol_count: number of volumes in this UBI device * @volumes: volumes of this UBI device * @volumes_lock: protects @volumes, @rsvd_pebs, @avail_pebs, beb_rsvd_pebs, @@ -287,7 +286,6 @@ struct ubi_device { struct device dev; int ubi_num; char ubi_name[sizeof(UBI_NAME_STR)+5]; - int major; int vol_count; struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT]; spinlock_t volumes_lock; diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 58d6abe50bf..6609c319c83 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -192,6 +192,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) struct ubi_volume *vol; struct ubi_vtbl_record vtbl_rec; uint64_t bytes; + dev_t dev; if (ubi->ro_mode) return -EROFS; @@ -301,7 +302,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) /* Register character device for the volume */ cdev_init(&vol->cdev, &ubi_vol_cdev_operations); vol->cdev.owner = THIS_MODULE; - err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol_id + 1), 1); + dev = MKDEV(MAJOR(ubi->cdev.dev), vol_id + 1); + err = cdev_add(&vol->cdev, dev, 1); if (err) { ubi_err("cannot add character device for volume %d", vol_id); goto out_mapping; @@ -313,7 +315,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) vol->dev.release = vol_release; vol->dev.parent = &ubi->dev; - vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1); + vol->dev.devt = dev; vol->dev.class = ubi_class; sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); err = device_register(&vol->dev); @@ -576,6 +578,7 @@ out_free: int ubi_add_volume(struct ubi_device *ubi, int vol_id) { int err; + dev_t dev; struct ubi_volume *vol = ubi->volumes[vol_id]; dbg_msg("add volume %d", vol_id); @@ -585,7 +588,8 @@ int ubi_add_volume(struct ubi_device *ubi, int vol_id) /* Register character device for the volume */ cdev_init(&vol->cdev, &ubi_vol_cdev_operations); vol->cdev.owner = THIS_MODULE; - err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol->vol_id + 1), 1); + dev = MKDEV(MAJOR(ubi->cdev.dev), vol->vol_id + 1); + err = cdev_add(&vol->cdev, dev, 1); if (err) { ubi_err("cannot add character device for volume %d", vol_id); return err; @@ -597,7 +601,7 @@ int ubi_add_volume(struct ubi_device *ubi, int vol_id) vol->dev.release = vol_release; vol->dev.parent = &ubi->dev; - vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1); + vol->dev.devt = dev; vol->dev.class = ubi_class; sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); err = device_register(&vol->dev); -- cgit v1.2.3 From 01f7b309e453dc8499c318f6810f76b606b66134 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sat, 15 Dec 2007 19:56:51 +0200 Subject: UBI: improve error messages Always print error code with error messages, sometimes it is extremely helpful info. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 13 +++++++++---- drivers/mtd/ubi/cdev.c | 3 ++- drivers/mtd/ubi/vmt.c | 11 ++++++++--- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index b0791f79505..5490a73deca 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -211,7 +211,8 @@ out_eraseblock_size: out_unregister: device_unregister(&ubi->dev); out: - ubi_err("failed to initialize sysfs for %s", ubi->ubi_name); + ubi_err("failed to initialize sysfs for %s, error %d", + ubi->ubi_name, err); return err; } @@ -285,7 +286,7 @@ static int uif_init(struct ubi_device *ubi) err = cdev_add(&ubi->cdev, dev, 1); if (err) { - ubi_err("cannot add character device %s", ubi->ubi_name); + ubi_err("cannot add character device"); goto out_unreg; } @@ -296,8 +297,10 @@ static int uif_init(struct ubi_device *ubi) for (i = 0; i < ubi->vtbl_slots; i++) if (ubi->volumes[i]) { err = ubi_add_volume(ubi, i); - if (err) + if (err) { + ubi_err("cannot add volume %d", i); goto out_volumes; + } } return 0; @@ -309,6 +312,7 @@ out_cdev: cdev_del(&ubi->cdev); out_unreg: unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); + ubi_err("cannot initialize UBI %s, error %d", ubi->ubi_name, err); return err; } @@ -422,7 +426,8 @@ static int io_init(struct ubi_device *ubi) /* Make sure minimal I/O unit is power of 2 */ if (!is_power_of_2(ubi->min_io_size)) { - ubi_err("bad min. I/O unit"); + ubi_err("min. I/O unit (%d) is not power of 2", + ubi->min_io_size); return -EINVAL; } diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 9771e7f410c..12777da47d3 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -377,7 +377,8 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, err = ubi_more_update_data(ubi, vol->vol_id, buf, count); if (err < 0) { - ubi_err("cannot write %zd bytes of update data", count); + ubi_err("cannot write %zd bytes of update data, error %d", + count, err); return err; } diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 6609c319c83..787ce9ec17a 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -305,7 +305,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) dev = MKDEV(MAJOR(ubi->cdev.dev), vol_id + 1); err = cdev_add(&vol->cdev, dev, 1); if (err) { - ubi_err("cannot add character device for volume %d", vol_id); + ubi_err("cannot add character device"); goto out_mapping; } @@ -319,8 +319,10 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) vol->dev.class = ubi_class; sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); err = device_register(&vol->dev); - if (err) + if (err) { + ubi_err("cannot register device"); goto out_gluebi; + } err = volume_sysfs_init(ubi, vol); if (err) @@ -364,6 +366,7 @@ out_acc: out_unlock: spin_unlock(&ubi->volumes_lock); kfree(vol); + ubi_err("cannot create volume %d, error %d", vol_id, err); return err; /* @@ -380,6 +383,7 @@ out_sysfs: ubi->volumes[vol_id] = NULL; spin_unlock(&ubi->volumes_lock); volume_sysfs_close(vol); + ubi_err("cannot create volume %d, error %d", vol_id, err); return err; } @@ -591,7 +595,8 @@ int ubi_add_volume(struct ubi_device *ubi, int vol_id) dev = MKDEV(MAJOR(ubi->cdev.dev), vol->vol_id + 1); err = cdev_add(&vol->cdev, dev, 1); if (err) { - ubi_err("cannot add character device for volume %d", vol_id); + ubi_err("cannot add character device for volume %d, error %d", + vol_id, err); return err; } -- cgit v1.2.3 From 3a8d4642861fb69b62401949e490c0bcb19ceb40 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 16 Dec 2007 12:32:51 +0200 Subject: UBI: create ltree_entry slab on initialization Since the ltree_entry slab cache is a global entity, which is used by all UBI devices, it is more logical to create it on module initialization time and destro on module exit time. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 26 +++++++++++++++ drivers/mtd/ubi/eba.c | 85 +++++++++++-------------------------------------- drivers/mtd/ubi/ubi.h | 23 +++++++++++++ 3 files changed, 67 insertions(+), 67 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 5490a73deca..44c852144a9 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -67,6 +67,9 @@ struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; /* Root UBI "class" object (corresponds to '//class/ubi/') */ struct class *ubi_class; +/* Slab cache for lock-tree entries */ +struct kmem_cache *ubi_ltree_slab; + /* "Show" method for files in '//class/ubi/' */ static ssize_t ubi_version_show(struct class *class, char *buf) { @@ -687,6 +690,20 @@ static void detach_mtd_dev(struct ubi_device *ubi) ubi_msg("mtd%d is detached from ubi%d", mtd_num, ubi_num); } +/** + * ltree_entry_ctor - lock tree entries slab cache constructor. + * @obj: the lock-tree entry to construct + * @cache: the lock tree entry slab cache + * @flags: constructor flags + */ +static void ltree_entry_ctor(struct kmem_cache *cache, void *obj) +{ + struct ubi_ltree_entry *le = obj; + + le->users = 0; + init_rwsem(&le->mutex); +} + static int __init ubi_init(void) { int err, i, k; @@ -709,6 +726,12 @@ static int __init ubi_init(void) if (err) goto out_class; + ubi_ltree_slab = kmem_cache_create("ubi_ltree_slab", + sizeof(struct ubi_ltree_entry), 0, + 0, <ree_entry_ctor); + if (!ubi_ltree_slab) + goto out_version; + /* Attach MTD devices */ for (i = 0; i < mtd_devs; i++) { struct mtd_dev_param *p = &mtd_dev_param[i]; @@ -724,6 +747,8 @@ static int __init ubi_init(void) out_detach: for (k = 0; k < i; k++) detach_mtd_dev(ubi_devices[k]); + kmem_cache_destroy(ubi_ltree_slab); +out_version: class_remove_file(ubi_class, &ubi_version); out_class: class_destroy(ubi_class); @@ -737,6 +762,7 @@ static void __exit ubi_exit(void) for (i = 0; i < n; i++) detach_mtd_dev(ubi_devices[i]); + kmem_cache_destroy(ubi_ltree_slab); class_remove_file(ubi_class, &ubi_version); class_destroy(ubi_class); } diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index c87db07bcd0..5fdb31bc563 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -31,7 +31,7 @@ * logical eraseblock it is locked for reading or writing. The per-logical * eraseblock locking is implemented by means of the lock tree. The lock tree * is an RB-tree which refers all the currently locked logical eraseblocks. The - * lock tree elements are &struct ltree_entry objects. They are indexed by + * lock tree elements are &struct ubi_ltree_entry objects. They are indexed by * (@vol_id, @lnum) pairs. * * EBA also maintains the global sequence counter which is incremented each @@ -49,29 +49,6 @@ /* Number of physical eraseblocks reserved for atomic LEB change operation */ #define EBA_RESERVED_PEBS 1 -/** - * struct ltree_entry - an entry in the lock tree. - * @rb: links RB-tree nodes - * @vol_id: volume ID of the locked logical eraseblock - * @lnum: locked logical eraseblock number - * @users: how many tasks are using this logical eraseblock or wait for it - * @mutex: read/write mutex to implement read/write access serialization to - * the (@vol_id, @lnum) logical eraseblock - * - * When a logical eraseblock is being locked - corresponding &struct ltree_entry - * object is inserted to the lock tree (@ubi->ltree). - */ -struct ltree_entry { - struct rb_node rb; - int vol_id; - int lnum; - int users; - struct rw_semaphore mutex; -}; - -/* Slab cache for lock-tree entries */ -static struct kmem_cache *ltree_slab; - /** * next_sqnum - get next sequence number. * @ubi: UBI device description object @@ -112,20 +89,20 @@ static int ubi_get_compat(const struct ubi_device *ubi, int vol_id) * @vol_id: volume ID * @lnum: logical eraseblock number * - * This function returns a pointer to the corresponding &struct ltree_entry + * This function returns a pointer to the corresponding &struct ubi_ltree_entry * object if the logical eraseblock is locked and %NULL if it is not. * @ubi->ltree_lock has to be locked. */ -static struct ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id, - int lnum) +static struct ubi_ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id, + int lnum) { struct rb_node *p; p = ubi->ltree.rb_node; while (p) { - struct ltree_entry *le; + struct ubi_ltree_entry *le; - le = rb_entry(p, struct ltree_entry, rb); + le = rb_entry(p, struct ubi_ltree_entry, rb); if (vol_id < le->vol_id) p = p->rb_left; @@ -155,12 +132,12 @@ static struct ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id, * Returns pointer to the lock tree entry or %-ENOMEM if memory allocation * failed. */ -static struct ltree_entry *ltree_add_entry(struct ubi_device *ubi, int vol_id, - int lnum) +static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi, + int vol_id, int lnum) { - struct ltree_entry *le, *le1, *le_free; + struct ubi_ltree_entry *le, *le1, *le_free; - le = kmem_cache_alloc(ltree_slab, GFP_NOFS); + le = kmem_cache_alloc(ubi_ltree_slab, GFP_NOFS); if (!le) return ERR_PTR(-ENOMEM); @@ -189,7 +166,7 @@ static struct ltree_entry *ltree_add_entry(struct ubi_device *ubi, int vol_id, p = &ubi->ltree.rb_node; while (*p) { parent = *p; - le1 = rb_entry(parent, struct ltree_entry, rb); + le1 = rb_entry(parent, struct ubi_ltree_entry, rb); if (vol_id < le1->vol_id) p = &(*p)->rb_left; @@ -211,7 +188,7 @@ static struct ltree_entry *ltree_add_entry(struct ubi_device *ubi, int vol_id, spin_unlock(&ubi->ltree_lock); if (le_free) - kmem_cache_free(ltree_slab, le_free); + kmem_cache_free(ubi_ltree_slab, le_free); return le; } @@ -227,7 +204,7 @@ static struct ltree_entry *ltree_add_entry(struct ubi_device *ubi, int vol_id, */ static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum) { - struct ltree_entry *le; + struct ubi_ltree_entry *le; le = ltree_add_entry(ubi, vol_id, lnum); if (IS_ERR(le)) @@ -245,7 +222,7 @@ static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum) static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum) { int free = 0; - struct ltree_entry *le; + struct ubi_ltree_entry *le; spin_lock(&ubi->ltree_lock); le = ltree_lookup(ubi, vol_id, lnum); @@ -259,7 +236,7 @@ static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum) up_read(&le->mutex); if (free) - kmem_cache_free(ltree_slab, le); + kmem_cache_free(ubi_ltree_slab, le); } /** @@ -273,7 +250,7 @@ static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum) */ static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum) { - struct ltree_entry *le; + struct ubi_ltree_entry *le; le = ltree_add_entry(ubi, vol_id, lnum); if (IS_ERR(le)) @@ -291,7 +268,7 @@ static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum) static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) { int free; - struct ltree_entry *le; + struct ubi_ltree_entry *le; spin_lock(&ubi->ltree_lock); le = ltree_lookup(ubi, vol_id, lnum); @@ -306,7 +283,7 @@ static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) up_write(&le->mutex); if (free) - kmem_cache_free(ltree_slab, le); + kmem_cache_free(ubi_ltree_slab, le); } /** @@ -930,20 +907,6 @@ write_error: goto retry; } -/** - * ltree_entry_ctor - lock tree entries slab cache constructor. - * @obj: the lock-tree entry to construct - * @cache: the lock tree entry slab cache - * @flags: constructor flags - */ -static void ltree_entry_ctor(struct kmem_cache *cache, void *obj) -{ - struct ltree_entry *le = obj; - - le->users = 0; - init_rwsem(&le->mutex); -} - /** * ubi_eba_copy_leb - copy logical eraseblock. * @ubi: UBI device description object @@ -1128,14 +1091,6 @@ int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) mutex_init(&ubi->alc_mutex); ubi->ltree = RB_ROOT; - if (ubi_devices_cnt == 0) { - ltree_slab = kmem_cache_create("ubi_ltree_slab", - sizeof(struct ltree_entry), 0, - 0, <ree_entry_ctor); - if (!ltree_slab) - return -ENOMEM; - } - ubi->global_sqnum = si->max_sqnum + 1; num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; @@ -1205,8 +1160,6 @@ out_free: continue; kfree(ubi->volumes[i]->eba_tbl); } - if (ubi_devices_cnt == 0) - kmem_cache_destroy(ltree_slab); return err; } @@ -1225,6 +1178,4 @@ void ubi_eba_close(const struct ubi_device *ubi) continue; kfree(ubi->volumes[i]->eba_tbl); } - if (ubi_devices_cnt == 1) - kmem_cache_destroy(ltree_slab); } diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 318ce2543fb..0f2ea81b312 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -97,6 +97,28 @@ enum { extern int ubi_devices_cnt; extern struct ubi_device *ubi_devices[]; +/** + * struct ubi_ltree_entry - an entry in the lock tree. + * @rb: links RB-tree nodes + * @vol_id: volume ID of the locked logical eraseblock + * @lnum: locked logical eraseblock number + * @users: how many tasks are using this logical eraseblock or wait for it + * @mutex: read/write mutex to implement read/write access serialization to + * the (@vol_id, @lnum) logical eraseblock + * + * This data structure is used in the EBA unit to implement per-LEB locking. + * When a logical eraseblock is being locked - corresponding + * &struct ubi_ltree_entry object is inserted to the lock tree (@ubi->ltree). + * See EBA unit for details. + */ +struct ubi_ltree_entry { + struct rb_node rb; + int vol_id; + int lnum; + int users; + struct rw_semaphore mutex; +}; + struct ubi_volume_desc; /** @@ -359,6 +381,7 @@ struct ubi_device { #endif }; +extern struct kmem_cache *ubi_ltree_slab; extern struct file_operations ubi_cdev_operations; extern struct file_operations ubi_vol_cdev_operations; extern struct class *ubi_class; -- cgit v1.2.3 From 06b68ba15671f32a3aa3bbddf04b0d2dd7fbf902 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 16 Dec 2007 12:49:01 +0200 Subject: UBI: create ubi_wl_entry slab on initialization Similarly to ltree_entry_slab, it makes more sense to create and destroy ubi_wl_entry slab on module initialization/exit. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 13 +++++++++++ drivers/mtd/ubi/ubi.h | 17 +++++++++++++++ drivers/mtd/ubi/wl.c | 58 ++++++++++++------------------------------------- 3 files changed, 44 insertions(+), 44 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 44c852144a9..7f6820becf1 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -70,6 +70,10 @@ struct class *ubi_class; /* Slab cache for lock-tree entries */ struct kmem_cache *ubi_ltree_slab; +/* Slab cache for wear-leveling entries */ +struct kmem_cache *ubi_wl_entry_slab; + + /* "Show" method for files in '//class/ubi/' */ static ssize_t ubi_version_show(struct class *class, char *buf) { @@ -732,6 +736,12 @@ static int __init ubi_init(void) if (!ubi_ltree_slab) goto out_version; + ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", + sizeof(struct ubi_wl_entry), + 0, 0, NULL); + if (!ubi_wl_entry_slab) + goto out_ltree; + /* Attach MTD devices */ for (i = 0; i < mtd_devs; i++) { struct mtd_dev_param *p = &mtd_dev_param[i]; @@ -747,6 +757,8 @@ static int __init ubi_init(void) out_detach: for (k = 0; k < i; k++) detach_mtd_dev(ubi_devices[k]); + kmem_cache_destroy(ubi_wl_entry_slab); +out_ltree: kmem_cache_destroy(ubi_ltree_slab); out_version: class_remove_file(ubi_class, &ubi_version); @@ -762,6 +774,7 @@ static void __exit ubi_exit(void) for (i = 0; i < n; i++) detach_mtd_dev(ubi_devices[i]); + kmem_cache_destroy(ubi_wl_entry_slab); kmem_cache_destroy(ubi_ltree_slab); class_remove_file(ubi_class, &ubi_version); class_destroy(ubi_class); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 0f2ea81b312..b7c93173e77 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -97,6 +97,22 @@ enum { extern int ubi_devices_cnt; extern struct ubi_device *ubi_devices[]; +/** + * struct ubi_wl_entry - wear-leveling entry. + * @rb: link in the corresponding RB-tree + * @ec: erase counter + * @pnum: physical eraseblock number + * + * This data structure is used in the WL unit. Each physical eraseblock has a + * corresponding &struct wl_entry object which may be kept in different + * RB-trees. See WL unit for details. + */ +struct ubi_wl_entry { + struct rb_node rb; + int ec; + int pnum; +}; + /** * struct ubi_ltree_entry - an entry in the lock tree. * @rb: links RB-tree nodes @@ -382,6 +398,7 @@ struct ubi_device { }; extern struct kmem_cache *ubi_ltree_slab; +extern struct kmem_cache *ubi_wl_entry_slab; extern struct file_operations ubi_cdev_operations; extern struct file_operations ubi_vol_cdev_operations; extern struct class *ubi_class; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 6330c8cc72b..a405d40faa2 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -116,21 +116,6 @@ */ #define WL_MAX_FAILURES 32 -/** - * struct ubi_wl_entry - wear-leveling entry. - * @rb: link in the corresponding RB-tree - * @ec: erase counter - * @pnum: physical eraseblock number - * - * Each physical eraseblock has a corresponding &struct wl_entry object which - * may be kept in different RB-trees. - */ -struct ubi_wl_entry { - struct rb_node rb; - int ec; - int pnum; -}; - /** * struct ubi_wl_prot_entry - PEB protection entry. * @rb_pnum: link in the @wl->prot.pnum RB-tree @@ -216,9 +201,6 @@ static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, #define paranoid_check_in_wl_tree(e, root) #endif -/* Slab cache for wear-leveling entries */ -static struct kmem_cache *wl_entries_slab; - /** * wl_tree_add - add a wear-leveling entry to a WL RB-tree. * @e: the wear-leveling entry to add @@ -878,14 +860,14 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, dbg_wl("PEB %d was put meanwhile, erase", e2->pnum); err = schedule_erase(ubi, e2, 0); if (err) { - kmem_cache_free(wl_entries_slab, e2); + kmem_cache_free(ubi_wl_entry_slab, e2); ubi_ro_mode(ubi); } } err = schedule_erase(ubi, e1, 0); if (err) { - kmem_cache_free(wl_entries_slab, e1); + kmem_cache_free(ubi_wl_entry_slab, e1); ubi_ro_mode(ubi); } @@ -920,14 +902,14 @@ error: dbg_wl("PEB %d was put meanwhile, erase", e1->pnum); err = schedule_erase(ubi, e1, 0); if (err) { - kmem_cache_free(wl_entries_slab, e1); + kmem_cache_free(ubi_wl_entry_slab, e1); ubi_ro_mode(ubi); } } err = schedule_erase(ubi, e2, 0); if (err) { - kmem_cache_free(wl_entries_slab, e2); + kmem_cache_free(ubi_wl_entry_slab, e2); ubi_ro_mode(ubi); } @@ -1020,7 +1002,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, if (cancel) { dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec); kfree(wl_wrk); - kmem_cache_free(wl_entries_slab, e); + kmem_cache_free(ubi_wl_entry_slab, e); return 0; } @@ -1049,7 +1031,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, ubi_err("failed to erase PEB %d, error %d", pnum, err); kfree(wl_wrk); - kmem_cache_free(wl_entries_slab, e); + kmem_cache_free(ubi_wl_entry_slab, e); if (err == -EINTR || err == -ENOMEM || err == -EAGAIN || err == -EBUSY) { @@ -1294,7 +1276,7 @@ static void tree_destroy(struct rb_root *root) rb->rb_right = NULL; } - kmem_cache_free(wl_entries_slab, e); + kmem_cache_free(ubi_wl_entry_slab, e); } } } @@ -1407,14 +1389,6 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) return err; } - if (ubi_devices_cnt == 0) { - wl_entries_slab = kmem_cache_create("ubi_wl_entry_slab", - sizeof(struct ubi_wl_entry), - 0, 0, NULL); - if (!wl_entries_slab) - return -ENOMEM; - } - err = -ENOMEM; ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL); if (!ubi->lookuptbl) @@ -1423,7 +1397,7 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) list_for_each_entry_safe(seb, tmp, &si->erase, u.list) { cond_resched(); - e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); if (!e) goto out_free; @@ -1431,7 +1405,7 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) e->ec = seb->ec; ubi->lookuptbl[e->pnum] = e; if (schedule_erase(ubi, e, 0)) { - kmem_cache_free(wl_entries_slab, e); + kmem_cache_free(ubi_wl_entry_slab, e); goto out_free; } } @@ -1439,7 +1413,7 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) list_for_each_entry(seb, &si->free, u.list) { cond_resched(); - e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); if (!e) goto out_free; @@ -1453,7 +1427,7 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) list_for_each_entry(seb, &si->corr, u.list) { cond_resched(); - e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); if (!e) goto out_free; @@ -1461,7 +1435,7 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) e->ec = seb->ec; ubi->lookuptbl[e->pnum] = e; if (schedule_erase(ubi, e, 0)) { - kmem_cache_free(wl_entries_slab, e); + kmem_cache_free(ubi_wl_entry_slab, e); goto out_free; } } @@ -1470,7 +1444,7 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { cond_resched(); - e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + e = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); if (!e) goto out_free; @@ -1510,8 +1484,6 @@ out_free: tree_destroy(&ubi->free); tree_destroy(&ubi->scrub); kfree(ubi->lookuptbl); - if (ubi_devices_cnt == 0) - kmem_cache_destroy(wl_entries_slab); return err; } @@ -1541,7 +1513,7 @@ static void protection_trees_destroy(struct ubi_device *ubi) rb->rb_right = NULL; } - kmem_cache_free(wl_entries_slab, pe->e); + kmem_cache_free(ubi_wl_entry_slab, pe->e); kfree(pe); } } @@ -1565,8 +1537,6 @@ void ubi_wl_close(struct ubi_device *ubi) tree_destroy(&ubi->free); tree_destroy(&ubi->scrub); kfree(ubi->lookuptbl); - if (ubi_devices_cnt == 1) - kmem_cache_destroy(wl_entries_slab); } #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID -- cgit v1.2.3 From b96bf4c33d4860bf1584ad2f9ed3b783d79aada8 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 16 Dec 2007 13:01:03 +0200 Subject: UBI: remove ubi_devices_cnt This global variablea is not really needed, remove it Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 42 +++++++++++++++++++++++++----------------- drivers/mtd/ubi/cdev.c | 2 +- drivers/mtd/ubi/ubi.h | 4 +--- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 7f6820becf1..9b94427be14 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -58,9 +58,6 @@ static int mtd_devs = 0; /* MTD devices specification parameters */ static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; -/* Number of UBI devices in system */ -int ubi_devices_cnt; - /* All UBI devices in system */ struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; @@ -566,26 +563,39 @@ static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, } /* Check if we already have the same MTD device attached */ - for (i = 0; i < ubi_devices_cnt; i++) - if (ubi_devices[i]->mtd->index == mtd->index) { + for (i = 0; i < UBI_MAX_DEVICES; i++) + ubi = ubi_devices[i]; + if (ubi && ubi->mtd->index == mtd->index) { ubi_err("mtd%d is already attached to ubi%d", mtd->index, i); err = -EINVAL; goto out_mtd; } - ubi = ubi_devices[ubi_devices_cnt] = kzalloc(sizeof(struct ubi_device), - GFP_KERNEL); + ubi = kzalloc(sizeof(struct ubi_device), GFP_KERNEL); if (!ubi) { err = -ENOMEM; goto out_mtd; } - ubi->ubi_num = ubi_devices_cnt; ubi->mtd = mtd; + /* Search for an empty slot in the @ubi_devices array */ + ubi->ubi_num = -1; + for (i = 0; i < UBI_MAX_DEVICES; i++) + if (!ubi_devices[i]) { + ubi->ubi_num = i; + break; + } + + if (ubi->ubi_num == -1) { + ubi_err("only %d UBI devices may be created", UBI_MAX_DEVICES); + err = -ENFILE; + goto out_free; + } + dbg_msg("attaching mtd%d to ubi%d: VID header offset %d data offset %d", - ubi->mtd->index, ubi_devices_cnt, vid_hdr_offset, data_offset); + ubi->mtd->index, ubi->ubi_num, vid_hdr_offset, data_offset); ubi->vid_hdr_offset = vid_hdr_offset; ubi->leb_start = data_offset; @@ -619,7 +629,7 @@ static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, if (err) goto out_detach; - ubi_msg("attached mtd%d to ubi%d", ubi->mtd->index, ubi_devices_cnt); + ubi_msg("attached mtd%d to ubi%d", ubi->mtd->index, ubi->ubi_num); ubi_msg("MTD device name: \"%s\"", ubi->mtd->name); ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); ubi_msg("physical eraseblock size: %d bytes (%d KiB)", @@ -648,7 +658,7 @@ static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, wake_up_process(ubi->bgt_thread); } - ubi_devices_cnt += 1; + ubi_devices[ubi->ubi_num] = ubi; return 0; out_detach: @@ -664,7 +674,6 @@ out_free: kfree(ubi); out_mtd: put_mtd_device(mtd); - ubi_devices[ubi_devices_cnt] = NULL; return err; } @@ -689,8 +698,6 @@ static void detach_mtd_dev(struct ubi_device *ubi) #endif kfree(ubi_devices[ubi_num]); ubi_devices[ubi_num] = NULL; - ubi_devices_cnt -= 1; - ubi_assert(ubi_devices_cnt >= 0); ubi_msg("mtd%d is detached from ubi%d", mtd_num, ubi_num); } @@ -770,10 +777,11 @@ module_init(ubi_init); static void __exit ubi_exit(void) { - int i, n = ubi_devices_cnt; + int i; - for (i = 0; i < n; i++) - detach_mtd_dev(ubi_devices[i]); + for (i = 0; i < UBI_MAX_DEVICES; i++) + if (ubi_devices[i]) + detach_mtd_dev(ubi_devices[i]); kmem_cache_destroy(ubi_wl_entry_slab); kmem_cache_destroy(ubi_ltree_slab); class_remove_file(ubi_class, &ubi_version); diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 12777da47d3..7697eda2d58 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -60,7 +60,7 @@ static struct ubi_device *major_to_device(int major) { int i; - for (i = 0; i < ubi_devices_cnt; i++) + for (i = 0; i < UBI_MAX_DEVICES; i++) if (ubi_devices[i] && MAJOR(ubi_devices[i]->cdev.dev) == major) return ubi_devices[i]; BUG(); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index b7c93173e77..23875bf6aa3 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -94,9 +94,6 @@ enum { UBI_IO_BITFLIPS }; -extern int ubi_devices_cnt; -extern struct ubi_device *ubi_devices[]; - /** * struct ubi_wl_entry - wear-leveling entry. * @rb: link in the corresponding RB-tree @@ -401,6 +398,7 @@ extern struct kmem_cache *ubi_ltree_slab; extern struct kmem_cache *ubi_wl_entry_slab; extern struct file_operations ubi_cdev_operations; extern struct file_operations ubi_vol_cdev_operations; +extern struct ubi_device *ubi_devices[]; extern struct class *ubi_class; /* vtbl.c */ -- cgit v1.2.3 From 77c722dde9975361051c5530475f8f92ed67a506 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 16 Dec 2007 16:46:57 +0200 Subject: UBI: bugfix: dont oops with NULL module parameter E.g., it oopsed in case of: modprobe ubi mtd = 0 Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 9b94427be14..b85ca186afc 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -845,6 +845,9 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) char *pbuf = &buf[0]; char *tokens[3] = {NULL, NULL, NULL}; + if (!val) + return -EINVAL; + if (mtd_devs == UBI_MAX_DEVICES) { printk("UBI error: too many parameters, max. is %d\n", UBI_MAX_DEVICES); -- cgit v1.2.3 From 89b96b69290668351a33b09372ec1c94cb5748e5 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 16 Dec 2007 20:00:38 +0200 Subject: UBI: improve internal interfaces Pass volume description object to the EBA function which makes more sense, and EBA function do not have to find the volume description object by volume ID. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 4 ++-- drivers/mtd/ubi/cdev.c | 6 +++--- drivers/mtd/ubi/eba.c | 51 +++++++++++++++++++++++------------------------- drivers/mtd/ubi/gluebi.c | 9 ++++----- drivers/mtd/ubi/kapi.c | 12 ++++++------ drivers/mtd/ubi/misc.c | 2 +- drivers/mtd/ubi/ubi.h | 21 ++++++++++---------- drivers/mtd/ubi/upd.c | 7 +++---- drivers/mtd/ubi/vmt.c | 22 ++++++++++----------- drivers/mtd/ubi/vtbl.c | 6 ++++-- 10 files changed, 68 insertions(+), 72 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index b85ca186afc..5d00364d4a4 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -249,7 +249,7 @@ static void kill_volumes(struct ubi_device *ubi) for (i = 0; i < ubi->vtbl_slots; i++) if (ubi->volumes[i]) - ubi_free_volume(ubi, i); + ubi_free_volume(ubi, ubi->volumes[i]); } /** @@ -300,7 +300,7 @@ static int uif_init(struct ubi_device *ubi) for (i = 0; i < ubi->vtbl_slots; i++) if (ubi->volumes[i]) { - err = ubi_add_volume(ubi, i); + err = ubi_add_volume(ubi, ubi->volumes[i]); if (err) { ubi_err("cannot add volume %d", i); goto out_volumes; diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 7697eda2d58..24344ba3cfe 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -249,7 +249,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, if (off + len >= vol->usable_leb_size) len = vol->usable_leb_size - off; - err = ubi_eba_read_leb(ubi, vol_id, lnum, tbuf, off, len, 0); + err = ubi_eba_read_leb(ubi, vol, lnum, tbuf, off, len, 0); if (err) break; @@ -339,7 +339,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, break; } - err = ubi_eba_write_leb(ubi, vol_id, lnum, tbuf, off, len, + err = ubi_eba_write_leb(ubi, vol, lnum, tbuf, off, len, UBI_UNKNOWN); if (err) break; @@ -484,7 +484,7 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, } dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); - err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum); + err = ubi_eba_unmap_leb(ubi, vol, lnum); if (err) break; diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 5fdb31bc563..b2b0f29bdc5 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -289,17 +289,17 @@ static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) /** * ubi_eba_unmap_leb - un-map logical eraseblock. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @lnum: logical eraseblock number * * This function un-maps logical eraseblock @lnum and schedules corresponding * physical eraseblock for erasure. Returns zero in case of success and a * negative error code in case of failure. */ -int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum) +int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum) { - int idx = vol_id2idx(ubi, vol_id), err, pnum; - struct ubi_volume *vol = ubi->volumes[idx]; + int err, pnum, vol_id = vol->vol_id; if (ubi->ro_mode) return -EROFS; @@ -326,7 +326,7 @@ out_unlock: /** * ubi_eba_read_leb - read data. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @lnum: logical eraseblock number * @buf: buffer to store the read data * @offset: offset from where to read @@ -342,12 +342,11 @@ out_unlock: * returned for any volume type if an ECC error was detected by the MTD device * driver. Other negative error cored may be returned in case of other errors. */ -int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, - int offset, int len, int check) +int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + void *buf, int offset, int len, int check) { - int err, pnum, scrub = 0, idx = vol_id2idx(ubi, vol_id); + int err, pnum, scrub = 0, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; - struct ubi_volume *vol = ubi->volumes[idx]; uint32_t uninitialized_var(crc); err = leb_read_lock(ubi, vol_id, lnum); @@ -555,7 +554,7 @@ write_error: /** * ubi_eba_write_leb - write data to dynamic volume. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @lnum: logical eraseblock number * @buf: the data to write * @offset: offset within the logical eraseblock where to write @@ -563,15 +562,14 @@ write_error: * @dtype: data type * * This function writes data to logical eraseblock @lnum of a dynamic volume - * @vol_id. Returns zero in case of success and a negative error code in case + * @vol. Returns zero in case of success and a negative error code in case * of failure. In case of error, it is possible that something was still * written to the flash media, but may be some garbage. */ -int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum, +int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, const void *buf, int offset, int len, int dtype) { - int idx = vol_id2idx(ubi, vol_id), err, pnum, tries = 0; - struct ubi_volume *vol = ubi->volumes[idx]; + int err, pnum, tries = 0, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; if (ubi->ro_mode) @@ -590,7 +588,8 @@ int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum, if (err) { ubi_warn("failed to write data to PEB %d", pnum); if (err == -EIO && ubi->bad_allowed) - err = recover_peb(ubi, pnum, vol_id, lnum, buf, offset, len); + err = recover_peb(ubi, pnum, vol_id, lnum, buf, + offset, len); if (err) ubi_ro_mode(ubi); } @@ -678,7 +677,7 @@ write_error: /** * ubi_eba_write_leb_st - write data to static volume. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @lnum: logical eraseblock number * @buf: data to write * @len: how many bytes to write @@ -686,7 +685,7 @@ write_error: * @used_ebs: how many logical eraseblocks will this volume contain * * This function writes data to logical eraseblock @lnum of static volume - * @vol_id. The @used_ebs argument should contain total number of logical + * @vol. The @used_ebs argument should contain total number of logical * eraseblock in this static volume. * * When writing to the last logical eraseblock, the @len argument doesn't have @@ -698,12 +697,11 @@ write_error: * volumes. This function returns zero in case of success and a negative error * code in case of failure. */ -int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum, - const void *buf, int len, int dtype, int used_ebs) +int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, const void *buf, int len, int dtype, + int used_ebs) { - int err, pnum, tries = 0, data_size = len; - int idx = vol_id2idx(ubi, vol_id); - struct ubi_volume *vol = ubi->volumes[idx]; + int err, pnum, tries = 0, data_size = len, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; uint32_t crc; @@ -799,7 +797,7 @@ write_error: /* * ubi_eba_atomic_leb_change - change logical eraseblock atomically. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume escription object * @lnum: logical eraseblock number * @buf: data to write * @len: how many bytes to write @@ -814,11 +812,10 @@ write_error: * UBI reserves one LEB for the "atomic LEB change" operation, so only one * LEB change may be done at a time. This is ensured by @ubi->alc_mutex. */ -int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum, - const void *buf, int len, int dtype) +int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, const void *buf, int len, int dtype) { - int err, pnum, tries = 0, idx = vol_id2idx(ubi, vol_id); - struct ubi_volume *vol = ubi->volumes[idx]; + int err, pnum, tries = 0, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; uint32_t crc; diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c index 41ff74c60e1..d397219238d 100644 --- a/drivers/mtd/ubi/gluebi.c +++ b/drivers/mtd/ubi/gluebi.c @@ -129,8 +129,7 @@ static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, if (to_read > total_read) to_read = total_read; - err = ubi_eba_read_leb(ubi, vol->vol_id, lnum, buf, offs, - to_read, 0); + err = ubi_eba_read_leb(ubi, vol, lnum, buf, offs, to_read, 0); if (err) break; @@ -187,8 +186,8 @@ static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, if (to_write > total_written) to_write = total_written; - err = ubi_eba_write_leb(ubi, vol->vol_id, lnum, buf, offs, - to_write, UBI_UNKNOWN); + err = ubi_eba_write_leb(ubi, vol, lnum, buf, offs, to_write, + UBI_UNKNOWN); if (err) break; @@ -237,7 +236,7 @@ static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) return -EROFS; for (i = 0; i < count; i++) { - err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum + i); + err = ubi_eba_unmap_leb(ubi, vol, lnum + i); if (err) goto out_err; } diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 3bf2c951c2e..c2fafe6fb2d 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -332,7 +332,7 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, if (len == 0) return 0; - err = ubi_eba_read_leb(ubi, vol_id, lnum, buf, offset, len, check); + err = ubi_eba_read_leb(ubi, vol, lnum, buf, offset, len, check); if (err && err == -EBADMSG && vol->vol_type == UBI_STATIC_VOLUME) { ubi_warn("mark volume %d as corrupted", vol_id); vol->corrupted = 1; @@ -399,7 +399,7 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, if (len == 0) return 0; - return ubi_eba_write_leb(ubi, vol_id, lnum, buf, offset, len, dtype); + return ubi_eba_write_leb(ubi, vol, lnum, buf, offset, len, dtype); } EXPORT_SYMBOL_GPL(ubi_leb_write); @@ -448,7 +448,7 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, if (len == 0) return 0; - return ubi_eba_atomic_leb_change(ubi, vol_id, lnum, buf, len, dtype); + return ubi_eba_atomic_leb_change(ubi, vol, lnum, buf, len, dtype); } EXPORT_SYMBOL_GPL(ubi_leb_change); @@ -481,7 +481,7 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum) if (vol->upd_marker) return -EBADF; - err = ubi_eba_unmap_leb(ubi, vol_id, lnum); + err = ubi_eba_unmap_leb(ubi, vol, lnum); if (err) return err; @@ -542,7 +542,7 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) if (vol->upd_marker) return -EBADF; - return ubi_eba_unmap_leb(ubi, vol_id, lnum); + return ubi_eba_unmap_leb(ubi, vol, lnum); } EXPORT_SYMBOL_GPL(ubi_leb_unmap); @@ -587,7 +587,7 @@ int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype) if (vol->eba_tbl[lnum] >= 0) return -EBADMSG; - return ubi_eba_write_leb(ubi, vol_id, lnum, NULL, 0, 0, dtype); + return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype); } EXPORT_SYMBOL_GPL(ubi_leb_map); diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c index 9e2338c8e2c..93e05281201 100644 --- a/drivers/mtd/ubi/misc.c +++ b/drivers/mtd/ubi/misc.c @@ -79,7 +79,7 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id) else size = vol->usable_leb_size; - err = ubi_eba_read_leb(ubi, vol_id, i, buf, 0, size, 1); + err = ubi_eba_read_leb(ubi, vol, i, buf, 0, size, 1); if (err) { if (err == -EBADMSG) err = 1; diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 23875bf6aa3..0a3a803dd22 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -410,8 +410,8 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si); int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req); int ubi_remove_volume(struct ubi_volume_desc *desc); int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs); -int ubi_add_volume(struct ubi_device *ubi, int vol_id); -void ubi_free_volume(struct ubi_device *ubi, int vol_id); +int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol); +void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol); /* upd.c */ int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes); @@ -435,16 +435,17 @@ void ubi_gluebi_updated(struct ubi_volume *vol); #endif /* eba.c */ -int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum); -int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, - int offset, int len, int check); -int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum, +int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum); +int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + void *buf, int offset, int len, int check); +int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, const void *buf, int offset, int len, int dtype); -int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum, - const void *buf, int len, int dtype, +int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, const void *buf, int len, int dtype, int used_ebs); -int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum, - const void *buf, int len, int dtype); +int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + int lnum, const void *buf, int len, int dtype); int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, struct ubi_vid_hdr *vid_hdr); int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index 0efc586a832..a95dcaa4a0c 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -136,7 +136,7 @@ int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) /* Before updating - wipe out the volume */ for (i = 0; i < vol->reserved_pebs; i++) { - err = ubi_eba_unmap_leb(ubi, vol_id, i); + err = ubi_eba_unmap_leb(ubi, vol, i); if (err) return err; } @@ -209,8 +209,7 @@ static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, if (len != l) dbg_msg("skip last %d bytes (0xFF)", len - l); - err = ubi_eba_write_leb(ubi, vol_id, lnum, buf, 0, l, - UBI_UNKNOWN); + err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, l, UBI_UNKNOWN); } else { /* * When writing static volume, and this is the last logical @@ -222,7 +221,7 @@ static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, * contain zeros, not random trash. */ memset(buf + len, 0, vol->usable_leb_size - len); - err = ubi_eba_write_leb_st(ubi, vol_id, lnum, buf, len, + err = ubi_eba_write_leb_st(ubi, vol, lnum, buf, len, UBI_UNKNOWN, used_ebs); } diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 787ce9ec17a..d2d12deead5 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -417,7 +417,7 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) return err; for (i = 0; i < vol->reserved_pebs; i++) { - err = ubi_eba_unmap_leb(ubi, vol_id, i); + err = ubi_eba_unmap_leb(ubi, vol, i); if (err) return err; } @@ -524,7 +524,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) if (pebs < 0) { for (i = 0; i < -pebs; i++) { - err = ubi_eba_unmap_leb(ubi, vol_id, reserved_pebs + i); + err = ubi_eba_unmap_leb(ubi, vol, reserved_pebs + i); if (err) goto out_acc; } @@ -573,17 +573,16 @@ out_free: /** * ubi_add_volume - add volume. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * * This function adds an existin volume and initializes all its data * structures. Returnes zero in case of success and a negative error code in * case of failure. */ -int ubi_add_volume(struct ubi_device *ubi, int vol_id) +int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) { - int err; + int err, vol_id = vol->vol_id; dev_t dev; - struct ubi_volume *vol = ubi->volumes[vol_id]; dbg_msg("add volume %d", vol_id); ubi_dbg_dump_vol_info(vol); @@ -634,22 +633,21 @@ out_cdev: /** * ubi_free_volume - free volume. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * - * This function frees all resources for volume @vol_id but does not remove it. + * This function frees all resources for volume @vol but does not remove it. * Used only when the UBI device is detached. */ -void ubi_free_volume(struct ubi_device *ubi, int vol_id) +void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol) { int err; - struct ubi_volume *vol = ubi->volumes[vol_id]; - dbg_msg("free volume %d", vol_id); + dbg_msg("free volume %d", vol->vol_id); ubi_assert(vol); vol->removed = 1; err = ubi_destroy_gluebi(vol); - ubi->volumes[vol_id] = NULL; + ubi->volumes[vol->vol_id] = NULL; cdev_del(&vol->cdev); volume_sysfs_close(vol); } diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index 25b3bd61c7e..3349c281bf9 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -86,8 +86,10 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, { int i, err; uint32_t crc; + struct ubi_volume *layout_vol; ubi_assert(idx >= 0 && idx < ubi->vtbl_slots); + layout_vol = ubi->volumes[vol_id2idx(UBI_LAYOUT_VOL_ID)]; if (!vtbl_rec) vtbl_rec = &empty_vtbl_record; @@ -99,12 +101,12 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, mutex_lock(&ubi->vtbl_mutex); memcpy(&ubi->vtbl[idx], vtbl_rec, sizeof(struct ubi_vtbl_record)); for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { - err = ubi_eba_unmap_leb(ubi, UBI_LAYOUT_VOL_ID, i); + err = ubi_eba_unmap_leb(ubi, layout_vol, i); if (err) { mutex_unlock(&ubi->vtbl_mutex); return err; } - err = ubi_eba_write_leb(ubi, UBI_LAYOUT_VOL_ID, i, ubi->vtbl, 0, + err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0, ubi->vtbl_size, UBI_LONGTERM); if (err) { mutex_unlock(&ubi->vtbl_mutex); -- cgit v1.2.3 From cae0a77125467c42f0918e78457913ee4a2f925b Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 17 Dec 2007 12:46:48 +0200 Subject: UBI: tweak volumes locking Transform vtbl_mutex to volumes_mutex - this just makes code easier to understand. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 2 +- drivers/mtd/ubi/kapi.c | 11 ++++++----- drivers/mtd/ubi/ubi.h | 5 +++-- drivers/mtd/ubi/upd.c | 4 ++++ drivers/mtd/ubi/vmt.c | 22 ++++++++++++++++------ drivers/mtd/ubi/vtbl.c | 13 ++++--------- 6 files changed, 34 insertions(+), 23 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 5d00364d4a4..61225f493f6 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -264,7 +264,7 @@ static int uif_init(struct ubi_device *ubi) int i, err; dev_t dev; - mutex_init(&ubi->vtbl_mutex); + mutex_init(&ubi->volumes_mutex); spin_lock_init(&ubi->volumes_lock); sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index c2fafe6fb2d..8e15002a36c 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -162,15 +162,16 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) desc->mode = mode; /* - * To prevent simultaneous checks of the same volume we use @vtbl_mutex, - * although it is not the purpose it was introduced for. + * To prevent simultaneous checks of the same volume we use + * @volumes_mutex, although it is not the purpose it was introduced + * for. */ - mutex_lock(&ubi->vtbl_mutex); + mutex_lock(&ubi->volumes_mutex); if (!vol->checked) { /* This is the first open - check the volume */ err = ubi_check_volume(ubi, vol_id); if (err < 0) { - mutex_unlock(&ubi->vtbl_mutex); + mutex_unlock(&ubi->volumes_mutex); ubi_close_volume(desc); return ERR_PTR(err); } @@ -181,7 +182,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) } vol->checked = 1; } - mutex_unlock(&ubi->vtbl_mutex); + mutex_unlock(&ubi->volumes_mutex); return desc; out_unlock: diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 0a3a803dd22..69cbee3be7a 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -255,7 +255,8 @@ struct ubi_wl_entry; * @vtbl_slots: how many slots are available in the volume table * @vtbl_size: size of the volume table in bytes * @vtbl: in-RAM volume table copy - * @vtbl_mutex: protects on-flash volume table + * @volumes_mutex: protects on-flash volume table and serializes volume + * changes, like creation, deletion, update, resize * * @max_ec: current highest erase counter value * @mean_ec: current mean erase counter value @@ -333,7 +334,7 @@ struct ubi_device { int vtbl_slots; int vtbl_size; struct ubi_vtbl_record *vtbl; - struct mutex vtbl_mutex; + struct mutex volumes_mutex; int max_ec; int mean_ec; diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index a95dcaa4a0c..e32b04d2e04 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -67,7 +67,9 @@ static int set_update_marker(struct ubi_device *ubi, int vol_id) memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); vtbl_rec.upd_marker = 1; + mutex_lock(&ubi->volumes_mutex); err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + mutex_unlock(&ubi->volumes_mutex); vol->upd_marker = 1; return err; } @@ -106,7 +108,9 @@ static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long byt vol->last_eb_bytes = vol->usable_leb_size; } + mutex_lock(&ubi->volumes_mutex); err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + mutex_unlock(&ubi->volumes_mutex); vol->upd_marker = 0; return err; } diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index d2d12deead5..ec2dd3c65c4 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -201,8 +201,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) if (!vol) return -ENOMEM; + mutex_lock(&ubi->volumes_mutex); spin_lock(&ubi->volumes_lock); - if (vol_id == UBI_VOL_NUM_AUTO) { /* Find unused volume ID */ dbg_msg("search for vacant volume ID"); @@ -350,6 +350,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) spin_unlock(&ubi->volumes_lock); paranoid_check_volumes(ubi); + mutex_unlock(&ubi->volumes_mutex); return 0; out_gluebi: @@ -365,6 +366,7 @@ out_acc: ubi->volumes[vol_id] = NULL; out_unlock: spin_unlock(&ubi->volumes_lock); + mutex_unlock(&ubi->volumes_mutex); kfree(vol); ubi_err("cannot create volume %d, error %d", vol_id, err); return err; @@ -382,6 +384,7 @@ out_sysfs: ubi->avail_pebs += vol->reserved_pebs; ubi->volumes[vol_id] = NULL; spin_unlock(&ubi->volumes_lock); + mutex_unlock(&ubi->volumes_mutex); volume_sysfs_close(vol); ubi_err("cannot create volume %d, error %d", vol_id, err); return err; @@ -408,18 +411,19 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) if (ubi->ro_mode) return -EROFS; + mutex_lock(&ubi->volumes_mutex); err = ubi_destroy_gluebi(vol); if (err) - return err; + goto out; err = ubi_change_vtbl_record(ubi, vol_id, NULL); if (err) - return err; + goto out; for (i = 0; i < vol->reserved_pebs; i++) { err = ubi_eba_unmap_leb(ubi, vol, i); if (err) - return err; + goto out; } spin_lock(&ubi->volumes_lock); @@ -449,8 +453,13 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) spin_unlock(&ubi->volumes_lock); paranoid_check_volumes(ubi); + mutex_unlock(&ubi->volumes_mutex); module_put(THIS_MODULE); return 0; + +out: + mutex_unlock(&ubi->volumes_mutex); + return err; } /** @@ -496,6 +505,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) new_mapping[i] = UBI_LEB_UNMAPPED; /* Reserve physical eraseblocks */ + mutex_lock(&ubi->volumes_mutex); pebs = reserved_pebs - vol->reserved_pebs; if (pebs > 0) { spin_lock(&ubi->volumes_lock); @@ -556,6 +566,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) } paranoid_check_volumes(ubi); + mutex_unlock(&ubi->volumes_mutex); return 0; out_acc: @@ -567,6 +578,7 @@ out_acc: } out_free: kfree(new_mapping); + mutex_unlock(&ubi->volumes_mutex); return err; } @@ -829,9 +841,7 @@ static void paranoid_check_volumes(struct ubi_device *ubi) { int i; - mutex_lock(&ubi->vtbl_mutex); for (i = 0; i < ubi->vtbl_slots; i++) paranoid_check_volume(ubi, i); - mutex_unlock(&ubi->vtbl_mutex); } #endif diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index 3349c281bf9..5879fdb3e6d 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -89,7 +89,7 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, struct ubi_volume *layout_vol; ubi_assert(idx >= 0 && idx < ubi->vtbl_slots); - layout_vol = ubi->volumes[vol_id2idx(UBI_LAYOUT_VOL_ID)]; + layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOL_ID)]; if (!vtbl_rec) vtbl_rec = &empty_vtbl_record; @@ -98,24 +98,19 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, vtbl_rec->crc = cpu_to_be32(crc); } - mutex_lock(&ubi->vtbl_mutex); memcpy(&ubi->vtbl[idx], vtbl_rec, sizeof(struct ubi_vtbl_record)); for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { err = ubi_eba_unmap_leb(ubi, layout_vol, i); - if (err) { - mutex_unlock(&ubi->vtbl_mutex); + if (err) return err; - } + err = ubi_eba_write_leb(ubi, layout_vol, i, ubi->vtbl, 0, ubi->vtbl_size, UBI_LONGTERM); - if (err) { - mutex_unlock(&ubi->vtbl_mutex); + if (err) return err; - } } paranoid_vtbl_check(ubi); - mutex_unlock(&ubi->vtbl_mutex); return ubi_wl_flush(ubi); } -- cgit v1.2.3 From c63a491d3737aec3c47c5e785d87021752ad9fa6 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 17 Dec 2007 13:21:07 +0200 Subject: UBI: add some more comments Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/eba.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index b2b0f29bdc5..2ff34923e51 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -797,7 +797,7 @@ write_error: /* * ubi_eba_atomic_leb_change - change logical eraseblock atomically. * @ubi: UBI device description object - * @vol: volume escription object + * @vol: volume description object * @lnum: logical eraseblock number * @buf: data to write * @len: how many bytes to write @@ -955,6 +955,12 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, /* * We may race with volume deletion/re-size, so we have to hold * @ubi->volumes_lock. + * + * Note, it is not a problem if we race with volume deletion or re-size + * here. If the volume is deleted or re-sized while we are moving an + * eraseblock which belongs to this volume, we'll end up with finding + * out that this LEB was unmapped at the end (see WL), and drop this + * PEB. */ spin_lock(&ubi->volumes_lock); vol = ubi->volumes[idx]; -- cgit v1.2.3 From 450f872a8e1763c883c9f723e6937b7ed223e6d3 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 17 Dec 2007 13:09:09 +0200 Subject: UBI: get device when opening volume When a volume is opened, get its kref via get_device() call. And put the reference when closing the volume. With this, we may have a bit saner volume delete. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 9 ++++++--- drivers/mtd/ubi/kapi.c | 2 ++ drivers/mtd/ubi/vmt.c | 5 ----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 24344ba3cfe..35d34b675c7 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -635,9 +635,12 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, } err = ubi_remove_volume(desc); - if (err) - ubi_close_volume(desc); - + /* + * The volume is deleted, and the 'struct ubi_volume' object + * will be freed when 'ubi_close_volume()' will call + * 'put_device()'. + */ + ubi_close_volume(desc); break; } diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 8e15002a36c..96f5fef5f3f 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -156,6 +156,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) vol->exclusive = 1; break; } + get_device(&vol->dev); spin_unlock(&ubi->volumes_lock); desc->vol = vol; @@ -274,6 +275,7 @@ void ubi_close_volume(struct ubi_volume_desc *desc) spin_unlock(&vol->ubi->volumes_lock); kfree(desc); + put_device(&vol->dev); module_put(THIS_MODULE); } EXPORT_SYMBOL_GPL(ubi_close_volume); diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index ec2dd3c65c4..9dd3689aecd 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -435,7 +435,6 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) vol->eba_tbl = NULL; cdev_del(&vol->cdev); volume_sysfs_close(vol); - kfree(desc); spin_lock(&ubi->volumes_lock); ubi->rsvd_pebs -= reserved_pebs; @@ -453,10 +452,6 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) spin_unlock(&ubi->volumes_lock); paranoid_check_volumes(ubi); - mutex_unlock(&ubi->volumes_mutex); - module_put(THIS_MODULE); - return 0; - out: mutex_unlock(&ubi->volumes_mutex); return err; -- cgit v1.2.3 From fc75a1e166268e0c3366c3b30888a024125f6665 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 17 Dec 2007 14:02:09 +0200 Subject: UBI: fix error path Error path in volume creation is bogus. First of, it ovverrides the 'err' variable and returns zero to the caller. Second, ubi_assert() in the release function is wrong. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/vmt.c | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 9dd3689aecd..3d6ac029c17 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -112,7 +112,6 @@ static void vol_release(struct device *dev) { struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); - ubi_assert(vol->removed); kfree(vol); } @@ -154,9 +153,7 @@ static int volume_sysfs_init(struct ubi_device *ubi, struct ubi_volume *vol) if (err) return err; err = device_create_file(&vol->dev, &attr_vol_upd_marker); - if (err) - return err; - return 0; + return err; } /** @@ -188,7 +185,7 @@ static void volume_sysfs_close(struct ubi_volume *vol) */ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) { - int i, err, vol_id = req->vol_id; + int i, err, vol_id = req->vol_id, dont_free = 0; struct ubi_volume *vol; struct ubi_vtbl_record vtbl_rec; uint64_t bytes; @@ -317,6 +314,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) vol->dev.parent = &ubi->dev; vol->dev.devt = dev; vol->dev.class = ubi_class; + sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); err = device_register(&vol->dev); if (err) { @@ -353,8 +351,20 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) mutex_unlock(&ubi->volumes_mutex); return 0; +out_sysfs: + /* + * We have degistered our device, we should not free the volume* + * description object in this function in case of an error - it is + * freed by the release function. + * + * Get device reference to prevent the release function from being + * called just after sysfs has been closed. + */ + dont_free = 1; + get_device(&vol->dev); + volume_sysfs_close(vol); out_gluebi: - err = ubi_destroy_gluebi(vol); + ubi_destroy_gluebi(vol); out_cdev: cdev_del(&vol->cdev); out_mapping: @@ -367,25 +377,10 @@ out_acc: out_unlock: spin_unlock(&ubi->volumes_lock); mutex_unlock(&ubi->volumes_mutex); - kfree(vol); - ubi_err("cannot create volume %d, error %d", vol_id, err); - return err; - - /* - * We are registered, so @vol is destroyed in the release function and - * we have to de-initialize differently. - */ -out_sysfs: - err = ubi_destroy_gluebi(vol); - cdev_del(&vol->cdev); - kfree(vol->eba_tbl); - spin_lock(&ubi->volumes_lock); - ubi->rsvd_pebs -= vol->reserved_pebs; - ubi->avail_pebs += vol->reserved_pebs; - ubi->volumes[vol_id] = NULL; - spin_unlock(&ubi->volumes_lock); - mutex_unlock(&ubi->volumes_mutex); - volume_sysfs_close(vol); + if (dont_free) + put_device(&vol->dev); + else + kfree(vol); ubi_err("cannot create volume %d, error %d", vol_id, err); return err; } -- cgit v1.2.3 From 35ad5fb76cc0a08e14068408b064103439feee36 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 17 Dec 2007 14:22:55 +0200 Subject: UBI: fix and cleanup volume opening functions This patch fixes error codes of the functions - if the device number is out of range, -EINVAL should be returned. It also removes unneeded try_module_get call from the open by name function. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/kapi.c | 61 +++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 96f5fef5f3f..9c283768319 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -104,37 +104,32 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode); - err = -ENODEV; - if (ubi_num < 0) - return ERR_PTR(err); - - ubi = ubi_devices[ubi_num]; + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return ERR_PTR(-EINVAL); - if (!try_module_get(THIS_MODULE)) - return ERR_PTR(err); + if (mode != UBI_READONLY && mode != UBI_READWRITE && + mode != UBI_EXCLUSIVE) + return ERR_PTR(-EINVAL); - if (ubi_num >= UBI_MAX_DEVICES || !ubi) - goto out_put; + ubi = ubi_devices[ubi_num]; + if (!ubi) + return ERR_PTR(-ENODEV); - err = -EINVAL; if (vol_id < 0 || vol_id >= ubi->vtbl_slots) - goto out_put; - if (mode != UBI_READONLY && mode != UBI_READWRITE && - mode != UBI_EXCLUSIVE) - goto out_put; + return ERR_PTR(-EINVAL); desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL); - if (!desc) { - err = -ENOMEM; - goto out_put; - } + if (!desc) + return ERR_PTR(-ENOMEM); + + err = -ENODEV; + if (!try_module_get(THIS_MODULE)) + goto out_free; spin_lock(&ubi->volumes_lock); vol = ubi->volumes[vol_id]; - if (!vol) { - err = -ENODEV; + if (!vol) goto out_unlock; - } err = -EBUSY; switch (mode) { @@ -184,13 +179,14 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) vol->checked = 1; } mutex_unlock(&ubi->volumes_mutex); + return desc; out_unlock: spin_unlock(&ubi->volumes_lock); - kfree(desc); -out_put: module_put(THIS_MODULE); +out_free: + kfree(desc); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(ubi_open_volume); @@ -207,7 +203,6 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, int mode) { int i, vol_id = -1, len; - struct ubi_volume_desc *ret; struct ubi_device *ubi; dbg_msg("open volume %s, mode %d", name, mode); @@ -219,14 +214,12 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, if (len > UBI_VOL_NAME_MAX) return ERR_PTR(-EINVAL); - ret = ERR_PTR(-ENODEV); - if (!try_module_get(THIS_MODULE)) - return ret; - - if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || !ubi_devices[ubi_num]) - goto out_put; + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return ERR_PTR(-EINVAL); ubi = ubi_devices[ubi_num]; + if (!ubi) + return ERR_PTR(-ENODEV); spin_lock(&ubi->volumes_lock); /* Walk all volumes of this UBI device */ @@ -241,13 +234,9 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, spin_unlock(&ubi->volumes_lock); if (vol_id < 0) - goto out_put; - - ret = ubi_open_volume(ubi_num, vol_id, mode); + return ERR_PTR(-ENODEV); -out_put: - module_put(THIS_MODULE); - return ret; + return ubi_open_volume(ubi_num, vol_id, mode); } EXPORT_SYMBOL_GPL(ubi_open_volume_nm); -- cgit v1.2.3 From db6e5770ef0ab351a403ac26e1ab1309e58f15d7 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 17 Dec 2007 15:48:49 +0200 Subject: UBI: simplify error handling If we fail halfway through sysfs file creation, we may just call sysfs remove function and it will delete all the files we created. For non-existing files it will also be OK - the remove functions just return -ENOENT. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 52 ++++++++++++------------------------------------- 1 file changed, 12 insertions(+), 40 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 61225f493f6..6ad291b33a1 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -159,64 +159,36 @@ static int ubi_sysfs_init(struct ubi_device *ubi) sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num); err = device_register(&ubi->dev); if (err) - goto out; + return err; err = device_create_file(&ubi->dev, &dev_eraseblock_size); if (err) - goto out_unregister; + return err; err = device_create_file(&ubi->dev, &dev_avail_eraseblocks); if (err) - goto out_eraseblock_size; + return err; err = device_create_file(&ubi->dev, &dev_total_eraseblocks); if (err) - goto out_avail_eraseblocks; + return err; err = device_create_file(&ubi->dev, &dev_volumes_count); if (err) - goto out_total_eraseblocks; + return err; err = device_create_file(&ubi->dev, &dev_max_ec); if (err) - goto out_volumes_count; + return err; err = device_create_file(&ubi->dev, &dev_reserved_for_bad); if (err) - goto out_volumes_max_ec; + return err; err = device_create_file(&ubi->dev, &dev_bad_peb_count); if (err) - goto out_reserved_for_bad; + return err; err = device_create_file(&ubi->dev, &dev_max_vol_count); if (err) - goto out_bad_peb_count; + return err; err = device_create_file(&ubi->dev, &dev_min_io_size); if (err) - goto out_max_vol_count; + return err; err = device_create_file(&ubi->dev, &dev_bgt_enabled); - if (err) - goto out_min_io_size; - - return 0; - -out_min_io_size: - device_remove_file(&ubi->dev, &dev_min_io_size); -out_max_vol_count: - device_remove_file(&ubi->dev, &dev_max_vol_count); -out_bad_peb_count: - device_remove_file(&ubi->dev, &dev_bad_peb_count); -out_reserved_for_bad: - device_remove_file(&ubi->dev, &dev_reserved_for_bad); -out_volumes_max_ec: - device_remove_file(&ubi->dev, &dev_max_ec); -out_volumes_count: - device_remove_file(&ubi->dev, &dev_volumes_count); -out_total_eraseblocks: - device_remove_file(&ubi->dev, &dev_total_eraseblocks); -out_avail_eraseblocks: - device_remove_file(&ubi->dev, &dev_avail_eraseblocks); -out_eraseblock_size: - device_remove_file(&ubi->dev, &dev_eraseblock_size); -out_unregister: - device_unregister(&ubi->dev); -out: - ubi_err("failed to initialize sysfs for %s, error %d", - ubi->ubi_name, err); return err; } @@ -296,7 +268,7 @@ static int uif_init(struct ubi_device *ubi) err = ubi_sysfs_init(ubi); if (err) - goto out_cdev; + goto out_sysfs; for (i = 0; i < ubi->vtbl_slots; i++) if (ubi->volumes[i]) { @@ -311,8 +283,8 @@ static int uif_init(struct ubi_device *ubi) out_volumes: kill_volumes(ubi); +out_sysfs: ubi_sysfs_close(ubi); -out_cdev: cdev_del(&ubi->cdev); out_unreg: unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1); -- cgit v1.2.3 From d05c77a816974c09f8c7e8f48e5b9f7b59dafdf3 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 17 Dec 2007 15:42:57 +0200 Subject: UBI: introduce volume refcounting Add ref_count field to UBI volumes and remove weired "vol->removed" field. This way things are better understandable and we do not have to do whold show_attr operation under spinlock. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/eba.c | 10 ++++++ drivers/mtd/ubi/kapi.c | 4 ++- drivers/mtd/ubi/ubi.h | 22 ++++++------- drivers/mtd/ubi/vmt.c | 88 +++++++++++++++++++++++++++++++++----------------- drivers/mtd/ubi/vtbl.c | 2 ++ 5 files changed, 84 insertions(+), 42 deletions(-) diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 2ff34923e51..84f7dc9fd3a 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -301,6 +301,8 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, { int err, pnum, vol_id = vol->vol_id; + ubi_assert(vol->ref_count > 0); + if (ubi->ro_mode) return -EROFS; @@ -349,6 +351,8 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, struct ubi_vid_hdr *vid_hdr; uint32_t uninitialized_var(crc); + ubi_assert(vol->ref_count > 0); + err = leb_read_lock(ubi, vol_id, lnum); if (err) return err; @@ -572,6 +576,8 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, int err, pnum, tries = 0, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; + ubi_assert(vol->ref_count > 0); + if (ubi->ro_mode) return -EROFS; @@ -705,6 +711,8 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, struct ubi_vid_hdr *vid_hdr; uint32_t crc; + ubi_assert(vol->ref_count > 0); + if (ubi->ro_mode) return -EROFS; @@ -819,6 +827,8 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, struct ubi_vid_hdr *vid_hdr; uint32_t crc; + ubi_assert(vol->ref_count > 0); + if (ubi->ro_mode) return -EROFS; diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 9c283768319..780c273ff45 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -152,6 +152,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) break; } get_device(&vol->dev); + vol->ref_count += 1; spin_unlock(&ubi->volumes_lock); desc->vol = vol; @@ -261,10 +262,11 @@ void ubi_close_volume(struct ubi_volume_desc *desc) case UBI_EXCLUSIVE: vol->exclusive = 0; } + vol->ref_count -= 1; spin_unlock(&vol->ubi->volumes_lock); - kfree(desc); put_device(&vol->dev); + kfree(desc); module_put(THIS_MODULE); } EXPORT_SYMBOL_GPL(ubi_close_volume); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 69cbee3be7a..f782d5aa849 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -140,10 +140,10 @@ struct ubi_volume_desc; * @cdev: character device object to create character device * @ubi: reference to the UBI device description object * @vol_id: volume ID + * @ref_count: volume reference count * @readers: number of users holding this volume in read-only mode * @writers: number of users holding this volume in read-write mode * @exclusive: whether somebody holds this volume in exclusive mode - * @removed: if the volume was removed * @checked: if this static volume was checked * * @reserved_pebs: how many physical eraseblocks are reserved for this volume @@ -156,7 +156,7 @@ struct ubi_volume_desc; * @corrupted: non-zero if the volume is corrupted (static volumes only) * @alignment: volume alignment * @data_pad: how many bytes are not used at the end of physical eraseblocks to - * satisfy the requested alignment + * satisfy the requested alignment * @name_len: volume name length * @name: volume name * @@ -185,10 +185,10 @@ struct ubi_volume { struct cdev cdev; struct ubi_device *ubi; int vol_id; + int ref_count; int readers; int writers; int exclusive; - int removed; int checked; int reserved_pebs; @@ -242,9 +242,9 @@ struct ubi_wl_entry; * @vol_count: number of volumes in this UBI device * @volumes: volumes of this UBI device * @volumes_lock: protects @volumes, @rsvd_pebs, @avail_pebs, beb_rsvd_pebs, - * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, @vol->readers, - * @vol->writers, @vol->exclusive, @vol->removed, @vol->mapping and - * @vol->eba_tbl. + * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, + * @vol->readers, @vol->writers, @vol->exclusive, + * @vol->ref_count, @vol->mapping and @vol->eba_tbl. * * @rsvd_pebs: count of reserved physical eraseblocks * @avail_pebs: count of available physical eraseblocks @@ -273,11 +273,11 @@ struct ubi_wl_entry; * @prot.pnum: protection tree indexed by physical eraseblock numbers * @prot.aec: protection tree indexed by absolute erase counter value * @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from, - * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works - * fields + * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works + * fields * @wl_scheduled: non-zero if the wear-leveling was scheduled * @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any - * physical eraseblock + * physical eraseblock * @abs_ec: absolute erase counter * @move_from: physical eraseblock from where the data is being moved * @move_to: physical eraseblock where the data is being moved to @@ -308,13 +308,13 @@ struct ubi_wl_entry; * @hdrs_min_io_size * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or - * not + * not * @mtd: MTD device descriptor * * @peb_buf1: a buffer of PEB size used for different purposes * @peb_buf2: another buffer of PEB size used for different purposes * @buf_mutex: proptects @peb_buf1 and @peb_buf2 - * @dbg_peb_buf: buffer of PEB size used for debugging + * @dbg_peb_buf: buffer of PEB size used for debugging * @dbg_buf_mutex: proptects @dbg_peb_buf */ struct ubi_device { diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 3d6ac029c17..18ef1e1da49 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -63,21 +63,24 @@ static struct device_attribute attr_vol_upd_marker = * B. process 2 removes volume Y; * C. process 1 starts reading the //class/ubi/ubiX_Y/reserved_ebs file; * - * What we want to do in a situation like that is to return error when the file - * is read. This is done by means of the 'removed' flag and the 'vol_lock' of - * the UBI volume description object. + * In this situation, this function will return %-ENODEV because it will find + * out that the volume was removed from the @ubi->volumes array. */ static ssize_t vol_attribute_show(struct device *dev, struct device_attribute *attr, char *buf) { - int ret = -ENODEV; + int ret; struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); + struct ubi_device *ubi = vol->ubi; - spin_lock(&vol->ubi->volumes_lock); - if (vol->removed) { - spin_unlock(&vol->ubi->volumes_lock); - return ret; + spin_lock(&ubi->volumes_lock); + if (!ubi->volumes[vol->vol_id]) { + spin_unlock(&ubi->volumes_lock); + return -ENODEV; } + /* Take a reference to prevent volume removal */ + vol->ref_count += 1; + spin_unlock(&ubi->volumes_lock); if (attr == &attr_vol_reserved_ebs) ret = sprintf(buf, "%d\n", vol->reserved_pebs); @@ -102,8 +105,13 @@ static ssize_t vol_attribute_show(struct device *dev, else if (attr == &attr_vol_upd_marker) ret = sprintf(buf, "%d\n", vol->upd_marker); else - BUG(); - spin_unlock(&vol->ubi->volumes_lock); + /* This must be a bug */ + ret = -EINVAL; + + spin_lock(&ubi->volumes_lock); + vol->ref_count -= 1; + ubi_assert(vol->ref_count >= 0); + spin_unlock(&ubi->volumes_lock); return ret; } @@ -179,7 +187,7 @@ static void volume_sysfs_close(struct ubi_volume *vol) * @req: volume creation request * * This function creates volume described by @req. If @req->vol_id id - * %UBI_VOL_NUM_AUTO, this function automatically assigne ID to the new volume + * %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume * and saves it in @req->vol_id. Returns zero in case of success and a negative * error code in case of failure. */ @@ -261,7 +269,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) memcpy(vol->name, req->name, vol->name_len + 1); vol->exclusive = 1; vol->ubi = ubi; - ubi->volumes[vol_id] = vol; spin_unlock(&ubi->volumes_lock); /* @@ -345,6 +352,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) spin_lock(&ubi->volumes_lock); ubi->vol_count += 1; vol->exclusive = 0; + ubi->volumes[vol_id] = vol; spin_unlock(&ubi->volumes_lock); paranoid_check_volumes(ubi); @@ -353,7 +361,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) out_sysfs: /* - * We have degistered our device, we should not free the volume* + * We have registered our device, we should not free the volume* * description object in this function in case of an error - it is * freed by the release function. * @@ -373,7 +381,6 @@ out_acc: spin_lock(&ubi->volumes_lock); ubi->rsvd_pebs -= vol->reserved_pebs; ubi->avail_pebs += vol->reserved_pebs; - ubi->volumes[vol_id] = NULL; out_unlock: spin_unlock(&ubi->volumes_lock); mutex_unlock(&ubi->volumes_mutex); @@ -407,25 +414,32 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) return -EROFS; mutex_lock(&ubi->volumes_mutex); + spin_lock(&ubi->volumes_lock); + if (vol->ref_count > 1) { + /* + * The volume is busy, probably someone is reading one of its + * sysfs files. + */ + err = -EBUSY; + goto out_unlock; + } + ubi->volumes[vol_id] = NULL; + spin_unlock(&ubi->volumes_lock); + err = ubi_destroy_gluebi(vol); if (err) - goto out; + goto out_err; err = ubi_change_vtbl_record(ubi, vol_id, NULL); if (err) - goto out; + goto out_err; for (i = 0; i < vol->reserved_pebs; i++) { err = ubi_eba_unmap_leb(ubi, vol, i); if (err) - goto out; + goto out_err; } - spin_lock(&ubi->volumes_lock); - vol->removed = 1; - ubi->volumes[vol_id] = NULL; - spin_unlock(&ubi->volumes_lock); - kfree(vol->eba_tbl); vol->eba_tbl = NULL; cdev_del(&vol->cdev); @@ -447,7 +461,15 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) spin_unlock(&ubi->volumes_lock); paranoid_check_volumes(ubi); -out: + mutex_unlock(&ubi->volumes_mutex); + return 0; + +out_err: + ubi_err("cannot remove volume %d, error %d", vol_id, err); + spin_lock(&ubi->volumes_lock); + ubi->volumes[vol_id] = vol; +out_unlock: + spin_unlock(&ubi->volumes_lock); mutex_unlock(&ubi->volumes_mutex); return err; } @@ -494,8 +516,17 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) for (i = 0; i < reserved_pebs; i++) new_mapping[i] = UBI_LEB_UNMAPPED; - /* Reserve physical eraseblocks */ mutex_lock(&ubi->volumes_mutex); + spin_lock(&ubi->volumes_lock); + if (vol->ref_count > 1) { + spin_unlock(&ubi->volumes_lock); + err = -EBUSY; + goto out_free; + } + spin_unlock(&ubi->volumes_lock); + + + /* Reserve physical eraseblocks */ pebs = reserved_pebs - vol->reserved_pebs; if (pebs > 0) { spin_lock(&ubi->volumes_lock); @@ -577,8 +608,8 @@ out_free: * @ubi: UBI device description object * @vol: volume description object * - * This function adds an existin volume and initializes all its data - * structures. Returnes zero in case of success and a negative error code in + * This function adds an existing volume and initializes all its data + * structures. Returns zero in case of success and a negative error code in * case of failure. */ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) @@ -588,7 +619,6 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol) dbg_msg("add volume %d", vol_id); ubi_dbg_dump_vol_info(vol); - ubi_assert(vol); /* Register character device for the volume */ cdev_init(&vol->cdev, &ubi_vol_cdev_operations); @@ -645,11 +675,9 @@ void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol) int err; dbg_msg("free volume %d", vol->vol_id); - ubi_assert(vol); - vol->removed = 1; - err = ubi_destroy_gluebi(vol); ubi->volumes[vol->vol_id] = NULL; + err = ubi_destroy_gluebi(vol); cdev_del(&vol->cdev); volume_sysfs_close(vol); } diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index 5879fdb3e6d..a37dc7a213b 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -565,6 +565,7 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, vol->last_eb_bytes = sv->last_data_size; } + /* And add the layout volume */ vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); if (!vol) return -ENOMEM; @@ -580,6 +581,7 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, vol->used_bytes = (long long)vol->used_ebs * (ubi->leb_size - vol->data_pad); vol->vol_id = UBI_LAYOUT_VOL_ID; + vol->ref_count = 1; ubi_assert(!ubi->volumes[i]); ubi->volumes[vol_id2idx(ubi, vol->vol_id)] = vol; -- cgit v1.2.3 From 40e4d0c1660f8ee01ea4ed570297b32c35c70aa3 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 17 Dec 2007 17:08:55 +0200 Subject: UBI: tweak volumes locking some more Make the code more consistent by requiring the caller to lock the ubi->volume_mutex, because this is what we do for updates. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 13 ++++++++++--- drivers/mtd/ubi/vmt.c | 20 +++++++------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 35d34b675c7..22c15a388f2 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -605,7 +605,9 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, req.name[req.name_len] = '\0'; + mutex_lock(&ubi->volumes_mutex); err = ubi_create_volume(ubi, &req); + mutex_unlock(&ubi->volumes_mutex); if (err) break; @@ -634,11 +636,14 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, break; } + mutex_lock(&ubi->volumes_mutex); err = ubi_remove_volume(desc); + mutex_unlock(&ubi->volumes_mutex); + /* - * The volume is deleted, and the 'struct ubi_volume' object - * will be freed when 'ubi_close_volume()' will call - * 'put_device()'. + * The volume is deleted (unless an error occurred), and the + * 'struct ubi_volume' object will be freed when + * 'ubi_close_volume()' will call 'put_device()'. */ ubi_close_volume(desc); break; @@ -673,7 +678,9 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, pebs = !!do_div(tmp, desc->vol->usable_leb_size); pebs += tmp; + mutex_lock(&ubi->volumes_mutex); err = ubi_resize_volume(desc, pebs); + mutex_unlock(&ubi->volumes_mutex); ubi_close_volume(desc); break; } diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 18ef1e1da49..3ed63dc3738 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -189,7 +189,8 @@ static void volume_sysfs_close(struct ubi_volume *vol) * This function creates volume described by @req. If @req->vol_id id * %UBI_VOL_NUM_AUTO, this function automatically assign ID to the new volume * and saves it in @req->vol_id. Returns zero in case of success and a negative - * error code in case of failure. + * error code in case of failure. Note, the caller has to have the + * @ubi->volumes_mutex locked. */ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) { @@ -206,7 +207,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) if (!vol) return -ENOMEM; - mutex_lock(&ubi->volumes_mutex); spin_lock(&ubi->volumes_lock); if (vol_id == UBI_VOL_NUM_AUTO) { /* Find unused volume ID */ @@ -356,7 +356,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) spin_unlock(&ubi->volumes_lock); paranoid_check_volumes(ubi); - mutex_unlock(&ubi->volumes_mutex); return 0; out_sysfs: @@ -383,7 +382,6 @@ out_acc: ubi->avail_pebs += vol->reserved_pebs; out_unlock: spin_unlock(&ubi->volumes_lock); - mutex_unlock(&ubi->volumes_mutex); if (dont_free) put_device(&vol->dev); else @@ -398,7 +396,8 @@ out_unlock: * * This function removes volume described by @desc. The volume has to be opened * in "exclusive" mode. Returns zero in case of success and a negative error - * code in case of failure. + * code in case of failure. The caller has to have the @ubi->volumes_mutex + * locked. */ int ubi_remove_volume(struct ubi_volume_desc *desc) { @@ -413,7 +412,6 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) if (ubi->ro_mode) return -EROFS; - mutex_lock(&ubi->volumes_mutex); spin_lock(&ubi->volumes_lock); if (vol->ref_count > 1) { /* @@ -461,7 +459,6 @@ int ubi_remove_volume(struct ubi_volume_desc *desc) spin_unlock(&ubi->volumes_lock); paranoid_check_volumes(ubi); - mutex_unlock(&ubi->volumes_mutex); return 0; out_err: @@ -470,7 +467,6 @@ out_err: ubi->volumes[vol_id] = vol; out_unlock: spin_unlock(&ubi->volumes_lock); - mutex_unlock(&ubi->volumes_mutex); return err; } @@ -479,8 +475,9 @@ out_unlock: * @desc: volume descriptor * @reserved_pebs: new size in physical eraseblocks * - * This function returns zero in case of success, and a negative error code in - * case of failure. + * This function re-sizes the volume and returns zero in case of success, and a + * negative error code in case of failure. The caller has to have the + * @ubi->volumes_mutex locked. */ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) { @@ -516,7 +513,6 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) for (i = 0; i < reserved_pebs; i++) new_mapping[i] = UBI_LEB_UNMAPPED; - mutex_lock(&ubi->volumes_mutex); spin_lock(&ubi->volumes_lock); if (vol->ref_count > 1) { spin_unlock(&ubi->volumes_lock); @@ -587,7 +583,6 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) } paranoid_check_volumes(ubi); - mutex_unlock(&ubi->volumes_mutex); return 0; out_acc: @@ -599,7 +594,6 @@ out_acc: } out_free: kfree(new_mapping); - mutex_unlock(&ubi->volumes_mutex); return err; } -- cgit v1.2.3 From d19bafd99d334cdc2530a7466235756bbc87c5d5 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 18 Dec 2007 12:57:52 +0200 Subject: UBI: add PID to debugging prints Also, use single dbg_msg() macro for all prints. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/debug.h | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index 467722eb618..51c40b17f1e 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -39,8 +39,9 @@ #ifdef CONFIG_MTD_UBI_DEBUG_MSG /* Generic debugging message */ -#define dbg_msg(fmt, ...) \ - printk(KERN_DEBUG "UBI DBG: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__) +#define dbg_msg(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG (pid %d): %s: " fmt "\n", \ + current->pid, __FUNCTION__, ##__VA_ARGS__) #define ubi_dbg_dump_stack() dump_stack() @@ -76,36 +77,28 @@ void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req); #ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA /* Messages from the eraseblock association unit */ -#define dbg_eba(fmt, ...) \ - printk(KERN_DEBUG "UBI DBG eba: %s: " fmt "\n", __FUNCTION__, \ - ##__VA_ARGS__) +#define dbg_eba(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_eba(fmt, ...) ({}) #endif #ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL /* Messages from the wear-leveling unit */ -#define dbg_wl(fmt, ...) \ - printk(KERN_DEBUG "UBI DBG wl: %s: " fmt "\n", __FUNCTION__, \ - ##__VA_ARGS__) +#define dbg_wl(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_wl(fmt, ...) ({}) #endif #ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO /* Messages from the input/output unit */ -#define dbg_io(fmt, ...) \ - printk(KERN_DEBUG "UBI DBG io: %s: " fmt "\n", __FUNCTION__, \ - ##__VA_ARGS__) +#define dbg_io(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_io(fmt, ...) ({}) #endif #ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD /* Initialization and build messages */ -#define dbg_bld(fmt, ...) \ - printk(KERN_DEBUG "UBI DBG bld: %s: " fmt "\n", __FUNCTION__, \ - ##__VA_ARGS__) +#define dbg_bld(fmt, ...) dbg_msg(fmt, ##__VA_ARGS__) #else #define dbg_bld(fmt, ...) ({}) #endif -- cgit v1.2.3 From d2c468550915ab2f16149e274a6f0da0b925a748 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 18 Dec 2007 13:17:24 +0200 Subject: UBI: improve comment Explain better the purpose of thie 'move_to_put' stuff. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/wl.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index a405d40faa2..36aa097203f 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -793,7 +793,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, rb); e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); paranoid_check_in_wl_tree(e1, &ubi->scrub); - rb_erase(&e1->rb, &ubi->scrub); + rb_erase(&e1->rb, &ubi->scrub); dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum); } @@ -1139,8 +1139,11 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) /* * User is putting the physical eraseblock which was selected * as the target the data is moved to. It may happen if the EBA - * unit already re-mapped the LEB but the WL unit did has not - * put the PEB to the "used" tree. + * unit already re-mapped the LEB in 'ubi_eba_copy_leb()' but + * the WL unit has not put the PEB to the "used" tree yet, but + * it is about to do this. So we just set a flag which will + * tell the WL worker that the PEB is not needed anymore and + * should be sheduled for erasure. */ dbg_wl("PEB %d is the target of data moving", pnum); ubi_assert(!ubi->move_to_put); -- cgit v1.2.3 From 43f9b25a9cdd7b177f77f026b1461abd1abbd174 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 18 Dec 2007 15:06:55 +0200 Subject: UBI: bugfix: protect from volume removal When the WL worker is moving an LEB, the volume might go away occasionally. UBI does not handle these situations correctly. This patch introduces a new mutex which serializes wear-levelling worker and the the 'ubi_wl_put_peb()' function. Now, if one puts an LEB, and its PEB is being moved, it will wait on the mutex. And because we unmap all LEBs when removing volumes, this will make the volume remove function to wait while the LEB movement finishes. Below is an example of an oops which should be fixed by this patch: Pid: 9167, comm: io_paral Not tainted (2.6.24-rc5-ubi-2.6.git #2) EIP: 0060:[] EFLAGS: 00010246 CPU: 0 EIP is at prot_tree_del+0x2a/0x63 [ubi] EAX: f39a90e0 EBX: 00000000 ECX: 00000000 EDX: 00000134 ESI: f39a90e0 EDI: f39a90e0 EBP: f2d55ddc ESP: f2d55dd4 DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 Process io_paral (pid: 9167, ti=f2d54000 task=f72a8030 task.ti=f2d54000) Stack: f39a95f8 ef6aae50 f2d55e08 f884a511 f88538e1 f884ecea 00000134 00000000 f39a9604 f39a95f0 efea8280 00000000 f39a90e0 f2d55e40 f8847261 f8850c3c f884eaad 00000001 000000b9 00000134 00000172 000000b9 00000134 00000001 Call Trace: [] show_trace_log_lvl+0x1a/0x30 [] show_stack_log_lvl+0xa5/0xca [] show_registers+0xcf/0x21b [] die+0x126/0x224 [] do_page_fault+0x27f/0x60d [] error_code+0x72/0x78 [] ubi_wl_put_peb+0xf0/0x191 [ubi] [] ubi_eba_unmap_leb+0xaf/0xcc [ubi] [] ubi_remove_volume+0x102/0x1e8 [ubi] [] ubi_cdev_ioctl+0x22a/0x383 [ubi] [] do_ioctl+0x68/0x71 [] vfs_ioctl+0x55/0x271 [] sys_ioctl+0x33/0x52 [] sysenter_past_esp+0x5f/0xa5 ======================= Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/eba.c | 145 ++++++++++++++++++++++----------- drivers/mtd/ubi/ubi.h | 10 ++- drivers/mtd/ubi/wl.c | 219 ++++++++++++++++++++++++++++++-------------------- 3 files changed, 239 insertions(+), 135 deletions(-) diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 84f7dc9fd3a..c94f475758d 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -259,6 +259,44 @@ static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum) return 0; } +/** + * leb_write_lock - lock logical eraseblock for writing. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function locks a logical eraseblock for writing if there is no + * contention and does nothing if there is contention. Returns %0 in case of + * success, %1 in case of contention, and and a negative error code in case of + * failure. + */ +static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum) +{ + int free; + struct ubi_ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (IS_ERR(le)) + return PTR_ERR(le); + if (down_write_trylock(&le->mutex)) + return 0; + + /* Contention, cancel */ + spin_lock(&ubi->ltree_lock); + le->users -= 1; + ubi_assert(le->users >= 0); + if (le->users == 0) { + rb_erase(&le->rb, &ubi->ltree); + free = 1; + } else + free = 0; + spin_unlock(&ubi->ltree_lock); + if (free) + kmem_cache_free(ubi_ltree_slab, le); + + return 1; +} + /** * leb_write_unlock - unlock logical eraseblock. * @ubi: UBI device description object @@ -923,14 +961,16 @@ write_error: * * This function copies logical eraseblock from physical eraseblock @from to * physical eraseblock @to. The @vid_hdr buffer may be changed by this - * function. Returns zero in case of success, %UBI_IO_BITFLIPS if the operation - * was canceled because bit-flips were detected at the target PEB, and a - * negative error code in case of failure. + * function. Returns: + * o %0 in case of success; + * o %1 if the operation was canceled and should be tried later (e.g., + * because a bit-flip was detected at the target PEB); + * o %2 if the volume is being deleted and this LEB should not be moved. */ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, struct ubi_vid_hdr *vid_hdr) { - int err, vol_id, lnum, data_size, aldata_size, pnum, idx; + int err, vol_id, lnum, data_size, aldata_size, idx; struct ubi_volume *vol; uint32_t crc; @@ -946,57 +986,67 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, data_size = aldata_size = ubi->leb_size - be32_to_cpu(vid_hdr->data_pad); - /* - * We do not want anybody to write to this logical eraseblock while we - * are moving it, so we lock it. - */ - err = leb_write_lock(ubi, vol_id, lnum); - if (err) - return err; - - mutex_lock(&ubi->buf_mutex); - - /* - * But the logical eraseblock might have been put by this time. - * Cancel if it is true. - */ idx = vol_id2idx(ubi, vol_id); - + spin_lock(&ubi->volumes_lock); /* - * We may race with volume deletion/re-size, so we have to hold - * @ubi->volumes_lock. - * - * Note, it is not a problem if we race with volume deletion or re-size - * here. If the volume is deleted or re-sized while we are moving an - * eraseblock which belongs to this volume, we'll end up with finding - * out that this LEB was unmapped at the end (see WL), and drop this - * PEB. + * Note, we may race with volume deletion, which means that the volume + * this logical eraseblock belongs to might be being deleted. Since the + * volume deletion unmaps all the volume's logical eraseblocks, it will + * be locked in 'ubi_wl_put_peb()' and wait for the WL worker to finish. */ - spin_lock(&ubi->volumes_lock); vol = ubi->volumes[idx]; if (!vol) { - dbg_eba("volume %d was removed meanwhile", vol_id); + /* No need to do further work, cancel */ + dbg_eba("volume %d is being removed, cancel", vol_id); spin_unlock(&ubi->volumes_lock); - goto out_unlock; + return 2; } + spin_unlock(&ubi->volumes_lock); - pnum = vol->eba_tbl[lnum]; - if (pnum != from) { - dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to " - "PEB %d, cancel", vol_id, lnum, from, pnum); - spin_unlock(&ubi->volumes_lock); - goto out_unlock; + /* + * We do not want anybody to write to this logical eraseblock while we + * are moving it, so lock it. + * + * Note, we are using non-waiting locking here, because we cannot sleep + * on the LEB, since it may cause deadlocks. Indeed, imagine a task is + * unmapping the LEB which is mapped to the PEB we are going to move + * (@from). This task locks the LEB and goes sleep in the + * 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are + * holding @ubi->move_mutex and go sleep on the LEB lock. So, if the + * LEB is already locked, we just do not move it and return %1. + */ + err = leb_write_trylock(ubi, vol_id, lnum); + if (err) { + dbg_eba("contention on LEB %d:%d, cancel", vol_id, lnum); + return err; } - spin_unlock(&ubi->volumes_lock); - /* OK, now the LEB is locked and we can safely start moving it */ + /* + * The LEB might have been put meanwhile, and the task which put it is + * probably waiting on @ubi->move_mutex. No need to continue the work, + * cancel it. + */ + if (vol->eba_tbl[lnum] != from) { + dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to " + "PEB %d, cancel", vol_id, lnum, from, + vol->eba_tbl[lnum]); + err = 1; + goto out_unlock_leb; + } + /* + * OK, now the LEB is locked and we can safely start moving iy. Since + * this function utilizes thie @ubi->peb1_buf buffer which is shared + * with some other functions, so lock the buffer by taking the + * @ubi->buf_mutex. + */ + mutex_lock(&ubi->buf_mutex); dbg_eba("read %d bytes of data", aldata_size); err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size); if (err && err != UBI_IO_BITFLIPS) { ubi_warn("error %d while reading data from PEB %d", err, from); - goto out_unlock; + goto out_unlock_buf; } /* @@ -1032,7 +1082,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, err = ubi_io_write_vid_hdr(ubi, to, vid_hdr); if (err) - goto out_unlock; + goto out_unlock_buf; cond_resched(); @@ -1041,13 +1091,15 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, if (err) { if (err != UBI_IO_BITFLIPS) ubi_warn("cannot read VID header back from PEB %d", to); - goto out_unlock; + else + err = 1; + goto out_unlock_buf; } if (data_size > 0) { err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size); if (err) - goto out_unlock; + goto out_unlock_buf; cond_resched(); @@ -1061,7 +1113,9 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, if (err != UBI_IO_BITFLIPS) ubi_warn("cannot read data back from PEB %d", to); - goto out_unlock; + else + err = 1; + goto out_unlock_buf; } cond_resched(); @@ -1069,15 +1123,16 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, if (memcmp(ubi->peb_buf1, ubi->peb_buf2, aldata_size)) { ubi_warn("read data back from PEB %d - it is different", to); - goto out_unlock; + goto out_unlock_buf; } } ubi_assert(vol->eba_tbl[lnum] == from); vol->eba_tbl[lnum] = to; -out_unlock: +out_unlock_buf: mutex_unlock(&ubi->buf_mutex); +out_unlock_leb: leb_write_unlock(ubi, vol_id, lnum); return err; } diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index f782d5aa849..ea9a6990a4d 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -275,13 +275,13 @@ struct ubi_wl_entry; * @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from, * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works * fields + * @move_mutex: serializes eraseblock moves * @wl_scheduled: non-zero if the wear-leveling was scheduled * @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any * physical eraseblock * @abs_ec: absolute erase counter * @move_from: physical eraseblock from where the data is being moved * @move_to: physical eraseblock where the data is being moved to - * @move_from_put: if the "from" PEB was put * @move_to_put: if the "to" PEB was put * @works: list of pending works * @works_count: count of pending works @@ -354,12 +354,12 @@ struct ubi_device { struct rb_root aec; } prot; spinlock_t wl_lock; + struct mutex move_mutex; int wl_scheduled; struct ubi_wl_entry **lookuptbl; unsigned long long abs_ec; struct ubi_wl_entry *move_from; struct ubi_wl_entry *move_to; - int move_from_put; int move_to_put; struct list_head works; int works_count; @@ -561,8 +561,10 @@ static inline int ubi_io_write_data(struct ubi_device *ubi, const void *buf, */ static inline void ubi_ro_mode(struct ubi_device *ubi) { - ubi->ro_mode = 1; - ubi_warn("switch to read-only mode"); + if (!ubi->ro_mode) { + ubi->ro_mode = 1; + ubi_warn("switch to read-only mode"); + } } /** diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 36aa097203f..a60f9425ab1 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -249,6 +249,8 @@ static int do_work(struct ubi_device *ubi) int err; struct ubi_work *wrk; + cond_resched(); + spin_lock(&ubi->wl_lock); if (list_empty(&ubi->works)) { @@ -531,8 +533,12 @@ retry: * prot_tree_del - remove a physical eraseblock from the protection trees * @ubi: UBI device description object * @pnum: the physical eraseblock to remove + * + * This function returns PEB @pnum from the protection trees and returns zero + * in case of success and %-ENODEV if the PEB was not found in the protection + * trees. */ -static void prot_tree_del(struct ubi_device *ubi, int pnum) +static int prot_tree_del(struct ubi_device *ubi, int pnum) { struct rb_node *p; struct ubi_wl_prot_entry *pe = NULL; @@ -543,7 +549,7 @@ static void prot_tree_del(struct ubi_device *ubi, int pnum) pe = rb_entry(p, struct ubi_wl_prot_entry, rb_pnum); if (pnum == pe->e->pnum) - break; + goto found; if (pnum < pe->e->pnum) p = p->rb_left; @@ -551,10 +557,14 @@ static void prot_tree_del(struct ubi_device *ubi, int pnum) p = p->rb_right; } + return -ENODEV; + +found: ubi_assert(pe->e->pnum == pnum); rb_erase(&pe->rb_aec, &ubi->prot.aec); rb_erase(&pe->rb_pnum, &ubi->prot.pnum); kfree(pe); + return 0; } /** @@ -726,7 +736,8 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, int cancel) { - int err, put = 0; + int err, put = 0, scrubbing = 0, protect = 0; + struct ubi_wl_prot_entry *pe; struct ubi_wl_entry *e1, *e2; struct ubi_vid_hdr *vid_hdr; @@ -739,21 +750,17 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, if (!vid_hdr) return -ENOMEM; + mutex_lock(&ubi->move_mutex); spin_lock(&ubi->wl_lock); + ubi_assert(!ubi->move_from && !ubi->move_to); + ubi_assert(!ubi->move_to_put); - /* - * Only one WL worker at a time is supported at this implementation, so - * make sure a PEB is not being moved already. - */ - if (ubi->move_to || !ubi->free.rb_node || + if (!ubi->free.rb_node || (!ubi->used.rb_node && !ubi->scrub.rb_node)) { /* - * Only one WL worker at a time is supported at this - * implementation, so if a LEB is already being moved, cancel. - * - * No free physical eraseblocks? Well, we cancel wear-leveling - * then. It will be triggered again when a free physical - * eraseblock appears. + * No free physical eraseblocks? Well, they must be waiting in + * the queue to be erased. Cancel movement - it will be + * triggered again when a free physical eraseblock appears. * * No used physical eraseblocks? They must be temporarily * protected from being moved. They will be moved to the @@ -762,10 +769,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, */ dbg_wl("cancel WL, a list is empty: free %d, used %d", !ubi->free.rb_node, !ubi->used.rb_node); - ubi->wl_scheduled = 0; - spin_unlock(&ubi->wl_lock); - ubi_free_vid_hdr(ubi, vid_hdr); - return 0; + goto out_cancel; } if (!ubi->scrub.rb_node) { @@ -780,16 +784,15 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) { dbg_wl("no WL needed: min used EC %d, max free EC %d", e1->ec, e2->ec); - ubi->wl_scheduled = 0; - spin_unlock(&ubi->wl_lock); - ubi_free_vid_hdr(ubi, vid_hdr); - return 0; + goto out_cancel; } paranoid_check_in_wl_tree(e1, &ubi->used); rb_erase(&e1->rb, &ubi->used); dbg_wl("move PEB %d EC %d to PEB %d EC %d", e1->pnum, e1->ec, e2->pnum, e2->ec); } else { + /* Perform scrubbing */ + scrubbing = 1; e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, rb); e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); paranoid_check_in_wl_tree(e1, &ubi->scrub); @@ -799,8 +802,6 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, paranoid_check_in_wl_tree(e2, &ubi->free); rb_erase(&e2->rb, &ubi->free); - ubi_assert(!ubi->move_from && !ubi->move_to); - ubi_assert(!ubi->move_to_put && !ubi->move_from_put); ubi->move_from = e1; ubi->move_to = e2; spin_unlock(&ubi->wl_lock); @@ -810,6 +811,10 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, * We so far do not know which logical eraseblock our physical * eraseblock (@e1) belongs to. We have to read the volume identifier * header first. + * + * Note, we are protected from this PEB being unmapped and erased. The + * 'ubi_wl_put_peb()' would wait for moving to be finished if the PEB + * which is being moved was unmapped. */ err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0); @@ -824,32 +829,51 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, * likely have the VID header in place. */ dbg_wl("PEB %d has no VID header", e1->pnum); - err = 0; - } else { - ubi_err("error %d while reading VID header from PEB %d", - err, e1->pnum); - if (err > 0) - err = -EIO; + goto out_not_moved; } - goto error; + + ubi_err("error %d while reading VID header from PEB %d", + err, e1->pnum); + if (err > 0) + err = -EIO; + goto out_error; } err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); if (err) { - if (err == UBI_IO_BITFLIPS) - err = 0; - goto error; + + if (err < 0) + goto out_error; + if (err == 1) + goto out_not_moved; + + /* + * For some reason the LEB was not moved - it might be because + * the volume is being deleted. We should prevent this PEB from + * being selected for wear-levelling movement for some "time", + * so put it to the protection tree. + */ + + dbg_wl("cancelled moving PEB %d", e1->pnum); + pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_NOFS); + if (!pe) { + err = -ENOMEM; + goto out_error; + } + + protect = 1; } ubi_free_vid_hdr(ubi, vid_hdr); spin_lock(&ubi->wl_lock); + if (protect) + prot_tree_add(ubi, e1, pe, protect); if (!ubi->move_to_put) wl_tree_add(e2, &ubi->used); else put = 1; ubi->move_from = ubi->move_to = NULL; - ubi->move_from_put = ubi->move_to_put = 0; - ubi->wl_scheduled = 0; + ubi->move_to_put = ubi->wl_scheduled = 0; spin_unlock(&ubi->wl_lock); if (put) { @@ -859,62 +883,67 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, */ dbg_wl("PEB %d was put meanwhile, erase", e2->pnum); err = schedule_erase(ubi, e2, 0); - if (err) { - kmem_cache_free(ubi_wl_entry_slab, e2); - ubi_ro_mode(ubi); - } + if (err) + goto out_error; } - err = schedule_erase(ubi, e1, 0); - if (err) { - kmem_cache_free(ubi_wl_entry_slab, e1); - ubi_ro_mode(ubi); + if (!protect) { + err = schedule_erase(ubi, e1, 0); + if (err) + goto out_error; } + dbg_wl("done"); - return err; + mutex_unlock(&ubi->move_mutex); + return 0; /* - * Some error occurred. @e1 was not changed, so return it back. @e2 - * might be changed, schedule it for erasure. + * For some reasons the LEB was not moved, might be an error, might be + * something else. @e1 was not changed, so return it back. @e2 might + * be changed, schedule it for erasure. */ -error: - if (err) - dbg_wl("error %d occurred, cancel operation", err); - ubi_assert(err <= 0); - +out_not_moved: ubi_free_vid_hdr(ubi, vid_hdr); spin_lock(&ubi->wl_lock); - ubi->wl_scheduled = 0; - if (ubi->move_from_put) - put = 1; + if (scrubbing) + wl_tree_add(e1, &ubi->scrub); else wl_tree_add(e1, &ubi->used); ubi->move_from = ubi->move_to = NULL; - ubi->move_from_put = ubi->move_to_put = 0; + ubi->move_to_put = ubi->wl_scheduled = 0; spin_unlock(&ubi->wl_lock); - if (put) { - /* - * Well, the target PEB was put meanwhile, schedule it for - * erasure. - */ - dbg_wl("PEB %d was put meanwhile, erase", e1->pnum); - err = schedule_erase(ubi, e1, 0); - if (err) { - kmem_cache_free(ubi_wl_entry_slab, e1); - ubi_ro_mode(ubi); - } - } - err = schedule_erase(ubi, e2, 0); - if (err) { - kmem_cache_free(ubi_wl_entry_slab, e2); - ubi_ro_mode(ubi); - } + if (err) + goto out_error; + + mutex_unlock(&ubi->move_mutex); + return 0; + +out_error: + ubi_err("error %d while moving PEB %d to PEB %d", + err, e1->pnum, e2->pnum); - yield(); + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); + ubi->move_from = ubi->move_to = NULL; + ubi->move_to_put = ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + + kmem_cache_free(ubi_wl_entry_slab, e1); + kmem_cache_free(ubi_wl_entry_slab, e2); + ubi_ro_mode(ubi); + + mutex_unlock(&ubi->move_mutex); return err; + +out_cancel: + ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + mutex_unlock(&ubi->move_mutex); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; } /** @@ -1101,8 +1130,7 @@ out_ro: } /** - * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling - * unit. + * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling unit. * @ubi: UBI device description object * @pnum: physical eraseblock to return * @torture: if this physical eraseblock has to be tortured @@ -1110,7 +1138,7 @@ out_ro: * This function is called to return physical eraseblock @pnum to the pool of * free physical eraseblocks. The @torture flag has to be set if an I/O error * occurred to this @pnum and it has to be tested. This function returns zero - * in case of success and a negative error code in case of failure. + * in case of success, and a negative error code in case of failure. */ int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) { @@ -1121,8 +1149,8 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) ubi_assert(pnum >= 0); ubi_assert(pnum < ubi->peb_count); +retry: spin_lock(&ubi->wl_lock); - e = ubi->lookuptbl[pnum]; if (e == ubi->move_from) { /* @@ -1130,11 +1158,13 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) * be moved. It will be scheduled for erasure in the * wear-leveling worker. */ - dbg_wl("PEB %d is being moved", pnum); - ubi_assert(!ubi->move_from_put); - ubi->move_from_put = 1; + dbg_wl("PEB %d is being moved, wait", pnum); spin_unlock(&ubi->wl_lock); - return 0; + + /* Wait for the WL worker by taking the @ubi->move_mutex */ + mutex_lock(&ubi->move_mutex); + mutex_unlock(&ubi->move_mutex); + goto retry; } else if (e == ubi->move_to) { /* * User is putting the physical eraseblock which was selected @@ -1157,8 +1187,15 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) } else if (in_wl_tree(e, &ubi->scrub)) { paranoid_check_in_wl_tree(e, &ubi->scrub); rb_erase(&e->rb, &ubi->scrub); - } else - prot_tree_del(ubi, e->pnum); + } else { + err = prot_tree_del(ubi, e->pnum); + if (err) { + ubi_err("PEB %d not found", pnum); + ubi_ro_mode(ubi); + spin_unlock(&ubi->wl_lock); + return err; + } + } } spin_unlock(&ubi->wl_lock); @@ -1212,8 +1249,17 @@ retry: if (in_wl_tree(e, &ubi->used)) { paranoid_check_in_wl_tree(e, &ubi->used); rb_erase(&e->rb, &ubi->used); - } else - prot_tree_del(ubi, pnum); + } else { + int err; + + err = prot_tree_del(ubi, e->pnum); + if (err) { + ubi_err("PEB %d not found", pnum); + ubi_ro_mode(ubi); + spin_unlock(&ubi->wl_lock); + return err; + } + } wl_tree_add(e, &ubi->scrub); spin_unlock(&ubi->wl_lock); @@ -1379,6 +1425,7 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) ubi->used = ubi->free = ubi->scrub = RB_ROOT; ubi->prot.pnum = ubi->prot.aec = RB_ROOT; spin_lock_init(&ubi->wl_lock); + mutex_init(&ubi->move_mutex); ubi->max_ec = si->max_ec; INIT_LIST_HEAD(&ubi->works); -- cgit v1.2.3 From 458dbb3d07574e8fcdcb921ac155ccd81b16b05f Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 19 Dec 2007 17:03:42 +0200 Subject: UBI: fix printk Add proper log level to printk's. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 6ad291b33a1..b3efb2fa3c1 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -696,8 +696,8 @@ static int __init ubi_init(void) BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); if (mtd_devs > UBI_MAX_DEVICES) { - printk("UBI error: too many MTD devices, maximum is %d\n", - UBI_MAX_DEVICES); + printk(KERN_ERR "UBI error: too many MTD devices, " + "maximum is %d\n", UBI_MAX_DEVICES); return -EINVAL; } @@ -776,7 +776,8 @@ static int __init bytes_str_to_int(const char *str) result = simple_strtoul(str, &endp, 0); if (str == endp || result < 0) { - printk("UBI error: incorrect bytes count: \"%s\"\n", str); + printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n", + str); return -EINVAL; } @@ -794,7 +795,8 @@ static int __init bytes_str_to_int(const char *str) case '\0': break; default: - printk("UBI error: incorrect bytes count: \"%s\"\n", str); + printk(KERN_ERR "UBI error: incorrect bytes count: \"%s\"\n", + str); return -EINVAL; } @@ -821,20 +823,21 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) return -EINVAL; if (mtd_devs == UBI_MAX_DEVICES) { - printk("UBI error: too many parameters, max. is %d\n", + printk(KERN_ERR "UBI error: too many parameters, max. is %d\n", UBI_MAX_DEVICES); return -EINVAL; } len = strnlen(val, MTD_PARAM_LEN_MAX); if (len == MTD_PARAM_LEN_MAX) { - printk("UBI error: parameter \"%s\" is too long, max. is %d\n", - val, MTD_PARAM_LEN_MAX); + printk(KERN_ERR "UBI error: parameter \"%s\" is too long, " + "max. is %d\n", val, MTD_PARAM_LEN_MAX); return -EINVAL; } if (len == 0) { - printk("UBI warning: empty 'mtd=' parameter - ignored\n"); + printk(KERN_WARNING "UBI warning: empty 'mtd=' parameter - " + "ignored\n"); return 0; } @@ -848,7 +851,8 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) tokens[i] = strsep(&pbuf, ","); if (pbuf) { - printk("UBI error: too many arguments at \"%s\"\n", val); + printk(KERN_ERR "UBI error: too many arguments at \"%s\"\n", + val); return -EINVAL; } -- cgit v1.2.3 From 593dd33c92c6529443d5df1350dc5cc76511232d Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 18 Dec 2007 15:54:35 +0200 Subject: UBI: fix ubi_wl_flush The flush function should finish all the pending jobs. But if somebody else is doing a work, this function should wait and let it finish. This patche uses rw semaphore for synchronization purpose - it just looks quite convinient. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/ubi.h | 1 + drivers/mtd/ubi/wl.c | 39 ++++++++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index ea9a6990a4d..994233d6e1e 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -355,6 +355,7 @@ struct ubi_device { } prot; spinlock_t wl_lock; struct mutex move_mutex; + struct rw_semaphore work_sem; int wl_scheduled; struct ubi_wl_entry **lookuptbl; unsigned long long abs_ec; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index a60f9425ab1..8421c7a9a83 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -251,10 +251,18 @@ static int do_work(struct ubi_device *ubi) cond_resched(); + /* + * @ubi->work_sem is used to synchronize with the workers. Workers take + * it in read mode, so many of them may be doing works at a time. But + * the queue flush code has to be sure the whole queue of works is + * done, and it takes the mutex in write mode. + */ + down_read(&ubi->work_sem); spin_lock(&ubi->wl_lock); if (list_empty(&ubi->works)) { spin_unlock(&ubi->wl_lock); + up_read(&ubi->work_sem); return 0; } @@ -275,6 +283,7 @@ static int do_work(struct ubi_device *ubi) ubi->works_count -= 1; ubi_assert(ubi->works_count >= 0); spin_unlock(&ubi->wl_lock); + up_read(&ubi->work_sem); return err; } @@ -1173,7 +1182,7 @@ retry: * the WL unit has not put the PEB to the "used" tree yet, but * it is about to do this. So we just set a flag which will * tell the WL worker that the PEB is not needed anymore and - * should be sheduled for erasure. + * should be scheduled for erasure. */ dbg_wl("PEB %d is the target of data moving", pnum); ubi_assert(!ubi->move_to_put); @@ -1280,17 +1289,32 @@ retry: */ int ubi_wl_flush(struct ubi_device *ubi) { - int err, pending_count; - - pending_count = ubi->works_count; - - dbg_wl("flush (%d pending works)", pending_count); + int err; /* * Erase while the pending works queue is not empty, but not more then * the number of currently pending works. */ - while (pending_count-- > 0) { + dbg_wl("flush (%d pending works)", ubi->works_count); + while (ubi->works_count) { + err = do_work(ubi); + if (err) + return err; + } + + /* + * Make sure all the works which have been done in parallel are + * finished. + */ + down_write(&ubi->work_sem); + up_write(&ubi->work_sem); + + /* + * And in case last was the WL worker and it cancelled the LEB + * movement, flush again. + */ + while (ubi->works_count) { + dbg_wl("flush more (%d pending works)", ubi->works_count); err = do_work(ubi); if (err) return err; @@ -1426,6 +1450,7 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) ubi->prot.pnum = ubi->prot.aec = RB_ROOT; spin_lock_init(&ubi->wl_lock); mutex_init(&ubi->move_mutex); + init_rwsem(&ubi->work_sem); ubi->max_ec = si->max_ec; INIT_LIST_HEAD(&ubi->works); -- cgit v1.2.3 From 16f557ecbf96dd13d13788a6f62d4d97ae73b1f9 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 19 Dec 2007 16:03:17 +0200 Subject: UBI: fix comment Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/vtbl.c | 5 ++--- drivers/mtd/ubi/wl.c | 9 +++------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index a37dc7a213b..7a1a8a1da61 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -115,9 +115,8 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, } /** - * vol_til_check - check if volume table is not corrupted and contains sensible - * data. - * + * vtbl_check - check if volume table is not corrupted and contains sensible + * data. * @ubi: UBI device description object * @vtbl: volume table * diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 8421c7a9a83..7d32f71d6f1 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -259,7 +259,6 @@ static int do_work(struct ubi_device *ubi) */ down_read(&ubi->work_sem); spin_lock(&ubi->wl_lock); - if (list_empty(&ubi->works)) { spin_unlock(&ubi->wl_lock); up_read(&ubi->work_sem); @@ -268,6 +267,8 @@ static int do_work(struct ubi_device *ubi) wrk = list_entry(ubi->works.next, struct ubi_work, list); list_del(&wrk->list); + ubi->works_count -= 1; + ubi_assert(ubi->works_count >= 0); spin_unlock(&ubi->wl_lock); /* @@ -278,12 +279,8 @@ static int do_work(struct ubi_device *ubi) err = wrk->func(ubi, wrk, 0); if (err) ubi_err("work failed with error code %d", err); - - spin_lock(&ubi->wl_lock); - ubi->works_count -= 1; - ubi_assert(ubi->works_count >= 0); - spin_unlock(&ubi->wl_lock); up_read(&ubi->work_sem); + return err; } -- cgit v1.2.3 From 9f961b57568960a150cc9781c52824c9093a0514 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 16 Dec 2007 16:59:31 +0200 Subject: UBI: add UBI control device This patch is a preparation to make UBI devices dynamic. It adds an UBI control device which has dynamically allocated major number and registers itself as "ubi_ctrl". It does not do anything so far. The idea is that this device will allow to attach/detach MTD devices from userspace. This is symilar to what the Linux device mapper has. The next things to do are: * Fix UBI, because it now assumes UBI devices cannot go away * Implement control device ioctls which will attach/detach MTD devices Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 52 +++++++++++++++++++++++++++++++++++++++---------- drivers/mtd/ubi/cdev.c | 10 ++++++++++ drivers/mtd/ubi/ubi.h | 3 ++- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index b3efb2fa3c1..3f37b16f877 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -21,11 +21,16 @@ */ /* - * This file includes UBI initialization and building of UBI devices. At the - * moment UBI devices may only be added while UBI is initialized, but dynamic - * device add/remove functionality is planned. Also, at the moment we only - * attach UBI devices by scanning, which will become a bottleneck when flashes - * reach certain large size. Then one may improve UBI and add other methods. + * This file includes UBI initialization and building of UBI devices. + * + * When UBI is initialized, it attaches all the MTD devices specified as the + * module load parameters or the kernel boot parameters. If MTD devices were + * specified, UBI does not attach any MTD device, but it is possible to do + * later using the "UBI control device". + * + * At the moment we only attach UBI devices by scanning, which will become a + * bottleneck when flashes reach certain large size. Then one may improve UBI + * and add other methods, although it does not seem to be easy to do. */ #include @@ -33,6 +38,7 @@ #include #include #include +#include #include #include "ubi.h" @@ -70,6 +76,12 @@ struct kmem_cache *ubi_ltree_slab; /* Slab cache for wear-leveling entries */ struct kmem_cache *ubi_wl_entry_slab; +/* UBI control character device */ +static struct miscdevice ubi_ctrl_cdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "ubi_ctrl", + .fops = &ubi_ctrl_cdev_operations, +}; /* "Show" method for files in '//class/ubi/' */ static ssize_t ubi_version_show(struct class *class, char *buf) @@ -701,19 +713,31 @@ static int __init ubi_init(void) return -EINVAL; } + /* Create base sysfs directory and sysfs files */ ubi_class = class_create(THIS_MODULE, UBI_NAME_STR); - if (IS_ERR(ubi_class)) - return PTR_ERR(ubi_class); + if (IS_ERR(ubi_class)) { + err = PTR_ERR(ubi_class); + printk(KERN_ERR "UBI error: cannot create UBI class\n"); + goto out; + } err = class_create_file(ubi_class, &ubi_version); - if (err) + if (err) { + printk(KERN_ERR "UBI error: cannot create sysfs file\n"); goto out_class; + } + + err = misc_register(&ubi_ctrl_cdev); + if (err) { + printk(KERN_ERR "UBI error: cannot register device\n"); + goto out_version; + } ubi_ltree_slab = kmem_cache_create("ubi_ltree_slab", sizeof(struct ubi_ltree_entry), 0, 0, <ree_entry_ctor); if (!ubi_ltree_slab) - goto out_version; + goto out_dev_unreg; ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", sizeof(struct ubi_wl_entry), @@ -727,8 +751,11 @@ static int __init ubi_init(void) cond_resched(); err = attach_mtd_dev(p->name, p->vid_hdr_offs, p->data_offs); - if (err) + if (err) { + printk(KERN_ERR "UBI error: cannot attach %s\n", + p->name); goto out_detach; + } } return 0; @@ -739,10 +766,14 @@ out_detach: kmem_cache_destroy(ubi_wl_entry_slab); out_ltree: kmem_cache_destroy(ubi_ltree_slab); +out_dev_unreg: + misc_deregister(&ubi_ctrl_cdev); out_version: class_remove_file(ubi_class, &ubi_version); out_class: class_destroy(ubi_class); +out: + printk(KERN_ERR "UBI error: cannot initialize UBI, error %d\n", err); return err; } module_init(ubi_init); @@ -756,6 +787,7 @@ static void __exit ubi_exit(void) detach_mtd_dev(ubi_devices[i]); kmem_cache_destroy(ubi_wl_entry_slab); kmem_cache_destroy(ubi_ltree_slab); + misc_deregister(&ubi_ctrl_cdev); class_remove_file(ubi_class, &ubi_version); class_destroy(ubi_class); } diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 22c15a388f2..bc900d24cdb 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -28,6 +28,11 @@ * * Major and minor numbers are assigned dynamically to both UBI and volume * character devices. + * + * Well, there is the third kind of character devices - the UBI control + * character device, which allows to manipulate by UBI devices - create and + * delete them. In other words, it is used for attaching and detaching MTD + * devices. */ #include @@ -693,6 +698,11 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, return err; } +/* UBI control character device operations */ +struct file_operations ubi_ctrl_cdev_operations = { + .owner = THIS_MODULE, +}; + /* UBI character device operations */ struct file_operations ubi_cdev_operations = { .owner = THIS_MODULE, diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 994233d6e1e..21c028366fd 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -235,7 +235,7 @@ struct ubi_wl_entry; /** * struct ubi_device - UBI device description structure - * @dev: class device object to use the the Linux device model + * @dev: UBI device object to use the the Linux device model * @cdev: character device object to create character device * @ubi_num: UBI device number * @ubi_name: UBI device name @@ -398,6 +398,7 @@ struct ubi_device { extern struct kmem_cache *ubi_ltree_slab; extern struct kmem_cache *ubi_wl_entry_slab; +extern struct file_operations ubi_ctrl_cdev_operations; extern struct file_operations ubi_cdev_operations; extern struct file_operations ubi_vol_cdev_operations; extern struct ubi_device *ubi_devices[]; -- cgit v1.2.3 From e73f4459d969bb266f03dd4cbe21bdba8cb2732c Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 17 Dec 2007 17:37:26 +0200 Subject: UBI: add UBI devices reference counting This is one more step on the way to "removable" UBI devices. It adds reference counting for UBI devices. Every time a volume on this device is opened - the device's refcount is increased. It is also increased if someone is reading any sysfs file of this UBI device or of one of its volumes. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 142 +++++++++++++++++++++++++++++++++++++++++++----- drivers/mtd/ubi/cdev.c | 34 ++++-------- drivers/mtd/ubi/eba.c | 5 ++ drivers/mtd/ubi/kapi.c | 59 ++++++++++++++------ drivers/mtd/ubi/ubi.h | 9 ++- drivers/mtd/ubi/vmt.c | 14 +++-- drivers/mtd/ubi/wl.c | 1 + 7 files changed, 201 insertions(+), 63 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 3f37b16f877..a4faf71ee3f 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -64,9 +64,6 @@ static int mtd_devs = 0; /* MTD devices specification parameters */ static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; -/* All UBI devices in system */ -struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; - /* Root UBI "class" object (corresponds to '//class/ubi/') */ struct class *ubi_class; @@ -83,6 +80,12 @@ static struct miscdevice ubi_ctrl_cdev = { .fops = &ubi_ctrl_cdev_operations, }; +/* All UBI devices in system */ +static struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; + +/* Protects @ubi_devices and @ubi->ref_count */ +static DEFINE_SPINLOCK(ubi_devices_lock); + /* "Show" method for files in '//class/ubi/' */ static ssize_t ubi_version_show(struct class *class, char *buf) { @@ -118,37 +121,145 @@ static struct device_attribute dev_min_io_size = static struct device_attribute dev_bgt_enabled = __ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL); +/** + * ubi_get_device - get UBI device. + * @ubi_num: UBI device number + * + * This function returns UBI device description object for UBI device number + * @ubi_num, or %NULL if the device does not exist. This function increases the + * device reference count to prevent removal of the device. In other words, the + * device cannot be removed if its reference count is not zero. + */ +struct ubi_device *ubi_get_device(int ubi_num) +{ + struct ubi_device *ubi; + + spin_lock(&ubi_devices_lock); + ubi = ubi_devices[ubi_num]; + if (ubi) { + ubi_assert(ubi->ref_count >= 0); + ubi->ref_count += 1; + get_device(&ubi->dev); + } + spin_unlock(&ubi_devices_lock); + + return ubi; +} + +/** + * ubi_put_device - drop an UBI device reference. + * @ubi: UBI device description object + */ +void ubi_put_device(struct ubi_device *ubi) +{ + spin_lock(&ubi_devices_lock); + ubi->ref_count -= 1; + put_device(&ubi->dev); + spin_unlock(&ubi_devices_lock); +} + +/** + * ubi_get_by_major - get UBI device description object by character device + * major number. + * @major: major number + * + * This function is similar to 'ubi_get_device()', but it searches the device + * by its major number. + */ +struct ubi_device *ubi_get_by_major(int major) +{ + int i; + struct ubi_device *ubi; + + spin_lock(&ubi_devices_lock); + for (i = 0; i < UBI_MAX_DEVICES; i++) { + ubi = ubi_devices[i]; + if (ubi && MAJOR(ubi->cdev.dev) == major) { + ubi_assert(ubi->ref_count >= 0); + ubi->ref_count += 1; + get_device(&ubi->dev); + spin_unlock(&ubi_devices_lock); + return ubi; + } + } + spin_unlock(&ubi_devices_lock); + + return NULL; +} + +/** + * ubi_major2num - get UBI device number by character device major number. + * @major: major number + * + * This function searches UBI device number object by its major number. If UBI + * device was not found, this function returns -ENODEV, othewise the UBI device + * number is returned. + */ +int ubi_major2num(int major) +{ + int i, ubi_num = -ENODEV; + + spin_lock(&ubi_devices_lock); + for (i = 0; i < UBI_MAX_DEVICES; i++) { + struct ubi_device *ubi = ubi_devices[i]; + + if (ubi && MAJOR(ubi->cdev.dev) == major) { + ubi_num = ubi->ubi_num; + break; + } + } + spin_unlock(&ubi_devices_lock); + + return ubi_num; +} + /* "Show" method for files in '//class/ubi/ubiX/' */ static ssize_t dev_attribute_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct ubi_device *ubi; + ssize_t ret; + struct ubi_device *ubi; + /* + * The below code looks weird, but it actually makes sense. We get the + * UBI device reference from the contained 'struct ubi_device'. But it + * is unclear if the device was removed or not yet. Indeed, if the + * device was removed before we increased its reference count, + * 'ubi_get_device()' will return -ENODEV and we fail. + * + * Remember, 'struct ubi_device' is freed in the release function, so + * we still can use 'ubi->ubi_num'. + */ ubi = container_of(dev, struct ubi_device, dev); + ubi = ubi_get_device(ubi->ubi_num); + if (!ubi) + return -ENODEV; + if (attr == &dev_eraseblock_size) - return sprintf(buf, "%d\n", ubi->leb_size); + ret = sprintf(buf, "%d\n", ubi->leb_size); else if (attr == &dev_avail_eraseblocks) - return sprintf(buf, "%d\n", ubi->avail_pebs); + ret = sprintf(buf, "%d\n", ubi->avail_pebs); else if (attr == &dev_total_eraseblocks) - return sprintf(buf, "%d\n", ubi->good_peb_count); + ret = sprintf(buf, "%d\n", ubi->good_peb_count); else if (attr == &dev_volumes_count) - return sprintf(buf, "%d\n", ubi->vol_count); + ret = sprintf(buf, "%d\n", ubi->vol_count); else if (attr == &dev_max_ec) - return sprintf(buf, "%d\n", ubi->max_ec); + ret = sprintf(buf, "%d\n", ubi->max_ec); else if (attr == &dev_reserved_for_bad) - return sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); + ret = sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); else if (attr == &dev_bad_peb_count) - return sprintf(buf, "%d\n", ubi->bad_peb_count); + ret = sprintf(buf, "%d\n", ubi->bad_peb_count); else if (attr == &dev_max_vol_count) - return sprintf(buf, "%d\n", ubi->vtbl_slots); + ret = sprintf(buf, "%d\n", ubi->vtbl_slots); else if (attr == &dev_min_io_size) - return sprintf(buf, "%d\n", ubi->min_io_size); + ret = sprintf(buf, "%d\n", ubi->min_io_size); else if (attr == &dev_bgt_enabled) - return sprintf(buf, "%d\n", ubi->thread_enabled); + ret = sprintf(buf, "%d\n", ubi->thread_enabled); else BUG(); - return 0; + ubi_put_device(ubi); + return ret; } /* Fake "release" method for UBI devices */ @@ -670,6 +781,7 @@ static void detach_mtd_dev(struct ubi_device *ubi) int ubi_num = ubi->ubi_num, mtd_num = ubi->mtd->index; dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num); + ubi_assert(ubi->ref_count == 0); uif_close(ubi); ubi_eba_close(ubi); ubi_wl_close(ubi); diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index bc900d24cdb..01978b57e9c 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -55,23 +55,6 @@ #define VOL_CDEV_IOC_MAX_SEQ 2 #endif -/** - * major_to_device - get UBI device object by character device major number. - * @major: major number - * - * This function returns a pointer to the UBI device object. - */ -static struct ubi_device *major_to_device(int major) -{ - int i; - - for (i = 0; i < UBI_MAX_DEVICES; i++) - if (ubi_devices[i] && MAJOR(ubi_devices[i]->cdev.dev) == major) - return ubi_devices[i]; - BUG(); - return NULL; -} - /** * get_exclusive - get exclusive access to an UBI volume. * @desc: volume descriptor @@ -129,9 +112,11 @@ static void revoke_exclusive(struct ubi_volume_desc *desc, int mode) static int vol_cdev_open(struct inode *inode, struct file *file) { struct ubi_volume_desc *desc; - const struct ubi_device *ubi = major_to_device(imajor(inode)); - int vol_id = iminor(inode) - 1; - int mode; + int vol_id = iminor(inode) - 1, mode, ubi_num; + + ubi_num = ubi_major2num(imajor(inode)); + if (ubi_num < 0) + return ubi_num; if (file->f_mode & FMODE_WRITE) mode = UBI_READWRITE; @@ -140,7 +125,7 @@ static int vol_cdev_open(struct inode *inode, struct file *file) dbg_msg("open volume %d, mode %d", vol_id, mode); - desc = ubi_open_volume(ubi->ubi_num, vol_id, mode); + desc = ubi_open_volume(ubi_num, vol_id, mode); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -586,9 +571,9 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, if (!capable(CAP_SYS_RESOURCE)) return -EPERM; - ubi = major_to_device(imajor(inode)); - if (IS_ERR(ubi)) - return PTR_ERR(ubi); + ubi = ubi_get_by_major(imajor(inode)); + if (!ubi) + return -ENODEV; switch (cmd) { /* Create volume command */ @@ -695,6 +680,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, break; } + ubi_put_device(ubi); return err; } diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index c94f475758d..85297cde4ac 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -339,6 +339,7 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, { int err, pnum, vol_id = vol->vol_id; + ubi_assert(ubi->ref_count > 0); ubi_assert(vol->ref_count > 0); if (ubi->ro_mode) @@ -389,6 +390,7 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, struct ubi_vid_hdr *vid_hdr; uint32_t uninitialized_var(crc); + ubi_assert(ubi->ref_count > 0); ubi_assert(vol->ref_count > 0); err = leb_read_lock(ubi, vol_id, lnum); @@ -614,6 +616,7 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, int err, pnum, tries = 0, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; + ubi_assert(ubi->ref_count > 0); ubi_assert(vol->ref_count > 0); if (ubi->ro_mode) @@ -749,6 +752,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, struct ubi_vid_hdr *vid_hdr; uint32_t crc; + ubi_assert(ubi->ref_count > 0); ubi_assert(vol->ref_count > 0); if (ubi->ro_mode) @@ -865,6 +869,7 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, struct ubi_vid_hdr *vid_hdr; uint32_t crc; + ubi_assert(ubi->ref_count > 0); ubi_assert(vol->ref_count > 0); if (ubi->ro_mode) diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 780c273ff45..4ec3a33b257 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -30,23 +30,27 @@ * @ubi_num: UBI device number * @di: the information is stored here * - * This function returns %0 in case of success and a %-ENODEV if there is no - * such UBI device. + * This function returns %0 in case of success, %-EINVAL if the UBI device + * number is invalid, and %-ENODEV if there is no such UBI device. */ int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) { - const struct ubi_device *ubi; + struct ubi_device *ubi; - if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || - !ubi_devices[ubi_num]) + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return -EINVAL; + + ubi = ubi_get_device(ubi_num); + if (!ubi) return -ENODEV; - ubi = ubi_devices[ubi_num]; di->ubi_num = ubi->ubi_num; di->leb_size = ubi->leb_size; di->min_io_size = ubi->min_io_size; di->ro_mode = ubi->ro_mode; di->cdev = ubi->cdev.dev; + + ubi_put_device(ubi); return 0; } EXPORT_SYMBOL_GPL(ubi_get_device_info); @@ -111,16 +115,23 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) mode != UBI_EXCLUSIVE) return ERR_PTR(-EINVAL); - ubi = ubi_devices[ubi_num]; + /* + * First of all, we have to get the UBI device to prevent its removal. + */ + ubi = ubi_get_device(ubi_num); if (!ubi) return ERR_PTR(-ENODEV); - if (vol_id < 0 || vol_id >= ubi->vtbl_slots) - return ERR_PTR(-EINVAL); + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) { + err = -EINVAL; + goto out_put_ubi; + } desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL); - if (!desc) - return ERR_PTR(-ENOMEM); + if (!desc) { + err = -ENOMEM; + goto out_put_ubi; + } err = -ENODEV; if (!try_module_get(THIS_MODULE)) @@ -188,6 +199,8 @@ out_unlock: module_put(THIS_MODULE); out_free: kfree(desc); +out_put_ubi: + ubi_put_device(ubi); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(ubi_open_volume); @@ -205,6 +218,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, { int i, vol_id = -1, len; struct ubi_device *ubi; + struct ubi_volume_desc *ret; dbg_msg("open volume %s, mode %d", name, mode); @@ -218,7 +232,7 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) return ERR_PTR(-EINVAL); - ubi = ubi_devices[ubi_num]; + ubi = ubi_get_device(ubi_num); if (!ubi) return ERR_PTR(-ENODEV); @@ -234,10 +248,17 @@ struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, } spin_unlock(&ubi->volumes_lock); - if (vol_id < 0) - return ERR_PTR(-ENODEV); + if (vol_id >= 0) + ret = ubi_open_volume(ubi_num, vol_id, mode); + else + ret = ERR_PTR(-ENODEV); - return ubi_open_volume(ubi_num, vol_id, mode); + /* + * We should put the UBI device even in case of success, because + * 'ubi_open_volume()' took a reference as well. + */ + ubi_put_device(ubi); + return ret; } EXPORT_SYMBOL_GPL(ubi_open_volume_nm); @@ -248,10 +269,11 @@ EXPORT_SYMBOL_GPL(ubi_open_volume_nm); void ubi_close_volume(struct ubi_volume_desc *desc) { struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode); - spin_lock(&vol->ubi->volumes_lock); + spin_lock(&ubi->volumes_lock); switch (desc->mode) { case UBI_READONLY: vol->readers -= 1; @@ -263,10 +285,11 @@ void ubi_close_volume(struct ubi_volume_desc *desc) vol->exclusive = 0; } vol->ref_count -= 1; - spin_unlock(&vol->ubi->volumes_lock); + spin_unlock(&ubi->volumes_lock); - put_device(&vol->dev); kfree(desc); + put_device(&vol->dev); + ubi_put_device(ubi); module_put(THIS_MODULE); } EXPORT_SYMBOL_GPL(ubi_close_volume); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 21c028366fd..91fde0e8ff5 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -245,6 +245,7 @@ struct ubi_wl_entry; * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, * @vol->readers, @vol->writers, @vol->exclusive, * @vol->ref_count, @vol->mapping and @vol->eba_tbl. + * @ref_count: count of references on the UBI device * * @rsvd_pebs: count of reserved physical eraseblocks * @avail_pebs: count of available physical eraseblocks @@ -325,6 +326,7 @@ struct ubi_device { int vol_count; struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT]; spinlock_t volumes_lock; + int ref_count; int rsvd_pebs; int avail_pebs; @@ -401,7 +403,6 @@ extern struct kmem_cache *ubi_wl_entry_slab; extern struct file_operations ubi_ctrl_cdev_operations; extern struct file_operations ubi_cdev_operations; extern struct file_operations ubi_vol_cdev_operations; -extern struct ubi_device *ubi_devices[]; extern struct class *ubi_class; /* vtbl.c */ @@ -479,6 +480,12 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum, int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, struct ubi_vid_hdr *vid_hdr); +/* build.c */ +struct ubi_device *ubi_get_device(int ubi_num); +void ubi_put_device(struct ubi_device *ubi); +struct ubi_device *ubi_get_by_major(int major); +int ubi_major2num(int major); + /* * ubi_rb_for_each_entry - walk an RB-tree. * @rb: a pointer to type 'struct rb_node' to to use as a loop counter diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 3ed63dc3738..42d3dd70f2d 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -71,11 +71,16 @@ static ssize_t vol_attribute_show(struct device *dev, { int ret; struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); - struct ubi_device *ubi = vol->ubi; + struct ubi_device *ubi; + + ubi = ubi_get_device(vol->ubi->ubi_num); + if (!ubi) + return -ENODEV; spin_lock(&ubi->volumes_lock); if (!ubi->volumes[vol->vol_id]) { spin_unlock(&ubi->volumes_lock); + ubi_put_device(ubi); return -ENODEV; } /* Take a reference to prevent volume removal */ @@ -108,10 +113,12 @@ static ssize_t vol_attribute_show(struct device *dev, /* This must be a bug */ ret = -EINVAL; + /* We've done the operation, drop volume and UBI device references */ spin_lock(&ubi->volumes_lock); vol->ref_count -= 1; ubi_assert(vol->ref_count >= 0); spin_unlock(&ubi->volumes_lock); + ubi_put_device(ubi); return ret; } @@ -260,6 +267,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) } ubi->avail_pebs -= vol->reserved_pebs; ubi->rsvd_pebs += vol->reserved_pebs; + spin_unlock(&ubi->volumes_lock); vol->vol_id = vol_id; vol->alignment = req->alignment; @@ -267,9 +275,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) vol->vol_type = req->vol_type; vol->name_len = req->name_len; memcpy(vol->name, req->name, vol->name_len + 1); - vol->exclusive = 1; vol->ubi = ubi; - spin_unlock(&ubi->volumes_lock); /* * Finish all pending erases because there may be some LEBs belonging @@ -350,8 +356,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) goto out_sysfs; spin_lock(&ubi->volumes_lock); - ubi->vol_count += 1; - vol->exclusive = 0; ubi->volumes[vol_id] = vol; spin_unlock(&ubi->volumes_lock); diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 7d32f71d6f1..bfc64c82416 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -1303,6 +1303,7 @@ int ubi_wl_flush(struct ubi_device *ubi) * Make sure all the works which have been done in parallel are * finished. */ + ubi_assert(ubi->ref_count > 0); down_write(&ubi->work_sem); up_write(&ubi->work_sem); -- cgit v1.2.3 From cdfa788acd134a35d3e5b73d1a76fca4033d8aa9 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 17 Dec 2007 20:33:20 +0200 Subject: UBI: prepare attach and detach functions Prepare the attach and detach functions to by used outside of module initialization: * detach function checks reference count before detaching * it kills the background thread as well Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 235 +++++++++++++++++++++++++++++++++--------------- drivers/mtd/ubi/ubi.h | 5 ++ drivers/mtd/ubi/wl.c | 16 +--- 3 files changed, 169 insertions(+), 87 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index a4faf71ee3f..07145437664 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "ubi.h" /* Maximum length of the 'mtd=' parameter */ @@ -83,6 +84,9 @@ static struct miscdevice ubi_ctrl_cdev = { /* All UBI devices in system */ static struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; +/* Serializes UBI devices creations and removals */ +DEFINE_MUTEX(ubi_devices_mutex); + /* Protects @ubi_devices and @ubi->ref_count */ static DEFINE_SPINLOCK(ubi_devices_lock); @@ -192,7 +196,7 @@ struct ubi_device *ubi_get_by_major(int major) * @major: major number * * This function searches UBI device number object by its major number. If UBI - * device was not found, this function returns -ENODEV, othewise the UBI device + * device was not found, this function returns -ENODEV, otherwise the UBI device * number is returned. */ int ubi_major2num(int major) @@ -485,9 +489,9 @@ out_si: * assumed: * o EC header is always at offset zero - this cannot be changed; * o VID header starts just after the EC header at the closest address - * aligned to @io->@hdrs_min_io_size; + * aligned to @io->hdrs_min_io_size; * o data starts just after the VID header at the closest address aligned to - * @io->@min_io_size + * @io->min_io_size * * This function returns zero in case of success and a negative error code in * case of failure. @@ -508,6 +512,9 @@ static int io_init(struct ubi_device *ubi) return -EINVAL; } + if (ubi->vid_hdr_offset < 0 || ubi->leb_start < ubi->vid_hdr_offset) + return -EINVAL; + /* * Note, in this implementation we support MTD devices with 0x7FFFFFFF * physical eraseblocks maximum. @@ -616,84 +623,62 @@ static int io_init(struct ubi_device *ubi) } /** - * attach_mtd_dev - attach an MTD device. - * @mtd_dev: MTD device name or number string + * ubi_attach_mtd_dev - attach an MTD device. + * @mtd_dev: MTD device description object * @vid_hdr_offset: VID header offset * @data_offset: data offset * * This function attaches an MTD device to UBI. It first treats @mtd_dev as the * MTD device name, and tries to open it by this name. If it is unable to open, * it tries to convert @mtd_dev to an integer and open the MTD device by its - * number. Returns zero in case of success and a negative error code in case of - * failure. + * number. Returns new UBI device's number in case of success and a negative + * error code in case of failure. + * + * Note, the invocations of this function has to be serialized by the + * @ubi_devices_mutex. */ -static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, - int data_offset) +int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset, + int data_offset) { struct ubi_device *ubi; - struct mtd_info *mtd; int i, err; - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - int mtd_num; - char *endp; - - if (PTR_ERR(mtd) != -ENODEV) - return PTR_ERR(mtd); - - /* - * Probably this is not MTD device name but MTD device number - - * check this out. - */ - mtd_num = simple_strtoul(mtd_dev, &endp, 0); - if (*endp != '\0' || mtd_dev == endp) { - ubi_err("incorrect MTD device: \"%s\"", mtd_dev); - return -ENODEV; - } - - mtd = get_mtd_device(NULL, mtd_num); - if (IS_ERR(mtd)) - return PTR_ERR(mtd); - } - - /* Check if we already have the same MTD device attached */ + /* + * Check if we already have the same MTD device attached. + * + * Note, this function assumes that UBI devices creations and deletions + * are serialized, so it does not take the &ubi_devices_lock. + */ for (i = 0; i < UBI_MAX_DEVICES; i++) ubi = ubi_devices[i]; - if (ubi && ubi->mtd->index == mtd->index) { + if (ubi && mtd->index == ubi->mtd->index) { ubi_err("mtd%d is already attached to ubi%d", mtd->index, i); - err = -EINVAL; - goto out_mtd; + return -EINVAL; } - ubi = kzalloc(sizeof(struct ubi_device), GFP_KERNEL); - if (!ubi) { - err = -ENOMEM; - goto out_mtd; - } - - ubi->mtd = mtd; - /* Search for an empty slot in the @ubi_devices array */ - ubi->ubi_num = -1; for (i = 0; i < UBI_MAX_DEVICES; i++) - if (!ubi_devices[i]) { - ubi->ubi_num = i; + if (!ubi_devices[i]) break; - } - if (ubi->ubi_num == -1) { + if (i == UBI_MAX_DEVICES) { ubi_err("only %d UBI devices may be created", UBI_MAX_DEVICES); - err = -ENFILE; - goto out_free; + return -ENFILE; } - dbg_msg("attaching mtd%d to ubi%d: VID header offset %d data offset %d", - ubi->mtd->index, ubi->ubi_num, vid_hdr_offset, data_offset); + ubi = kzalloc(sizeof(struct ubi_device), GFP_KERNEL); + if (!ubi) + return -ENOMEM; + ubi->mtd = mtd; + ubi->ubi_num = i; ubi->vid_hdr_offset = vid_hdr_offset; ubi->leb_start = data_offset; + + dbg_msg("attaching mtd%d to ubi%d: VID header offset %d data offset %d", + mtd->index, ubi->ubi_num, vid_hdr_offset, data_offset); + err = io_init(ubi); if (err) goto out_free; @@ -724,8 +709,16 @@ static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, if (err) goto out_detach; - ubi_msg("attached mtd%d to ubi%d", ubi->mtd->index, ubi->ubi_num); - ubi_msg("MTD device name: \"%s\"", ubi->mtd->name); + ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); + if (IS_ERR(ubi->bgt_thread)) { + err = PTR_ERR(ubi->bgt_thread); + ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name, + err); + goto out_uif; + } + + ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi->ubi_num); + ubi_msg("MTD device name: \"%s\"", mtd->name); ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); ubi_msg("physical eraseblock size: %d bytes (%d KiB)", ubi->peb_size, ubi->peb_size >> 10); @@ -754,8 +747,10 @@ static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, } ubi_devices[ubi->ubi_num] = ubi; - return 0; + return ubi->ubi_num; +out_uif: + uif_close(ubi); out_detach: ubi_eba_close(ubi); ubi_wl_close(ubi); @@ -767,21 +762,57 @@ out_free: vfree(ubi->dbg_peb_buf); #endif kfree(ubi); -out_mtd: - put_mtd_device(mtd); return err; } /** - * detach_mtd_dev - detach an MTD device. - * @ubi: UBI device description object + * ubi_detach_mtd_dev - detach an MTD device. + * @ubi_num: UBI device number to detach from + * @anyway: detach MTD even if device reference count is not zero + * + * This function destroys an UBI device number @ubi_num and detaches the + * underlying MTD device. Returns zero in case of success and %-EBUSY if the + * UBI device is busy and cannot be destroyed, and %-EINVAL if it does not + * exist. + * + * Note, the invocations of this function has to be serialized by the + * @ubi_devices_mutex. */ -static void detach_mtd_dev(struct ubi_device *ubi) +int ubi_detach_mtd_dev(int ubi_num, int anyway) { - int ubi_num = ubi->ubi_num, mtd_num = ubi->mtd->index; + struct ubi_device *ubi; + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES) + return -EINVAL; + + spin_lock(&ubi_devices_lock); + ubi = ubi_devices[ubi_num]; + if (!ubi) { + spin_lock(&ubi_devices_lock); + return -EINVAL; + } + + if (ubi->ref_count) { + if (!anyway) { + spin_lock(&ubi_devices_lock); + return -EBUSY; + } + /* This may only happen if there is a bug */ + ubi_err("%s reference count %d, destroy anyway", + ubi->ubi_name, ubi->ref_count); + } + ubi_devices[ubi->ubi_num] = NULL; + spin_unlock(&ubi_devices_lock); + + dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi->ubi_num); + + /* + * Before freeing anything, we have to stop the background thread to + * prevent it from doing anything on this device while we are freeing. + */ + if (ubi->bgt_thread) + kthread_stop(ubi->bgt_thread); - dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num); - ubi_assert(ubi->ref_count == 0); uif_close(ubi); ubi_eba_close(ubi); ubi_wl_close(ubi); @@ -792,9 +823,9 @@ static void detach_mtd_dev(struct ubi_device *ubi) #ifdef CONFIG_MTD_UBI_DEBUG vfree(ubi->dbg_peb_buf); #endif - kfree(ubi_devices[ubi_num]); - ubi_devices[ubi_num] = NULL; - ubi_msg("mtd%d is detached from ubi%d", mtd_num, ubi_num); + ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num); + kfree(ubi); + return 0; } /** @@ -811,6 +842,46 @@ static void ltree_entry_ctor(struct kmem_cache *cache, void *obj) init_rwsem(&le->mutex); } +/** + * find_mtd_device - open an MTD device by its name or number. + * @mtd_dev: name or number of the device + * + * This function tries to open and MTD device with name @mtd_dev, and if it + * fails, then it tries to interpret the @mtd_dev string as an ASCII-coded + * integer and open an MTD device with this number. Returns MTD device + * description object in case of success and a negative error code in case of + * failure. + */ +static struct mtd_info * __init open_mtd_device(const char *mtd_dev) +{ + struct mtd_info *mtd; + + mtd = get_mtd_device_nm(mtd_dev); + if (IS_ERR(mtd)) { + int mtd_num; + char *endp; + + if (PTR_ERR(mtd) != -ENODEV) + return mtd; + + /* + * Probably this is not MTD device name but MTD device number - + * check this out. + */ + mtd_num = simple_strtoul(mtd_dev, &endp, 0); + if (*endp != '\0' || mtd_dev == endp) { + ubi_err("incorrect MTD device: \"%s\"", mtd_dev); + return ERR_PTR(-ENODEV); + } + + mtd = get_mtd_device(NULL, mtd_num); + if (IS_ERR(mtd)) + return mtd; + } + + return mtd; +} + static int __init ubi_init(void) { int err, i, k; @@ -860,10 +931,21 @@ static int __init ubi_init(void) /* Attach MTD devices */ for (i = 0; i < mtd_devs; i++) { struct mtd_dev_param *p = &mtd_dev_param[i]; + struct mtd_info *mtd; cond_resched(); - err = attach_mtd_dev(p->name, p->vid_hdr_offs, p->data_offs); - if (err) { + + mtd = open_mtd_device(p->name); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + goto out_detach; + } + + mutex_lock(&ubi_devices_mutex); + err = ubi_attach_mtd_dev(mtd, p->vid_hdr_offs, p->data_offs); + mutex_unlock(&ubi_devices_mutex); + if (err < 0) { + put_mtd_device(mtd); printk(KERN_ERR "UBI error: cannot attach %s\n", p->name); goto out_detach; @@ -874,7 +956,11 @@ static int __init ubi_init(void) out_detach: for (k = 0; k < i; k++) - detach_mtd_dev(ubi_devices[k]); + if (ubi_devices[k]) { + mutex_lock(&ubi_devices_mutex); + ubi_detach_mtd_dev(ubi_devices[k]->ubi_num, 1); + mutex_unlock(&ubi_devices_mutex); + } kmem_cache_destroy(ubi_wl_entry_slab); out_ltree: kmem_cache_destroy(ubi_ltree_slab); @@ -895,8 +981,11 @@ static void __exit ubi_exit(void) int i; for (i = 0; i < UBI_MAX_DEVICES; i++) - if (ubi_devices[i]) - detach_mtd_dev(ubi_devices[i]); + if (ubi_devices[i]) { + mutex_lock(&ubi_devices_mutex); + ubi_detach_mtd_dev(ubi_devices[i]->ubi_num, 1); + mutex_unlock(&ubi_devices_mutex); + } kmem_cache_destroy(ubi_wl_entry_slab); kmem_cache_destroy(ubi_ltree_slab); misc_deregister(&ubi_ctrl_cdev); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 91fde0e8ff5..25ff15a7fc5 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -404,6 +404,7 @@ extern struct file_operations ubi_ctrl_cdev_operations; extern struct file_operations ubi_cdev_operations; extern struct file_operations ubi_vol_cdev_operations; extern struct class *ubi_class; +extern struct mutex ubi_devices_mutex; /* vtbl.c */ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, @@ -462,6 +463,7 @@ int ubi_wl_flush(struct ubi_device *ubi); int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum); int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); void ubi_wl_close(struct ubi_device *ubi); +int ubi_thread(void *u); /* io.c */ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, @@ -481,6 +483,9 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, struct ubi_vid_hdr *vid_hdr); /* build.c */ +int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset, + int data_offset); +int ubi_detach_mtd_dev(int ubi_num, int anyway); struct ubi_device *ubi_get_device(int ubi_num); void ubi_put_device(struct ubi_device *ubi); struct ubi_device *ubi_get_by_major(int major); diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index bfc64c82416..1142aabcfc8 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -1356,7 +1356,7 @@ static void tree_destroy(struct rb_root *root) * ubi_thread - UBI background thread. * @u: the UBI device description object pointer */ -static int ubi_thread(void *u) +int ubi_thread(void *u) { int failures = 0; struct ubi_device *ubi = u; @@ -1454,18 +1454,10 @@ int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num); - ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); - if (IS_ERR(ubi->bgt_thread)) { - err = PTR_ERR(ubi->bgt_thread); - ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name, - err); - return err; - } - err = -ENOMEM; ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL); if (!ubi->lookuptbl) - goto out_free; + return err; list_for_each_entry_safe(seb, tmp, &si->erase, u.list) { cond_resched(); @@ -1598,10 +1590,6 @@ static void protection_trees_destroy(struct ubi_device *ubi) */ void ubi_wl_close(struct ubi_device *ubi) { - dbg_wl("disable \"%s\"", ubi->bgt_name); - if (ubi->bgt_thread) - kthread_stop(ubi->bgt_thread); - dbg_wl("close the UBI wear-leveling unit"); cancel_pending(ubi); -- cgit v1.2.3 From dd38fccfbc77e12417512c38508a5283ea79a375 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 19 Dec 2007 21:43:32 +0200 Subject: UBI: remove data_offset 'data_offset' parameter does not really make sense and it is not needed. Get rid of it. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 42 +++++++++++++++--------------------------- drivers/mtd/ubi/ubi.h | 3 +-- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 07145437664..403c10a668b 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -50,13 +50,11 @@ * struct mtd_dev_param - MTD device parameter description data structure. * @name: MTD device name or number string * @vid_hdr_offs: VID header offset - * @data_offs: data offset */ struct mtd_dev_param { char name[MTD_PARAM_LEN_MAX]; int vid_hdr_offs; - int data_offs; }; /* Numbers of elements set in the @mtd_dev_param array */ @@ -512,7 +510,7 @@ static int io_init(struct ubi_device *ubi) return -EINVAL; } - if (ubi->vid_hdr_offset < 0 || ubi->leb_start < ubi->vid_hdr_offset) + if (ubi->vid_hdr_offset < 0) return -EINVAL; /* @@ -562,10 +560,8 @@ static int io_init(struct ubi_device *ubi) } /* Similar for the data offset */ - if (ubi->leb_start == 0) { - ubi->leb_start = ubi->vid_hdr_offset + ubi->vid_hdr_alsize; - ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size); - } + ubi->leb_start = ubi->vid_hdr_offset + ubi->vid_hdr_alsize; + ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size); dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset); dbg_msg("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset); @@ -626,7 +622,6 @@ static int io_init(struct ubi_device *ubi) * ubi_attach_mtd_dev - attach an MTD device. * @mtd_dev: MTD device description object * @vid_hdr_offset: VID header offset - * @data_offset: data offset * * This function attaches an MTD device to UBI. It first treats @mtd_dev as the * MTD device name, and tries to open it by this name. If it is unable to open, @@ -637,8 +632,7 @@ static int io_init(struct ubi_device *ubi) * Note, the invocations of this function has to be serialized by the * @ubi_devices_mutex. */ -int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset, - int data_offset) +int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset) { struct ubi_device *ubi; int i, err; @@ -674,10 +668,9 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset, ubi->mtd = mtd; ubi->ubi_num = i; ubi->vid_hdr_offset = vid_hdr_offset; - ubi->leb_start = data_offset; - dbg_msg("attaching mtd%d to ubi%d: VID header offset %d data offset %d", - mtd->index, ubi->ubi_num, vid_hdr_offset, data_offset); + dbg_msg("attaching mtd%d to ubi%d: VID header offset %d", + mtd->index, ubi->ubi_num, vid_hdr_offset); err = io_init(ubi); if (err) @@ -942,7 +935,7 @@ static int __init ubi_init(void) } mutex_lock(&ubi_devices_mutex); - err = ubi_attach_mtd_dev(mtd, p->vid_hdr_offs, p->data_offs); + err = ubi_attach_mtd_dev(mtd, p->vid_hdr_offs); mutex_unlock(&ubi_devices_mutex); if (err < 0) { put_mtd_device(mtd); @@ -1094,13 +1087,9 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) if (tokens[1]) p->vid_hdr_offs = bytes_str_to_int(tokens[1]); - if (tokens[2]) - p->data_offs = bytes_str_to_int(tokens[2]); if (p->vid_hdr_offs < 0) return p->vid_hdr_offs; - if (p->data_offs < 0) - return p->data_offs; mtd_devs += 1; return 0; @@ -1108,16 +1097,15 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000); MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: " - "mtd=[,,]. " + "mtd=[,].\n" "Multiple \"mtd\" parameters may be specified.\n" - "MTD devices may be specified by their number or name. " - "Optional \"vid_hdr_offs\" and \"data_offs\" parameters " - "specify UBI VID header position and data starting " - "position to be used by UBI.\n" - "Example: mtd=content,1984,2048 mtd=4 - attach MTD device" - "with name content using VID header offset 1984 and data " - "start 2048, and MTD device number 4 using default " - "offsets"); + "MTD devices may be specified by their number or name.\n" + "Optional \"vid_hdr_offs\" parameter specifies UBI VID " + "header position and data starting position to be used " + "by UBI.\n" + "Example: mtd=content,1984 mtd=4 - attach MTD device" + "with name \"content\" using VID header offset 1984, and " + "MTD device number 4 with default VID header offset."); MODULE_VERSION(__stringify(UBI_VERSION)); MODULE_DESCRIPTION("UBI - Unsorted Block Images"); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 25ff15a7fc5..4c3607e5743 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -483,8 +483,7 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, struct ubi_vid_hdr *vid_hdr); /* build.c */ -int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset, - int data_offset); +int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset); int ubi_detach_mtd_dev(int ubi_num, int anyway); struct ubi_device *ubi_get_device(int ubi_num); void ubi_put_device(struct ubi_device *ubi); -- cgit v1.2.3 From 9b79cc0f84edecceb04b806b9014fcec1306c34d Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 18 Dec 2007 18:22:16 +0200 Subject: UBI: introduce attach ioctls Signed-off-by: Artem Bityutskiy --- include/mtd/ubi-user.h | 80 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 8 deletions(-) diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h index fe06ded0e6b..4d184a7f80a 100644 --- a/include/mtd/ubi-user.h +++ b/include/mtd/ubi-user.h @@ -22,6 +22,21 @@ #define __UBI_USER_H__ /* + * UBI device creation (the same as MTD device attachment) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * MTD devices may be attached using %UBI_IOCATT ioctl command of the UBI + * control device. The caller has to properly fill and pass + * &struct ubi_attach_req object - UBI will attach the MTD device specified in + * the request and return the newly created UBI device number as the ioctl + * return value. + * + * UBI device deletion (the same as MTD device detachment) + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * An UBI device maybe deleted with %UBI_IOCDET ioctl command of the UBI + * control device. + * * UBI volume creation * ~~~~~~~~~~~~~~~~~~~ * @@ -60,11 +75,12 @@ */ /* - * When a new volume is created, users may either specify the volume number they - * want to create or to let UBI automatically assign a volume number using this - * constant. + * When a new UBI volume or UBI device is created, users may either specify the + * volume/device number they want to create or to let UBI automatically assign + * the number using these constants. */ #define UBI_VOL_NUM_AUTO (-1) +#define UBI_DEV_NUM_AUTO (-1) /* Maximum volume name length */ #define UBI_MAX_VOLUME_NAME 127 @@ -80,6 +96,15 @@ /* Re-size an UBI volume */ #define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req) +/* IOCTL commands of the UBI control character device */ + +#define UBI_CTRL_IOC_MAGIC 'o' + +/* Attach an MTD device */ +#define UBI_IOCATT _IOW(UBI_CTRL_IOC_MAGIC, 64, struct ubi_attach_req) +/* Detach an MTD device */ +#define UBI_IOCDET _IOW(UBI_CTRL_IOC_MAGIC, 65, int32_t) + /* IOCTL commands of UBI volume character devices */ #define UBI_VOL_IOC_MAGIC 'O' @@ -89,6 +114,9 @@ /* An eraseblock erasure command, used for debugging, disabled by default */ #define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t) +/* Maximum MTD device name length supported by UBI */ +#define MAX_UBI_MTD_NAME_LEN 127 + /* * UBI volume type constants. * @@ -97,19 +125,55 @@ */ enum { UBI_DYNAMIC_VOLUME = 3, - UBI_STATIC_VOLUME = 4 + UBI_STATIC_VOLUME = 4, +}; + +/** + * struct ubi_attach_req - attach MTD device request. + * @ubi_num: UBI device number to create + * @mtd_num: MTD device number to attach + * @vid_hdr_offset: VID header offset (use defaults if %0) + * @padding: reserved for future, not used, has to be zeroed + * + * This data structure is used to specify MTD device UBI has to attach and the + * parameters it has to use. The number which should be assigned to the new UBI + * device is passed in @ubi_num. UBI may automatically assing the number if + * @UBI_DEV_NUM_AUTO is passed. In this case, the device number is returned in + * @ubi_num. + * + * Most applications should pass %0 in @vid_hdr_offset to make UBI use default + * offset of the VID header within physical eraseblocks. The default offset is + * the next min. I/O unit after the EC header. For example, it will be offset + * 512 in case of a 512 bytes page NAND flash with no sub-page support. Or + * it will be 512 in case of a 2KiB page NAND flash with 4 512-byte sub-pages. + * + * But in rare cases, if this optimizes things, the VID header may be placed to + * a different offset. For example, the boot-loader might do things faster if the + * VID header sits at the end of the first 2KiB NAND page with 4 sub-pages. As + * the boot-loader would not normally need to read EC headers (unless it needs + * UBI in RW mode), it might be faster to calculate ECC. This is weird example, + * but it real-life example. So, in this example, @vid_hdr_offer would be + * 2KiB-64 bytes = 1984. Note, that this position is not even 512-bytes + * aligned, which is OK, as UBI is clever enough to realize this is 4th sub-page + * of the first page and add needed padding. + */ +struct ubi_attach_req { + int32_t ubi_num; + int32_t mtd_num; + int32_t vid_hdr_offset; + uint8_t padding[12]; }; /** * struct ubi_mkvol_req - volume description data structure used in - * volume creation requests. + * volume creation requests. * @vol_id: volume number * @alignment: volume alignment * @bytes: volume size in bytes * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) - * @padding1: reserved for future, not used + * @padding1: reserved for future, not used, has to be zeroed * @name_len: volume name length - * @padding2: reserved for future, not used + * @padding2: reserved for future, not used, has to be zeroed * @name: volume name * * This structure is used by userspace programs when creating new volumes. The @@ -139,7 +203,7 @@ struct ubi_mkvol_req { int8_t padding1; int16_t name_len; int8_t padding2[4]; - char name[UBI_MAX_VOLUME_NAME+1]; + char name[UBI_MAX_VOLUME_NAME + 1]; } __attribute__ ((packed)); /** -- cgit v1.2.3 From 897a316c9e6f7fea6f1d3759797b75c0ebaec479 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 18 Dec 2007 18:23:39 +0200 Subject: UBI: handle attach ioctl Actually implement the MTD device attach/detach handlers. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 80 +++++++++++++++++++++++++++-------------- drivers/mtd/ubi/cdev.c | 95 +++++++++++++++++++++++++++++++++++++++++-------- drivers/mtd/ubi/ubi.h | 2 +- 3 files changed, 135 insertions(+), 42 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 403c10a668b..70c0b9a9e6e 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -621,18 +621,19 @@ static int io_init(struct ubi_device *ubi) /** * ubi_attach_mtd_dev - attach an MTD device. * @mtd_dev: MTD device description object + * @ubi_num: number to assign to the new UBI device * @vid_hdr_offset: VID header offset * - * This function attaches an MTD device to UBI. It first treats @mtd_dev as the - * MTD device name, and tries to open it by this name. If it is unable to open, - * it tries to convert @mtd_dev to an integer and open the MTD device by its - * number. Returns new UBI device's number in case of success and a negative - * error code in case of failure. + * This function attaches MTD device @mtd_dev to UBI and assign @ubi_num number + * to the newly created UBI device, unless @ubi_num is %UBI_DEV_NUM_AUTO, in + * which case this function finds a vacant device nubert and assings it + * automatically. Returns the new UBI device number in case of success and a + * negative error code in case of failure. * * Note, the invocations of this function has to be serialized by the * @ubi_devices_mutex. */ -int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset) +int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) { struct ubi_device *ubi; int i, err; @@ -643,22 +644,47 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset) * Note, this function assumes that UBI devices creations and deletions * are serialized, so it does not take the &ubi_devices_lock. */ - for (i = 0; i < UBI_MAX_DEVICES; i++) + for (i = 0; i < UBI_MAX_DEVICES; i++) { ubi = ubi_devices[i]; if (ubi && mtd->index == ubi->mtd->index) { - ubi_err("mtd%d is already attached to ubi%d", + dbg_err("mtd%d is already attached to ubi%d", mtd->index, i); - return -EINVAL; + return -EEXIST; } + } - /* Search for an empty slot in the @ubi_devices array */ - for (i = 0; i < UBI_MAX_DEVICES; i++) - if (!ubi_devices[i]) - break; + /* + * Make sure this MTD device is not emulated on top of an UBI volume + * already. Well, generally this recursion works fine, but there are + * different problems like the UBI module takes a reference to itself + * by attaching (and thus, opening) the emulated MTD device. This + * results in inability to unload the module. And in general it makes + * no sense to attach emulated MTD devices, so we prohibit this. + */ + if (mtd->type == MTD_UBIVOLUME) { + ubi_err("refuse attaching mtd%d - it is already emulated on " + "top of UBI", mtd->index); + return -EINVAL; + } + + if (ubi_num == UBI_DEV_NUM_AUTO) { + /* Search for an empty slot in the @ubi_devices array */ + for (ubi_num = 0; ubi_num < UBI_MAX_DEVICES; ubi_num++) + if (!ubi_devices[ubi_num]) + break; + if (ubi_num == UBI_MAX_DEVICES) { + dbg_err("only %d UBI devices may be created", UBI_MAX_DEVICES); + return -ENFILE; + } + } else { + if (ubi_num >= UBI_MAX_DEVICES) + return -EINVAL; - if (i == UBI_MAX_DEVICES) { - ubi_err("only %d UBI devices may be created", UBI_MAX_DEVICES); - return -ENFILE; + /* Make sure ubi_num is not busy */ + if (ubi_devices[ubi_num]) { + dbg_err("ubi%d already exists", ubi_num); + return -EEXIST; + } } ubi = kzalloc(sizeof(struct ubi_device), GFP_KERNEL); @@ -666,11 +692,11 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset) return -ENOMEM; ubi->mtd = mtd; - ubi->ubi_num = i; + ubi->ubi_num = ubi_num; ubi->vid_hdr_offset = vid_hdr_offset; dbg_msg("attaching mtd%d to ubi%d: VID header offset %d", - mtd->index, ubi->ubi_num, vid_hdr_offset); + mtd->index, ubi_num, vid_hdr_offset); err = io_init(ubi); if (err) @@ -710,7 +736,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset) goto out_uif; } - ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi->ubi_num); + ubi_msg("attached mtd%d to ubi%d", mtd->index, ubi_num); ubi_msg("MTD device name: \"%s\"", mtd->name); ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); ubi_msg("physical eraseblock size: %d bytes (%d KiB)", @@ -739,8 +765,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset) wake_up_process(ubi->bgt_thread); } - ubi_devices[ubi->ubi_num] = ubi; - return ubi->ubi_num; + ubi_devices[ubi_num] = ubi; + return ubi_num; out_uif: uif_close(ubi); @@ -781,23 +807,24 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) spin_lock(&ubi_devices_lock); ubi = ubi_devices[ubi_num]; if (!ubi) { - spin_lock(&ubi_devices_lock); + spin_unlock(&ubi_devices_lock); return -EINVAL; } if (ubi->ref_count) { if (!anyway) { - spin_lock(&ubi_devices_lock); + spin_unlock(&ubi_devices_lock); return -EBUSY; } /* This may only happen if there is a bug */ ubi_err("%s reference count %d, destroy anyway", ubi->ubi_name, ubi->ref_count); } - ubi_devices[ubi->ubi_num] = NULL; + ubi_devices[ubi_num] = NULL; spin_unlock(&ubi_devices_lock); - dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi->ubi_num); + ubi_assert(ubi_num == ubi->ubi_num); + dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num); /* * Before freeing anything, we have to stop the background thread to @@ -935,7 +962,8 @@ static int __init ubi_init(void) } mutex_lock(&ubi_devices_mutex); - err = ubi_attach_mtd_dev(mtd, p->vid_hdr_offs); + err = ubi_attach_mtd_dev(mtd, UBI_DEV_NUM_AUTO, + p->vid_hdr_offs); mutex_unlock(&ubi_devices_mutex); if (err < 0) { put_mtd_device(mtd); diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 01978b57e9c..a60a3a24c2a 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -44,17 +44,6 @@ #include #include "ubi.h" -/* - * Maximum sequence numbers of UBI and volume character device IOCTLs (direct - * logical eraseblock erase is a debug-only feature). - */ -#define UBI_CDEV_IOC_MAX_SEQ 2 -#ifndef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO -#define VOL_CDEV_IOC_MAX_SEQ 1 -#else -#define VOL_CDEV_IOC_MAX_SEQ 2 -#endif - /** * get_exclusive - get exclusive access to an UBI volume. * @desc: volume descriptor @@ -582,8 +571,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, struct ubi_mkvol_req req; dbg_msg("create volume"); - err = copy_from_user(&req, argp, - sizeof(struct ubi_mkvol_req)); + err = copy_from_user(&req, argp, sizeof(struct ubi_mkvol_req)); if (err) { err = -EFAULT; break; @@ -647,8 +635,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, struct ubi_rsvol_req req; dbg_msg("re-size volume"); - err = copy_from_user(&req, argp, - sizeof(struct ubi_rsvol_req)); + err = copy_from_user(&req, argp, sizeof(struct ubi_rsvol_req)); if (err) { err = -EFAULT; break; @@ -684,8 +671,86 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, return err; } +static int ctrl_cdev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + void __user *argp = (void __user *)arg; + + if (!capable(CAP_SYS_RESOURCE)) + return -EPERM; + + switch (cmd) { + /* Attach an MTD device command */ + case UBI_IOCATT: + { + struct ubi_attach_req req; + struct mtd_info *mtd; + + dbg_msg("attach MTD device"); + err = copy_from_user(&req, argp, sizeof(struct ubi_attach_req)); + if (err) { + err = -EFAULT; + break; + } + + if (req.mtd_num < 0 || + (req.ubi_num < 0 && req.ubi_num != UBI_DEV_NUM_AUTO)) { + err = -EINVAL; + break; + } + + mtd = get_mtd_device(NULL, req.mtd_num); + if (IS_ERR(mtd)) { + err = PTR_ERR(mtd); + break; + } + + /* + * Note, further request verification is done by + * 'ubi_attach_mtd_dev()'. + */ + mutex_lock(&ubi_devices_mutex); + err = ubi_attach_mtd_dev(mtd, req.ubi_num, req.vid_hdr_offset); + mutex_unlock(&ubi_devices_mutex); + if (err < 0) + put_mtd_device(mtd); + else + /* @err contains UBI device number */ + err = put_user(err, (__user int32_t *)argp); + + break; + } + + /* Detach an MTD device command */ + case UBI_IOCDET: + { + int ubi_num; + + dbg_msg("dettach MTD device"); + err = get_user(ubi_num, (__user int32_t *)argp); + if (err) { + err = -EFAULT; + break; + } + + mutex_lock(&ubi_devices_mutex); + err = ubi_detach_mtd_dev(ubi_num, 0); + mutex_unlock(&ubi_devices_mutex); + break; + } + + default: + err = -ENOTTY; + break; + } + + return err; +} + /* UBI control character device operations */ struct file_operations ubi_ctrl_cdev_operations = { + .ioctl = ctrl_cdev_ioctl, .owner = THIS_MODULE, }; diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 4c3607e5743..2a6171226f1 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -483,7 +483,7 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, struct ubi_vid_hdr *vid_hdr); /* build.c */ -int ubi_attach_mtd_dev(struct mtd_info *mtd, int vid_hdr_offset); +int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset); int ubi_detach_mtd_dev(int ubi_num, int anyway); struct ubi_device *ubi_get_device(int ubi_num); void ubi_put_device(struct ubi_device *ubi); -- cgit v1.2.3 From 783b273afab43437dca731a229d53d72faf77fd3 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 25 Dec 2007 18:13:33 +0200 Subject: UBI: use separate mutex for volumes checking Introduce a separate mutex which serializes volumes checking, because we cammot really use volumes_mutex - it cases reverse locking problems with mtd_tbl_mutex when gluebi is used - thanks to lockdep. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 1 + drivers/mtd/ubi/kapi.c | 11 +++-------- drivers/mtd/ubi/ubi.h | 1 + 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 70c0b9a9e6e..6ac133994f9 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -703,6 +703,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) goto out_free; mutex_init(&ubi->buf_mutex); + mutex_init(&ubi->ckvol_mutex); ubi->peb_buf1 = vmalloc(ubi->peb_size); if (!ubi->peb_buf1) goto out_free; diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 4ec3a33b257..146957c3380 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -169,17 +169,12 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) desc->vol = vol; desc->mode = mode; - /* - * To prevent simultaneous checks of the same volume we use - * @volumes_mutex, although it is not the purpose it was introduced - * for. - */ - mutex_lock(&ubi->volumes_mutex); + mutex_lock(&ubi->ckvol_mutex); if (!vol->checked) { /* This is the first open - check the volume */ err = ubi_check_volume(ubi, vol_id); if (err < 0) { - mutex_unlock(&ubi->volumes_mutex); + mutex_unlock(&ubi->ckvol_mutex); ubi_close_volume(desc); return ERR_PTR(err); } @@ -190,7 +185,7 @@ struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) } vol->checked = 1; } - mutex_unlock(&ubi->volumes_mutex); + mutex_unlock(&ubi->ckvol_mutex); return desc; diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 2a6171226f1..ef22f922f58 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -392,6 +392,7 @@ struct ubi_device { void *peb_buf1; void *peb_buf2; struct mutex buf_mutex; + struct mutex ckvol_mutex; #ifdef CONFIG_MTD_UBI_DEBUG void *dbg_peb_buf; struct mutex dbg_buf_mutex; -- cgit v1.2.3 From d1f3dd6cc00f5bf744118fb2820ecdf09a1f4b73 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 25 Dec 2007 19:17:00 +0200 Subject: UBI: fix mtd device string parsing UBI allows to specify MTD device name or number when the module is being loaded. When parsing MTD device identity string, it first tries to treat it as device NAME, and if that fails, it treats it as device number. Make it vice-versa as this is more logical and makes less troubles when you have an MTD device named "1" and try to load mtd1 which has different name. This is especially easy to hit when gluebi is enabled. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 6ac133994f9..0ed8105f9c1 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -867,38 +867,26 @@ static void ltree_entry_ctor(struct kmem_cache *cache, void *obj) * find_mtd_device - open an MTD device by its name or number. * @mtd_dev: name or number of the device * - * This function tries to open and MTD device with name @mtd_dev, and if it - * fails, then it tries to interpret the @mtd_dev string as an ASCII-coded - * integer and open an MTD device with this number. Returns MTD device - * description object in case of success and a negative error code in case of - * failure. + * This function tries to open and MTD device described by @mtd_dev string, + * which is first treated as an ASCII number, and if it is not true, it is + * treated as MTD device name. Returns MTD device description object in case of + * success and a negative error code in case of failure. */ static struct mtd_info * __init open_mtd_device(const char *mtd_dev) { struct mtd_info *mtd; + int mtd_num; + char *endp; - mtd = get_mtd_device_nm(mtd_dev); - if (IS_ERR(mtd)) { - int mtd_num; - char *endp; - - if (PTR_ERR(mtd) != -ENODEV) - return mtd; - + mtd_num = simple_strtoul(mtd_dev, &endp, 0); + if (*endp != '\0' || mtd_dev == endp) { /* - * Probably this is not MTD device name but MTD device number - - * check this out. + * This does not look like an ASCII integer, probably this is + * MTD device name. */ - mtd_num = simple_strtoul(mtd_dev, &endp, 0); - if (*endp != '\0' || mtd_dev == endp) { - ubi_err("incorrect MTD device: \"%s\"", mtd_dev); - return ERR_PTR(-ENODEV); - } - + mtd = get_mtd_device_nm(mtd_dev); + } else mtd = get_mtd_device(NULL, mtd_num); - if (IS_ERR(mtd)) - return mtd; - } return mtd; } -- cgit v1.2.3 From b6b76ba466bbd47397efad0fdaeaa5ebf7d462c7 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 26 Dec 2007 13:46:46 +0200 Subject: UBI: add mtd_num sysfs attribute Expose number or the underlying MTD device in sysfs. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 0ed8105f9c1..5098e6d5709 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -122,6 +122,8 @@ static struct device_attribute dev_min_io_size = __ATTR(min_io_size, S_IRUGO, dev_attribute_show, NULL); static struct device_attribute dev_bgt_enabled = __ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_mtd_num = + __ATTR(mtd_num, S_IRUGO, dev_attribute_show, NULL); /** * ubi_get_device - get UBI device. @@ -257,8 +259,10 @@ static ssize_t dev_attribute_show(struct device *dev, ret = sprintf(buf, "%d\n", ubi->min_io_size); else if (attr == &dev_bgt_enabled) ret = sprintf(buf, "%d\n", ubi->thread_enabled); + else if (attr == &dev_mtd_num) + ret = sprintf(buf, "%d\n", ubi->mtd->index); else - BUG(); + ret = -EINVAL; ubi_put_device(ubi); return ret; @@ -314,6 +318,9 @@ static int ubi_sysfs_init(struct ubi_device *ubi) if (err) return err; err = device_create_file(&ubi->dev, &dev_bgt_enabled); + if (err) + return err; + err = device_create_file(&ubi->dev, &dev_mtd_num); return err; } @@ -323,6 +330,7 @@ static int ubi_sysfs_init(struct ubi_device *ubi) */ static void ubi_sysfs_close(struct ubi_device *ubi) { + device_remove_file(&ubi->dev, &dev_mtd_num); device_remove_file(&ubi->dev, &dev_bgt_enabled); device_remove_file(&ubi->dev, &dev_min_io_size); device_remove_file(&ubi->dev, &dev_max_vol_count); -- cgit v1.2.3 From aeddb87718823fb81b896155b34d1bb4c8cae874 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 26 Dec 2007 14:25:58 +0200 Subject: UBI: do not support kiB Be strict and accept only KiB, MiB and GiB, not Kib, not kib, etc. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 5098e6d5709..b967191cc97 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -1038,10 +1038,8 @@ static int __init bytes_str_to_int(const char *str) case 'M': result *= 1024; case 'K': - case 'k': result *= 1024; - if (endp[1] == 'i' && (endp[2] == '\0' || - endp[2] == 'B' || endp[2] == 'b')) + if (endp[1] == 'i' && endp[2] == 'B') endp += 2; case '\0': break; -- cgit v1.2.3 From 4b3cc340614e552c476bec29d984c5a363b26494 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 26 Dec 2007 15:59:39 +0200 Subject: UBI: bugfix: do not forget to increment vol_count When creating a new volume, do not forget to increment the vol_count variable. Also, users are not interested in internal volumes, so do not show them in the volumes_count sysfs file. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 2 +- drivers/mtd/ubi/vmt.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index b967191cc97..8f1f9feb2d6 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -246,7 +246,7 @@ static ssize_t dev_attribute_show(struct device *dev, else if (attr == &dev_total_eraseblocks) ret = sprintf(buf, "%d\n", ubi->good_peb_count); else if (attr == &dev_volumes_count) - ret = sprintf(buf, "%d\n", ubi->vol_count); + ret = sprintf(buf, "%d\n", ubi->vol_count - UBI_INT_VOL_COUNT); else if (attr == &dev_max_ec) ret = sprintf(buf, "%d\n", ubi->max_ec); else if (attr == &dev_reserved_for_bad) diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 42d3dd70f2d..177227e1f80 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -357,6 +357,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) spin_lock(&ubi->volumes_lock); ubi->volumes[vol_id] = vol; + ubi->vol_count += 1; spin_unlock(&ubi->volumes_lock); paranoid_check_volumes(ubi); -- cgit v1.2.3 From e9d8d48253c50106d85b288939e5227083360863 Mon Sep 17 00:00:00 2001 From: David Scidmore Date: Tue, 11 Dec 2007 17:44:30 -0600 Subject: [MTD] mtdchar.c: ioctl always returns 0 as size written for ppc64 "include/linux/mtd/mtd.h" declares "mtd_oob_ops.retlen" as size_t, which is 64 bits on targets with a 64 bit addressing. The MEMWRITEOOB ioctl calls copy_to_user() to write it back to "mtd_oob_buf.length", which is declared in "include/linux/mtd-abi.h" as uint32_t. Since powerpc is a big endian architecture, this only copies the upper 32 bits of the address, which is always 0. Signed-off-by: David Scidmore Signed-off-by: David Woodhouse --- drivers/mtd/mtdchar.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 22ed96c4b7b..b42553cd9af 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -483,6 +483,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, { struct mtd_oob_buf buf; struct mtd_oob_ops ops; + uint32_t retlen; if(!(file->f_mode & 2)) return -EPERM; @@ -522,8 +523,11 @@ static int mtd_ioctl(struct inode *inode, struct file *file, buf.start &= ~(mtd->oobsize - 1); ret = mtd->write_oob(mtd, buf.start, &ops); - if (copy_to_user(argp + sizeof(uint32_t), &ops.oobretlen, - sizeof(uint32_t))) + if (ops.oobretlen > 0xFFFFFFFFU) + ret = -EOVERFLOW; + retlen = ops.oobretlen; + if (copy_to_user(&((struct mtd_oob_buf *)argp)->length, + &retlen, sizeof(buf.length))) ret = -EFAULT; kfree(ops.oobbuf); -- cgit v1.2.3 From 71053fb1c645e86feb48051d6a4c58b8f2b26806 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 31 Dec 2007 17:52:29 +0000 Subject: [MTD] [MAPS] Remove Photron PNC-2000 map driver It should be done as a physmap device, and people keep turning it on and whining about it. Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 7 ---- drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/pnc2000.c | 93 ---------------------------------------------- 3 files changed, 101 deletions(-) delete mode 100644 drivers/mtd/maps/pnc2000.c diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 93dcb780db4..12c253664eb 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -110,13 +110,6 @@ config MTD_SUN_UFLASH Sun Microsystems boardsets. This driver will require CFI support in the kernel, so if you did not enable CFI previously, do that now. -config MTD_PNC2000 - tristate "CFI Flash device mapped on Photron PNC-2000" - depends on X86 && MTD_CFI && MTD_PARTITIONS - help - PNC-2000 is the name of Network Camera product from PHOTRON - Ltd. in Japan. It uses CFI-compliant flash. - config MTD_SC520CDP tristate "CFI Flash device mapped on AMD SC520 CDP" depends on X86 && MTD_CFI && MTD_CONCAT diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 316382a1401..a9cbe80f99a 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -28,7 +28,6 @@ obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o -obj-$(CONFIG_MTD_PNC2000) += pnc2000.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o diff --git a/drivers/mtd/maps/pnc2000.c b/drivers/mtd/maps/pnc2000.c deleted file mode 100644 index d7e16c2d5c4..00000000000 --- a/drivers/mtd/maps/pnc2000.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * pnc2000.c - mapper for Photron PNC-2000 board. - * - * Copyright (C) 2000 Crossnet Co. - * - * This code is GPL - * - * $Id: pnc2000.c,v 1.18 2005/11/07 11:14:28 gleixner Exp $ - */ - -#include -#include -#include -#include - -#include -#include -#include - - -#define WINDOW_ADDR 0xbf000000 -#define WINDOW_SIZE 0x00400000 - -/* - * MAP DRIVER STUFF - */ - - -static struct map_info pnc_map = { - .name = "PNC-2000", - .size = WINDOW_SIZE, - .bankwidth = 4, - .phys = 0xFFFFFFFF, - .virt = (void __iomem *)WINDOW_ADDR, -}; - - -/* - * MTD 'PARTITIONING' STUFF - */ -static struct mtd_partition pnc_partitions[3] = { - { - .name = "PNC-2000 boot firmware", - .size = 0x20000, - .offset = 0 - }, - { - .name = "PNC-2000 kernel", - .size = 0x1a0000, - .offset = 0x20000 - }, - { - .name = "PNC-2000 filesystem", - .size = 0x240000, - .offset = 0x1c0000 - } -}; - -/* - * This is the master MTD device for which all the others are just - * auto-relocating aliases. - */ -static struct mtd_info *mymtd; - -static int __init init_pnc2000(void) -{ - printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); - - simple_map_init(&pnc_map); - - mymtd = do_map_probe("cfi_probe", &pnc_map); - if (mymtd) { - mymtd->owner = THIS_MODULE; - return add_mtd_partitions(mymtd, pnc_partitions, 3); - } - - return -ENXIO; -} - -static void __exit cleanup_pnc2000(void) -{ - if (mymtd) { - del_mtd_partitions(mymtd); - map_destroy(mymtd); - } -} - -module_init(init_pnc2000); -module_exit(cleanup_pnc2000); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Crossnet Co. "); -MODULE_DESCRIPTION("MTD map driver for Photron PNC-2000 board"); -- cgit v1.2.3 From 36f97bc617e2c31d16b74e89cd2406de4d24ede5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 6 Jan 2008 17:50:34 +0100 Subject: [JFFS2] Add missing call to posix_acl_release posix_acl_clone does a memory allocation and sets a reference count, so posix_acl_release is needed afterwards to free it. The problem was fixed using the following semantic patch. (http://www.emn.fr/x-info/coccinelle/) // @@ type T; identifier E; expression E1, E2; int ret; statement S; @@ T E; <+... ( E = \(posix_acl_clone\|posix_acl_alloc\|posix_acl_dup\)(...); if (E == NULL) S | if ((E = \(posix_acl_clone\|posix_acl_alloc\|posix_acl_dup\)(...)) == NULL) S ) ... when != E2 = E when strict ( posix_acl_release(E); | E1 = E; | + posix_acl_release(E); return; | + posix_acl_release(E); return ret; ) ...+> // Signed-off-by: Julia Lawall Acked-by: KaiGai Kohei Signed-off-by: David Woodhouse --- fs/jffs2/acl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 993ddfce031..4c80404a9ab 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -345,8 +345,10 @@ int jffs2_init_acl_pre(struct inode *dir_i, struct inode *inode, int *i_mode) if (!clone) return -ENOMEM; rc = posix_acl_create_masq(clone, (mode_t *)i_mode); - if (rc < 0) + if (rc < 0) { + posix_acl_release(clone); return rc; + } if (rc > 0) jffs2_iset_acl(inode, &f->i_acl_access, clone); -- cgit v1.2.3 From 78b65179d08e7e4466ba69d5ede85035a2c96358 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Thu, 13 Dec 2007 11:15:28 -0600 Subject: [MTD] [NAND] Don't panic if a controller driver does ecc its own way. Some hardware, such as the enhanced local bus controller used on some mpc83xx chips, does ecc transparently when reading and writing data, rather than providing a generic calculate/correct mechanism that can be exported to the nand subsystem. The subsystem should not BUG() when calculate, correct, or hwctl are missing, if the methods that call them have been overridden. Signed-off-by: Scott Wood Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index e29c1da7f56..85a7283845f 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2469,8 +2469,12 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.write_oob = nand_write_oob_std; case NAND_ECC_HW_SYNDROME: - if (!chip->ecc.calculate || !chip->ecc.correct || - !chip->ecc.hwctl) { + if ((!chip->ecc.calculate || !chip->ecc.correct || + !chip->ecc.hwctl) && + (!chip->ecc.read_page || + chip->ecc.read_page == nand_read_page_hwecc) || + !chip->ecc.write_page || + chip->ecc.write_page == nand_write_page_hwecc) { printk(KERN_WARNING "No ECC functions supplied, " "Hardware ECC not possible\n"); BUG(); -- cgit v1.2.3 From de7921f01a407e0cb38143363995db89a5f9a5c5 Mon Sep 17 00:00:00 2001 From: Bartlomiej Sieka Date: Mon, 26 Nov 2007 18:55:18 +0100 Subject: [MTD] [NOR] Fix incorrect interface code for x16/x32 chips According to "Common Flash Memory Interface Publication 100" dated December 1, 2001, the interface code for x16/x32 chips is 0x0005, and not 0x0004 used so far. Signed-off-by: Bartlomiej Sieka Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 6 ++++-- drivers/mtd/chips/cfi_probe.c | 12 ++++++------ drivers/mtd/maps/scb2_flash.c | 2 +- include/linux/mtd/cfi.h | 12 ++++++++++++ 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 571226eefeb..796bfeadea2 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -342,10 +342,12 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) /* Modify the unlock address if we are in compatibility mode */ if ( /* x16 in x8 mode */ ((cfi->device_type == CFI_DEVICETYPE_X8) && - (cfi->cfiq->InterfaceDesc == 2)) || + (cfi->cfiq->InterfaceDesc == + CFI_INTERFACE_X8_BY_X16_ASYNC)) || /* x32 in x16 mode */ ((cfi->device_type == CFI_DEVICETYPE_X16) && - (cfi->cfiq->InterfaceDesc == 4))) + (cfi->cfiq->InterfaceDesc == + CFI_INTERFACE_X16_BY_X32_ASYNC))) { cfi->addr_unlock1 = 0xaaa; cfi->addr_unlock2 = 0x555; diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index 60e11a0ada9..f651b6ef1c5 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -370,27 +370,27 @@ static void print_cfi_ident(struct cfi_ident *cfip) printk("Device size: 0x%X bytes (%d MiB)\n", 1 << cfip->DevSize, 1<< (cfip->DevSize - 20)); printk("Flash Device Interface description: 0x%4.4X\n", cfip->InterfaceDesc); switch(cfip->InterfaceDesc) { - case 0: + case CFI_INTERFACE_X8_ASYNC: printk(" - x8-only asynchronous interface\n"); break; - case 1: + case CFI_INTERFACE_X16_ASYNC: printk(" - x16-only asynchronous interface\n"); break; - case 2: + case CFI_INTERFACE_X8_BY_X16_ASYNC: printk(" - supports x8 and x16 via BYTE# with asynchronous interface\n"); break; - case 3: + case CFI_INTERFACE_X32_ASYNC: printk(" - x32-only asynchronous interface\n"); break; - case 4: + case CFI_INTERFACE_X16_BY_X32_ASYNC: printk(" - supports x16 and x32 via Word# with asynchronous interface\n"); break; - case 65535: + case CFI_INTERFACE_NOT_ALLOWED: printk(" - Not Allowed / Reserved\n"); break; diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index dcfb85840d1..0fc5584324e 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -79,7 +79,7 @@ scb2_fixup_mtd(struct mtd_info *mtd) struct cfi_private *cfi = map->fldrv_priv; /* barf if this doesn't look right */ - if (cfi->cfiq->InterfaceDesc != 1) { + if (cfi->cfiq->InterfaceDesc != CFI_INTERFACE_X16_ASYNC) { printk(KERN_ERR MODNAME ": unsupported InterfaceDesc: %#x\n", cfi->cfiq->InterfaceDesc); return -1; diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index e17c5343cf5..b0ddf4b2586 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -98,6 +98,18 @@ static inline int cfi_interleave_supported(int i) #define CFI_DEVICETYPE_X32 (32 / 8) #define CFI_DEVICETYPE_X64 (64 / 8) + +/* Device Interface Code Assignments from the "Common Flash Memory Interface + * Publication 100" dated December 1, 2001. + */ +#define CFI_INTERFACE_X8_ASYNC 0x0000 +#define CFI_INTERFACE_X16_ASYNC 0x0001 +#define CFI_INTERFACE_X8_BY_X16_ASYNC 0x0002 +#define CFI_INTERFACE_X32_ASYNC 0x0003 +#define CFI_INTERFACE_X16_BY_X32_ASYNC 0x0005 +#define CFI_INTERFACE_NOT_ALLOWED 0xffff + + /* NB: We keep these structures in memory in HOST byteorder, except * where individually noted. */ -- cgit v1.2.3 From 4fac9f698404a5cd50b978fbdb7e54235353c215 Mon Sep 17 00:00:00 2001 From: Matt Reimer Date: Thu, 18 Oct 2007 18:02:44 -0700 Subject: [MTD] [NAND] make s3c2410 indicate an error for multi-bit read errors If there were multiple bit errors in the data s3c2410_nand_correct_data() was returning 0 (no error) instead of -1, so the upper layers (like JFFS2) would not know the data is corrupt. Signed-off-by: Matt Reimer Signed-off-by: David Woodhouse --- drivers/mtd/nand/s3c2410.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 512acfc8901..21a2cc8636d 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -401,7 +401,7 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, if ((diff0 & ~(1< Date: Wed, 16 Jan 2008 12:11:54 +0200 Subject: UBI: get rid of ubi_ltree_slab This slab cache is not really needed since the number of objects is low and the constructor does not make much sense because we allocate oblects when doint I/O, which is way slower then allocation. Suggested-by: Arnd Bergmann Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 28 +--------------------------- drivers/mtd/ubi/eba.c | 12 +++++++----- drivers/mtd/ubi/ubi.h | 1 - 3 files changed, 8 insertions(+), 33 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 8f1f9feb2d6..8b4573559df 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -66,9 +66,6 @@ static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; /* Root UBI "class" object (corresponds to '//class/ubi/') */ struct class *ubi_class; -/* Slab cache for lock-tree entries */ -struct kmem_cache *ubi_ltree_slab; - /* Slab cache for wear-leveling entries */ struct kmem_cache *ubi_wl_entry_slab; @@ -857,20 +854,6 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) return 0; } -/** - * ltree_entry_ctor - lock tree entries slab cache constructor. - * @obj: the lock-tree entry to construct - * @cache: the lock tree entry slab cache - * @flags: constructor flags - */ -static void ltree_entry_ctor(struct kmem_cache *cache, void *obj) -{ - struct ubi_ltree_entry *le = obj; - - le->users = 0; - init_rwsem(&le->mutex); -} - /** * find_mtd_device - open an MTD device by its name or number. * @mtd_dev: name or number of the device @@ -933,17 +916,11 @@ static int __init ubi_init(void) goto out_version; } - ubi_ltree_slab = kmem_cache_create("ubi_ltree_slab", - sizeof(struct ubi_ltree_entry), 0, - 0, <ree_entry_ctor); - if (!ubi_ltree_slab) - goto out_dev_unreg; - ubi_wl_entry_slab = kmem_cache_create("ubi_wl_entry_slab", sizeof(struct ubi_wl_entry), 0, 0, NULL); if (!ubi_wl_entry_slab) - goto out_ltree; + goto out_dev_unreg; /* Attach MTD devices */ for (i = 0; i < mtd_devs; i++) { @@ -980,8 +957,6 @@ out_detach: mutex_unlock(&ubi_devices_mutex); } kmem_cache_destroy(ubi_wl_entry_slab); -out_ltree: - kmem_cache_destroy(ubi_ltree_slab); out_dev_unreg: misc_deregister(&ubi_ctrl_cdev); out_version: @@ -1005,7 +980,6 @@ static void __exit ubi_exit(void) mutex_unlock(&ubi_devices_mutex); } kmem_cache_destroy(ubi_wl_entry_slab); - kmem_cache_destroy(ubi_ltree_slab); misc_deregister(&ubi_ctrl_cdev); class_remove_file(ubi_class, &ubi_version); class_destroy(ubi_class); diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 85297cde4ac..7c05c6e1abc 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -137,10 +137,12 @@ static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi, { struct ubi_ltree_entry *le, *le1, *le_free; - le = kmem_cache_alloc(ubi_ltree_slab, GFP_NOFS); + le = kmalloc(sizeof(struct ubi_ltree_entry), GFP_NOFS); if (!le) return ERR_PTR(-ENOMEM); + le->users = 0; + init_rwsem(&le->mutex); le->vol_id = vol_id; le->lnum = lnum; @@ -188,7 +190,7 @@ static struct ubi_ltree_entry *ltree_add_entry(struct ubi_device *ubi, spin_unlock(&ubi->ltree_lock); if (le_free) - kmem_cache_free(ubi_ltree_slab, le_free); + kfree(le_free); return le; } @@ -236,7 +238,7 @@ static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum) up_read(&le->mutex); if (free) - kmem_cache_free(ubi_ltree_slab, le); + kfree(le); } /** @@ -292,7 +294,7 @@ static int leb_write_trylock(struct ubi_device *ubi, int vol_id, int lnum) free = 0; spin_unlock(&ubi->ltree_lock); if (free) - kmem_cache_free(ubi_ltree_slab, le); + kfree(le); return 1; } @@ -321,7 +323,7 @@ static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) up_write(&le->mutex); if (free) - kmem_cache_free(ubi_ltree_slab, le); + kfree(le); } /** diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index ef22f922f58..3cf1aa1a024 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -399,7 +399,6 @@ struct ubi_device { #endif }; -extern struct kmem_cache *ubi_ltree_slab; extern struct kmem_cache *ubi_wl_entry_slab; extern struct file_operations ubi_ctrl_cdev_operations; extern struct file_operations ubi_cdev_operations; -- cgit v1.2.3 From ae616e1be13599c3b64e544ebe99e69ea851e99c Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 16 Jan 2008 12:15:47 +0200 Subject: UBI: fix warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/mtd/ubi/cdev.c: In function ‘vol_cdev_read’: drivers/mtd/ubi/cdev.c:187: warning: unused variable ‘vol_id’ CC [M] drivers/mtd/ubi/kapi.o drivers/mtd/ubi/kapi.c: In function ‘ubi_leb_erase’: drivers/mtd/ubi/kapi.c:483: warning: unused variable ‘vol_id’ drivers/mtd/ubi/kapi.c: In function ‘ubi_leb_unmap’: drivers/mtd/ubi/kapi.c:544: warning: unused variable ‘vol_id’ drivers/mtd/ubi/kapi.c: In function ‘ubi_leb_map’: drivers/mtd/ubi/kapi.c:582: warning: unused variable ‘vol_id’ Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 10 +++++----- drivers/mtd/ubi/kapi.c | 10 ++++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index a60a3a24c2a..a7aa123afaf 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -184,13 +184,13 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, struct ubi_volume_desc *desc = file->private_data; struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - int err, lnum, off, len, vol_id = desc->vol->vol_id, tbuf_size; + int err, lnum, off, len, tbuf_size; size_t count_save = count; void *tbuf; uint64_t tmp; dbg_msg("read %zd bytes from offset %lld of volume %d", - count, *offp, vol_id); + count, *offp, vol->vol_id); if (vol->updating) { dbg_err("updating"); @@ -204,7 +204,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, return 0; if (vol->corrupted) - dbg_msg("read from corrupted volume %d", vol_id); + dbg_msg("read from corrupted volume %d", vol->vol_id); if (*offp + count > vol->used_bytes) count_save = count = vol->used_bytes - *offp; @@ -268,13 +268,13 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, struct ubi_volume_desc *desc = file->private_data; struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - int lnum, off, len, tbuf_size, vol_id = vol->vol_id, err = 0; + int lnum, off, len, tbuf_size, err = 0; size_t count_save = count; char *tbuf; uint64_t tmp; dbg_msg("requested: write %zd bytes to offset %lld of volume %u", - count, *offp, desc->vol->vol_id); + count, *offp, vol->vol_id); if (vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 146957c3380..a70d58823f8 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -480,9 +480,9 @@ int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum) { struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - int err, vol_id = vol->vol_id; + int err; - dbg_msg("erase LEB %d:%d", vol_id, lnum); + dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; @@ -541,9 +541,8 @@ int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) { struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - int vol_id = vol->vol_id; - dbg_msg("unmap LEB %d:%d", vol_id, lnum); + dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum); if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; @@ -579,9 +578,8 @@ int ubi_leb_map(struct ubi_volume_desc *desc, int lnum, int dtype) { struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - int vol_id = vol->vol_id; - dbg_msg("unmap LEB %d:%d", vol_id, lnum); + dbg_msg("unmap LEB %d:%d", vol->vol_id, lnum); if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) return -EROFS; -- cgit v1.2.3 From 896c0c06aa30147630e9a75949b6ae2014c841fc Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 16 Jan 2008 14:24:14 +0200 Subject: UBI: use bit-fields Save 12 bytes of RAM per volume by using bit-fields instead of integers. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/ubi.h | 16 ++++++++-------- drivers/mtd/ubi/vmt.c | 12 +----------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 3cf1aa1a024..90cdcad83cb 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -144,7 +144,6 @@ struct ubi_volume_desc; * @readers: number of users holding this volume in read-only mode * @writers: number of users holding this volume in read-write mode * @exclusive: whether somebody holds this volume in exclusive mode - * @checked: if this static volume was checked * * @reserved_pebs: how many physical eraseblocks are reserved for this volume * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) @@ -152,21 +151,22 @@ struct ubi_volume_desc; * @used_ebs: how many logical eraseblocks in this volume contain data * @last_eb_bytes: how many bytes are stored in the last logical eraseblock * @used_bytes: how many bytes of data this volume contains - * @upd_marker: non-zero if the update marker is set for this volume - * @corrupted: non-zero if the volume is corrupted (static volumes only) * @alignment: volume alignment * @data_pad: how many bytes are not used at the end of physical eraseblocks to * satisfy the requested alignment * @name_len: volume name length * @name: volume name * - * @updating: whether the volume is being updated * @upd_ebs: how many eraseblocks are expected to be updated * @upd_bytes: how many bytes are expected to be received * @upd_received: how many update bytes were already received * @upd_buf: update buffer which is used to collect update data * * @eba_tbl: EBA table of this volume (LEB->PEB mapping) + * @checked: %1 if this static volume was checked + * @corrupted: %1 if the volume is corrupted (static volumes only) + * @upd_marker: %1 if the update marker is set for this volume + * @updating: %1 if the volume is being updated * * @gluebi_desc: gluebi UBI volume descriptor * @gluebi_refcount: reference count of the gluebi MTD device @@ -189,7 +189,6 @@ struct ubi_volume { int readers; int writers; int exclusive; - int checked; int reserved_pebs; int vol_type; @@ -197,20 +196,21 @@ struct ubi_volume { int used_ebs; int last_eb_bytes; long long used_bytes; - int upd_marker; - int corrupted; int alignment; int data_pad; int name_len; char name[UBI_VOL_NAME_MAX+1]; - int updating; int upd_ebs; long long upd_bytes; long long upd_received; void *upd_buf; int *eba_tbl; + int checked:1; + int corrupted:1; + int upd_marker:1; + int updating:1; #ifdef CONFIG_MTD_UBI_GLUEBI /* Gluebi-related stuff may be compiled out */ diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 177227e1f80..221ce70be56 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -526,7 +526,6 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) } spin_unlock(&ubi->volumes_lock); - /* Reserve physical eraseblocks */ pebs = reserved_pebs - vol->reserved_pebs; if (pebs > 0) { @@ -746,11 +745,6 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) goto fail; } - if (vol->upd_marker != 0 && vol->upd_marker != 1) { - ubi_err("bad upd_marker"); - goto fail; - } - if (vol->upd_marker && vol->corrupted) { dbg_err("update marker and corrupted simultaneously"); goto fail; @@ -785,7 +779,7 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) n = (long long)vol->used_ebs * vol->usable_leb_size; if (vol->vol_type == UBI_DYNAMIC_VOLUME) { - if (vol->corrupted != 0) { + if (vol->corrupted) { ubi_err("corrupted dynamic volume"); goto fail; } @@ -802,10 +796,6 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) goto fail; } } else { - if (vol->corrupted != 0 && vol->corrupted != 1) { - ubi_err("bad corrupted"); - goto fail; - } if (vol->used_ebs < 0 || vol->used_ebs > vol->reserved_pebs) { ubi_err("bad used_ebs"); goto fail; -- cgit v1.2.3 From 4ccf8cffa963c7b5bdc6d455ea9417084ee49aa8 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Wed, 16 Jan 2008 15:44:24 +0200 Subject: UBI: add auto-resize feature The problem: NAND flashes have different amount of initial bad physical eraseblocks (marked as bad by the manufacturer). For example, for 256MiB Samsung OneNAND flash there might be from 0 to 40 bad initial eraseblocks, which is about 2%. When UBI is used as the base system, one needs to know the exact amount of good physical eraseblocks, because this number is needed to create the UBI image which is put to the devices during production. But this number is not know, which forces us to use the minimum number of good physical eraseblocks. And UBI additionally reserves some percentage of physical eraseblocks for bad block handling (default is 1%), so we have 1-3% of PEBs reserved at the end, depending on the amount of initial bad PEBs. But it is desired to always have 1% (or more, depending on the configuration). Solution: this patch adds an "auto-resize" flag to the volume table. The volume which has the "auto-resize" flag will automatically be re-sized (enlarged) on the first UBI initialization. UBI clears the flag when the volume is re-sized. Only one volume may have the "auto-resize" flag. So, the production UBI image may have one volume with "auto-resize" flag set, and its size is automatically adjusted on the first boot of the device. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 69 ++++++++++++++++++++++++++++++++++++++++++++---- drivers/mtd/ubi/eba.c | 15 ----------- drivers/mtd/ubi/ubi.h | 6 ++++- drivers/mtd/ubi/vmt.c | 2 -- drivers/mtd/ubi/vtbl.c | 11 ++++++++ drivers/mtd/ubi/wl.c | 1 - include/mtd/ubi-header.h | 43 ++++++++++++++++++++++++++++-- 7 files changed, 121 insertions(+), 26 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 8b4573559df..4e761e957de 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -366,9 +366,6 @@ static int uif_init(struct ubi_device *ubi) int i, err; dev_t dev; - mutex_init(&ubi->volumes_mutex); - spin_lock_init(&ubi->volumes_lock); - sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); /* @@ -623,6 +620,58 @@ static int io_init(struct ubi_device *ubi) return 0; } +/** + * autoresize - re-size the volume which has the "auto-resize" flag set. + * @ubi: UBI device description object + * @vol_id: ID of the volume to re-size + * + * This function re-sizes the volume marked by the @UBI_VTBL_AUTORESIZE_FLG in + * the volume table to the largest possible size. See comments in ubi-header.h + * for more description of the flag. Returns zero in case of success and a + * negative error code in case of failure. + */ +static int autoresize(struct ubi_device *ubi, int vol_id) +{ + struct ubi_volume_desc desc; + struct ubi_volume *vol = ubi->volumes[vol_id]; + int err, old_reserved_pebs = vol->reserved_pebs; + + /* + * Clear the auto-resize flag in the volume in-memory copy of the + * volume table, and 'ubi_resize_volume()' will propogate this change + * to the flash. + */ + ubi->vtbl[vol_id].flags &= ~UBI_VTBL_AUTORESIZE_FLG; + + if (ubi->avail_pebs == 0) { + struct ubi_vtbl_record vtbl_rec; + + /* + * No avalilable PEBs to re-size the volume, clear the flag on + * flash and exit. + */ + memcpy(&vtbl_rec, &ubi->vtbl[vol_id], + sizeof(struct ubi_vtbl_record)); + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + if (err) + ubi_err("cannot clean auto-resize flag for volume %d", + vol_id); + } else { + desc.vol = vol; + err = ubi_resize_volume(&desc, + old_reserved_pebs + ubi->avail_pebs); + if (err) + ubi_err("cannot auto-resize volume %d", vol_id); + } + + if (err) + return err; + + ubi_msg("volume %d (\"%s\") re-sized from %d to %d LEBs", vol_id, + vol->name, old_reserved_pebs, vol->reserved_pebs); + return 0; +} + /** * ubi_attach_mtd_dev - attach an MTD device. * @mtd_dev: MTD device description object @@ -699,6 +748,12 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) ubi->mtd = mtd; ubi->ubi_num = ubi_num; ubi->vid_hdr_offset = vid_hdr_offset; + ubi->autoresize_vol_id = -1; + + mutex_init(&ubi->buf_mutex); + mutex_init(&ubi->ckvol_mutex); + mutex_init(&ubi->volumes_mutex); + spin_lock_init(&ubi->volumes_lock); dbg_msg("attaching mtd%d to ubi%d: VID header offset %d", mtd->index, ubi_num, vid_hdr_offset); @@ -707,8 +762,6 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) if (err) goto out_free; - mutex_init(&ubi->buf_mutex); - mutex_init(&ubi->ckvol_mutex); ubi->peb_buf1 = vmalloc(ubi->peb_size); if (!ubi->peb_buf1) goto out_free; @@ -730,6 +783,12 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) goto out_free; } + if (ubi->autoresize_vol_id != -1) { + err = autoresize(ubi, ubi->autoresize_vol_id); + if (err) + goto out_detach; + } + err = uif_init(ubi); if (err) goto out_detach; diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 7c05c6e1abc..1f951e39c53 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -341,9 +341,6 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, { int err, pnum, vol_id = vol->vol_id; - ubi_assert(ubi->ref_count > 0); - ubi_assert(vol->ref_count > 0); - if (ubi->ro_mode) return -EROFS; @@ -392,9 +389,6 @@ int ubi_eba_read_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, struct ubi_vid_hdr *vid_hdr; uint32_t uninitialized_var(crc); - ubi_assert(ubi->ref_count > 0); - ubi_assert(vol->ref_count > 0); - err = leb_read_lock(ubi, vol_id, lnum); if (err) return err; @@ -618,9 +612,6 @@ int ubi_eba_write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, int err, pnum, tries = 0, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; - ubi_assert(ubi->ref_count > 0); - ubi_assert(vol->ref_count > 0); - if (ubi->ro_mode) return -EROFS; @@ -754,9 +745,6 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol, struct ubi_vid_hdr *vid_hdr; uint32_t crc; - ubi_assert(ubi->ref_count > 0); - ubi_assert(vol->ref_count > 0); - if (ubi->ro_mode) return -EROFS; @@ -871,9 +859,6 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, struct ubi_vid_hdr *vid_hdr; uint32_t crc; - ubi_assert(ubi->ref_count > 0); - ubi_assert(vol->ref_count > 0); - if (ubi->ro_mode) return -EROFS; diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 90cdcad83cb..a8cdbd0364f 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -250,9 +250,11 @@ struct ubi_wl_entry; * @rsvd_pebs: count of reserved physical eraseblocks * @avail_pebs: count of available physical eraseblocks * @beb_rsvd_pebs: how many physical eraseblocks are reserved for bad PEB - * handling + * handling * @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling * + * @autoresize_vol_id: ID of the volume which has to be auto-resized at the end + * of UBI ititializetion * @vtbl_slots: how many slots are available in the volume table * @vtbl_size: size of the volume table in bytes * @vtbl: in-RAM volume table copy @@ -333,12 +335,14 @@ struct ubi_device { int beb_rsvd_pebs; int beb_rsvd_level; + int autoresize_vol_id; int vtbl_slots; int vtbl_size; struct ubi_vtbl_record *vtbl; struct mutex volumes_mutex; int max_ec; + /* TODO: mean_ec is not updated run-time, fix */ int mean_ec; /* EBA unit's stuff */ diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 221ce70be56..a3ca2257e60 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -497,8 +497,6 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) dbg_msg("re-size volume %d to from %d to %d PEBs", vol_id, vol->reserved_pebs, reserved_pebs); - ubi_assert(desc->mode == UBI_EXCLUSIVE); - ubi_assert(vol == ubi->volumes[vol_id]); if (vol->vol_type == UBI_STATIC_VOLUME && reserved_pebs < vol->used_ebs) { diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index 7a1a8a1da61..2fd9cf4cea7 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -514,6 +514,17 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, vol->name[vol->name_len] = '\0'; vol->vol_id = i; + if (vtbl[i].flags & UBI_VTBL_AUTORESIZE_FLG) { + /* Auto re-size flag may be set only for one volume */ + if (ubi->autoresize_vol_id != -1) { + ubi_err("more then one auto-resize volume (%d " + "and %d)", ubi->autoresize_vol_id, i); + return -EINVAL; + } + + ubi->autoresize_vol_id = i; + } + ubi_assert(!ubi->volumes[i]); ubi->volumes[i] = vol; ubi->vol_count += 1; diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 1142aabcfc8..8bfb7434c99 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -1303,7 +1303,6 @@ int ubi_wl_flush(struct ubi_device *ubi) * Make sure all the works which have been done in parallel are * finished. */ - ubi_assert(ubi->ref_count > 0); down_write(&ubi->work_sem); up_write(&ubi->work_sem); diff --git a/include/mtd/ubi-header.h b/include/mtd/ubi-header.h index 74efa776347..69d5d7e2289 100644 --- a/include/mtd/ubi-header.h +++ b/include/mtd/ubi-header.h @@ -57,6 +57,43 @@ enum { UBI_VID_STATIC = 2 }; +/* + * Volume flags used in the volume table record. + * + * @UBI_VTBL_AUTORESIZE_FLG: auto-resize this volume + * + * %UBI_VTBL_AUTORESIZE_FLG flag can be set only for one volume in the volume + * table. UBI automatically re-sizes the volume which has this flag and makes + * the volume to be of largest possible size. This means that if after the + * initialization UBI finds out that there are available physical eraseblocks + * present on the device, it automatically appends all of them to the volume + * (the physical eraseblocks reserved for bad eraseblocks handling and other + * reserved physical eraseblocks are not taken). So, if there is a volume with + * the %UBI_VTBL_AUTORESIZE_FLG flag set, the amount of available logical + * eraseblocks will be zero after UBI is loaded, because all of them will be + * reserved for this volume. Note, the %UBI_VTBL_AUTORESIZE_FLG bit is cleared + * after the volume had been initialized. + * + * The auto-resize feature is useful for device production purposes. For + * example, different NAND flash chips may have different amount of initial bad + * eraseblocks, depending of particular chip instance. Manufacturers of NAND + * chips usually guarantee that the amount of initial bad eraseblocks does not + * exceed certain percent, e.g. 2%. When one creates an UBI image which will be + * flashed to the end devices in production, he does not know the exact amount + * of good physical eraseblocks the NAND chip on the device will have, but this + * number is required to calculate the volume sized and put them to the volume + * table of the UBI image. In this case, one of the volumes (e.g., the one + * which will store the root file system) is marked as "auto-resizable", and + * UBI will adjust its size on the first boot if needed. + * + * Note, first UBI reserves some amount of physical eraseblocks for bad + * eraseblock handling, and then re-sizes the volume, not vice-versa. This + * means that the pool of reserved physical eraseblocks will always be present. + */ +enum { + UBI_VTBL_AUTORESIZE_FLG = 0x01, +}; + /* * Compatibility constants used by internal volumes. * @@ -289,7 +326,8 @@ struct ubi_vid_hdr { * @upd_marker: if volume update was started but not finished * @name_len: volume name length * @name: the volume name - * @padding2: reserved, zeroes + * @flags: volume flags (%UBI_VTBL_AUTORESIZE_FLG) + * @padding: reserved, zeroes * @crc: a CRC32 checksum of the record * * The volume table records are stored in the volume table, which is stored in @@ -324,7 +362,8 @@ struct ubi_vtbl_record { __u8 upd_marker; __be16 name_len; __u8 name[UBI_VOL_NAME_MAX+1]; - __u8 padding2[24]; + __u8 flags; + __u8 padding[23]; __be32 crc; } __attribute__ ((packed)); -- cgit v1.2.3 From ddc4939161c502452392b353f9e0dd088239e4c1 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 17 Jan 2008 15:35:57 +0200 Subject: UBI: amend array size Since the data offset parameter was removed, the size of the parameters array is now 2, not 3. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 4e761e957de..51bff88342a 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -1099,7 +1099,7 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) struct mtd_dev_param *p; char buf[MTD_PARAM_LEN_MAX]; char *pbuf = &buf[0]; - char *tokens[3] = {NULL, NULL, NULL}; + char *tokens[2] = {NULL, NULL}; if (!val) return -EINVAL; @@ -1129,7 +1129,7 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) if (buf[len - 1] == '\n') buf[len - 1] = '\0'; - for (i = 0; i < 3; i++) + for (i = 0; i < 2; i++) tokens[i] = strsep(&pbuf, ","); if (pbuf) { -- cgit v1.2.3 From d536058752274b2fe60135142da550b5355ffa94 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 17 Jan 2008 15:41:14 +0200 Subject: UBI: bugfix: calculate data offset properly Data offset is VID header offset + VID header size aligned to the min. I/O unit size up. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/build.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 51bff88342a..6ac81e35355 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -562,7 +562,7 @@ static int io_init(struct ubi_device *ubi) } /* Similar for the data offset */ - ubi->leb_start = ubi->vid_hdr_offset + ubi->vid_hdr_alsize; + ubi->leb_start = ubi->vid_hdr_offset + UBI_EC_HDR_SIZE; ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size); dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset); -- cgit v1.2.3 From 2f9270e7fe86591d6ba01c0df6ad3f6c035687ea Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 22 Jan 2008 12:31:30 +0200 Subject: UBI: remove bogus assertion Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/scan.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index c7b0afc9d28..0c05f7b9012 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -390,7 +390,6 @@ out_free_buf: vfree(buf); out_free_vidh: ubi_free_vid_hdr(ubi, vh); - ubi_assert(err < 0); return err; } -- cgit v1.2.3 From 64203195edf44601d9825284101dcaf7ad54ece8 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 22 Jan 2008 12:38:15 +0200 Subject: UBI: add sanity check Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/scan.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 0c05f7b9012..e4766387050 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -286,9 +286,14 @@ static int compare_lebs(struct ubi_device *ubi, const struct ubi_scan_leb *seb, * FIXME: but this is anyway obsolete and will be removed at * some point. */ - dbg_bld("using old crappy leb_ver stuff"); + if (v1 == v2) { + ubi_err("PEB %d and PEB %d have the same version %lld", + seb->pnum, pnum, v1); + return -EINVAL; + } + abs = v1 - v2; if (abs < 0) abs = -abs; -- cgit v1.2.3 From c18a84186cc05bee19d55823f1a35f4ea91a92d6 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 24 Jan 2008 11:19:14 +0200 Subject: UBI: fix warnings Old gcc complains: CC drivers/mtd/ubi/wl.o drivers/mtd/ubi/wl.c: In function 'wear_leveling_worker': drivers/mtd/ubi/wl.c:746: warning: 'pe' may be used uninitialized in this function CC drivers/mtd/ubi/scan.o drivers/mtd/ubi/scan.c: In function 'ubi_scan': drivers/mtd/ubi/scan.c:772: warning: 'ec' may be used uninitialized in this function drivers/mtd/ubi/scan.c:772: note: 'ec' was declared here Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/scan.c | 2 +- drivers/mtd/ubi/wl.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index e4766387050..6f2680f423f 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -773,7 +773,7 @@ struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi, */ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum) { - long long ec; + long long uninitialized_var(ec); int err, bitflips = 0, vol_id, ec_corr = 0; dbg_bld("scan PEB %d", pnum); diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 8bfb7434c99..a471a491f0a 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -743,7 +743,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, int cancel) { int err, put = 0, scrubbing = 0, protect = 0; - struct ubi_wl_prot_entry *pe; + struct ubi_wl_prot_entry *uninitialized_var(pe); struct ubi_wl_entry *e1, *e2; struct ubi_vid_hdr *vid_hdr; -- cgit v1.2.3 From 91f2d53cd75a8fa3557246af965155208c4c69a7 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 24 Jan 2008 11:23:23 +0200 Subject: UBI: add layout volume information Add more information about layout volume to make userspace tools use the macros instead of constants. Also rename UBI_LAYOUT_VOL_ID to make it consistent with other macros. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/eba.c | 2 +- drivers/mtd/ubi/scan.c | 2 +- drivers/mtd/ubi/vtbl.c | 10 +++++----- include/mtd/ubi-header.h | 4 +++- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 1f951e39c53..1f7375e2ffb 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -78,7 +78,7 @@ static unsigned long long next_sqnum(struct ubi_device *ubi) */ static int ubi_get_compat(const struct ubi_device *ubi, int vol_id) { - if (vol_id == UBI_LAYOUT_VOL_ID) + if (vol_id == UBI_LAYOUT_VOLUME_ID) return UBI_LAYOUT_VOLUME_COMPAT; return 0; } diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 6f2680f423f..05aa3e7daba 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -858,7 +858,7 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum } vol_id = be32_to_cpu(vidh->vol_id); - if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOL_ID) { + if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOLUME_ID) { int lnum = be32_to_cpu(vidh->lnum); /* Unsupported internal volume */ diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index 2fd9cf4cea7..d8222db4754 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -89,7 +89,7 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, struct ubi_volume *layout_vol; ubi_assert(idx >= 0 && idx < ubi->vtbl_slots); - layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOL_ID)]; + layout_vol = ubi->volumes[vol_id2idx(ubi, UBI_LAYOUT_VOLUME_ID)]; if (!vtbl_rec) vtbl_rec = &empty_vtbl_record; @@ -269,7 +269,7 @@ static int create_vtbl(struct ubi_device *ubi, struct ubi_scan_info *si, * this volume table copy was found during scanning. It has to be wiped * out. */ - sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID); + sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID); if (sv) old_seb = ubi_scan_find_seb(sv, copy); @@ -281,7 +281,7 @@ retry: } vid_hdr->vol_type = UBI_VID_DYNAMIC; - vid_hdr->vol_id = cpu_to_be32(UBI_LAYOUT_VOL_ID); + vid_hdr->vol_id = cpu_to_be32(UBI_LAYOUT_VOLUME_ID); vid_hdr->compat = UBI_LAYOUT_VOLUME_COMPAT; vid_hdr->data_size = vid_hdr->used_ebs = vid_hdr->data_pad = cpu_to_be32(0); @@ -590,7 +590,7 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, vol->last_eb_bytes = vol->reserved_pebs; vol->used_bytes = (long long)vol->used_ebs * (ubi->leb_size - vol->data_pad); - vol->vol_id = UBI_LAYOUT_VOL_ID; + vol->vol_id = UBI_LAYOUT_VOLUME_ID; vol->ref_count = 1; ubi_assert(!ubi->volumes[i]); @@ -743,7 +743,7 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) ubi->vtbl_size = ubi->vtbl_slots * UBI_VTBL_RECORD_SIZE; ubi->vtbl_size = ALIGN(ubi->vtbl_size, ubi->min_io_size); - sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID); + sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOLUME_ID); if (!sv) { /* * No logical eraseblocks belonging to the layout volume were diff --git a/include/mtd/ubi-header.h b/include/mtd/ubi-header.h index 69d5d7e2289..292f916ea56 100644 --- a/include/mtd/ubi-header.h +++ b/include/mtd/ubi-header.h @@ -299,7 +299,9 @@ struct ubi_vid_hdr { /* The layout volume contains the volume table */ -#define UBI_LAYOUT_VOL_ID UBI_INTERNAL_VOL_START +#define UBI_LAYOUT_VOLUME_ID UBI_INTERNAL_VOL_START +#define UBI_LAYOUT_VOLUME_TYPE UBI_VID_DYNAMIC +#define UBI_LAYOUT_VOLUME_ALIGN 1 #define UBI_LAYOUT_VOLUME_EBS 2 #define UBI_LAYOUT_VOLUME_NAME "layout volume" #define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT -- cgit v1.2.3 From 0411e7353192d7deebd4f50b9ee41974ec3a634c Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 24 Jan 2008 16:45:57 +0200 Subject: UBI: do not change file pointer while updating Since we do not change semantics of seek(), changing the file pointer while updating does not make much sense. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 4 +--- drivers/mtd/ubi/upd.c | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index a7aa123afaf..d9bd49421cc 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -368,6 +368,7 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, */ count = err; + vol->updating = 0; err = ubi_check_volume(ubi, vol->vol_id); if (err < 0) return err; @@ -382,7 +383,6 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, revoke_exclusive(desc, UBI_READWRITE); } - *offp += count; return count; } @@ -430,8 +430,6 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, err = ubi_start_update(ubi, vol->vol_id, bytes); if (bytes == 0) revoke_exclusive(desc, UBI_READWRITE); - - file->f_pos = 0; break; } diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index e32b04d2e04..3defa579fab 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -343,7 +343,6 @@ int ubi_more_update_data(struct ubi_device *ubi, int vol_id, if (err == 0) { err = to_write; vfree(vol->upd_buf); - vol->updating = 0; } } -- cgit v1.2.3 From 1b68d0eea5daddc762c54bf02154f4ad607d9ce8 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 24 Jan 2008 17:04:01 +0200 Subject: UBI: simplify internal interfaces Instead of passing vol_id to all functions and then find struct ubi_volume, pass struct ubi_volume pointer. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 4 ++-- drivers/mtd/ubi/ubi.h | 5 ++-- drivers/mtd/ubi/upd.c | 63 +++++++++++++++++++++++++------------------------- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index d9bd49421cc..0c4044d6cae 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -354,7 +354,7 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, if (!vol->updating) return vol_cdev_direct_write(file, buf, count, offp); - err = ubi_more_update_data(ubi, vol->vol_id, buf, count); + err = ubi_more_update_data(ubi, vol, buf, count); if (err < 0) { ubi_err("cannot write %zd bytes of update data, error %d", count, err); @@ -427,7 +427,7 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, if (err < 0) break; - err = ubi_start_update(ubi, vol->vol_id, bytes); + err = ubi_start_update(ubi, vol, bytes); if (bytes == 0) revoke_exclusive(desc, UBI_READWRITE); break; diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index a8cdbd0364f..3a88cf1eaaa 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -423,8 +423,9 @@ int ubi_add_volume(struct ubi_device *ubi, struct ubi_volume *vol); void ubi_free_volume(struct ubi_device *ubi, struct ubi_volume *vol); /* upd.c */ -int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes); -int ubi_more_update_data(struct ubi_device *ubi, int vol_id, +int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, + long long bytes); +int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count); /* misc.c */ diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index 3defa579fab..59c61ab4f2a 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -45,30 +45,30 @@ /** * set_update_marker - set update marker. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * - * This function sets the update marker flag for volume @vol_id. Returns zero + * This function sets the update marker flag for volume @vol. Returns zero * in case of success and a negative error code in case of failure. */ -static int set_update_marker(struct ubi_device *ubi, int vol_id) +static int set_update_marker(struct ubi_device *ubi, struct ubi_volume *vol) { int err; struct ubi_vtbl_record vtbl_rec; - struct ubi_volume *vol = ubi->volumes[vol_id]; - dbg_msg("set update marker for volume %d", vol_id); + dbg_msg("set update marker for volume %d", vol->vol_id); if (vol->upd_marker) { - ubi_assert(ubi->vtbl[vol_id].upd_marker); + ubi_assert(ubi->vtbl[vol->vol_id].upd_marker); dbg_msg("already set"); return 0; } - memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); + memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], + sizeof(struct ubi_vtbl_record)); vtbl_rec.upd_marker = 1; mutex_lock(&ubi->volumes_mutex); - err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); mutex_unlock(&ubi->volumes_mutex); vol->upd_marker = 1; return err; @@ -77,23 +77,24 @@ static int set_update_marker(struct ubi_device *ubi, int vol_id) /** * clear_update_marker - clear update marker. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @bytes: new data size in bytes * - * This function clears the update marker for volume @vol_id, sets new volume + * This function clears the update marker for volume @vol, sets new volume * data size and clears the "corrupted" flag (static volumes only). Returns * zero in case of success and a negative error code in case of failure. */ -static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long bytes) +static int clear_update_marker(struct ubi_device *ubi, struct ubi_volume *vol, + long long bytes) { int err; uint64_t tmp; struct ubi_vtbl_record vtbl_rec; - struct ubi_volume *vol = ubi->volumes[vol_id]; - dbg_msg("clear update marker for volume %d", vol_id); + dbg_msg("clear update marker for volume %d", vol->vol_id); - memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); + memcpy(&vtbl_rec, &ubi->vtbl[vol->vol_id], + sizeof(struct ubi_vtbl_record)); ubi_assert(vol->upd_marker && vtbl_rec.upd_marker); vtbl_rec.upd_marker = 0; @@ -109,7 +110,7 @@ static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long byt } mutex_lock(&ubi->volumes_mutex); - err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + err = ubi_change_vtbl_record(ubi, vol->vol_id, &vtbl_rec); mutex_unlock(&ubi->volumes_mutex); vol->upd_marker = 0; return err; @@ -118,23 +119,23 @@ static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long byt /** * ubi_start_update - start volume update. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @bytes: update bytes * * This function starts volume update operation. If @bytes is zero, the volume * is just wiped out. Returns zero in case of success and a negative error code * in case of failure. */ -int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) +int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, + long long bytes) { int i, err; uint64_t tmp; - struct ubi_volume *vol = ubi->volumes[vol_id]; - dbg_msg("start update of volume %d, %llu bytes", vol_id, bytes); + dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes); vol->updating = 1; - err = set_update_marker(ubi, vol_id); + err = set_update_marker(ubi, vol); if (err) return err; @@ -146,7 +147,7 @@ int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) } if (bytes == 0) { - err = clear_update_marker(ubi, vol_id, 0); + err = clear_update_marker(ubi, vol, 0); if (err) return err; err = ubi_wl_flush(ubi); @@ -169,7 +170,7 @@ int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) /** * write_leb - write update data. * @ubi: UBI device description object - * @vol_id: volume ID + * @vol: volume description object * @lnum: logical eraseblock number * @buf: data to write * @len: data size @@ -195,11 +196,10 @@ int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) * This function returns zero in case of success and a negative error code in * case of failure. */ -static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, - int len, int used_ebs) +static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, + void *buf, int len, int used_ebs) { int err, l; - struct ubi_volume *vol = ubi->volumes[vol_id]; if (vol->vol_type == UBI_DYNAMIC_VOLUME) { l = ALIGN(len, ubi->min_io_size); @@ -244,11 +244,10 @@ static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, * the last call if the whole volume update was successfully finished, and a * negative error code in case of failure. */ -int ubi_more_update_data(struct ubi_device *ubi, int vol_id, +int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count) { uint64_t tmp; - struct ubi_volume *vol = ubi->volumes[vol_id]; int lnum, offs, err = 0, len, to_write = count; dbg_msg("write %d of %lld bytes, %lld already passed", @@ -293,8 +292,8 @@ int ubi_more_update_data(struct ubi_device *ubi, int vol_id, * is the last chunk, it's time to flush the buffer. */ ubi_assert(flush_len <= vol->usable_leb_size); - err = write_leb(ubi, vol_id, lnum, vol->upd_buf, - flush_len, vol->upd_ebs); + err = write_leb(ubi, vol, lnum, vol->upd_buf, flush_len, + vol->upd_ebs); if (err) return err; } @@ -321,8 +320,8 @@ int ubi_more_update_data(struct ubi_device *ubi, int vol_id, if (len == vol->usable_leb_size || vol->upd_received + len == vol->upd_bytes) { - err = write_leb(ubi, vol_id, lnum, vol->upd_buf, len, - vol->upd_ebs); + err = write_leb(ubi, vol, lnum, vol->upd_buf, + len, vol->upd_ebs); if (err) break; } @@ -336,7 +335,7 @@ int ubi_more_update_data(struct ubi_device *ubi, int vol_id, ubi_assert(vol->upd_received <= vol->upd_bytes); if (vol->upd_received == vol->upd_bytes) { /* The update is finished, clear the update marker */ - err = clear_update_marker(ubi, vol_id, vol->upd_bytes); + err = clear_update_marker(ubi, vol, vol->upd_bytes); if (err) return err; err = ubi_wl_flush(ubi); -- cgit v1.2.3 From 60c031531a85b3580f66c2530f9b2802adcad4df Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 24 Jan 2008 17:56:14 +0200 Subject: UBI: handle zero-length case ubi_eba_atomic_leb_change() has to just map the LEB to a free PEB if data length is zero. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/eba.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 1f7375e2ffb..7ce91ca742b 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -862,6 +862,17 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, if (ubi->ro_mode) return -EROFS; + if (len == 0) { + /* + * Special case when data length is zero. In this case the LEB + * has to be unmapped and mapped somewhere else. + */ + err = ubi_eba_unmap_leb(ubi, vol, lnum); + if (err) + return err; + return ubi_eba_write_leb(ubi, vol, lnum, NULL, 0, 0, dtype); + } + vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS); if (!vid_hdr) return -ENOMEM; -- cgit v1.2.3 From 866136827b9a71c39dcb06d23ce523f719eab74b Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 24 Jan 2008 16:15:14 +0200 Subject: UBI: introduce atomic LEB change ioctl We have to be able to change individual LEBs for utilities like ubifsck, ubifstune. For example, ubifsck has to be able to fix errors on the media, ubifstune has to be able to change the the superblock, hence this ioctl. Signed-off-by: Artem Bityutskiy --- include/linux/mtd/ubi.h | 17 ----------------- include/mtd/ubi-user.h | 51 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/include/linux/mtd/ubi.h b/include/linux/mtd/ubi.h index c4abe035122..f71201d0f3e 100644 --- a/include/linux/mtd/ubi.h +++ b/include/linux/mtd/ubi.h @@ -25,23 +25,6 @@ #include #include -/* - * UBI data type hint constants. - * - * UBI_LONGTERM: long-term data - * UBI_SHORTTERM: short-term data - * UBI_UNKNOWN: data persistence is unknown - * - * These constants are used when data is written to UBI volumes in order to - * help the UBI wear-leveling unit to find more appropriate physical - * eraseblocks. - */ -enum { - UBI_LONGTERM = 1, - UBI_SHORTTERM, - UBI_UNKNOWN -}; - /* * enum ubi_open_mode - UBI volume open mode constants. * diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h index 4d184a7f80a..a7421f130cc 100644 --- a/include/mtd/ubi-user.h +++ b/include/mtd/ubi-user.h @@ -63,7 +63,7 @@ * * Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the * corresponding UBI volume character device. A pointer to a 64-bit update - * size should be passed to the IOCTL. After then, UBI expects user to write + * size should be passed to the IOCTL. After this, UBI expects user to write * this number of bytes to the volume character device. The update is finished * when the claimed number of bytes is passed. So, the volume update sequence * is something like: @@ -72,6 +72,15 @@ * ioctl(fd, UBI_IOCVOLUP, &image_size); * write(fd, buf, image_size); * close(fd); + * + * Atomic eraseblock change + * ~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Atomic eraseblock change operation is done via the %UBI_IOCEBCH IOCTL + * command of the corresponding UBI volume character device. A pointer to + * &struct ubi_leb_change_req has to be passed to the IOCTL. Then the user is + * expected to write the requested amount of bytes. This is similar to the + * "volume update" IOCTL. */ /* @@ -113,10 +122,29 @@ #define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t) /* An eraseblock erasure command, used for debugging, disabled by default */ #define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t) +/* An atomic eraseblock change command */ +#define UBI_IOCEBCH _IOW(UBI_VOL_IOC_MAGIC, 2, int32_t) /* Maximum MTD device name length supported by UBI */ #define MAX_UBI_MTD_NAME_LEN 127 +/* + * UBI data type hint constants. + * + * UBI_LONGTERM: long-term data + * UBI_SHORTTERM: short-term data + * UBI_UNKNOWN: data persistence is unknown + * + * These constants are used when data is written to UBI volumes in order to + * help the UBI wear-leveling unit to find more appropriate physical + * eraseblocks. + */ +enum { + UBI_LONGTERM = 1, + UBI_SHORTTERM = 2, + UBI_UNKNOWN = 3, +}; + /* * UBI volume type constants. * @@ -125,7 +153,7 @@ */ enum { UBI_DYNAMIC_VOLUME = 3, - UBI_STATIC_VOLUME = 4, + UBI_STATIC_VOLUME = 4, }; /** @@ -137,7 +165,7 @@ enum { * * This data structure is used to specify MTD device UBI has to attach and the * parameters it has to use. The number which should be assigned to the new UBI - * device is passed in @ubi_num. UBI may automatically assing the number if + * device is passed in @ubi_num. UBI may automatically assign the number if * @UBI_DEV_NUM_AUTO is passed. In this case, the device number is returned in * @ubi_num. * @@ -176,7 +204,7 @@ struct ubi_attach_req { * @padding2: reserved for future, not used, has to be zeroed * @name: volume name * - * This structure is used by userspace programs when creating new volumes. The + * This structure is used by user-space programs when creating new volumes. The * @used_bytes field is only necessary when creating static volumes. * * The @alignment field specifies the required alignment of the volume logical @@ -222,4 +250,19 @@ struct ubi_rsvol_req { int32_t vol_id; } __attribute__ ((packed)); +/** + * struct ubi_leb_change_req - a data structure used in atomic logical + * eraseblock change requests. + * @lnum: logical eraseblock number to change + * @bytes: how many bytes will be written to the logical eraseblock + * @dtype: data type (%UBI_LONGTERM, %UBI_SHORTTERM, %UBI_UNKNOWN) + * @padding: reserved for future, not used, has to be zeroed + */ +struct ubi_leb_change_req { + int32_t lnum; + int32_t bytes; + uint8_t dtype; + uint8_t padding[7]; +} __attribute__ ((packed)); + #endif /* __UBI_USER_H__ */ -- cgit v1.2.3 From e653879c269735c9ff6684e03edf1d4e041ff3d3 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 24 Jan 2008 18:48:21 +0200 Subject: UBI: implement atomic LEB change ioctl Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/cdev.c | 72 +++++++++++++++++++++++++------ drivers/mtd/ubi/ubi.h | 27 ++++++++++-- drivers/mtd/ubi/upd.c | 112 +++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 183 insertions(+), 28 deletions(-) diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 0c4044d6cae..9d6aae5449b 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -132,8 +132,15 @@ static int vol_cdev_release(struct inode *inode, struct file *file) if (vol->updating) { ubi_warn("update of volume %d not finished, volume is damaged", vol->vol_id); + ubi_assert(!vol->changing_leb); vol->updating = 0; vfree(vol->upd_buf); + } else if (vol->changing_leb) { + dbg_msg("only %lld of %lld bytes received for atomic LEB change" + " for volume %d:%d, cancel", vol->upd_received, + vol->upd_bytes, vol->ubi->ubi_num, vol->vol_id); + vol->changing_leb = 0; + vfree(vol->upd_buf); } ubi_close_volume(desc); @@ -351,24 +358,32 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - if (!vol->updating) + if (!vol->updating && !vol->changing_leb) return vol_cdev_direct_write(file, buf, count, offp); - err = ubi_more_update_data(ubi, vol, buf, count); + if (vol->updating) + err = ubi_more_update_data(ubi, vol, buf, count); + else + err = ubi_more_leb_change_data(ubi, vol, buf, count); + if (err < 0) { - ubi_err("cannot write %zd bytes of update data, error %d", + ubi_err("cannot accept more %zd bytes of data, error %d", count, err); return err; } if (err) { /* - * Update is finished, @err contains number of actually written - * bytes now. + * The operation is finished, @err contains number of actually + * written bytes. */ count = err; - vol->updating = 0; + if (vol->changing_leb) { + revoke_exclusive(desc, UBI_READWRITE); + return count; + } + err = ubi_check_volume(ubi, vol->vol_id); if (err < 0) return err; @@ -433,6 +448,43 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, break; } + /* Atomic logical eraseblock change command */ + case UBI_IOCEBCH: + { + struct ubi_leb_change_req req; + + err = copy_from_user(&req, argp, + sizeof(struct ubi_leb_change_req)); + if (err) { + err = -EFAULT; + break; + } + + if (desc->mode == UBI_READONLY || + vol->vol_type == UBI_STATIC_VOLUME) { + err = -EROFS; + break; + } + + /* Validate the request */ + err = -EINVAL; + if (req.lnum < 0 || req.lnum >= vol->reserved_pebs || + req.bytes < 0 || req.lnum >= vol->usable_leb_size) + break; + if (req.dtype != UBI_LONGTERM && req.dtype != UBI_SHORTTERM && + req.dtype != UBI_UNKNOWN) + break; + + err = get_exclusive(desc); + if (err < 0) + break; + + err = ubi_start_leb_change(ubi, vol, &req); + if (req.bytes == 0) + revoke_exclusive(desc, UBI_READWRITE); + break; + } + #ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO /* Logical eraseblock erasure command */ case UBI_IOCEBER: @@ -445,7 +497,8 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, break; } - if (desc->mode == UBI_READONLY) { + if (desc->mode == UBI_READONLY || + vol->vol_type == UBI_STATIC_VOLUME) { err = -EROFS; break; } @@ -455,11 +508,6 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, break; } - if (vol->vol_type != UBI_DYNAMIC_VOLUME) { - err = -EROFS; - break; - } - dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); err = ubi_eba_unmap_leb(ubi, vol, lnum); if (err) diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 3a88cf1eaaa..45771061526 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -158,15 +158,23 @@ struct ubi_volume_desc; * @name: volume name * * @upd_ebs: how many eraseblocks are expected to be updated - * @upd_bytes: how many bytes are expected to be received - * @upd_received: how many update bytes were already received - * @upd_buf: update buffer which is used to collect update data + * @ch_lnum: LEB number which is being changing by the atomic LEB change + * operation + * @ch_dtype: data persistency type which is being changing by the atomic LEB + * change operation + * @upd_bytes: how many bytes are expected to be received for volume update or + * atomic LEB change + * @upd_received: how many bytes were already received for volume update or + * atomic LEB change + * @upd_buf: update buffer which is used to collect update data or data for + * atomic LEB change * * @eba_tbl: EBA table of this volume (LEB->PEB mapping) * @checked: %1 if this static volume was checked * @corrupted: %1 if the volume is corrupted (static volumes only) * @upd_marker: %1 if the update marker is set for this volume * @updating: %1 if the volume is being updated + * @changing_leb: %1 if the atomic LEB change ioctl command is in progress * * @gluebi_desc: gluebi UBI volume descriptor * @gluebi_refcount: reference count of the gluebi MTD device @@ -202,6 +210,8 @@ struct ubi_volume { char name[UBI_VOL_NAME_MAX+1]; int upd_ebs; + int ch_lnum; + int ch_dtype; long long upd_bytes; long long upd_received; void *upd_buf; @@ -211,9 +221,14 @@ struct ubi_volume { int corrupted:1; int upd_marker:1; int updating:1; + int changing_leb:1; #ifdef CONFIG_MTD_UBI_GLUEBI - /* Gluebi-related stuff may be compiled out */ + /* + * Gluebi-related stuff may be compiled out. + * TODO: this should not be built into UBI but should be a separate + * ubimtd driver which works on top of UBI and emulates MTD devices. + */ struct ubi_volume_desc *gluebi_desc; int gluebi_refcount; struct mtd_info gluebi_mtd; @@ -427,6 +442,10 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, long long bytes); int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, const void __user *buf, int count); +int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + const struct ubi_leb_change_req *req); +int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, + const void __user *buf, int count); /* misc.c */ int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length); diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index 59c61ab4f2a..ddaa1a56cc6 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -22,7 +22,8 @@ */ /* - * This file contains implementation of the volume update functionality. + * This file contains implementation of the volume update and atomic LEB change + * functionality. * * The update operation is based on the per-volume update marker which is * stored in the volume table. The update marker is set before the update @@ -133,6 +134,7 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, uint64_t tmp; dbg_msg("start update of volume %d, %llu bytes", vol->vol_id, bytes); + ubi_assert(!vol->updating && !vol->changing_leb); vol->updating = 1; err = set_update_marker(ubi, vol); @@ -167,6 +169,39 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, return 0; } +/** + * ubi_start_leb_change - start atomic LEB change. + * @ubi: UBI device description object + * @vol: volume description object + * @req: operation request + * + * This function starts atomic LEB change operation. Returns zero in case of + * success and a negative error code in case of failure. + */ +int ubi_start_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, + const struct ubi_leb_change_req *req) +{ + ubi_assert(!vol->updating && !vol->changing_leb); + + dbg_msg("start changing LEB %d:%d, %u bytes", + vol->vol_id, req->lnum, req->bytes); + if (req->bytes == 0) + return ubi_eba_atomic_leb_change(ubi, vol, req->lnum, NULL, 0, + req->dtype); + + vol->upd_bytes = req->bytes; + vol->upd_received = 0; + vol->changing_leb = 1; + vol->ch_lnum = req->lnum; + vol->ch_dtype = req->dtype; + + vol->upd_buf = vmalloc(req->bytes); + if (!vol->upd_buf) + return -ENOMEM; + + return 0; +} + /** * write_leb - write update data. * @ubi: UBI device description object @@ -199,21 +234,19 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, void *buf, int len, int used_ebs) { - int err, l; + int err; if (vol->vol_type == UBI_DYNAMIC_VOLUME) { - l = ALIGN(len, ubi->min_io_size); - memset(buf + len, 0xFF, l - len); + len = ALIGN(len, ubi->min_io_size); + memset(buf + len, 0xFF, len - len); - l = ubi_calc_data_len(ubi, buf, l); - if (l == 0) { + len = ubi_calc_data_len(ubi, buf, len); + if (len == 0) { dbg_msg("all %d bytes contain 0xFF - skip", len); return 0; } - if (len != l) - dbg_msg("skip last %d bytes (0xFF)", len - l); - err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, l, UBI_UNKNOWN); + err = ubi_eba_write_leb(ubi, vol, lnum, buf, 0, len, UBI_UNKNOWN); } else { /* * When writing static volume, and this is the last logical @@ -239,9 +272,9 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, * @count: how much bytes to write * * This function writes more data to the volume which is being updated. It may - * be called arbitrary number of times until all of the update data arrive. - * This function returns %0 in case of success, number of bytes written during - * the last call if the whole volume update was successfully finished, and a + * be called arbitrary number of times until all the update data arriveis. This + * function returns %0 in case of success, number of bytes written during the + * last call if the whole volume update has been successfully finished, and a * negative error code in case of failure. */ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, @@ -340,6 +373,7 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, return err; err = ubi_wl_flush(ubi); if (err == 0) { + vol->updating = 0; err = to_write; vfree(vol->upd_buf); } @@ -347,3 +381,57 @@ int ubi_more_update_data(struct ubi_device *ubi, struct ubi_volume *vol, return err; } + +/** + * ubi_more_leb_change_data - accept more data for atomic LEB change. + * @vol: volume description object + * @buf: write data (user-space memory buffer) + * @count: how much bytes to write + * + * This function accepts more data to the volume which is being under the + * "atomic LEB change" operation. It may be called arbitrary number of times + * until all data arrives. This function returns %0 in case of success, number + * of bytes written during the last call if the whole "atomic LEB change" + * operation has been successfully finished, and a negative error code in case + * of failure. + */ +int ubi_more_leb_change_data(struct ubi_device *ubi, struct ubi_volume *vol, + const void __user *buf, int count) +{ + int err; + + dbg_msg("write %d of %lld bytes, %lld already passed", + count, vol->upd_bytes, vol->upd_received); + + if (ubi->ro_mode) + return -EROFS; + + if (vol->upd_received + count > vol->upd_bytes) + count = vol->upd_bytes - vol->upd_received; + + err = copy_from_user(vol->upd_buf + vol->upd_received, buf, count); + if (err) + return -EFAULT; + + vol->upd_received += count; + + if (vol->upd_received == vol->upd_bytes) { + int len = ALIGN((int)vol->upd_bytes, ubi->min_io_size); + + memset(vol->upd_buf + vol->upd_bytes, 0xFF, len - vol->upd_bytes); + len = ubi_calc_data_len(ubi, vol->upd_buf, len); + err = ubi_eba_atomic_leb_change(ubi, vol, vol->ch_lnum, + vol->upd_buf, len, UBI_UNKNOWN); + if (err) + return err; + } + + ubi_assert(vol->upd_received <= vol->upd_bytes); + if (vol->upd_received == vol->upd_bytes) { + vol->changing_leb = 0; + err = count; + vfree(vol->upd_buf); + } + + return err; +} -- cgit v1.2.3 From d0bf37932ac98cc793da2164ac1a4656fbf34bbc Mon Sep 17 00:00:00 2001 From: Matt Reimer Date: Thu, 18 Oct 2007 18:02:43 -0700 Subject: [MTD] [NAND] fix s3c2410 error correction The single-bit error correction was, well, incorrect. For determing which bit to correct it was using P1' P2' P4' P8' instead of P1 P2 P4 P8, and it was using P16' P32' P64' P128' P256' P512' P1024' P2048' instead of P16 P32 P64 P128 P256 P512 P1024 P2048. Signed-off-by: Matt Reimer Signed-off-by: David Woodhouse --- drivers/mtd/nand/s3c2410.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 21a2cc8636d..d31cb7b3fee 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -366,23 +366,21 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { /* calculate the bit position of the error */ - bit = (diff2 >> 2) & 1; - bit |= (diff2 >> 3) & 2; - bit |= (diff2 >> 4) & 4; + bit = ((diff2 >> 3) & 1) | + ((diff2 >> 4) & 2) | + ((diff2 >> 5) & 4); /* calculate the byte position of the error */ - byte = (diff1 << 1) & 0x80; - byte |= (diff1 << 2) & 0x40; - byte |= (diff1 << 3) & 0x20; - byte |= (diff1 << 4) & 0x10; - - byte |= (diff0 >> 3) & 0x08; - byte |= (diff0 >> 2) & 0x04; - byte |= (diff0 >> 1) & 0x02; - byte |= (diff0 >> 0) & 0x01; - - byte |= (diff2 << 8) & 0x100; + byte = ((diff2 << 7) & 0x100) | + ((diff1 << 0) & 0x80) | + ((diff1 << 1) & 0x40) | + ((diff1 << 2) & 0x20) | + ((diff1 << 3) & 0x10) | + ((diff0 >> 4) & 0x08) | + ((diff0 >> 3) & 0x04) | + ((diff0 >> 2) & 0x02) | + ((diff0 >> 1) & 0x01); dev_dbg(info->device, "correcting error bit %d, byte %d\n", bit, byte); -- cgit v1.2.3 From d0b36d8cc8f345ec5faadd15daaecfb409b94523 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 24 Jan 2008 12:48:37 +0200 Subject: [MTD] [OneNAND] Do not release chip twice Signed-off-by: Adrian Hunter Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index ed9f9c061ac..e5882b609cf 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1447,9 +1447,6 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, buf += thislen; } - /* Deselect and wake up anyone waiting on the device */ - onenand_release_device(mtd); - ops->retlen = written; return ret; -- cgit v1.2.3 From 9d2f0b7a3de28d06ba4011b835b9a7e772553f0d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 21 Jan 2008 17:13:07 +0200 Subject: [MTD] [OneNAND] fix call to onenand_verify when writing subpages Signed-off-by: Adrian Hunter Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index e5882b609cf..799bb1bc3db 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1431,7 +1431,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, } /* Only check verify write turn on */ - ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen); + ret = onenand_verify(mtd, buf, to, thislen); if (ret) { printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); break; -- cgit v1.2.3 From b21b72cf33bb212414c1d967850e261b795befa4 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Tue, 11 Dec 2007 11:13:18 +0900 Subject: [MTD] [OneNAND] Consolidate OneNAND operation order Consolidate OneNAND operation order as OneNAND Spec. It also doesn't break previous operation order. Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 799bb1bc3db..c79bc2ef3f5 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -182,8 +182,7 @@ static int onenand_buffer_address(int dataram1, int sectors, int count) static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len) { struct onenand_chip *this = mtd->priv; - int value, readcmd = 0, block_cmd = 0; - int block, page; + int value, block, page; /* Address translation */ switch (cmd) { @@ -198,7 +197,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le case ONENAND_CMD_ERASE: case ONENAND_CMD_BUFFERRAM: case ONENAND_CMD_OTP_ACCESS: - block_cmd = 1; block = (int) (addr >> this->erase_shift); page = -1; break; @@ -240,11 +238,9 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le value = onenand_block_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); - if (block_cmd) { - /* Select DataRAM for DDP */ - value = onenand_bufferram_address(this, block); - this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - } + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); } if (page != -1) { @@ -256,7 +252,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le case ONENAND_CMD_READ: case ONENAND_CMD_READOOB: dataram = ONENAND_SET_NEXT_BUFFERRAM(this); - readcmd = 1; break; default: @@ -273,12 +268,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le /* Write 'BSA, BSC' of DataRAM */ value = onenand_buffer_address(dataram, sectors, count); this->write_word(value, this->base + ONENAND_REG_START_BUFFER); - - if (readcmd) { - /* Select DataRAM for DDP */ - value = onenand_bufferram_address(this, block); - this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - } } /* Interrupt clear */ -- cgit v1.2.3 From e71f04fc9234b14636887ceb5862755f1690642c Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Tue, 11 Dec 2007 11:23:45 +0900 Subject: [MTD] [OneNAND] Get correct density from device ID Use the higher bits for other purpose. Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 20 ++++++++++++++++---- include/linux/mtd/onenand_regs.h | 1 + 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index c79bc2ef3f5..cf800932999 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -169,6 +169,18 @@ static int onenand_buffer_address(int dataram1, int sectors, int count) return ((bsa << ONENAND_BSA_SHIFT) | bsc); } +/** + * onenand_get_density - [DEFAULT] Get OneNAND density + * @param dev_id OneNAND device ID + * + * Get OneNAND density from device ID + */ +static inline int onenand_get_density(int dev_id) +{ + int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; + return (density & ONENAND_DEVICE_DENSITY_MASK); +} + /** * onenand_command - [DEFAULT] Send command to OneNAND device * @param mtd MTD device structure @@ -2146,7 +2158,7 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, *retlen = 0; - density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT; + density = onenand_get_density(this->device_id); if (density < ONENAND_DEVICE_DENSITY_512Mb) otp_pages = 20; else @@ -2337,7 +2349,7 @@ static void onenand_check_features(struct mtd_info *mtd) unsigned int density, process; /* Lock scheme depends on density and process */ - density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT; + density = onenand_get_density(this->device_id); process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; /* Lock scheme */ @@ -2386,7 +2398,7 @@ static void onenand_print_device_info(int device, int version) vcc = device & ONENAND_DEVICE_VCC_MASK; demuxed = device & ONENAND_DEVICE_IS_DEMUX; ddp = device & ONENAND_DEVICE_IS_DDP; - density = device >> ONENAND_DEVICE_DENSITY_SHIFT; + density = onenand_get_density(device); printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", demuxed ? "" : "Muxed ", ddp ? "(DDP)" : "", @@ -2478,7 +2490,7 @@ static int onenand_probe(struct mtd_info *mtd) this->device_id = dev_id; this->version_id = ver_id; - density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; + density = onenand_get_density(dev_id); this->chipsize = (16 << density) << 20; /* Set density mask. it is used for DDP */ if (ONENAND_IS_DDP(this)) diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index c46161f4eee..d1b310c92eb 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -67,6 +67,7 @@ /* * Device ID Register F001h (R) */ +#define ONENAND_DEVICE_DENSITY_MASK (0xf) #define ONENAND_DEVICE_DENSITY_SHIFT (4) #define ONENAND_DEVICE_IS_DDP (1 << 3) #define ONENAND_DEVICE_IS_DEMUX (1 << 2) -- cgit v1.2.3 From b2581be291aa8595eadf3d6933d04d0f1d01b46d Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Thu, 13 Dec 2007 09:39:29 +0900 Subject: [MTD] [OneNAND] Check the initial bad block using ONENAND_CTRL_ERROR Some chips don't set the ONENAND_CTRL_LOAD bit. Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index cf800932999..44c327a335e 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1119,12 +1119,10 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state) interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); + /* Initial bad block case: 0x2400 or 0x0400 */ if (ctrl & ONENAND_CTRL_ERROR) { printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); - /* Initial bad block case */ - if (ctrl & ONENAND_CTRL_LOAD) - return ONENAND_BBT_READ_ERROR; - return ONENAND_BBT_READ_FATAL_ERROR; + return ONENAND_BBT_READ_ERROR; } if (interrupt & ONENAND_INT_READ) { -- cgit v1.2.3 From 978cb38a296fceac82a8a757f6387d7ef2a21ac6 Mon Sep 17 00:00:00 2001 From: "Sheng Yongjie (Sam" Date: Thu, 13 Dec 2007 11:47:33 +0900 Subject: [MTD] [OneNAND] Use the u_char instead of char in oobbuf In function onenand_verify_oob, local variable oobbuf shall be unsigned char. In the case of a value is >= 0x80, it's unequal in comparing the value in an unsigned char and signed char. Signed-off-by: Sheng Yongjie (Sam) Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 44c327a335e..ad052c8233c 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1217,7 +1217,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) { struct onenand_chip *this = mtd->priv; - char oobbuf[64]; + u_char oobbuf[64]; int status, i; this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); -- cgit v1.2.3 From 69d79186dc48ca22a0ce69511bef8ef6c2465ada Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Fri, 14 Dec 2007 14:47:21 +0900 Subject: [MTD] [OneNAND] Use pre-alloced oob buffer instead of local buffer Signed-off-by: Kyungmin Park --- drivers/mtd/onenand/onenand_base.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index ad052c8233c..b281b116aae 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1217,7 +1217,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) { struct onenand_chip *this = mtd->priv; - u_char oobbuf[64]; + u_char *oob_buf = this->oob_buf; int status, i; this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); @@ -1226,9 +1226,9 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to if (status) return status; - this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); + this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); for (i = 0; i < mtd->oobsize; i++) - if (buf[i] != 0xFF && buf[i] != oobbuf[i]) + if (buf[i] != 0xFF && buf[i] != oob_buf[i]) return -EBADMSG; return 0; @@ -2307,7 +2307,8 @@ static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from, static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) { - unsigned char oob_buf[64]; + struct onenand_chip *this = mtd->priv; + u_char *oob_buf = this->oob_buf; size_t retlen; int ret; -- cgit v1.2.3 From 6dc4a8717fadd47103b5015cc678c75afda43ae0 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Fri, 1 Feb 2008 13:48:49 +0200 Subject: UBI: do not flush queue on each vtbl change This is just not necessary. We re-write whole layout copy, so the old contents cannot show up again sice scan process will drop it. Signed-off-by: Artem Bityutskiy --- drivers/mtd/ubi/vtbl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index d8222db4754..56fc3fbce83 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -111,7 +111,7 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, } paranoid_vtbl_check(ubi); - return ubi_wl_flush(ubi); + return 0; } /** -- cgit v1.2.3 From 53d885539293e98f5b979c20518858fc8f933749 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 7 Jan 2008 18:00:17 +0200 Subject: [MTD] JEDEC probe: kill some inline bloat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit $ codiff $OBJ.old $OBJ drivers/mtd/chips/jedec_probe.c: cfi_jedec_setup | -320 jedec_probe_chip | -7073 2 functions changed, 7393 bytes removed, diff: -7393 drivers/mtd/chips/jedec_probe.c: jedec_reset | +1151 1 function changed, 1151 bytes added, diff: +1151 drivers/mtd/chips/jedec_probe.o: 3 functions changed, 1151 bytes added, 7393 bytes removed, diff: -6242 Signed-off-by: Ilpo Järvinen Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 64059384521..4204bb23a17 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1676,8 +1676,7 @@ static inline u32 jedec_read_id(struct map_info *map, uint32_t base, return result.x[0] & mask; } -static inline void jedec_reset(u32 base, struct map_info *map, - struct cfi_private *cfi) +static void jedec_reset(u32 base, struct map_info *map, struct cfi_private *cfi) { /* Reset */ -- cgit v1.2.3 From c0d2a48a65a8fc286aa84871cfc20d8c10dbf492 Mon Sep 17 00:00:00 2001 From: Ilpo Järvinen Date: Mon, 7 Jan 2008 18:00:58 +0200 Subject: [MTD] jedec probe: drop unnecessary forward declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ilpo Järvinen Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 4204bb23a17..4be51a86a85 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1646,14 +1646,6 @@ static const struct amd_flash_info jedec_table[] = { } }; - -static int cfi_jedec_setup(struct cfi_private *p_cfi, int index); - -static int jedec_probe_chip(struct map_info *map, uint32_t base, - unsigned long *chip_map, struct cfi_private *cfi); - -static struct mtd_info *jedec_probe(struct map_info *map); - static inline u32 jedec_read_mfr(struct map_info *map, uint32_t base, struct cfi_private *cfi) { -- cgit v1.2.3 From 9a310d21196f38f6ad0ad146057548653e495c09 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 15 Jan 2008 17:54:43 -0600 Subject: [MTD] Factor out OF partition support from the NOR driver. Signed-off-by: Scott Wood Signed-off-by: David Woodhouse --- drivers/mtd/Kconfig | 8 ++++ drivers/mtd/Makefile | 1 + drivers/mtd/maps/physmap_of.c | 89 +++++++++++++----------------------------- drivers/mtd/ofpart.c | 74 +++++++++++++++++++++++++++++++++++ include/linux/mtd/partitions.h | 9 ++++- 5 files changed, 118 insertions(+), 63 deletions(-) create mode 100644 drivers/mtd/ofpart.c diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 661eac09f5c..e8503341e3b 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -150,6 +150,14 @@ config MTD_AFS_PARTS for your particular device. It won't happen automatically. The 'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example. +config MTD_OF_PARTS + tristate "Flash partition map based on OF description" + depends on PPC_OF && MTD_PARTITIONS + help + This provides a partition parsing function which derives + the partition map from the children of the flash node, + as described in Documentation/powerpc/booting-without-of.txt. + comment "User Modules And Translation Layers" config MTD_CHAR diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 7f0b04b4caa..538e33d11d4 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o +obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o # 'Users' - code which presents functionality to userspace. obj-$(CONFIG_MTD_CHAR) += mtdchar.o diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index d4bcd3f8c57..49acd417189 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -80,65 +80,6 @@ static int parse_obsolete_partitions(struct of_device *dev, return nr_parts; } - -static int __devinit parse_partitions(struct of_flash *info, - struct of_device *dev) -{ - const char *partname; - static const char *part_probe_types[] - = { "cmdlinepart", "RedBoot", NULL }; - struct device_node *dp = dev->node, *pp; - int nr_parts, i; - - /* First look for RedBoot table or partitions on the command - * line, these take precedence over device tree information */ - nr_parts = parse_mtd_partitions(info->mtd, part_probe_types, - &info->parts, 0); - if (nr_parts > 0) - return nr_parts; - - /* First count the subnodes */ - nr_parts = 0; - for (pp = of_get_next_child(dp, NULL); pp; - pp = of_get_next_child(dp, pp)) - nr_parts++; - - if (nr_parts == 0) - return parse_obsolete_partitions(dev, info, dp); - - info->parts = kzalloc(nr_parts * sizeof(*info->parts), - GFP_KERNEL); - if (!info->parts) - return -ENOMEM; - - for (pp = of_get_next_child(dp, NULL), i = 0; pp; - pp = of_get_next_child(dp, pp), i++) { - const u32 *reg; - int len; - - reg = of_get_property(pp, "reg", &len); - if (!reg || (len != 2*sizeof(u32))) { - of_node_put(pp); - dev_err(&dev->dev, "Invalid 'reg' on %s\n", - dp->full_name); - kfree(info->parts); - info->parts = NULL; - return -EINVAL; - } - info->parts[i].offset = reg[0]; - info->parts[i].size = reg[1]; - - partname = of_get_property(pp, "label", &len); - if (!partname) - partname = of_get_property(pp, "name", &len); - info->parts[i].name = (char *)partname; - - if (of_get_property(pp, "read-only", &len)) - info->parts[i].mask_flags = MTD_WRITEABLE; - } - - return nr_parts; -} #else /* MTD_PARTITIONS */ #define OF_FLASH_PARTS(info) (0) #define parse_partitions(info, dev) (0) @@ -213,6 +154,10 @@ static struct mtd_info * __devinit obsolete_probe(struct of_device *dev, static int __devinit of_flash_probe(struct of_device *dev, const struct of_device_id *match) { +#ifdef CONFIG_MTD_PARTITIONS + static const char *part_probe_types[] + = { "cmdlinepart", "RedBoot", NULL }; +#endif struct device_node *dp = dev->node; struct resource res; struct of_flash *info; @@ -275,13 +220,33 @@ static int __devinit of_flash_probe(struct of_device *dev, } info->mtd->owner = THIS_MODULE; - err = parse_partitions(info, dev); +#ifdef CONFIG_MTD_PARTITIONS + /* First look for RedBoot table or partitions on the command + * line, these take precedence over device tree information */ + err = parse_mtd_partitions(info->mtd, part_probe_types, + &info->parts, 0); if (err < 0) - goto err_out; + return err; + +#ifdef CONFIG_MTD_OF_PARTS + if (err == 0) { + err = of_mtd_parse_partitions(&dev->dev, info->mtd, + dp, &info->parts); + if (err < 0) + return err; + } +#endif + + if (err == 0) { + err = parse_obsolete_partitions(dev, info, dp); + if (err < 0) + return err; + } if (err > 0) - add_mtd_partitions(info->mtd, OF_FLASH_PARTS(info), err); + add_mtd_partitions(info->mtd, info->parts, err); else +#endif add_mtd_device(info->mtd); return 0; diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c new file mode 100644 index 00000000000..f86e06934cd --- /dev/null +++ b/drivers/mtd/ofpart.c @@ -0,0 +1,74 @@ +/* + * Flash partitions described by the OF (or flattened) device tree + * + * Copyright (C) 2006 MontaVista Software Inc. + * Author: Vitaly Wool + * + * Revised to handle newer style flash binding by: + * Copyright (C) 2007 David Gibson, IBM Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include + +int __devinit of_mtd_parse_partitions(struct device *dev, + struct mtd_info *mtd, + struct device_node *node, + struct mtd_partition **pparts) +{ + const char *partname; + struct device_node *pp; + int nr_parts, i; + + /* First count the subnodes */ + pp = NULL; + nr_parts = 0; + while ((pp = of_get_next_child(node, pp))) + nr_parts++; + + if (nr_parts == 0) + return 0; + + *pparts = kzalloc(nr_parts * sizeof(**pparts), GFP_KERNEL); + if (!*pparts) + return -ENOMEM; + + pp = NULL; + i = 0; + while ((pp = of_get_next_child(node, pp))) { + const u32 *reg; + int len; + + reg = of_get_property(pp, "reg", &len); + if (!reg || (len != 2 * sizeof(u32))) { + of_node_put(pp); + dev_err(dev, "Invalid 'reg' on %s\n", node->full_name); + kfree(*pparts); + *pparts = NULL; + return -EINVAL; + } + (*pparts)[i].offset = reg[0]; + (*pparts)[i].size = reg[1]; + + partname = of_get_property(pp, "label", &len); + if (!partname) + partname = of_get_property(pp, "name", &len); + (*pparts)[i].name = (char *)partname; + + if (of_get_property(pp, "read-only", &len)) + (*pparts)[i].mask_flags = MTD_WRITEABLE; + + i++; + } + + return nr_parts; +} +EXPORT_SYMBOL(of_mtd_parse_partitions); diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index da6b3d6f12a..7c37d7e55ab 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -71,5 +71,12 @@ extern int parse_mtd_partitions(struct mtd_info *master, const char **types, #define put_partition_parser(p) do { module_put((p)->owner); } while(0) -#endif +struct device; +struct device_node; + +int __devinit of_mtd_parse_partitions(struct device *dev, + struct mtd_info *mtd, + struct device_node *node, + struct mtd_partition **pparts); +#endif -- cgit v1.2.3 From 5b1defe73a538dfe35f7b59bcaf047c0005bff4d Mon Sep 17 00:00:00 2001 From: "akpm@linux-foundation.org" Date: Wed, 16 Jan 2008 01:24:27 -0800 Subject: [UBI] drivers/mtd/ubi/cdev.c: unused var Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/ubi/cdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index a60a3a24c2a..5ec13dc4705 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -268,7 +268,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, struct ubi_volume_desc *desc = file->private_data; struct ubi_volume *vol = desc->vol; struct ubi_device *ubi = vol->ubi; - int lnum, off, len, tbuf_size, vol_id = vol->vol_id, err = 0; + int lnum, off, len, tbuf_size, err = 0; size_t count_save = count; char *tbuf; uint64_t tmp; -- cgit v1.2.3 From 4354c5a4ef8d484a463ed8d996e9653ad8be5c72 Mon Sep 17 00:00:00 2001 From: "akpm@linux-foundation.org" Date: Wed, 16 Jan 2008 02:47:01 -0800 Subject: [UBI] drivers/mtd/ubi/wl.c: fix uninitialized var warning drivers/mtd/ubi/wl.c:746: warning: 'pe' may be used uninitialized in this function Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/ubi/wl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 1142aabcfc8..0d44ad95ab8 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -743,7 +743,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, int cancel) { int err, put = 0, scrubbing = 0, protect = 0; - struct ubi_wl_prot_entry *pe; + struct ubi_wl_prot_entry *uninitialized_var(pe); struct ubi_wl_entry *e1, *e2; struct ubi_vid_hdr *vid_hdr; -- cgit v1.2.3 From 9308758c50610c077ca41e82c4b98b2de96e8387 Mon Sep 17 00:00:00 2001 From: "akpm@linux-foundation.org" Date: Wed, 16 Jan 2008 02:48:49 -0800 Subject: [UBI] drivers/mtd/ubi/scan.c: fix uninitialized var warning drivers/mtd/ubi/scan.c: In function 'ubi_scan': drivers/mtd/ubi/scan.c:772: warning: 'ec' may be used uninitialized in this function Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/ubi/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index c7b0afc9d28..c57e8eff986 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -769,7 +769,7 @@ struct ubi_scan_leb *ubi_scan_get_free_peb(struct ubi_device *ubi, */ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum) { - long long ec; + long long uninitialized_var(ec); int err, bitflips = 0, vol_id, ec_corr = 0; dbg_bld("scan PEB %d", pnum); -- cgit v1.2.3 From 0bdf77f85bd89d5e00d32af0fb7d2dac63ce8ff5 Mon Sep 17 00:00:00 2001 From: Roel Kluin <12o3l@tiscali.nl> Date: Mon, 28 Jan 2008 11:48:09 +0100 Subject: [MTD] [NOR] Test devtype, not definition in flash_probe(), drivers/mtd/devices/lart.c drivers/mtd/devices/lart.c:119:#define FLASH_DEVICE_16mbit_BOTTOM 0x88f488f4 As was, unless "manufacturer != FLASH_MANUFACTURER" this returned true Signed-off-by: Roel Kluin <12o3l@tiscali.nl> Signed-off-by: David Woodhouse --- drivers/mtd/devices/lart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 4ea50a1dda8..99fd210feae 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -323,7 +323,7 @@ static int flash_probe (void) /* put the flash back into command mode */ write32 (DATA_TO_FLASH (READ_ARRAY),0x00000000); - return (manufacturer == FLASH_MANUFACTURER && (devtype == FLASH_DEVICE_16mbit_TOP || FLASH_DEVICE_16mbit_BOTTOM)); + return (manufacturer == FLASH_MANUFACTURER && (devtype == FLASH_DEVICE_16mbit_TOP || devtype == FLASH_DEVICE_16mbit_BOTTOM)); } /* -- cgit v1.2.3 From 2986bd2a330e57af53c016276d63d46ded10e9c5 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Tue, 29 Jan 2008 11:27:09 +0000 Subject: [MTD] mtdoops: Add further error return code checking Add further error return code checks to the mtdoops driver. Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 20eaf294f62..723ca76d6b5 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -103,7 +103,7 @@ static int mtdoops_inc_counter(struct mtdoops_context *cxt) ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count); - if ((retlen != 4) || (ret < 0)) { + if ((retlen != 4) || ((ret < 0) && (ret != -EUCLEAN))) { printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" ", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE, retlen, ret); @@ -136,8 +136,14 @@ static void mtdoops_prepare(struct mtdoops_context *cxt) cxt->nextpage = 0; } - while (mtd->block_isbad && - mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE)) { + while (mtd->block_isbad) { + ret = mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); + if (!ret) + break; + if (ret < 0) { + printk(KERN_ERR "mtdoops: block_isbad failed, aborting.\n"); + return; + } badblock: printk(KERN_WARNING "mtdoops: Bad block at %08x\n", cxt->nextpage * OOPS_PAGE_SIZE); @@ -154,15 +160,20 @@ badblock: for (j = 0, ret = -1; (j < 3) && (ret < 0); j++) ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE); - if (ret < 0) { - if (mtd->block_markbad) - mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); - goto badblock; + if (ret >= 0) { + printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount); + cxt->ready = 1; + return; } - printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount); - - cxt->ready = 1; + if (mtd->block_markbad && (ret == -EIO)) { + ret = mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); + if (ret < 0) { + printk(KERN_ERR "mtdoops: block_markbad failed, aborting.\n"); + return; + } + } + goto badblock; } static void mtdoops_workfunc(struct work_struct *work) @@ -176,12 +187,18 @@ static void mtdoops_workfunc(struct work_struct *work) static int find_next_position(struct mtdoops_context *cxt) { struct mtd_info *mtd = cxt->mtd; - int page, maxpos = 0; + int ret, page, maxpos = 0; u32 count, maxcount = 0xffffffff; size_t retlen; for (page = 0; page < cxt->oops_pages; page++) { - mtd->read(mtd, page * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count); + ret = mtd->read(mtd, page * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count); + if ((retlen != 4) || ((ret < 0) && (ret != -EUCLEAN))) { + printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" + ", err %d.\n", page * OOPS_PAGE_SIZE, retlen, ret); + continue; + } + if (count == 0xffffffff) continue; if (maxcount == 0xffffffff) { -- cgit v1.2.3 From 6ce0a856c10c8ab8568764436864616efa88e908 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Tue, 29 Jan 2008 11:27:11 +0000 Subject: [MTD] mtdoops: Perform write operations in a workqueue Writing to the flash needs to be done in a workqueue. The console write functions may be called in any context which can lead to lockups otherwise. Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 82 ++++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 723ca76d6b5..72c434c61b0 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -32,9 +32,10 @@ #define OOPS_PAGE_SIZE 4096 -static struct mtdoops_context { +struct mtdoops_context { int mtd_index; - struct work_struct work; + struct work_struct work_erase; + struct work_struct work_write; struct mtd_info *mtd; int oops_pages; int nextpage; @@ -87,7 +88,7 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset) return 0; } -static int mtdoops_inc_counter(struct mtdoops_context *cxt) +static void mtdoops_inc_counter(struct mtdoops_context *cxt) { struct mtd_info *mtd = cxt->mtd; size_t retlen; @@ -107,21 +108,26 @@ static int mtdoops_inc_counter(struct mtdoops_context *cxt) printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" ", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE, retlen, ret); - return 1; + schedule_work(&cxt->work_erase); + return; } /* See if we need to erase the next block */ - if (count != 0xffffffff) - return 1; + if (count != 0xffffffff) { + schedule_work(&cxt->work_erase); + return; + } printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n", cxt->nextpage, cxt->nextcount); cxt->ready = 1; - return 0; } -static void mtdoops_prepare(struct mtdoops_context *cxt) +/* Scheduled work - when we can't proceed without erasing a block */ +static void mtdoops_workfunc_erase(struct work_struct *work) { + struct mtdoops_context *cxt = + container_of(work, struct mtdoops_context, work_erase); struct mtd_info *mtd = cxt->mtd; int i = 0, j, ret, mod; @@ -176,15 +182,31 @@ badblock: goto badblock; } -static void mtdoops_workfunc(struct work_struct *work) +static void mtdoops_workfunc_write(struct work_struct *work) { struct mtdoops_context *cxt = - container_of(work, struct mtdoops_context, work); + container_of(work, struct mtdoops_context, work_write); + struct mtd_info *mtd = cxt->mtd; + size_t retlen; + int ret; - mtdoops_prepare(cxt); -} + if (cxt->writecount < OOPS_PAGE_SIZE) + memset(cxt->oops_buf + cxt->writecount, 0xff, + OOPS_PAGE_SIZE - cxt->writecount); + + ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, + OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); + + cxt->writecount = 0; + + if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) + printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n", + cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); + + mtdoops_inc_counter(cxt); +} -static int find_next_position(struct mtdoops_context *cxt) +static void find_next_position(struct mtdoops_context *cxt) { struct mtd_info *mtd = cxt->mtd; int ret, page, maxpos = 0; @@ -222,20 +244,19 @@ static int find_next_position(struct mtdoops_context *cxt) cxt->ready = 1; printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n", cxt->nextpage, cxt->nextcount); - return 0; + return; } cxt->nextpage = maxpos; cxt->nextcount = maxcount; - return mtdoops_inc_counter(cxt); + mtdoops_inc_counter(cxt); } static void mtdoops_notify_add(struct mtd_info *mtd) { struct mtdoops_context *cxt = &oops_cxt; - int ret; if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) return; @@ -249,9 +270,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd) cxt->mtd = mtd; cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE; - ret = find_next_position(cxt); - if (ret == 1) - mtdoops_prepare(cxt); + find_next_position(cxt); printk(KERN_DEBUG "mtdoops: Attached to MTD device %d\n", mtd->index); } @@ -271,31 +290,13 @@ static void mtdoops_console_sync(void) { struct mtdoops_context *cxt = &oops_cxt; struct mtd_info *mtd = cxt->mtd; - size_t retlen; - int ret; - if (!cxt->ready || !mtd) + if (!cxt->ready || !mtd || cxt->writecount == 0) return; - if (cxt->writecount == 0) - return; - - if (cxt->writecount < OOPS_PAGE_SIZE) - memset(cxt->oops_buf + cxt->writecount, 0xff, - OOPS_PAGE_SIZE - cxt->writecount); - - ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, - OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); cxt->ready = 0; - cxt->writecount = 0; - - if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) - printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n", - cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); - ret = mtdoops_inc_counter(cxt); - if (ret == 1) - schedule_work(&cxt->work); + schedule_work(&cxt->work_write); } static void @@ -365,7 +366,8 @@ static int __init mtdoops_console_init(void) return -ENOMEM; } - INIT_WORK(&cxt->work, mtdoops_workfunc); + INIT_WORK(&cxt->work_erase, mtdoops_workfunc_erase); + INIT_WORK(&cxt->work_write, mtdoops_workfunc_write); register_console(&mtdoops_console); register_mtd_user(&mtdoops_notifier); -- cgit v1.2.3 From 47c152b88c185c7e97462a35893df6e552075a8c Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Tue, 29 Jan 2008 10:21:56 +0000 Subject: [MTD] mtdoops: Ensure sequential write to the buffer Add a spinlock to ensure writes to the mtdoops buffer memory are sequential and don't race. Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 72c434c61b0..1687521b4aa 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #define OOPS_PAGE_SIZE 4096 @@ -42,6 +43,9 @@ struct mtdoops_context { int nextcount; void *oops_buf; + + /* writecount and disabling ready are spin lock protected */ + spinlock_t writecount_lock; int ready; int writecount; } oops_cxt; @@ -290,11 +294,22 @@ static void mtdoops_console_sync(void) { struct mtdoops_context *cxt = &oops_cxt; struct mtd_info *mtd = cxt->mtd; + unsigned long flags; if (!cxt->ready || !mtd || cxt->writecount == 0) return; + /* + * Once ready is 0 and we've held the lock no further writes to the + * buffer will happen + */ + spin_lock_irqsave(&cxt->writecount_lock, flags); + if (!cxt->ready) { + spin_unlock_irqrestore(&cxt->writecount_lock, flags); + return; + } cxt->ready = 0; + spin_unlock_irqrestore(&cxt->writecount_lock, flags); schedule_work(&cxt->work_write); } @@ -304,6 +319,7 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) { struct mtdoops_context *cxt = co->data; struct mtd_info *mtd = cxt->mtd; + unsigned long flags; if (!oops_in_progress) { mtdoops_console_sync(); @@ -313,6 +329,13 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) if (!cxt->ready || !mtd) return; + /* Locking on writecount ensures sequential writes to the buffer */ + spin_lock_irqsave(&cxt->writecount_lock, flags); + + /* Check ready status didn't change whilst waiting for the lock */ + if (!cxt->ready) + return; + if (cxt->writecount == 0) { u32 *stamp = cxt->oops_buf; *stamp = cxt->nextcount; @@ -324,6 +347,11 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) memcpy(cxt->oops_buf + cxt->writecount, s, count); cxt->writecount += count; + + spin_unlock_irqrestore(&cxt->writecount_lock, flags); + + if (cxt->writecount == OOPS_PAGE_SIZE) + mtdoops_console_sync(); } static int __init mtdoops_console_setup(struct console *co, char *options) -- cgit v1.2.3 From 79dcd8e9e1f2864ade80f45e144e5e80fef71613 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Tue, 29 Jan 2008 10:25:55 +0000 Subject: [MTD] mtdoops: Various minor cleanups Various minor cleaups to mtdoops: * Don't support the mtd->erasesize < OOPS_PAGE_SIZE case * Tweak printks and make the device mtdoops connects to more visible * CON_PRINTBUFFER flag is uneeded Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 1687521b4aa..34681bc9110 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -67,10 +67,7 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset) erase.mtd = mtd; erase.callback = mtdoops_erase_callback; erase.addr = offset; - if (mtd->erasesize < OOPS_PAGE_SIZE) - erase.len = OOPS_PAGE_SIZE; - else - erase.len = mtd->erasesize; + erase.len = mtd->erasesize; erase.priv = (u_long)&wait_q; set_current_state(TASK_INTERRUPTIBLE); @@ -271,12 +268,18 @@ static void mtdoops_notify_add(struct mtd_info *mtd) return; } + if (mtd->erasesize < OOPS_PAGE_SIZE) { + printk(KERN_ERR "Eraseblock size of MTD partition %d too small\n", + mtd->index); + return; + } + cxt->mtd = mtd; cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE; find_next_position(cxt); - printk(KERN_DEBUG "mtdoops: Attached to MTD device %d\n", mtd->index); + printk(KERN_INFO "mtdoops: Attached to MTD device %d\n", mtd->index); } static void mtdoops_notify_remove(struct mtd_info *mtd) @@ -377,7 +380,6 @@ static struct console mtdoops_console = { .write = mtdoops_console_write, .setup = mtdoops_console_setup, .unblank = mtdoops_console_sync, - .flags = CON_PRINTBUFFER, .index = -1, .data = &oops_cxt, }; @@ -390,7 +392,7 @@ static int __init mtdoops_console_init(void) cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE); if (!cxt->oops_buf) { - printk(KERN_ERR "Failed to allocate oops buffer workspace\n"); + printk(KERN_ERR "Failed to allocate mtdoops buffer workspace\n"); return -ENOMEM; } -- cgit v1.2.3 From 842b1a105c95d22c3e4257879539413d3152629e Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Tue, 29 Jan 2008 22:28:22 +0900 Subject: [MTD] [NAND] at91_nand: Make mtdparts option can override board info Call parse_mtd_partitions before checking board's partition_info, so that "mtdparts=" option can override board's default setting. Signed-off-by: Atsushi Nemoto Signed-off-by: David Woodhouse --- drivers/mtd/nand/at91_nand.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/at91_nand.c index b2a5672df6e..c9fb2acf405 100644 --- a/drivers/mtd/nand/at91_nand.c +++ b/drivers/mtd/nand/at91_nand.c @@ -156,14 +156,14 @@ static int __init at91_nand_probe(struct platform_device *pdev) } #ifdef CONFIG_MTD_PARTITIONS - if (host->board->partition_info) - partitions = host->board->partition_info(mtd->size, &num_partitions); #ifdef CONFIG_MTD_CMDLINE_PARTS - else { - mtd->name = "at91_nand"; - num_partitions = parse_mtd_partitions(mtd, part_probes, &partitions, 0); - } + mtd->name = "at91_nand"; + num_partitions = parse_mtd_partitions(mtd, part_probes, + &partitions, 0); #endif + if (num_partitions <= 0 && host->board->partition_info) + partitions = host->board->partition_info(mtd->size, + &num_partitions); if ((!partitions) || (num_partitions == 0)) { printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n"); -- cgit v1.2.3 From e619a75ff6201b567a539e787aa9af9bc63a3187 Mon Sep 17 00:00:00 2001 From: Justin Treon Date: Wed, 30 Jan 2008 10:25:49 -0800 Subject: [MTD] Unlocking all Intel flash that is locked on power up. Patch for unlocking all Intel flash that has instant locking on power up. The patch has been tested on Intel M18, P30 and J3D Strata Flash. 1. The automatic unlocking can be disabled for a particular partition in the map or the command line. a. For the bit mask in the map it should look like: .mask_flags = MTD_POWERUP_LOCK, b. For the command line parsing it should look like: mtdparts=0x80000(bootloader)lk 2. This will only unlock parts with instant individual block locking. Intel parts with legacy unlocking will not be unlocked. Signed-off-by: Justin Treon Signed-off-by: Jared Hulbert Acked-by: Nicolas Pitre Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 18 ++++++++++++------ drivers/mtd/chips/cfi_cmdset_0002.c | 2 +- drivers/mtd/cmdlinepart.c | 9 ++++++++- drivers/mtd/mtdcore.c | 2 +- include/mtd/mtd-abi.h | 2 +- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 350671ec522..8189adfefae 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -269,10 +269,16 @@ static void fixup_use_write_buffers(struct mtd_info *mtd, void *param) /* * Some chips power-up with all sectors locked by default. */ -static void fixup_use_powerup_lock(struct mtd_info *mtd, void *param) +static void fixup_unlock_powerup_lock(struct mtd_info *mtd, void *param) { - printk(KERN_INFO "Using auto-unlock on power-up/resume\n" ); - mtd->flags |= MTD_STUPID_LOCK; + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *cfip = cfi->cmdset_priv; + + if (cfip->FeatureSupport&32) { + printk(KERN_INFO "Using auto-unlock on power-up/resume\n" ); + mtd->flags |= MTD_POWERUP_LOCK; + } } static struct cfi_fixup cfi_fixup_table[] = { @@ -288,7 +294,7 @@ static struct cfi_fixup cfi_fixup_table[] = { #endif { CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL }, { CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL }, - { MANUFACTURER_INTEL, 0x891c, fixup_use_powerup_lock, NULL, }, + { MANUFACTURER_INTEL, CFI_ID_ANY, fixup_unlock_powerup_lock, NULL, }, { 0, 0, NULL, NULL } }; @@ -2349,7 +2355,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) struct flchip *chip; int ret = 0; - if ((mtd->flags & MTD_STUPID_LOCK) + if ((mtd->flags & MTD_POWERUP_LOCK) && extp && (extp->FeatureSupport & (1 << 5))) cfi_intelext_save_locks(mtd); @@ -2460,7 +2466,7 @@ static void cfi_intelext_resume(struct mtd_info *mtd) spin_unlock(chip->mutex); } - if ((mtd->flags & MTD_STUPID_LOCK) + if ((mtd->flags & MTD_POWERUP_LOCK) && extp && (extp->FeatureSupport & (1 << 5))) cfi_intelext_restore_locks(mtd); } diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 796bfeadea2..d072e87ce4e 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -217,7 +217,7 @@ static void fixup_use_atmel_lock(struct mtd_info *mtd, void *param) { mtd->lock = cfi_atmel_lock; mtd->unlock = cfi_atmel_unlock; - mtd->flags |= MTD_STUPID_LOCK; + mtd->flags |= MTD_POWERUP_LOCK; } static struct cfi_fixup cfi_fixup_table[] = { diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index 23fab14f163..b44292abd9f 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -9,7 +9,7 @@ * * mtdparts=[; := :[,] - * := [@offset][][ro] + * := [@offset][][ro][lk] * := unique name used in mapping driver/device (mtd->name) * := standard linux memsize OR "-" to denote all remaining space * := '(' NAME ')' @@ -143,6 +143,13 @@ static struct mtd_partition * newpart(char *s, s += 2; } + /* if lk is found do NOT unlock the MTD partition*/ + if (strncmp(s, "lk", 2) == 0) + { + mask_flags |= MTD_POWERUP_LOCK; + s += 2; + } + /* test if more partitions are following */ if (*s == ',') { diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 6c2645e2837..f7e7890e5bc 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -61,7 +61,7 @@ int add_mtd_device(struct mtd_info *mtd) /* Some chips always power up locked. Unlock them now */ if ((mtd->flags & MTD_WRITEABLE) - && (mtd->flags & MTD_STUPID_LOCK) && mtd->unlock) { + && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) { if (mtd->unlock(mtd, 0, mtd->size)) printk(KERN_WARNING "%s: unlock failed, " diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index f71dac42039..615072c4da0 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -29,7 +29,7 @@ struct mtd_oob_buf { #define MTD_WRITEABLE 0x400 /* Device is writeable */ #define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ #define MTD_NO_ERASE 0x1000 /* No erase necessary */ -#define MTD_STUPID_LOCK 0x2000 /* Always locked after reset */ +#define MTD_POWERUP_LOCK 0x2000 /* Always locked after reset */ // Some common devices / combinations of capabilities #define MTD_CAP_ROM 0 -- cgit v1.2.3 From a50367784ea6ef8d4276daac0dfcfaa896f8530b Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Sun, 3 Feb 2008 10:06:45 +0800 Subject: [MTD] [NAND] Remove unused variable in plat_nand_remove With CONFIG_MTD_PARTITIONS not set, got this: drivers/mtd/nand/plat_nand.c:113: warning: unused variable 'pdata' Signed-off-by: Li Zefan Signed-off-by: David Woodhouse --- drivers/mtd/nand/plat_nand.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index cd725fc5e81..f6d5c2adc4f 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -110,7 +110,9 @@ out: static int __devexit plat_nand_remove(struct platform_device *pdev) { struct plat_nand_data *data = platform_get_drvdata(pdev); +#ifdef CONFIG_MTD_PARTITIONS struct platform_nand_data *pdata = pdev->dev.platform_data; +#endif nand_release(&data->mtd); #ifdef CONFIG_MTD_PARTITIONS -- cgit v1.2.3 From f4dda0914e76feb1ff5ec349cc68f319f6a4e40f Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Wed, 30 Jan 2008 17:18:17 +0800 Subject: [MTD] [NAND] Remove wrong operation in PM function of the BF54x NFC driver There is no suspend/resume operation in NFC driver at all, currently. Signed-off-by: Bryan Wu Signed-off-by: David Woodhouse --- drivers/mtd/nand/bf5xx_nand.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 542850cd4c3..2fe9678e754 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -760,9 +760,6 @@ static int bf5xx_nand_resume(struct platform_device *dev) { struct bf5xx_nand_info *info = platform_get_drvdata(dev); - if (info) - bf5xx_nand_hw_init(info); - return 0; } -- cgit v1.2.3 From 5eb91034f3d825f43b3c8ace7b69f94752b7deda Mon Sep 17 00:00:00 2001 From: Bryan Wu Date: Wed, 30 Jan 2008 17:18:18 +0800 Subject: [MTD] [NAND] Fix Blackfin NFC ECC calculating bug with page size 512 bytes Signed-off-by: Bryan Wu Signed-off-by: David Woodhouse --- drivers/mtd/nand/bf5xx_nand.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 2fe9678e754..7d6ac6a7d9a 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -293,7 +293,6 @@ static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd, u16 ecc0, ecc1; u32 code[2]; u8 *p; - int bytes = 3, i; /* first 4 bytes ECC code for 256 page size */ ecc0 = bfin_read_NFC_ECC0(); @@ -303,19 +302,24 @@ static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd, dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]); + /* first 3 bytes in ecc_code for 256 page size */ + p = (u8 *) code; + memcpy(ecc_code, p, 3); + /* second 4 bytes ECC code for 512 page size */ if (page_size == 512) { ecc0 = bfin_read_NFC_ECC2(); ecc1 = bfin_read_NFC_ECC3(); code[1] = (ecc0 & 0x3FF) | ((ecc1 & 0x3FF) << 11); - bytes = 6; + + /* second 3 bytes in ecc_code for second 256 + * bytes of 512 page size + */ + p = (u8 *) (code + 1); + memcpy((ecc_code + 3), p, 3); dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]); } - p = (u8 *)code; - for (i = 0; i < bytes; i++) - ecc_code[i] = p[i]; - return 0; } -- cgit v1.2.3 From 1c45f60406e61a06631416264e49eb5afd9fc324 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Wed, 16 Jan 2008 10:36:03 -0600 Subject: [MTD] [NAND] Fix misparenthesization introduced by commit 78b65179... Signed-off-by: Scott Wood Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 85a7283845f..971d58c391f 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2472,9 +2472,9 @@ int nand_scan_tail(struct mtd_info *mtd) if ((!chip->ecc.calculate || !chip->ecc.correct || !chip->ecc.hwctl) && (!chip->ecc.read_page || - chip->ecc.read_page == nand_read_page_hwecc) || + chip->ecc.read_page == nand_read_page_hwecc || !chip->ecc.write_page || - chip->ecc.write_page == nand_write_page_hwecc) { + chip->ecc.write_page == nand_write_page_hwecc)) { printk(KERN_WARNING "No ECC functions supplied, " "Hardware ECC not possible\n"); BUG(); -- cgit v1.2.3 From df66e7167ac756baf14d2b8ea7a2cfa056600a93 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Fri, 1 Feb 2008 15:26:54 +0100 Subject: [MTD] physmap.c: Add support for multiple resources This patch extends the physmap mapping driver to support multiple resources for non-identical NOR chips that will be concatenated together when selected. This is needed for example for Intel 48F4400 512MBit chips, since they consist of 2 single different NOR chips with different geometries. The first (lower) one has botton boot sectors and the 2nd (upper) has top boot sectors. This currently isn't handled correctly by calling the physmap driver once with only one resource covering both chips in one memory region. The same geometrie is used for both chips. With this patch the following resource structure can be used to describe the 48F4400 chip correctly: static struct resource board_nor_resource[] = { [0] = { .start = 0xf8000000, .end = 0xfbffffff, .flags = IORESOURCE_MEM, }, [1] = { .start = 0xfc000000, .end = 0xffffffff, .flags = IORESOURCE_MEM, } }; Signed-off-by: Stefan Roese Signed-off-by: David Woodhouse --- drivers/mtd/maps/physmap.c | 162 +++++++++++++++++++++++++++++---------------- 1 file changed, 104 insertions(+), 58 deletions(-) diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 28c5ffd7523..5a83ae7e14f 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -20,11 +20,15 @@ #include #include #include +#include #include +#define MAX_RESOURCES 4 + struct physmap_flash_info { - struct mtd_info *mtd; - struct map_info map; + struct mtd_info *mtd[MAX_RESOURCES]; + struct mtd_info *cmtd; + struct map_info map[MAX_RESOURCES]; struct resource *res; #ifdef CONFIG_MTD_PARTITIONS int nr_parts; @@ -32,11 +36,11 @@ struct physmap_flash_info { #endif }; - static int physmap_flash_remove(struct platform_device *dev) { struct physmap_flash_info *info; struct physmap_flash_data *physmap_data; + int i; info = platform_get_drvdata(dev); if (info == NULL) @@ -45,24 +49,33 @@ static int physmap_flash_remove(struct platform_device *dev) physmap_data = dev->dev.platform_data; - if (info->mtd != NULL) { +#ifdef CONFIG_MTD_CONCAT + if (info->cmtd != info->mtd[0]) { + del_mtd_device(info->cmtd); + mtd_concat_destroy(info->cmtd); + } +#endif + + for (i = 0; i < MAX_RESOURCES; i++) { + if (info->mtd[i] != NULL) { #ifdef CONFIG_MTD_PARTITIONS - if (info->nr_parts) { - del_mtd_partitions(info->mtd); - kfree(info->parts); - } else if (physmap_data->nr_parts) { - del_mtd_partitions(info->mtd); - } else { - del_mtd_device(info->mtd); - } + if (info->nr_parts) { + del_mtd_partitions(info->mtd[i]); + kfree(info->parts); + } else if (physmap_data->nr_parts) { + del_mtd_partitions(info->mtd[i]); + } else { + del_mtd_device(info->mtd[i]); + } #else - del_mtd_device(info->mtd); + del_mtd_device(info->mtd[i]); #endif - map_destroy(info->mtd); - } + map_destroy(info->mtd[i]); + } - if (info->map.virt != NULL) - iounmap(info->map.virt); + if (info->map[i].virt != NULL) + iounmap(info->map[i].virt); + } if (info->res != NULL) { release_resource(info->res); @@ -82,16 +95,14 @@ static int physmap_flash_probe(struct platform_device *dev) struct physmap_flash_data *physmap_data; struct physmap_flash_info *info; const char **probe_type; - int err; + int err = 0; + int i; + int devices_found = 0; physmap_data = dev->dev.platform_data; if (physmap_data == NULL) return -ENODEV; - printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n", - (unsigned long long)(dev->resource->end - dev->resource->start + 1), - (unsigned long long)dev->resource->start); - info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); if (info == NULL) { err = -ENOMEM; @@ -100,56 +111,83 @@ static int physmap_flash_probe(struct platform_device *dev) platform_set_drvdata(dev, info); - info->res = request_mem_region(dev->resource->start, - dev->resource->end - dev->resource->start + 1, - dev->dev.bus_id); - if (info->res == NULL) { - dev_err(&dev->dev, "Could not reserve memory region\n"); - err = -ENOMEM; - goto err_out; - } + for (i = 0; i < dev->num_resources; i++) { + printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n", + (unsigned long long)(dev->resource[i].end - dev->resource[i].start + 1), + (unsigned long long)dev->resource[i].start); + + info->res = request_mem_region(dev->resource[i].start, + dev->resource[i].end - dev->resource[i].start + 1, + dev->dev.bus_id); + if (info->res == NULL) { + dev_err(&dev->dev, "Could not reserve memory region\n"); + err = -ENOMEM; + goto err_out; + } - info->map.name = dev->dev.bus_id; - info->map.phys = dev->resource->start; - info->map.size = dev->resource->end - dev->resource->start + 1; - info->map.bankwidth = physmap_data->width; - info->map.set_vpp = physmap_data->set_vpp; + info->map[i].name = dev->dev.bus_id; + info->map[i].phys = dev->resource[i].start; + info->map[i].size = dev->resource[i].end - dev->resource[i].start + 1; + info->map[i].bankwidth = physmap_data->width; + info->map[i].set_vpp = physmap_data->set_vpp; + + info->map[i].virt = ioremap(info->map[i].phys, info->map[i].size); + if (info->map[i].virt == NULL) { + dev_err(&dev->dev, "Failed to ioremap flash region\n"); + err = EIO; + goto err_out; + } - info->map.virt = ioremap(info->map.phys, info->map.size); - if (info->map.virt == NULL) { - dev_err(&dev->dev, "Failed to ioremap flash region\n"); - err = EIO; - goto err_out; - } + simple_map_init(&info->map[i]); - simple_map_init(&info->map); + probe_type = rom_probe_types; + for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++) + info->mtd[i] = do_map_probe(*probe_type, &info->map[i]); + if (info->mtd[i] == NULL) { + dev_err(&dev->dev, "map_probe failed\n"); + err = -ENXIO; + goto err_out; + } else { + devices_found++; + } + info->mtd[i]->owner = THIS_MODULE; + } - probe_type = rom_probe_types; - for (; info->mtd == NULL && *probe_type != NULL; probe_type++) - info->mtd = do_map_probe(*probe_type, &info->map); - if (info->mtd == NULL) { - dev_err(&dev->dev, "map_probe failed\n"); + if (devices_found == 1) { + info->cmtd = info->mtd[0]; + } else if (devices_found > 1) { + /* + * We detected multiple devices. Concatenate them together. + */ +#ifdef CONFIG_MTD_CONCAT + info->cmtd = mtd_concat_create(info->mtd, devices_found, dev->dev.bus_id); + if (info->cmtd == NULL) + err = -ENXIO; +#else + printk(KERN_ERR "physmap-flash: multiple devices " + "found but MTD concat support disabled.\n"); err = -ENXIO; - goto err_out; +#endif } - info->mtd->owner = THIS_MODULE; + if (err) + goto err_out; #ifdef CONFIG_MTD_PARTITIONS - err = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0); + err = parse_mtd_partitions(info->cmtd, part_probe_types, &info->parts, 0); if (err > 0) { - add_mtd_partitions(info->mtd, info->parts, err); + add_mtd_partitions(info->cmtd, info->parts, err); return 0; } if (physmap_data->nr_parts) { printk(KERN_NOTICE "Using physmap partition information\n"); - add_mtd_partitions(info->mtd, physmap_data->parts, - physmap_data->nr_parts); + add_mtd_partitions(info->cmtd, physmap_data->parts, + physmap_data->nr_parts); return 0; } #endif - add_mtd_device(info->mtd); + add_mtd_device(info->cmtd); return 0; err_out: @@ -162,9 +200,11 @@ static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state { struct physmap_flash_info *info = platform_get_drvdata(dev); int ret = 0; + int i; if (info) - ret = info->mtd->suspend(info->mtd); + for (i = 0; i < MAX_RESOURCES; i++) + ret |= info->mtd[i].suspend(info->mtd[i]); return ret; } @@ -172,16 +212,22 @@ static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state static int physmap_flash_resume(struct platform_device *dev) { struct physmap_flash_info *info = platform_get_drvdata(dev); + int i; + if (info) - info->mtd->resume(info->mtd); + for (i = 0; i < MAX_RESOURCES; i++) + info->mtd[i].resume(info->mtd[i]); return 0; } static void physmap_flash_shutdown(struct platform_device *dev) { struct physmap_flash_info *info = platform_get_drvdata(dev); - if (info && info->mtd->suspend(info->mtd) == 0) - info->mtd->resume(info->mtd); + int i; + + for (i = 0; i < MAX_RESOURCES; i++) + if (info && info->mtd[i].suspend(info->mtd[i]) == 0) + info->mtd[i].resume(info->mtd[i]); } #endif -- cgit v1.2.3 From 76b104671632c225ad594a50f9e26ada67bc0a74 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Wed, 6 Feb 2008 15:36:21 -0600 Subject: [MTD] [NAND] Freescale enhanced Local Bus Controller FCM NAND support. Signed-off-by: Nick Spence Signed-off-by: Scott Wood Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 9 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/fsl_elbc_nand.c | 1244 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 1254 insertions(+) create mode 100644 drivers/mtd/nand/fsl_elbc_nand.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 0a840d5d75a..4a3c6759492 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -321,4 +321,13 @@ config MTD_NAND_ORION No board specific support is done by this driver, each board must advertise a platform_device for the driver to attach. +config MTD_NAND_FSL_ELBC + tristate "NAND support for Freescale eLBC controllers" + depends on MTD_NAND && PPC_OF + help + Various Freescale chips, including the 8313, include a NAND Flash + Controller Module with built-in hardware ECC capabilities. + Enabling this option will enable you to use this to control + external NAND devices. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index e35f5ea3a7a..80d575eeee9 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -31,5 +31,6 @@ obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_ALAUDA) += alauda.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o +obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c new file mode 100644 index 00000000000..b025dfe0b27 --- /dev/null +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -0,0 +1,1244 @@ +/* Freescale Enhanced Local Bus Controller NAND driver + * + * Copyright (c) 2006-2007 Freescale Semiconductor + * + * Authors: Nick Spence , + * Scott Wood + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + + +#define MAX_BANKS 8 +#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ +#define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */ + +struct elbc_bank { + __be32 br; /**< Base Register */ +#define BR_BA 0xFFFF8000 +#define BR_BA_SHIFT 15 +#define BR_PS 0x00001800 +#define BR_PS_SHIFT 11 +#define BR_PS_8 0x00000800 /* Port Size 8 bit */ +#define BR_PS_16 0x00001000 /* Port Size 16 bit */ +#define BR_PS_32 0x00001800 /* Port Size 32 bit */ +#define BR_DECC 0x00000600 +#define BR_DECC_SHIFT 9 +#define BR_DECC_OFF 0x00000000 /* HW ECC checking and generation off */ +#define BR_DECC_CHK 0x00000200 /* HW ECC checking on, generation off */ +#define BR_DECC_CHK_GEN 0x00000400 /* HW ECC checking and generation on */ +#define BR_WP 0x00000100 +#define BR_WP_SHIFT 8 +#define BR_MSEL 0x000000E0 +#define BR_MSEL_SHIFT 5 +#define BR_MS_GPCM 0x00000000 /* GPCM */ +#define BR_MS_FCM 0x00000020 /* FCM */ +#define BR_MS_SDRAM 0x00000060 /* SDRAM */ +#define BR_MS_UPMA 0x00000080 /* UPMA */ +#define BR_MS_UPMB 0x000000A0 /* UPMB */ +#define BR_MS_UPMC 0x000000C0 /* UPMC */ +#define BR_V 0x00000001 +#define BR_V_SHIFT 0 +#define BR_RES ~(BR_BA|BR_PS|BR_DECC|BR_WP|BR_MSEL|BR_V) + + __be32 or; /**< Base Register */ +#define OR0 0x5004 +#define OR1 0x500C +#define OR2 0x5014 +#define OR3 0x501C +#define OR4 0x5024 +#define OR5 0x502C +#define OR6 0x5034 +#define OR7 0x503C + +#define OR_FCM_AM 0xFFFF8000 +#define OR_FCM_AM_SHIFT 15 +#define OR_FCM_BCTLD 0x00001000 +#define OR_FCM_BCTLD_SHIFT 12 +#define OR_FCM_PGS 0x00000400 +#define OR_FCM_PGS_SHIFT 10 +#define OR_FCM_CSCT 0x00000200 +#define OR_FCM_CSCT_SHIFT 9 +#define OR_FCM_CST 0x00000100 +#define OR_FCM_CST_SHIFT 8 +#define OR_FCM_CHT 0x00000080 +#define OR_FCM_CHT_SHIFT 7 +#define OR_FCM_SCY 0x00000070 +#define OR_FCM_SCY_SHIFT 4 +#define OR_FCM_SCY_1 0x00000010 +#define OR_FCM_SCY_2 0x00000020 +#define OR_FCM_SCY_3 0x00000030 +#define OR_FCM_SCY_4 0x00000040 +#define OR_FCM_SCY_5 0x00000050 +#define OR_FCM_SCY_6 0x00000060 +#define OR_FCM_SCY_7 0x00000070 +#define OR_FCM_RST 0x00000008 +#define OR_FCM_RST_SHIFT 3 +#define OR_FCM_TRLX 0x00000004 +#define OR_FCM_TRLX_SHIFT 2 +#define OR_FCM_EHTR 0x00000002 +#define OR_FCM_EHTR_SHIFT 1 +}; + +struct elbc_regs { + struct elbc_bank bank[8]; + u8 res0[0x28]; + __be32 mar; /**< UPM Address Register */ + u8 res1[0x4]; + __be32 mamr; /**< UPMA Mode Register */ + __be32 mbmr; /**< UPMB Mode Register */ + __be32 mcmr; /**< UPMC Mode Register */ + u8 res2[0x8]; + __be32 mrtpr; /**< Memory Refresh Timer Prescaler Register */ + __be32 mdr; /**< UPM Data Register */ + u8 res3[0x4]; + __be32 lsor; /**< Special Operation Initiation Register */ + __be32 lsdmr; /**< SDRAM Mode Register */ + u8 res4[0x8]; + __be32 lurt; /**< UPM Refresh Timer */ + __be32 lsrt; /**< SDRAM Refresh Timer */ + u8 res5[0x8]; + __be32 ltesr; /**< Transfer Error Status Register */ +#define LTESR_BM 0x80000000 +#define LTESR_FCT 0x40000000 +#define LTESR_PAR 0x20000000 +#define LTESR_WP 0x04000000 +#define LTESR_ATMW 0x00800000 +#define LTESR_ATMR 0x00400000 +#define LTESR_CS 0x00080000 +#define LTESR_CC 0x00000001 +#define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC) + __be32 ltedr; /**< Transfer Error Disable Register */ + __be32 lteir; /**< Transfer Error Interrupt Register */ + __be32 lteatr; /**< Transfer Error Attributes Register */ + __be32 ltear; /**< Transfer Error Address Register */ + u8 res6[0xC]; + __be32 lbcr; /**< Configuration Register */ +#define LBCR_LDIS 0x80000000 +#define LBCR_LDIS_SHIFT 31 +#define LBCR_BCTLC 0x00C00000 +#define LBCR_BCTLC_SHIFT 22 +#define LBCR_AHD 0x00200000 +#define LBCR_LPBSE 0x00020000 +#define LBCR_LPBSE_SHIFT 17 +#define LBCR_EPAR 0x00010000 +#define LBCR_EPAR_SHIFT 16 +#define LBCR_BMT 0x0000FF00 +#define LBCR_BMT_SHIFT 8 +#define LBCR_INIT 0x00040000 + __be32 lcrr; /**< Clock Ratio Register */ +#define LCRR_DBYP 0x80000000 +#define LCRR_DBYP_SHIFT 31 +#define LCRR_BUFCMDC 0x30000000 +#define LCRR_BUFCMDC_SHIFT 28 +#define LCRR_ECL 0x03000000 +#define LCRR_ECL_SHIFT 24 +#define LCRR_EADC 0x00030000 +#define LCRR_EADC_SHIFT 16 +#define LCRR_CLKDIV 0x0000000F +#define LCRR_CLKDIV_SHIFT 0 + u8 res7[0x8]; + __be32 fmr; /**< Flash Mode Register */ +#define FMR_CWTO 0x0000F000 +#define FMR_CWTO_SHIFT 12 +#define FMR_BOOT 0x00000800 +#define FMR_ECCM 0x00000100 +#define FMR_AL 0x00000030 +#define FMR_AL_SHIFT 4 +#define FMR_OP 0x00000003 +#define FMR_OP_SHIFT 0 + __be32 fir; /**< Flash Instruction Register */ +#define FIR_OP0 0xF0000000 +#define FIR_OP0_SHIFT 28 +#define FIR_OP1 0x0F000000 +#define FIR_OP1_SHIFT 24 +#define FIR_OP2 0x00F00000 +#define FIR_OP2_SHIFT 20 +#define FIR_OP3 0x000F0000 +#define FIR_OP3_SHIFT 16 +#define FIR_OP4 0x0000F000 +#define FIR_OP4_SHIFT 12 +#define FIR_OP5 0x00000F00 +#define FIR_OP5_SHIFT 8 +#define FIR_OP6 0x000000F0 +#define FIR_OP6_SHIFT 4 +#define FIR_OP7 0x0000000F +#define FIR_OP7_SHIFT 0 +#define FIR_OP_NOP 0x0 /* No operation and end of sequence */ +#define FIR_OP_CA 0x1 /* Issue current column address */ +#define FIR_OP_PA 0x2 /* Issue current block+page address */ +#define FIR_OP_UA 0x3 /* Issue user defined address */ +#define FIR_OP_CM0 0x4 /* Issue command from FCR[CMD0] */ +#define FIR_OP_CM1 0x5 /* Issue command from FCR[CMD1] */ +#define FIR_OP_CM2 0x6 /* Issue command from FCR[CMD2] */ +#define FIR_OP_CM3 0x7 /* Issue command from FCR[CMD3] */ +#define FIR_OP_WB 0x8 /* Write FBCR bytes from FCM buffer */ +#define FIR_OP_WS 0x9 /* Write 1 or 2 bytes from MDR[AS] */ +#define FIR_OP_RB 0xA /* Read FBCR bytes to FCM buffer */ +#define FIR_OP_RS 0xB /* Read 1 or 2 bytes to MDR[AS] */ +#define FIR_OP_CW0 0xC /* Wait then issue FCR[CMD0] */ +#define FIR_OP_CW1 0xD /* Wait then issue FCR[CMD1] */ +#define FIR_OP_RBW 0xE /* Wait then read FBCR bytes */ +#define FIR_OP_RSW 0xE /* Wait then read 1 or 2 bytes */ + __be32 fcr; /**< Flash Command Register */ +#define FCR_CMD0 0xFF000000 +#define FCR_CMD0_SHIFT 24 +#define FCR_CMD1 0x00FF0000 +#define FCR_CMD1_SHIFT 16 +#define FCR_CMD2 0x0000FF00 +#define FCR_CMD2_SHIFT 8 +#define FCR_CMD3 0x000000FF +#define FCR_CMD3_SHIFT 0 + __be32 fbar; /**< Flash Block Address Register */ +#define FBAR_BLK 0x00FFFFFF + __be32 fpar; /**< Flash Page Address Register */ +#define FPAR_SP_PI 0x00007C00 +#define FPAR_SP_PI_SHIFT 10 +#define FPAR_SP_MS 0x00000200 +#define FPAR_SP_CI 0x000001FF +#define FPAR_SP_CI_SHIFT 0 +#define FPAR_LP_PI 0x0003F000 +#define FPAR_LP_PI_SHIFT 12 +#define FPAR_LP_MS 0x00000800 +#define FPAR_LP_CI 0x000007FF +#define FPAR_LP_CI_SHIFT 0 + __be32 fbcr; /**< Flash Byte Count Register */ +#define FBCR_BC 0x00000FFF + u8 res11[0x8]; + u8 res8[0xF00]; +}; + +struct fsl_elbc_ctrl; + +/* mtd information per set */ + +struct fsl_elbc_mtd { + struct mtd_info mtd; + struct nand_chip chip; + struct fsl_elbc_ctrl *ctrl; + + struct device *dev; + int bank; /* Chip select bank number */ + u8 __iomem *vbase; /* Chip select base virtual address */ + int page_size; /* NAND page size (0=512, 1=2048) */ + unsigned int fmr; /* FCM Flash Mode Register value */ +}; + +/* overview of the fsl elbc controller */ + +struct fsl_elbc_ctrl { + struct nand_hw_control controller; + struct fsl_elbc_mtd *chips[MAX_BANKS]; + + /* device info */ + struct device *dev; + struct elbc_regs __iomem *regs; + int irq; + wait_queue_head_t irq_wait; + unsigned int irq_status; /* status read from LTESR by irq handler */ + u8 __iomem *addr; /* Address of assigned FCM buffer */ + unsigned int page; /* Last page written to / read from */ + unsigned int read_bytes; /* Number of bytes read during command */ + unsigned int column; /* Saved column from SEQIN */ + unsigned int index; /* Pointer to next byte to 'read' */ + unsigned int status; /* status read from LTESR after last op */ + unsigned int mdr; /* UPM/FCM Data Register value */ + unsigned int use_mdr; /* Non zero if the MDR is to be set */ + unsigned int oob; /* Non zero if operating on OOB data */ + char *oob_poi; /* Place to write ECC after read back */ +}; + +/* These map to the positions used by the FCM hardware ECC generator */ + +/* Small Page FLASH with FMR[ECCM] = 0 */ +static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = { + .eccbytes = 3, + .eccpos = {6, 7, 8}, + .oobfree = { {0, 5}, {9, 7} }, + .oobavail = 12, +}; + +/* Small Page FLASH with FMR[ECCM] = 1 */ +static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = { + .eccbytes = 3, + .eccpos = {8, 9, 10}, + .oobfree = { {0, 5}, {6, 2}, {11, 5} }, + .oobavail = 12, +}; + +/* Large Page FLASH with FMR[ECCM] = 0 */ +static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = { + .eccbytes = 12, + .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56}, + .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} }, + .oobavail = 48, +}; + +/* Large Page FLASH with FMR[ECCM] = 1 */ +static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = { + .eccbytes = 12, + .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58}, + .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} }, + .oobavail = 48, +}; + +/*=================================*/ + +/* + * Set up the FCM hardware block and page address fields, and the fcm + * structure addr field to point to the correct FCM buffer in memory + */ +static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct elbc_regs __iomem *lbc = ctrl->regs; + int buf_num; + + ctrl->page = page_addr; + + out_be32(&lbc->fbar, + page_addr >> (chip->phys_erase_shift - chip->page_shift)); + + if (priv->page_size) { + out_be32(&lbc->fpar, + ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) | + (oob ? FPAR_LP_MS : 0) | column); + buf_num = (page_addr & 1) << 2; + } else { + out_be32(&lbc->fpar, + ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) | + (oob ? FPAR_SP_MS : 0) | column); + buf_num = page_addr & 7; + } + + ctrl->addr = priv->vbase + buf_num * 1024; + ctrl->index = column; + + /* for OOB data point to the second half of the buffer */ + if (oob) + ctrl->index += priv->page_size ? 2048 : 512; + + dev_vdbg(ctrl->dev, "set_addr: bank=%d, ctrl->addr=0x%p (0x%p), " + "index %x, pes %d ps %d\n", + buf_num, ctrl->addr, priv->vbase, ctrl->index, + chip->phys_erase_shift, chip->page_shift); +} + +/* + * execute FCM command and wait for it to complete + */ +static int fsl_elbc_run_command(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct elbc_regs __iomem *lbc = ctrl->regs; + + /* Setup the FMR[OP] to execute without write protection */ + out_be32(&lbc->fmr, priv->fmr | 3); + if (ctrl->use_mdr) + out_be32(&lbc->mdr, ctrl->mdr); + + dev_vdbg(ctrl->dev, + "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n", + in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr)); + dev_vdbg(ctrl->dev, + "fsl_elbc_run_command: fbar=%08x fpar=%08x " + "fbcr=%08x bank=%d\n", + in_be32(&lbc->fbar), in_be32(&lbc->fpar), + in_be32(&lbc->fbcr), priv->bank); + + /* execute special operation */ + out_be32(&lbc->lsor, priv->bank); + + /* wait for FCM complete flag or timeout */ + ctrl->irq_status = 0; + wait_event_timeout(ctrl->irq_wait, ctrl->irq_status, + FCM_TIMEOUT_MSECS * HZ/1000); + ctrl->status = ctrl->irq_status; + + /* store mdr value in case it was needed */ + if (ctrl->use_mdr) + ctrl->mdr = in_be32(&lbc->mdr); + + ctrl->use_mdr = 0; + + dev_vdbg(ctrl->dev, + "fsl_elbc_run_command: stat=%08x mdr=%08x fmr=%08x\n", + ctrl->status, ctrl->mdr, in_be32(&lbc->fmr)); + + /* returns 0 on success otherwise non-zero) */ + return ctrl->status == LTESR_CC ? 0 : -EIO; +} + +static void fsl_elbc_do_read(struct nand_chip *chip, int oob) +{ + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct elbc_regs __iomem *lbc = ctrl->regs; + + if (priv->page_size) { + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_CW1 << FIR_OP3_SHIFT) | + (FIR_OP_RBW << FIR_OP4_SHIFT)); + + out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) | + (NAND_CMD_READSTART << FCR_CMD1_SHIFT)); + } else { + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_RBW << FIR_OP3_SHIFT)); + + if (oob) + out_be32(&lbc->fcr, NAND_CMD_READOOB << FCR_CMD0_SHIFT); + else + out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT); + } +} + +/* cmdfunc send commands to the FCM */ +static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct elbc_regs __iomem *lbc = ctrl->regs; + + ctrl->use_mdr = 0; + + /* clear the read buffer */ + ctrl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) + ctrl->index = 0; + + switch (command) { + /* READ0 and READ1 read the entire buffer to use hardware ECC. */ + case NAND_CMD_READ1: + column += 256; + + /* fall-through */ + case NAND_CMD_READ0: + dev_dbg(ctrl->dev, + "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + + + out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */ + set_addr(mtd, 0, page_addr, 0); + + ctrl->read_bytes = mtd->writesize + mtd->oobsize; + ctrl->index += column; + + fsl_elbc_do_read(chip, 0); + fsl_elbc_run_command(mtd); + return; + + /* READOOB reads only the OOB because no ECC is performed. */ + case NAND_CMD_READOOB: + dev_vdbg(ctrl->dev, + "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:" + " 0x%x, column: 0x%x.\n", page_addr, column); + + out_be32(&lbc->fbcr, mtd->oobsize - column); + set_addr(mtd, column, page_addr, 1); + + ctrl->read_bytes = mtd->writesize + mtd->oobsize; + + fsl_elbc_do_read(chip, 1); + fsl_elbc_run_command(mtd); + return; + + /* READID must read all 5 possible bytes while CEB is active */ + case NAND_CMD_READID: + dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n"); + + out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_UA << FIR_OP1_SHIFT) | + (FIR_OP_RBW << FIR_OP2_SHIFT)); + out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT); + /* 5 bytes for manuf, device and exts */ + out_be32(&lbc->fbcr, 5); + ctrl->read_bytes = 5; + ctrl->use_mdr = 1; + ctrl->mdr = 0; + + set_addr(mtd, 0, 0, 0); + fsl_elbc_run_command(mtd); + return; + + /* ERASE1 stores the block and page address */ + case NAND_CMD_ERASE1: + dev_vdbg(ctrl->dev, + "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, " + "page_addr: 0x%x.\n", page_addr); + set_addr(mtd, 0, page_addr, 0); + return; + + /* ERASE2 uses the block and page address from ERASE1 */ + case NAND_CMD_ERASE2: + dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); + + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_PA << FIR_OP1_SHIFT) | + (FIR_OP_CM1 << FIR_OP2_SHIFT)); + + out_be32(&lbc->fcr, + (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) | + (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT)); + + out_be32(&lbc->fbcr, 0); + ctrl->read_bytes = 0; + + fsl_elbc_run_command(mtd); + return; + + /* SEQIN sets up the addr buffer and all registers except the length */ + case NAND_CMD_SEQIN: { + __be32 fcr; + dev_vdbg(ctrl->dev, + "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, " + "page_addr: 0x%x, column: 0x%x.\n", + page_addr, column); + + ctrl->column = column; + ctrl->oob = 0; + + fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) | + (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); + + if (priv->page_size) { + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CA << FIR_OP1_SHIFT) | + (FIR_OP_PA << FIR_OP2_SHIFT) | + (FIR_OP_WB << FIR_OP3_SHIFT) | + (FIR_OP_CW1 << FIR_OP4_SHIFT)); + + fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; + } else { + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_CM2 << FIR_OP1_SHIFT) | + (FIR_OP_CA << FIR_OP2_SHIFT) | + (FIR_OP_PA << FIR_OP3_SHIFT) | + (FIR_OP_WB << FIR_OP4_SHIFT) | + (FIR_OP_CW1 << FIR_OP5_SHIFT)); + + if (column >= mtd->writesize) { + /* OOB area --> READOOB */ + column -= mtd->writesize; + fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; + ctrl->oob = 1; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; + } else { + /* Second 256 bytes --> READ1 */ + fcr |= NAND_CMD_READ1 << FCR_CMD0_SHIFT; + } + } + + out_be32(&lbc->fcr, fcr); + set_addr(mtd, column, page_addr, ctrl->oob); + return; + } + + /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ + case NAND_CMD_PAGEPROG: { + int full_page; + dev_vdbg(ctrl->dev, + "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " + "writing %d bytes.\n", ctrl->index); + + /* if the write did not start at 0 or is not a full page + * then set the exact length, otherwise use a full page + * write so the HW generates the ECC. + */ + if (ctrl->oob || ctrl->column != 0 || + ctrl->index != mtd->writesize + mtd->oobsize) { + out_be32(&lbc->fbcr, ctrl->index); + full_page = 0; + } else { + out_be32(&lbc->fbcr, 0); + full_page = 1; + } + + fsl_elbc_run_command(mtd); + + /* Read back the page in order to fill in the ECC for the + * caller. Is this really needed? + */ + if (full_page && ctrl->oob_poi) { + out_be32(&lbc->fbcr, 3); + set_addr(mtd, 6, page_addr, 1); + + ctrl->read_bytes = mtd->writesize + 9; + + fsl_elbc_do_read(chip, 1); + fsl_elbc_run_command(mtd); + + memcpy_fromio(ctrl->oob_poi + 6, + &ctrl->addr[ctrl->index], 3); + ctrl->index += 3; + } + + ctrl->oob_poi = NULL; + return; + } + + /* CMD_STATUS must read the status byte while CEB is active */ + /* Note - it does not wait for the ready line */ + case NAND_CMD_STATUS: + out_be32(&lbc->fir, + (FIR_OP_CM0 << FIR_OP0_SHIFT) | + (FIR_OP_RBW << FIR_OP1_SHIFT)); + out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); + out_be32(&lbc->fbcr, 1); + set_addr(mtd, 0, 0, 0); + ctrl->read_bytes = 1; + + fsl_elbc_run_command(mtd); + + /* The chip always seems to report that it is + * write-protected, even when it is not. + */ + setbits8(ctrl->addr, NAND_STATUS_WP); + return; + + /* RESET without waiting for the ready line */ + case NAND_CMD_RESET: + dev_dbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); + out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT); + out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT); + fsl_elbc_run_command(mtd); + return; + + default: + dev_err(ctrl->dev, + "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n", + command); + } +} + +static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip) +{ + /* The hardware does not seem to support multiple + * chips per bank. + */ +} + +/* + * Write buf to the FCM Controller Data Buffer + */ +static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + unsigned int bufsize = mtd->writesize + mtd->oobsize; + + if (len < 0) { + dev_err(ctrl->dev, "write_buf of %d bytes", len); + ctrl->status = 0; + return; + } + + if ((unsigned int)len > bufsize - ctrl->index) { + dev_err(ctrl->dev, + "write_buf beyond end of buffer " + "(%d requested, %u available)\n", + len, bufsize - ctrl->index); + len = bufsize - ctrl->index; + } + + memcpy_toio(&ctrl->addr[ctrl->index], buf, len); + ctrl->index += len; +} + +/* + * read a byte from either the FCM hardware buffer if it has any data left + * otherwise issue a command to read a single byte. + */ +static u8 fsl_elbc_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + + /* If there are still bytes in the FCM, then use the next byte. */ + if (ctrl->index < ctrl->read_bytes) + return in_8(&ctrl->addr[ctrl->index++]); + + dev_err(ctrl->dev, "read_byte beyond end of buffer\n"); + return ERR_BYTE; +} + +/* + * Read from the FCM Controller Data Buffer + */ +static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + int avail; + + if (len < 0) + return; + + avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index); + memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail); + ctrl->index += avail; + + if (len > avail) + dev_err(ctrl->dev, + "read_buf beyond end of buffer " + "(%d requested, %d available)\n", + len, avail); +} + +/* + * Verify buffer against the FCM Controller Data Buffer + */ +static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + int i; + + if (len < 0) { + dev_err(ctrl->dev, "write_buf of %d bytes", len); + return -EINVAL; + } + + if ((unsigned int)len > ctrl->read_bytes - ctrl->index) { + dev_err(ctrl->dev, + "verify_buf beyond end of buffer " + "(%d requested, %u available)\n", + len, ctrl->read_bytes - ctrl->index); + + ctrl->index = ctrl->read_bytes; + return -EINVAL; + } + + for (i = 0; i < len; i++) + if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i]) + break; + + ctrl->index += len; + return i == len && ctrl->status == LTESR_CC ? 0 : -EIO; +} + +/* This function is called after Program and Erase Operations to + * check for success or failure. + */ +static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct elbc_regs __iomem *lbc = ctrl->regs; + + if (ctrl->status != LTESR_CC) + return NAND_STATUS_FAIL; + + /* Use READ_STATUS command, but wait for the device to be ready */ + ctrl->use_mdr = 0; + out_be32(&lbc->fir, + (FIR_OP_CW0 << FIR_OP0_SHIFT) | + (FIR_OP_RBW << FIR_OP1_SHIFT)); + out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); + out_be32(&lbc->fbcr, 1); + set_addr(mtd, 0, 0, 0); + ctrl->read_bytes = 1; + + fsl_elbc_run_command(mtd); + + if (ctrl->status != LTESR_CC) + return NAND_STATUS_FAIL; + + /* The chip always seems to report that it is + * write-protected, even when it is not. + */ + setbits8(ctrl->addr, NAND_STATUS_WP); + return fsl_elbc_read_byte(mtd); +} + +static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct elbc_regs __iomem *lbc = ctrl->regs; + unsigned int al; + + /* calculate FMR Address Length field */ + al = 0; + if (chip->pagemask & 0xffff0000) + al++; + if (chip->pagemask & 0xff000000) + al++; + + /* add to ECCM mode set in fsl_elbc_init */ + priv->fmr |= (12 << FMR_CWTO_SHIFT) | /* Timeout > 12 ms */ + (al << FMR_AL_SHIFT); + + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->numchips = %d\n", + chip->numchips); + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chipsize = %ld\n", + chip->chipsize); + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->pagemask = %8x\n", + chip->pagemask); + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_delay = %d\n", + chip->chip_delay); + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->badblockpos = %d\n", + chip->badblockpos); + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_shift = %d\n", + chip->chip_shift); + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->page_shift = %d\n", + chip->page_shift); + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n", + chip->phys_erase_shift); + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecclayout = %p\n", + chip->ecclayout); + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.mode = %d\n", + chip->ecc.mode); + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.steps = %d\n", + chip->ecc.steps); + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n", + chip->ecc.bytes); + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.total = %d\n", + chip->ecc.total); + dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.layout = %p\n", + chip->ecc.layout); + dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags); + dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->size = %d\n", mtd->size); + dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->erasesize = %d\n", + mtd->erasesize); + dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->writesize = %d\n", + mtd->writesize); + dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->oobsize = %d\n", + mtd->oobsize); + + /* adjust Option Register and ECC to match Flash page size */ + if (mtd->writesize == 512) { + priv->page_size = 0; + clrbits32(&lbc->bank[priv->bank].or, ~OR_FCM_PGS); + } else if (mtd->writesize == 2048) { + priv->page_size = 1; + setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); + /* adjust ecc setup if needed */ + if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) == + BR_DECC_CHK_GEN) { + chip->ecc.size = 512; + chip->ecc.layout = (priv->fmr & FMR_ECCM) ? + &fsl_elbc_oob_lp_eccm1 : + &fsl_elbc_oob_lp_eccm0; + mtd->ecclayout = chip->ecc.layout; + mtd->oobavail = chip->ecc.layout->oobavail; + } + } else { + dev_err(ctrl->dev, + "fsl_elbc_init: page size %d is not supported\n", + mtd->writesize); + return -1; + } + + /* The default u-boot configuration on MPC8313ERDB causes errors; + * more delay is needed. This should be safe for other boards + * as well. + */ + setbits32(&lbc->bank[priv->bank].or, 0x70); + return 0; +} + +static int fsl_elbc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf) +{ + fsl_elbc_read_buf(mtd, buf, mtd->writesize); + fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); + + if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL) + mtd->ecc_stats.failed++; + + return 0; +} + +/* ECC will be calculated automatically, and errors will be detected in + * waitfunc. + */ +static void fsl_elbc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf) +{ + struct fsl_elbc_mtd *priv = chip->priv; + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + + fsl_elbc_write_buf(mtd, buf, mtd->writesize); + fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); + + ctrl->oob_poi = chip->oob_poi; +} + +static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) +{ + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + struct elbc_regs __iomem *lbc = ctrl->regs; + struct nand_chip *chip = &priv->chip; + + dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank); + + /* Fill in fsl_elbc_mtd structure */ + priv->mtd.priv = chip; + priv->mtd.owner = THIS_MODULE; + priv->fmr = 0; /* rest filled in later */ + + /* fill in nand_chip structure */ + /* set up function call table */ + chip->read_byte = fsl_elbc_read_byte; + chip->write_buf = fsl_elbc_write_buf; + chip->read_buf = fsl_elbc_read_buf; + chip->verify_buf = fsl_elbc_verify_buf; + chip->select_chip = fsl_elbc_select_chip; + chip->cmdfunc = fsl_elbc_cmdfunc; + chip->waitfunc = fsl_elbc_wait; + + /* set up nand options */ + chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR; + + chip->controller = &ctrl->controller; + chip->priv = priv; + + chip->ecc.read_page = fsl_elbc_read_page; + chip->ecc.write_page = fsl_elbc_write_page; + + /* If CS Base Register selects full hardware ECC then use it */ + if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) == + BR_DECC_CHK_GEN) { + chip->ecc.mode = NAND_ECC_HW; + /* put in small page settings and adjust later if needed */ + chip->ecc.layout = (priv->fmr & FMR_ECCM) ? + &fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0; + chip->ecc.size = 512; + chip->ecc.bytes = 3; + } else { + /* otherwise fall back to default software ECC */ + chip->ecc.mode = NAND_ECC_SOFT; + } + + return 0; +} + +static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) +{ + struct fsl_elbc_ctrl *ctrl = priv->ctrl; + + nand_release(&priv->mtd); + + if (priv->vbase) + iounmap(priv->vbase); + + ctrl->chips[priv->bank] = NULL; + kfree(priv); + + return 0; +} + +static int fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, + struct device_node *node) +{ + struct elbc_regs __iomem *lbc = ctrl->regs; + struct fsl_elbc_mtd *priv; + struct resource res; +#ifdef CONFIG_MTD_PARTITIONS + static const char *part_probe_types[] + = { "cmdlinepart", "RedBoot", NULL }; + struct mtd_partition *parts; +#endif + int ret; + int bank; + + /* get, allocate and map the memory resource */ + ret = of_address_to_resource(node, 0, &res); + if (ret) { + dev_err(ctrl->dev, "failed to get resource\n"); + return ret; + } + + /* find which chip select it is connected to */ + for (bank = 0; bank < MAX_BANKS; bank++) + if ((in_be32(&lbc->bank[bank].br) & BR_V) && + (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM && + (in_be32(&lbc->bank[bank].br) & + in_be32(&lbc->bank[bank].or) & BR_BA) + == res.start) + break; + + if (bank >= MAX_BANKS) { + dev_err(ctrl->dev, "address did not match any chip selects\n"); + return -ENODEV; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ctrl->chips[bank] = priv; + priv->bank = bank; + priv->ctrl = ctrl; + priv->dev = ctrl->dev; + + priv->vbase = ioremap(res.start, res.end - res.start + 1); + if (!priv->vbase) { + dev_err(ctrl->dev, "failed to map chip region\n"); + ret = -ENOMEM; + goto err; + } + + ret = fsl_elbc_chip_init(priv); + if (ret) + goto err; + + ret = nand_scan_ident(&priv->mtd, 1); + if (ret) + goto err; + + ret = fsl_elbc_chip_init_tail(&priv->mtd); + if (ret) + goto err; + + ret = nand_scan_tail(&priv->mtd); + if (ret) + goto err; + +#ifdef CONFIG_MTD_PARTITIONS + /* First look for RedBoot table or partitions on the command + * line, these take precedence over device tree information */ + ret = parse_mtd_partitions(&priv->mtd, part_probe_types, &parts, 0); + if (ret < 0) + goto err; + +#ifdef CONFIG_MTD_OF_PARTS + if (ret == 0) { + ret = of_mtd_parse_partitions(priv->dev, &priv->mtd, + node, &parts); + if (ret < 0) + goto err; + } +#endif + + if (ret > 0) + add_mtd_partitions(&priv->mtd, parts, ret); + else +#endif + add_mtd_device(&priv->mtd); + + printk(KERN_INFO "eLBC NAND device at 0x%zx, bank %d\n", + res.start, priv->bank); + return 0; + +err: + fsl_elbc_chip_remove(priv); + return ret; +} + +static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl) +{ + struct elbc_regs __iomem *lbc = ctrl->regs; + + /* clear event registers */ + setbits32(&lbc->ltesr, LTESR_NAND_MASK); + out_be32(&lbc->lteatr, 0); + + /* Enable interrupts for any detected events */ + out_be32(&lbc->lteir, LTESR_NAND_MASK); + + ctrl->read_bytes = 0; + ctrl->index = 0; + ctrl->addr = NULL; + + return 0; +} + +static int __devexit fsl_elbc_ctrl_remove(struct of_device *ofdev) +{ + struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev); + int i; + + for (i = 0; i < MAX_BANKS; i++) + if (ctrl->chips[i]) + fsl_elbc_chip_remove(ctrl->chips[i]); + + if (ctrl->irq) + free_irq(ctrl->irq, ctrl); + + if (ctrl->regs) + iounmap(ctrl->regs); + + dev_set_drvdata(&ofdev->dev, NULL); + kfree(ctrl); + return 0; +} + +/* NOTE: This interrupt is also used to report other localbus events, + * such as transaction errors on other chipselects. If we want to + * capture those, we'll need to move the IRQ code into a shared + * LBC driver. + */ + +static irqreturn_t fsl_elbc_ctrl_irq(int irqno, void *data) +{ + struct fsl_elbc_ctrl *ctrl = data; + struct elbc_regs __iomem *lbc = ctrl->regs; + __be32 status = in_be32(&lbc->ltesr) & LTESR_NAND_MASK; + + if (status) { + out_be32(&lbc->ltesr, status); + out_be32(&lbc->lteatr, 0); + + ctrl->irq_status = status; + smp_wmb(); + wake_up(&ctrl->irq_wait); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/* fsl_elbc_ctrl_probe + * + * called by device layer when it finds a device matching + * one our driver can handled. This code allocates all of + * the resources needed for the controller only. The + * resources for the NAND banks themselves are allocated + * in the chip probe function. +*/ + +static int __devinit fsl_elbc_ctrl_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct device_node *child; + struct fsl_elbc_ctrl *ctrl; + int ret; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + dev_set_drvdata(&ofdev->dev, ctrl); + + spin_lock_init(&ctrl->controller.lock); + init_waitqueue_head(&ctrl->controller.wq); + init_waitqueue_head(&ctrl->irq_wait); + + ctrl->regs = of_iomap(ofdev->node, 0); + if (!ctrl->regs) { + dev_err(&ofdev->dev, "failed to get memory region\n"); + ret = -ENODEV; + goto err; + } + + ctrl->irq = of_irq_to_resource(ofdev->node, 0, NULL); + if (ctrl->irq == NO_IRQ) { + dev_err(&ofdev->dev, "failed to get irq resource\n"); + ret = -ENODEV; + goto err; + } + + ctrl->dev = &ofdev->dev; + + ret = fsl_elbc_ctrl_init(ctrl); + if (ret < 0) + goto err; + + ret = request_irq(ctrl->irq, fsl_elbc_ctrl_irq, 0, "fsl-elbc", ctrl); + if (ret != 0) { + dev_err(&ofdev->dev, "failed to install irq (%d)\n", + ctrl->irq); + ret = ctrl->irq; + goto err; + } + + for_each_child_of_node(ofdev->node, child) + if (of_device_is_compatible(child, "fsl,elbc-fcm-nand")) + fsl_elbc_chip_probe(ctrl, child); + + return 0; + +err: + fsl_elbc_ctrl_remove(ofdev); + return ret; +} + +static const struct of_device_id fsl_elbc_match[] = { + { + .compatible = "fsl,elbc", + }, + {} +}; + +static struct of_platform_driver fsl_elbc_ctrl_driver = { + .driver = { + .name = "fsl-elbc", + }, + .match_table = fsl_elbc_match, + .probe = fsl_elbc_ctrl_probe, + .remove = __devexit_p(fsl_elbc_ctrl_remove), +}; + +static int __init fsl_elbc_init(void) +{ + return of_register_platform_driver(&fsl_elbc_ctrl_driver); +} + +static void __exit fsl_elbc_exit(void) +{ + of_unregister_platform_driver(&fsl_elbc_ctrl_driver); +} + +module_init(fsl_elbc_init); +module_exit(fsl_elbc_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale"); +MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller MTD NAND driver"); -- cgit v1.2.3 From 388bbb09b991c792310af2f6b49f6c55edb3dff0 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Wed, 6 Feb 2008 10:17:15 +0000 Subject: [MTD] Add mtd panic_write function pointer MTDs are well suited for logging critical data and the mtdoops driver allows kernel panics/oops to be written to flash in a blackbox flight recorder fashion allowing better debugging and analysis of crashes. Any kernel oops in user context can be easily handled since the kernel continues as normal and any queued mtd writes are scheduled. Any kernel oops in interrupt context results in a panic and the delayed writes will not be scheduled however. The existing mtd->write function cannot be called in interrupt context so these messages can never be written to flash. This patch adds a panic_write function pointer that drivers can optionally implement which can be called in interrupt context. It is only intended to be called when its known the kernel is about to panic and we need to write to succeed. Since the kernel is not going to be running for much longer, this function can break locks and delay to ensure the write succeeds (but not sleep). Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse --- drivers/mtd/mtdpart.c | 17 +++++++++++++++++ include/linux/mtd/mtd.h | 9 +++++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 6174a97d790..c66902df317 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -151,6 +151,20 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len, len, retlen, buf); } +static int part_panic_write (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (to >= mtd->size) + len = 0; + else if (to + len > mtd->size) + len = mtd->size - to; + return part->master->panic_write (part->master, to + part->offset, + len, retlen, buf); +} + static int part_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { @@ -352,6 +366,9 @@ int add_mtd_partitions(struct mtd_info *master, slave->mtd.read = part_read; slave->mtd.write = part_write; + if (master->panic_write) + slave->mtd.panic_write = part_panic_write; + if(master->point && master->unpoint){ slave->mtd.point = part_point; slave->mtd.unpoint = part_unpoint; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 783fc983417..0a13bb35f04 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -152,6 +152,15 @@ struct mtd_info { int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); + /* In blackbox flight recorder like scenarios we want to make successful + writes in interrupt context. panic_write() is only intended to be + called when its known the kernel is about to panic and we need the + write to succeed. Since the kernel is not going to be running for much + longer, this function can break locks and delay to ensure the write + succeeds (but not sleep). */ + + int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); + int (*read_oob) (struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops); int (*write_oob) (struct mtd_info *mtd, loff_t to, -- cgit v1.2.3 From 621e4f8e9b208245d1f64eac7e6782b7aa506c21 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Wed, 6 Feb 2008 10:17:50 +0000 Subject: [MTD] mtdoops: Use the panic_write function when present When the MTD provides a panic_write function, use it. Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 34681bc9110..fd98e38f10b 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -183,10 +184,8 @@ badblock: goto badblock; } -static void mtdoops_workfunc_write(struct work_struct *work) +static void mtdoops_write(struct mtdoops_context *cxt, int panic) { - struct mtdoops_context *cxt = - container_of(work, struct mtdoops_context, work_write); struct mtd_info *mtd = cxt->mtd; size_t retlen; int ret; @@ -195,7 +194,11 @@ static void mtdoops_workfunc_write(struct work_struct *work) memset(cxt->oops_buf + cxt->writecount, 0xff, OOPS_PAGE_SIZE - cxt->writecount); - ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, + if (panic) + ret = mtd->panic_write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, + OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); + else + ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); cxt->writecount = 0; @@ -205,6 +208,15 @@ static void mtdoops_workfunc_write(struct work_struct *work) cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); mtdoops_inc_counter(cxt); +} + + +static void mtdoops_workfunc_write(struct work_struct *work) +{ + struct mtdoops_context *cxt = + container_of(work, struct mtdoops_context, work_write); + + mtdoops_write(cxt, 0); } static void find_next_position(struct mtdoops_context *cxt) @@ -314,7 +326,11 @@ static void mtdoops_console_sync(void) cxt->ready = 0; spin_unlock_irqrestore(&cxt->writecount_lock, flags); - schedule_work(&cxt->work_write); + if (mtd->panic_write && in_interrupt()) + /* Interrupt context, we're going to panic so try and log */ + mtdoops_write(cxt, 1); + else + schedule_work(&cxt->work_write); } static void -- cgit v1.2.3 From 6c77fd649fab4bea1b44cb534381a22e37650bc3 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Wed, 6 Feb 2008 10:18:22 +0000 Subject: [MTD] onenand: Add panic_write function to the onenand driver Implement the panic_write function for the onenand driver. This waits for any active command to complete/timeout, performs the write, waits for it to complete and then returns. Signed-off-by: Richard Purdie Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 108 +++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index b281b116aae..8d7d21be154 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -1284,6 +1285,112 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, #define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0) +static void onenand_panic_wait(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + unsigned int interrupt; + int i; + + for (i = 0; i < 2000; i++) { + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + if (interrupt & ONENAND_INT_MASTER) + break; + udelay(10); + } +} + +/** + * onenand_panic_write - [MTD Interface] write buffer to FLASH in a panic context + * @param mtd MTD device structure + * @param to offset to write to + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of written bytes + * @param buf the data to write + * + * Write with ECC + */ +static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct onenand_chip *this = mtd->priv; + int column, subpage; + int written = 0; + int ret = 0; + + if (this->state == FL_PM_SUSPENDED) + return -EBUSY; + + /* Wait for any existing operation to clear */ + onenand_panic_wait(mtd); + + DEBUG(MTD_DEBUG_LEVEL3, "onenand_panic_write: to = 0x%08x, len = %i\n", + (unsigned int) to, (int) len); + + /* Initialize retlen, in case of early exit */ + *retlen = 0; + + /* Do not allow writes past end of device */ + if (unlikely((to + len) > mtd->size)) { + printk(KERN_ERR "onenand_panic_write: Attempt write to past end of device\n"); + return -EINVAL; + } + + /* Reject writes, which are not page aligned */ + if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { + printk(KERN_ERR "onenand_panic_write: Attempt to write not page aligned data\n"); + return -EINVAL; + } + + column = to & (mtd->writesize - 1); + + /* Loop until all data write */ + while (written < len) { + int thislen = min_t(int, mtd->writesize - column, len - written); + u_char *wbuf = (u_char *) buf; + + this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); + + /* Partial page write */ + subpage = thislen < mtd->writesize; + if (subpage) { + memset(this->page_buf, 0xff, mtd->writesize); + memcpy(this->page_buf + column, buf, thislen); + wbuf = this->page_buf; + } + + this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); + this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); + + this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); + + onenand_panic_wait(mtd); + + /* In partial page write we don't update bufferram */ + onenand_update_bufferram(mtd, to, !ret && !subpage); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); + } + + if (ret) { + printk(KERN_ERR "onenand_panic_write: write failed %d\n", ret); + break; + } + + written += thislen; + + if (written == len) + break; + + column = 0; + to += thislen; + buf += thislen; + } + + *retlen = written; + return ret; +} + /** * onenand_fill_auto_oob - [Internal] oob auto-placement transfer * @param mtd MTD device structure @@ -2673,6 +2780,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) mtd->write = onenand_write; mtd->read_oob = onenand_read_oob; mtd->write_oob = onenand_write_oob; + mtd->panic_write = onenand_panic_write; #ifdef CONFIG_MTD_ONENAND_OTP mtd->get_fact_prot_info = onenand_get_fact_prot_info; mtd->read_fact_prot_reg = onenand_read_fact_prot_reg; -- cgit v1.2.3 From d5476689afd48e71395602698409e9f48cbba413 Mon Sep 17 00:00:00 2001 From: "akpm@linux-foundation.org" Date: Sun, 3 Feb 2008 12:56:03 -0800 Subject: [MTD] Fix maps/physmap.c compilation with CONFIG_PM Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/maps/physmap.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 5a83ae7e14f..f00e04efbe2 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -204,7 +204,7 @@ static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state if (info) for (i = 0; i < MAX_RESOURCES; i++) - ret |= info->mtd[i].suspend(info->mtd[i]); + ret |= info->mtd[i]->suspend(info->mtd[i]); return ret; } @@ -216,7 +216,7 @@ static int physmap_flash_resume(struct platform_device *dev) if (info) for (i = 0; i < MAX_RESOURCES; i++) - info->mtd[i].resume(info->mtd[i]); + info->mtd[i]->resume(info->mtd[i]); return 0; } @@ -226,19 +226,21 @@ static void physmap_flash_shutdown(struct platform_device *dev) int i; for (i = 0; i < MAX_RESOURCES; i++) - if (info && info->mtd[i].suspend(info->mtd[i]) == 0) - info->mtd[i].resume(info->mtd[i]); + if (info && info->mtd[i]->suspend(info->mtd[i]) == 0) + info->mtd[i]->resume(info->mtd[i]); } +#else +#define physmap_flash_suspend NULL +#define physmap_flash_resume NULL +#define physmap_flash_shutdown NULL #endif static struct platform_driver physmap_flash_driver = { .probe = physmap_flash_probe, .remove = physmap_flash_remove, -#ifdef CONFIG_PM .suspend = physmap_flash_suspend, .resume = physmap_flash_resume, .shutdown = physmap_flash_shutdown, -#endif .driver = { .name = "physmap-flash", }, -- cgit v1.2.3 From 53fb84a0695ffeeeadf8ae92db28cbccf5325531 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 4 Feb 2008 23:44:47 -0800 Subject: [MTD] [DOC200x] eccbuf is statically defined and always evaluate to true Signed-off-by: Samuel Tardieu Acked-by: Joern Engel Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/devices/doc2000.c | 4 ++-- drivers/mtd/devices/doc2001plus.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index c73e96bfafc..e9ce241b7fe 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -632,7 +632,7 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, len = ((from | 0x1ff) + 1) - from; /* The ECC will not be calculated correctly if less than 512 is read */ - if (len != 0x200 && eccbuf) + if (len != 0x200) printk(KERN_WARNING "ECC needs a full sector read (adr: %lx size %lx)\n", (long) from, (long) len); @@ -896,7 +896,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, /* Let the caller know we completed it */ *retlen += len; - if (eccbuf) { + { unsigned char x[8]; size_t dummy; int ret; diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 2b30b587c6e..83be3461658 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -748,7 +748,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd); /* On interleaved devices the flags for 2nd half 512 are before data */ - if (eccbuf && before) + if (before) fto -= 2; /* issue the Serial Data In command to initial the Page Program process */ -- cgit v1.2.3 From 484b8e64c848185af0d3671fafba4dd66ca412d2 Mon Sep 17 00:00:00 2001 From: Rizzo Davide Date: Mon, 4 Feb 2008 23:44:48 -0800 Subject: [MTD] [NOR] fix startup lock when using multiple nor flash chips Taken from http://bugzilla.kernel.org/show_bug.cgi?id=9829 I found and solved the problem, at line 115 of drivers/mtd/chips/gen_probe.c (kernel 2.6.24): mapsize value must be calculated in bytes, not in long. Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/chips/gen_probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index 2eb696d7b97..d338b8c9278 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -112,7 +112,7 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi max_chips = 1; } - mapsize = (max_chips + BITS_PER_LONG-1) / BITS_PER_LONG; + mapsize = sizeof(long) * ( (max_chips + BITS_PER_LONG-1) / BITS_PER_LONG ); chip_map = kzalloc(mapsize, GFP_KERNEL); if (!chip_map) { printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name); -- cgit v1.2.3 From f9f7dd222364a6428d2ad99a515935dd1dd89d18 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 7 Feb 2008 10:50:57 +0000 Subject: [MTD] Fix mtdoops.c compilation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/mtd/mtdoops.c: In function ‘mtdoops_console_sync’: drivers/mtd/mtdoops.c:329: error: implicit declaration of function ‘in_interrupt’ Signed-off-by: David Woodhouse --- drivers/mtd/mtdoops.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index fd98e38f10b..d3cf05012b4 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #define OOPS_PAGE_SIZE 4096 -- cgit v1.2.3