Merge git://git.infradead.org/mtd-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 16 Dec 2009 18:23:43 +0000 (10:23 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 16 Dec 2009 18:23:43 +0000 (10:23 -0800)
* git://git.infradead.org/mtd-2.6: (90 commits)
  jffs2: Fix long-standing bug with symlink garbage collection.
  mtd: OneNAND: Fix test of unsigned in onenand_otp_walk()
  mtd: cfi_cmdset_0002, fix lock imbalance
  Revert "mtd: move mxcnd_remove to .exit.text"
  mtd: m25p80: add support for Macronix MX25L4005A
  kmsg_dump: fix build for CONFIG_PRINTK=n
  mtd: nandsim: add support for 4KiB pages
  mtd: mtdoops: refactor as a kmsg_dumper
  mtd: mtdoops: make record size configurable
  mtd: mtdoops: limit the maximum mtd partition size
  mtd: mtdoops: keep track of used/unused pages in an array
  mtd: mtdoops: several minor cleanups
  core: Add kernel message dumper to call on oopses and panics
  mtd: add ARM pismo support
  mtd: pxa3xx_nand: Fix PIO data transfer
  mtd: nand: fix multi-chip suspend problem
  mtd: add support for switching old SST chips into QRY mode
  mtd: fix M29W800D dev_id and uaddr
  mtd: don't use PF_MEMALLOC
  mtd: Add bad block table overrides to Davinci NAND driver
  ...

Fixed up conflicts (mostly trivial) in
drivers/mtd/devices/m25p80.c
drivers/mtd/maps/pcmciamtd.c
drivers/mtd/nand/pxa3xx_nand.c
kernel/printk.c

14 files changed:
1  2 
arch/arm/mach-bcmring/arch.c
arch/arm/mach-nomadik/board-nhk8815.c
arch/arm/plat-s3c/include/plat/nand.h
drivers/mtd/devices/m25p80.c
drivers/mtd/maps/Kconfig
drivers/mtd/maps/Makefile
drivers/mtd/mtd_blkdevs.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/nand_ecc.c
drivers/mtd/nand/s3c2410.c
drivers/mtd/onenand/omap2.c
fs/jffs2/readinode.c
kernel/panic.c
kernel/printk.c

@@@ -47,6 -47,10 +47,6 @@@ HW_DECLARE_SPINLOCK(gpio
      EXPORT_SYMBOL(bcmring_gpio_reg_lock);
  #endif
  
 -/* FIXME: temporary solution */
 -#define BCM_SYSCTL_REBOOT_WARM               1
 -#define CTL_BCM_REBOOT                 112
 -
  /* sysctl */
  int bcmring_arch_warm_reboot; /* do a warm reboot on hard reset */
  
@@@ -54,25 -58,37 +54,35 @@@ static struct ctl_table_header *bcmring
  
  static struct ctl_table bcmring_sysctl_warm_reboot[] = {
        {
 -       .ctl_name = BCM_SYSCTL_REBOOT_WARM,
         .procname = "warm",
         .data = &bcmring_arch_warm_reboot,
         .maxlen = sizeof(int),
         .mode = 0644,
 -       .proc_handler = &proc_dointvec},
 +       .proc_handler = proc_dointvec},
        {}
  };
  
  static struct ctl_table bcmring_sysctl_reboot[] = {
        {
 -       .ctl_name = CTL_BCM_REBOOT,
         .procname = "reboot",
         .mode = 0555,
         .child = bcmring_sysctl_warm_reboot},
        {}
  };
  
+ static struct resource nand_resource[] = {
+       [0] = {
+               .start = MM_ADDR_IO_NAND,
+               .end = MM_ADDR_IO_NAND + 0x1000 - 1,
+               .flags = IORESOURCE_MEM,
+       },
+ };
  static struct platform_device nand_device = {
        .name = "bcm-nand",
        .id = -1,
+       .resource = nand_resource,
+       .num_resources  = ARRAY_SIZE(nand_resource),
  };
  
  static struct platform_device *devices[] __initdata = {
@@@ -18,6 -18,7 +18,7 @@@
  #include <linux/gpio.h>
  #include <linux/mtd/mtd.h>
  #include <linux/mtd/nand.h>
+ #include <linux/mtd/onenand.h>
  #include <linux/mtd/partitions.h>
  #include <linux/io.h>
  #include <asm/sizes.h>
  #include <asm/mach/arch.h>
  #include <asm/mach/irq.h>
  #include <asm/mach/flash.h>
 +
 +#include <plat/mtu.h>
 +
  #include <mach/setup.h>
  #include <mach/nand.h>
  #include <mach/fsmc.h>
  #include "clock.h"
  
 +/* Initial value for SRC control register: all timers use MXTAL/8 source */
 +#define SRC_CR_INIT_MASK      0x00007fff
 +#define SRC_CR_INIT_VAL               0x2aaa8000
 +
  /* These adresses span 16MB, so use three individual pages */
  static struct resource nhk8815_nand_resources[] = {
        {
@@@ -149,7 -143,7 +150,7 @@@ static struct mtd_partition nhk8815_one
        }
  };
  
- static struct flash_platform_data nhk8815_onenand_data = {
+ static struct onenand_platform_data nhk8815_onenand_data = {
        .parts          = nhk8815_onenand_partitions,
        .nr_parts       = ARRAY_SIZE(nhk8815_onenand_partitions),
  };
@@@ -163,7 -157,7 +164,7 @@@ static struct resource nhk8815_onenand_
  };
  
  static struct platform_device nhk8815_onenand_device = {
-       .name           = "onenand",
+       .name           = "onenand-flash",
        .id             = -1,
        .dev            = {
                .platform_data  = &nhk8815_onenand_data,
  
  static void __init nhk8815_onenand_init(void)
  {
- #ifdef CONFIG_ONENAND
+ #ifdef CONFIG_MTD_ONENAND
         /* Set up SMCS0 for OneNand */
-        writel(0x000030db, FSMC_BCR0);
-        writel(0x02100551, FSMC_BTR0);
+       writel(0x000030db, FSMC_BCR(0));
+       writel(0x02100551, FSMC_BTR(0));
  #endif
  }
  
@@@ -246,26 -240,6 +247,26 @@@ static struct platform_device *nhk8815_
        /* will add more devices */
  };
  
 +static void __init nomadik_timer_init(void)
 +{
 +      u32 src_cr;
 +
 +      /* Configure timer sources in "system reset controller" ctrl reg */
 +      src_cr = readl(io_p2v(NOMADIK_SRC_BASE));
 +      src_cr &= SRC_CR_INIT_MASK;
 +      src_cr |= SRC_CR_INIT_VAL;
 +      writel(src_cr, io_p2v(NOMADIK_SRC_BASE));
 +
 +      /* Save global pointer to mtu, used by platform timer code */
 +      mtu_base = io_p2v(NOMADIK_MTU0_BASE);
 +
 +      nmdk_timer_init();
 +}
 +
 +static struct sys_timer nomadik_timer = {
 +      .init   = nomadik_timer_init,
 +};
 +
  static void __init nhk8815_platform_init(void)
  {
        int i;
@@@ -17,6 -17,7 +17,7 @@@
   *                    Setting this flag will allow the kernel to
   *                    look for it at boot time and also skip the NAND
   *                    scan.
+  * @options:          Default value to set into 'struct nand_chip' options.
   * @nr_chips:         Number of chips in this set
   * @nr_partitions:    Number of partitions pointed to by @partitions
   * @name:             Name of set (optional)
@@@ -31,6 -32,7 +32,7 @@@ struct s3c2410_nand_set 
        unsigned int            disable_ecc:1;
        unsigned int            flash_bbt:1;
  
+       unsigned int            options;
        int                     nr_chips;
        int                     nr_partitions;
        char                    *name;
@@@ -55,11 -57,3 +57,11 @@@ struct s3c2410_platform_nand 
                                               int chip);
  };
  
 +/**
 + * s3c_nand_set_platdata() - register NAND platform data.
 + * @nand: The NAND platform data to register with s3c_device_nand.
 + *
 + * This function copies the given NAND platform data, @nand and registers
 + * it with the s3c_device_nand. This allows @nand to be __initdata.
 +*/
 +extern void s3c_nand_set_platdata(struct s3c2410_platform_nand *nand);
@@@ -21,7 -21,7 +21,8 @@@
  #include <linux/interrupt.h>
  #include <linux/mutex.h>
  #include <linux/math64.h>
 +#include <linux/sched.h>
+ #include <linux/mod_devicetable.h>
  
  #include <linux/mtd/mtd.h>
  #include <linux/mtd/partitions.h>
@@@ -29,9 -29,6 +30,6 @@@
  #include <linux/spi/spi.h>
  #include <linux/spi/flash.h>
  
- #define FLASH_PAGESIZE                256
  /* Flash opcodes. */
  #define       OPCODE_WREN             0x06    /* Write enable */
  #define       OPCODE_RDSR             0x05    /* Read status register */
@@@ -61,7 -58,7 +59,7 @@@
  
  /* Define max times to check status register before we give up. */
  #define       MAX_READY_WAIT_JIFFIES  (40 * HZ)       /* M25P16 specs 40s max chip erase */
- #define       CMD_SIZE                4
+ #define       MAX_CMD_SIZE            4
  
  #ifdef CONFIG_M25PXX_USE_FAST_READ
  #define OPCODE_READ   OPCODE_FAST_READ
@@@ -78,8 -75,10 +76,10 @@@ struct m25p 
        struct mutex            lock;
        struct mtd_info         mtd;
        unsigned                partitioned:1;
+       u16                     page_size;
+       u16                     addr_width;
        u8                      erase_opcode;
-       u8                      command[CMD_SIZE + FAST_READ_DUMMY_BYTE];
+       u8                      *command;
  };
  
  static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
@@@ -198,6 -197,19 +198,19 @@@ static int erase_chip(struct m25p *flas
        return 0;
  }
  
+ static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd)
+ {
+       /* opcode is in cmd[0] */
+       cmd[1] = addr >> (flash->addr_width * 8 -  8);
+       cmd[2] = addr >> (flash->addr_width * 8 - 16);
+       cmd[3] = addr >> (flash->addr_width * 8 - 24);
+ }
+ static int m25p_cmdsz(struct m25p *flash)
+ {
+       return 1 + flash->addr_width;
+ }
  /*
   * Erase one sector of flash memory at offset ``offset'' which is any
   * address within the sector which should be erased.
@@@ -219,11 -231,9 +232,9 @@@ static int erase_sector(struct m25p *fl
  
        /* Set up command buffer. */
        flash->command[0] = flash->erase_opcode;
-       flash->command[1] = offset >> 16;
-       flash->command[2] = offset >> 8;
-       flash->command[3] = offset;
+       m25p_addr2cmd(flash, offset, flash->command);
  
-       spi_write(flash->spi, flash->command, CMD_SIZE);
+       spi_write(flash->spi, flash->command, m25p_cmdsz(flash));
  
        return 0;
  }
@@@ -325,7 -335,7 +336,7 @@@ static int m25p80_read(struct mtd_info 
         * Should add 1 byte DUMMY_BYTE.
         */
        t[0].tx_buf = flash->command;
-       t[0].len = CMD_SIZE + FAST_READ_DUMMY_BYTE;
+       t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
        spi_message_add_tail(&t[0], &m);
  
        t[1].rx_buf = buf;
  
        /* Set up the write data buffer. */
        flash->command[0] = OPCODE_READ;
-       flash->command[1] = from >> 16;
-       flash->command[2] = from >> 8;
-       flash->command[3] = from;
+       m25p_addr2cmd(flash, from, flash->command);
  
        spi_sync(flash->spi, &m);
  
-       *retlen = m.actual_length - CMD_SIZE - FAST_READ_DUMMY_BYTE;
+       *retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE;
  
        mutex_unlock(&flash->lock);
  
@@@ -396,7 -404,7 +405,7 @@@ static int m25p80_write(struct mtd_inf
        memset(t, 0, (sizeof t));
  
        t[0].tx_buf = flash->command;
-       t[0].len = CMD_SIZE;
+       t[0].len = m25p_cmdsz(flash);
        spi_message_add_tail(&t[0], &m);
  
        t[1].tx_buf = buf;
  
        /* Set up the opcode in the write buffer. */
        flash->command[0] = OPCODE_PP;
-       flash->command[1] = to >> 16;
-       flash->command[2] = to >> 8;
-       flash->command[3] = to;
+       m25p_addr2cmd(flash, to, flash->command);
  
-       /* what page do we start with? */
-       page_offset = to % FLASH_PAGESIZE;
+       page_offset = to & (flash->page_size - 1);
  
        /* do all the bytes fit onto one page? */
-       if (page_offset + len <= FLASH_PAGESIZE) {
+       if (page_offset + len <= flash->page_size) {
                t[1].len = len;
  
                spi_sync(flash->spi, &m);
  
-               *retlen = m.actual_length - CMD_SIZE;
+               *retlen = m.actual_length - m25p_cmdsz(flash);
        } else {
                u32 i;
  
                /* the size of data remaining on the first page */
-               page_size = FLASH_PAGESIZE - page_offset;
+               page_size = flash->page_size - page_offset;
  
                t[1].len = page_size;
                spi_sync(flash->spi, &m);
  
-               *retlen = m.actual_length - CMD_SIZE;
+               *retlen = m.actual_length - m25p_cmdsz(flash);
  
-               /* write everything in PAGESIZE chunks */
+               /* write everything in flash->page_size chunks */
                for (i = page_size; i < len; i += page_size) {
                        page_size = len - i;
-                       if (page_size > FLASH_PAGESIZE)
-                               page_size = FLASH_PAGESIZE;
+                       if (page_size > flash->page_size)
+                               page_size = flash->page_size;
  
                        /* write the next page to flash */
-                       flash->command[1] = (to + i) >> 16;
-                       flash->command[2] = (to + i) >> 8;
-                       flash->command[3] = (to + i);
+                       m25p_addr2cmd(flash, to + i, flash->command);
  
                        t[1].tx_buf = buf + i;
                        t[1].len = page_size;
                        spi_sync(flash->spi, &m);
  
                        if (retlen)
-                               *retlen += m.actual_length - CMD_SIZE;
+                               *retlen += m.actual_length - m25p_cmdsz(flash);
                }
        }
  
@@@ -492,7 -495,7 +496,7 @@@ static int sst_write(struct mtd_info *m
        memset(t, 0, (sizeof t));
  
        t[0].tx_buf = flash->command;
-       t[0].len = CMD_SIZE;
+       t[0].len = m25p_cmdsz(flash);
        spi_message_add_tail(&t[0], &m);
  
        t[1].tx_buf = buf;
        /* Start write from odd address. */
        if (actual) {
                flash->command[0] = OPCODE_BP;
-               flash->command[1] = to >> 16;
-               flash->command[2] = to >> 8;
-               flash->command[3] = to;
+               m25p_addr2cmd(flash, to, flash->command);
  
                /* write one byte. */
                t[1].len = 1;
                ret = wait_till_ready(flash);
                if (ret)
                        goto time_out;
-               *retlen += m.actual_length - CMD_SIZE;
+               *retlen += m.actual_length - m25p_cmdsz(flash);
        }
        to += actual;
  
        flash->command[0] = OPCODE_AAI_WP;
-       flash->command[1] = to >> 16;
-       flash->command[2] = to >> 8;
-       flash->command[3] = to;
+       m25p_addr2cmd(flash, to, flash->command);
  
        /* Write out most of the data here. */
-       cmd_sz = CMD_SIZE;
+       cmd_sz = m25p_cmdsz(flash);
        for (; actual < len - 1; actual += 2) {
                t[0].len = cmd_sz;
                /* write two bytes. */
        if (actual != len) {
                write_enable(flash);
                flash->command[0] = OPCODE_BP;
-               flash->command[1] = to >> 16;
-               flash->command[2] = to >> 8;
-               flash->command[3] = to;
-               t[0].len = CMD_SIZE;
+               m25p_addr2cmd(flash, to, flash->command);
+               t[0].len = m25p_cmdsz(flash);
                t[1].len = 1;
                t[1].tx_buf = buf + actual;
  
                ret = wait_till_ready(flash);
                if (ret)
                        goto time_out;
-               *retlen += m.actual_length - CMD_SIZE;
+               *retlen += m.actual_length - m25p_cmdsz(flash);
                write_disable(flash);
        }
  
@@@ -582,8 -579,6 +580,6 @@@ time_out
   */
  
  struct flash_info {
-       char            *name;
        /* JEDEC id zero means "no ID" (most older chips); otherwise it has
         * a high byte of zero plus three data bytes: the manufacturer id,
         * then a two byte device id.
        unsigned        sector_size;
        u16             n_sectors;
  
+       u16             page_size;
+       u16             addr_width;
        u16             flags;
  #define       SECT_4K         0x01            /* OPCODE_BE_4K works uniformly */
+ #define       M25P_NO_ERASE   0x02            /* No erase command needed */
  };
  
+ #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)    \
+       ((kernel_ulong_t)&(struct flash_info) {                         \
+               .jedec_id = (_jedec_id),                                \
+               .ext_id = (_ext_id),                                    \
+               .sector_size = (_sector_size),                          \
+               .n_sectors = (_n_sectors),                              \
+               .page_size = 256,                                       \
+               .addr_width = 3,                                        \
+               .flags = (_flags),                                      \
+       })
+ #define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width) \
+       ((kernel_ulong_t)&(struct flash_info) {                         \
+               .sector_size = (_sector_size),                          \
+               .n_sectors = (_n_sectors),                              \
+               .page_size = (_page_size),                              \
+               .addr_width = (_addr_width),                            \
+               .flags = M25P_NO_ERASE,                                 \
+       })
  
  /* NOTE: double check command sets and memory organization when you add
   * more flash chips.  This current list focusses on newer chips, which
   * have been converging on command sets which including JEDEC ID.
   */
- static struct flash_info __devinitdata m25p_data [] = {
+ static const struct spi_device_id m25p_ids[] = {
        /* Atmel -- some are (confusingly) marketed as "DataFlash" */
-       { "at25fs010",  0x1f6601, 0, 32 * 1024, 4, SECT_4K, },
-       { "at25fs040",  0x1f6604, 0, 64 * 1024, 8, SECT_4K, },
+       { "at25fs010",  INFO(0x1f6601, 0, 32 * 1024,   4, SECT_4K) },
+       { "at25fs040",  INFO(0x1f6604, 0, 64 * 1024,   8, SECT_4K) },
  
-       { "at25df041a", 0x1f4401, 0, 64 * 1024, 8, SECT_4K, },
-       { "at25df641",  0x1f4800, 0, 64 * 1024, 128, SECT_4K, },
+       { "at25df041a", INFO(0x1f4401, 0, 64 * 1024,   8, SECT_4K) },
+       { "at25df641",  INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
  
-       { "at26f004",   0x1f0400, 0, 64 * 1024, 8, SECT_4K, },
-       { "at26df081a", 0x1f4501, 0, 64 * 1024, 16, SECT_4K, },
-       { "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SECT_4K, },
-       { "at26df321",  0x1f4701, 0, 64 * 1024, 64, SECT_4K, },
+       { "at26f004",   INFO(0x1f0400, 0, 64 * 1024,  8, SECT_4K) },
+       { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },
+       { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
+       { "at26df321",  INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) },
  
        /* Macronix */
-       { "mx25l3205d", 0xc22016, 0, 64 * 1024, 64, },
-       { "mx25l6405d", 0xc22017, 0, 64 * 1024, 128, },
-       { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, },
-       { "mx25l12855e", 0xc22618, 0, 64 * 1024, 256, },
+       { "mx25l4005a",  INFO(0xc22013, 0, 64 * 1024,   8, SECT_4K) },
+       { "mx25l3205d",  INFO(0xc22016, 0, 64 * 1024,  64, 0) },
+       { "mx25l6405d",  INFO(0xc22017, 0, 64 * 1024, 128, 0) },
+       { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
+       { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
  
        /* Spansion -- single (large) sector size only, at least
         * for the chips listed here (without boot sectors).
         */
-       { "s25sl004a", 0x010212, 0, 64 * 1024, 8, },
-       { "s25sl008a", 0x010213, 0, 64 * 1024, 16, },
-       { "s25sl016a", 0x010214, 0, 64 * 1024, 32, },
-       { "s25sl032a", 0x010215, 0, 64 * 1024, 64, },
-       { "s25sl064a", 0x010216, 0, 64 * 1024, 128, },
-       { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, },
-       { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, },
-       { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, },
-       { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, },
+       { "s25sl004a",  INFO(0x010212,      0,  64 * 1024,   8, 0) },
+       { "s25sl008a",  INFO(0x010213,      0,  64 * 1024,  16, 0) },
+       { "s25sl016a",  INFO(0x010214,      0,  64 * 1024,  32, 0) },
+       { "s25sl032a",  INFO(0x010215,      0,  64 * 1024,  64, 0) },
+       { "s25sl064a",  INFO(0x010216,      0,  64 * 1024, 128, 0) },
+       { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
+       { "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) },
+       { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, 0) },
+       { "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) },
  
        /* SST -- large erase sizes are "overlays", "sectors" are 4K */
-       { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, },
-       { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, },
-       { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, },
-       { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, },
-       { "sst25wf512",  0xbf2501, 0, 64 * 1024, 1, SECT_4K, },
-       { "sst25wf010",  0xbf2502, 0, 64 * 1024, 2, SECT_4K, },
-       { "sst25wf020",  0xbf2503, 0, 64 * 1024, 4, SECT_4K, },
-       { "sst25wf040",  0xbf2504, 0, 64 * 1024, 8, SECT_4K, },
+       { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SECT_4K) },
+       { "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K) },
+       { "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K) },
+       { "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K) },
+       { "sst25wf512",  INFO(0xbf2501, 0, 64 * 1024,  1, SECT_4K) },
+       { "sst25wf010",  INFO(0xbf2502, 0, 64 * 1024,  2, SECT_4K) },
+       { "sst25wf020",  INFO(0xbf2503, 0, 64 * 1024,  4, SECT_4K) },
+       { "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K) },
  
        /* ST Microelectronics -- newer production may have feature updates */
-       { "m25p05",  0x202010,  0, 32 * 1024, 2, },
-       { "m25p10",  0x202011,  0, 32 * 1024, 4, },
-       { "m25p20",  0x202012,  0, 64 * 1024, 4, },
-       { "m25p40",  0x202013,  0, 64 * 1024, 8, },
-       { "m25p80",         0,  0, 64 * 1024, 16, },
-       { "m25p16",  0x202015,  0, 64 * 1024, 32, },
-       { "m25p32",  0x202016,  0, 64 * 1024, 64, },
-       { "m25p64",  0x202017,  0, 64 * 1024, 128, },
-       { "m25p128", 0x202018, 0, 256 * 1024, 64, },
-       { "m45pe10", 0x204011,  0, 64 * 1024, 2, },
-       { "m45pe80", 0x204014,  0, 64 * 1024, 16, },
-       { "m45pe16", 0x204015,  0, 64 * 1024, 32, },
-       { "m25pe80", 0x208014,  0, 64 * 1024, 16, },
-       { "m25pe16", 0x208015,  0, 64 * 1024, 32, SECT_4K, },
+       { "m25p05",  INFO(0x202010,  0,  32 * 1024,   2, 0) },
+       { "m25p10",  INFO(0x202011,  0,  32 * 1024,   4, 0) },
+       { "m25p20",  INFO(0x202012,  0,  64 * 1024,   4, 0) },
+       { "m25p40",  INFO(0x202013,  0,  64 * 1024,   8, 0) },
+       { "m25p80",  INFO(0x202014,  0,  64 * 1024,  16, 0) },
+       { "m25p16",  INFO(0x202015,  0,  64 * 1024,  32, 0) },
+       { "m25p32",  INFO(0x202016,  0,  64 * 1024,  64, 0) },
+       { "m25p64",  INFO(0x202017,  0,  64 * 1024, 128, 0) },
+       { "m25p128", INFO(0x202018,  0, 256 * 1024,  64, 0) },
+       { "m45pe10", INFO(0x204011,  0, 64 * 1024,    2, 0) },
+       { "m45pe80", INFO(0x204014,  0, 64 * 1024,   16, 0) },
+       { "m45pe16", INFO(0x204015,  0, 64 * 1024,   32, 0) },
+       { "m25pe80", INFO(0x208014,  0, 64 * 1024, 16,       0) },
+       { "m25pe16", INFO(0x208015,  0, 64 * 1024, 32, SECT_4K) },
  
        /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
-       { "w25x10", 0xef3011, 0, 64 * 1024, 2, SECT_4K, },
-       { "w25x20", 0xef3012, 0, 64 * 1024, 4, SECT_4K, },
-       { "w25x40", 0xef3013, 0, 64 * 1024, 8, SECT_4K, },
-       { "w25x80", 0xef3014, 0, 64 * 1024, 16, SECT_4K, },
-       { "w25x16", 0xef3015, 0, 64 * 1024, 32, SECT_4K, },
-       { "w25x32", 0xef3016, 0, 64 * 1024, 64, SECT_4K, },
-       { "w25x64", 0xef3017, 0, 64 * 1024, 128, SECT_4K, },
+       { "w25x10", INFO(0xef3011, 0, 64 * 1024,  2,  SECT_4K) },
+       { "w25x20", INFO(0xef3012, 0, 64 * 1024,  4,  SECT_4K) },
+       { "w25x40", INFO(0xef3013, 0, 64 * 1024,  8,  SECT_4K) },
+       { "w25x80", INFO(0xef3014, 0, 64 * 1024,  16, SECT_4K) },
+       { "w25x16", INFO(0xef3015, 0, 64 * 1024,  32, SECT_4K) },
+       { "w25x32", INFO(0xef3016, 0, 64 * 1024,  64, SECT_4K) },
+       { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
+       /* Catalyst / On Semiconductor -- non-JEDEC */
+       { "cat25c11", CAT25_INFO(  16, 8, 16, 1) },
+       { "cat25c03", CAT25_INFO(  32, 8, 16, 2) },
+       { "cat25c09", CAT25_INFO( 128, 8, 32, 2) },
+       { "cat25c17", CAT25_INFO( 256, 8, 32, 2) },
+       { "cat25128", CAT25_INFO(2048, 8, 64, 2) },
+       { },
  };
+ MODULE_DEVICE_TABLE(spi, m25p_ids);
  
- static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
+ static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi)
  {
        int                     tmp;
        u8                      code = OPCODE_RDID;
        jedec = jedec << 8;
        jedec |= id[2];
  
+       /*
+        * Some chips (like Numonyx M25P80) have JEDEC and non-JEDEC variants,
+        * which depend on technology process. Officially RDID command doesn't
+        * exist for non-JEDEC chips, but for compatibility they return ID 0.
+        */
+       if (jedec == 0)
+               return NULL;
        ext_jedec = id[3] << 8 | id[4];
  
-       for (tmp = 0, info = m25p_data;
-                       tmp < ARRAY_SIZE(m25p_data);
-                       tmp++, info++) {
+       for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) {
+               info = (void *)m25p_ids[tmp].driver_data;
                if (info->jedec_id == jedec) {
                        if (info->ext_id != 0 && info->ext_id != ext_jedec)
                                continue;
-                       return info;
+                       return &m25p_ids[tmp];
                }
        }
-       dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
        return NULL;
  }
  
   */
  static int __devinit m25p_probe(struct spi_device *spi)
  {
+       const struct spi_device_id      *id = spi_get_device_id(spi);
        struct flash_platform_data      *data;
        struct m25p                     *flash;
        struct flash_info               *info;
         */
        data = spi->dev.platform_data;
        if (data && data->type) {
-               for (i = 0, info = m25p_data;
-                               i < ARRAY_SIZE(m25p_data);
-                               i++, info++) {
-                       if (strcmp(data->type, info->name) == 0)
-                               break;
-               }
+               const struct spi_device_id *plat_id;
  
-               /* unrecognized chip? */
-               if (i == ARRAY_SIZE(m25p_data)) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n",
-                                       dev_name(&spi->dev), data->type);
-                       info = NULL;
-               /* recognized; is that chip really what's there? */
-               } else if (info->jedec_id) {
-                       struct flash_info       *chip = jedec_probe(spi);
-                       if (!chip || chip != info) {
-                               dev_warn(&spi->dev, "found %s, expected %s\n",
-                                               chip ? chip->name : "UNKNOWN",
-                                               info->name);
-                               info = NULL;
-                       }
+               for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) {
+                       plat_id = &m25p_ids[i];
+                       if (strcmp(data->type, plat_id->name))
+                               continue;
+                       break;
                }
-       } else
-               info = jedec_probe(spi);
  
-       if (!info)
-               return -ENODEV;
+               if (plat_id)
+                       id = plat_id;
+               else
+                       dev_warn(&spi->dev, "unrecognized id %s\n", data->type);
+       }
+       info = (void *)id->driver_data;
+       if (info->jedec_id) {
+               const struct spi_device_id *jid;
+               jid = jedec_probe(spi);
+               if (!jid) {
+                       dev_info(&spi->dev, "non-JEDEC variant of %s\n",
+                                id->name);
+               } else if (jid != id) {
+                       /*
+                        * JEDEC knows better, so overwrite platform ID. We
+                        * can't trust partitions any longer, but we'll let
+                        * mtd apply them anyway, since some partitions may be
+                        * marked read-only, and we don't want to lose that
+                        * information, even if it's not 100% accurate.
+                        */
+                       dev_warn(&spi->dev, "found %s, expected %s\n",
+                                jid->name, id->name);
+                       id = jid;
+                       info = (void *)jid->driver_data;
+               }
+       }
  
        flash = kzalloc(sizeof *flash, GFP_KERNEL);
        if (!flash)
                return -ENOMEM;
+       flash->command = kmalloc(MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE, GFP_KERNEL);
+       if (!flash->command) {
+               kfree(flash);
+               return -ENOMEM;
+       }
  
        flash->spi = spi;
        mutex_init(&flash->lock);
        dev_set_drvdata(&spi->dev, flash);
  
        /*
-        * Atmel serial flash tend to power up
-        * with the software protection bits set
+        * Atmel and SST serial flash tend to power
+        * up with the software protection bits set
         */
  
-       if (info->jedec_id >> 16 == 0x1f) {
+       if (info->jedec_id >> 16 == 0x1f ||
+           info->jedec_id >> 16 == 0xbf) {
                write_enable(flash);
                write_sr(flash, 0);
        }
                flash->mtd.erasesize = info->sector_size;
        }
  
+       if (info->flags & M25P_NO_ERASE)
+               flash->mtd.flags |= MTD_NO_ERASE;
        flash->mtd.dev.parent = &spi->dev;
+       flash->page_size = info->page_size;
+       flash->addr_width = info->addr_width;
  
-       dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name,
+       dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name,
                        (long long)flash->mtd.size >> 10);
  
        DEBUG(MTD_DEBUG_LEVEL2,
@@@ -888,8 -942,10 +943,10 @@@ static int __devexit m25p_remove(struc
                status = del_mtd_partitions(&flash->mtd);
        else
                status = del_mtd_device(&flash->mtd);
-       if (status == 0)
+       if (status == 0) {
+               kfree(flash->command);
                kfree(flash);
+       }
        return 0;
  }
  
@@@ -900,6 -956,7 +957,7 @@@ static struct spi_driver m25p80_driver 
                .bus    = &spi_bus_type,
                .owner  = THIS_MODULE,
        },
+       .id_table       = m25p_ids,
        .probe  = m25p_probe,
        .remove = __devexit_p(m25p_remove),
  
diff --combined drivers/mtd/maps/Kconfig
@@@ -1,3 -1,5 +1,3 @@@
 -# drivers/mtd/maps/Kconfig
 -
  menu "Mapping drivers for chip access"
        depends on MTD!=n
  
@@@ -72,7 -74,7 +72,7 @@@ config MTD_PHYSMAP_BANKWIDT
  
  config MTD_PHYSMAP_OF
        tristate "Flash device in physical memory map based on OF description"
 -      depends on PPC_OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM)
 +      depends on (MICROBLAZE || PPC_OF) && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM)
        help
          This provides a 'mapping' driver which allows the NOR Flash and
          ROM driver code to communicate with chips which are mapped
@@@ -359,12 -361,6 +359,6 @@@ config MTD_SA110
          the SA1100 and SA1110, including the Assabet and the Compaq iPAQ.
          If you have such a board, say 'Y'.
  
- config MTD_IPAQ
-       tristate "CFI Flash device mapped on Compaq/HP iPAQ"
-       depends on IPAQ_HANDHELD && MTD_CFI
-       help
-         This provides a driver for the on-board flash of the iPAQ.
  config MTD_DC21285
        tristate "CFI Flash device mapped on DC21285 Footbridge"
        depends on MTD_CFI && ARCH_FOOTBRIDGE && MTD_COMPLEX_MAPPINGS
@@@ -387,9 -383,9 +381,9 @@@ config MTD_IXP200
        depends on MTD_CFI && MTD_COMPLEX_MAPPINGS && ARCH_IXP2000
        help
          This enables MTD access to flash devices on platforms based
 -        on Intel's IXP2000 family of network processors such as the
 -        IXDP425 and Coyote. If you have an IXP2000 based board and
 -        would like to use the flash chips on it, say 'Y'.
 +        on Intel's IXP2000 family of network processors. If you have an
 +        IXP2000 based board and would like to use the flash chips on it,
 +        say 'Y'.
  
  config MTD_FORTUNET
        tristate "CFI Flash device mapped on the FortuNet board"
@@@ -24,12 -24,12 +24,12 @@@ obj-$(CONFIG_MTD_CEIVA)            += ceiva.
  obj-$(CONFIG_MTD_OCTAGON)     += octagon-5066.o
  obj-$(CONFIG_MTD_PHYSMAP)     += physmap.o
  obj-$(CONFIG_MTD_PHYSMAP_OF)  += physmap_of.o
+ obj-$(CONFIG_MTD_PISMO)               += pismo.o
  obj-$(CONFIG_MTD_PMC_MSP_EVM)   += pmcmsp-flash.o
  obj-$(CONFIG_MTD_PCMCIA)      += pcmciamtd.o
  obj-$(CONFIG_MTD_RPXLITE)     += rpxlite.o
  obj-$(CONFIG_MTD_TQM8XXL)     += tqm8xxl.o
  obj-$(CONFIG_MTD_SA1100)      += sa1100-flash.o
- obj-$(CONFIG_MTD_IPAQ)                += ipaq-flash.o
  obj-$(CONFIG_MTD_SBC_GXX)     += sbc_gxx.o
  obj-$(CONFIG_MTD_SC520CDP)    += sc520cdp.o
  obj-$(CONFIG_MTD_NETSC520)    += netsc520.o
@@@ -58,6 -58,4 +58,6 @@@ obj-$(CONFIG_MTD_PLATRAM)     += plat-ram.
  obj-$(CONFIG_MTD_OMAP_NOR)    += omap_nor.o
  obj-$(CONFIG_MTD_INTEL_VR_NOR)        += intel_vr_nor.o
  obj-$(CONFIG_MTD_BFIN_ASYNC)  += bfin-async-flash.o
 +obj-$(CONFIG_MTD_RBTX4939)    += rbtx4939-flash.o
 +obj-$(CONFIG_MTD_VMU)         += vmu-flash.o
  obj-$(CONFIG_MTD_GPIO_ADDR)   += gpio-addr-flash.o
@@@ -32,6 -32,14 +32,6 @@@ struct mtd_blkcore_priv 
        spinlock_t queue_lock;
  };
  
 -static int blktrans_discard_request(struct request_queue *q,
 -                                  struct request *req)
 -{
 -      req->cmd_type = REQ_TYPE_LINUX_BLOCK;
 -      req->cmd[0] = REQ_LB_OP_DISCARD;
 -      return 0;
 -}
 -
  static int do_blktrans_request(struct mtd_blktrans_ops *tr,
                               struct mtd_blktrans_dev *dev,
                               struct request *req)
  
        buf = req->buffer;
  
 -      if (req->cmd_type == REQ_TYPE_LINUX_BLOCK &&
 -          req->cmd[0] == REQ_LB_OP_DISCARD)
 -              return tr->discard(dev, block, nsect);
 -
        if (!blk_fs_request(req))
                return -EIO;
  
            get_capacity(req->rq_disk))
                return -EIO;
  
 +      if (blk_discard_rq(req))
 +              return tr->discard(dev, block, nsect);
 +
        switch(rq_data_dir(req)) {
        case READ:
                for (; nsect > 0; nsect--, block++, buf += tr->blksize)
                        if (tr->readsect(dev, block, buf))
                                return -EIO;
 +              rq_flush_dcache_pages(req);
                return 0;
  
        case WRITE:
                if (!tr->writesect)
                        return -EIO;
  
 +              rq_flush_dcache_pages(req);
                for (; nsect > 0; nsect--, block++, buf += tr->blksize)
                        if (tr->writesect(dev, block, buf))
                                return -EIO;
@@@ -84,9 -91,6 +84,6 @@@ static int mtd_blktrans_thread(void *ar
        struct request_queue *rq = tr->blkcore_priv->rq;
        struct request *req = NULL;
  
-       /* we might get involved when memory gets low, so use PF_MEMALLOC */
-       current->flags |= PF_MEMALLOC;
        spin_lock_irq(rq->queue_lock);
  
        while (!kthread_should_stop()) {
@@@ -373,15 -377,15 +370,15 @@@ int register_mtd_blktrans(struct mtd_bl
        tr->blkcore_priv->rq->queuedata = tr;
        blk_queue_logical_block_size(tr->blkcore_priv->rq, tr->blksize);
        if (tr->discard)
 -              blk_queue_set_discard(tr->blkcore_priv->rq,
 -                                    blktrans_discard_request);
 +              queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
 +                                      tr->blkcore_priv->rq);
  
        tr->blkshift = ffs(tr->blksize) - 1;
  
        tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
                        "%sd", tr->name);
        if (IS_ERR(tr->blkcore_priv->thread)) {
-               int ret = PTR_ERR(tr->blkcore_priv->thread);
+               ret = PTR_ERR(tr->blkcore_priv->thread);
                blk_cleanup_queue(tr->blkcore_priv->rq);
                unregister_blkdev(tr->major, tr->name);
                kfree(tr->blkcore_priv);
diff --combined drivers/mtd/nand/Kconfig
@@@ -1,3 -1,5 +1,3 @@@
 -# drivers/mtd/nand/Kconfig
 -
  menuconfig MTD_NAND
        tristate "NAND Device Support"
        depends on MTD
@@@ -201,6 -203,22 +201,22 @@@ config MTD_NAND_S3C2410_CLKSTO
          when the is NAND chip selected or released, but will save
          approximately 5mA of power when there is nothing happening.
  
+ config MTD_NAND_BCM_UMI
+       tristate "NAND Flash support for BCM Reference Boards"
+       depends on ARCH_BCMRING && MTD_NAND
+       help
+         This enables the NAND flash controller on the BCM UMI block.
+         No board specfic support is done by this driver, each board
+         must advertise a platform_device for the driver to attach.
+ config MTD_NAND_BCM_UMI_HWCS
+       bool "BCM UMI NAND Hardware CS"
+       depends on MTD_NAND_BCM_UMI
+       help
+         Enable the use of the BCM UMI block's internal CS using NAND.
+         This should only be used if you know the external NAND CS can toggle.
  config MTD_NAND_DISKONCHIP
        tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
        depends on EXPERIMENTAL
@@@ -356,7 -374,7 +372,7 @@@ endchoic
  
  config MTD_NAND_PXA3xx
        tristate "Support for NAND flash devices on PXA3xx"
 -      depends on MTD_NAND && PXA3xx
 +      depends on MTD_NAND && (PXA3xx || ARCH_MMP)
        help
          This enables the driver for the NAND flash device found on
          PXA3xx processors
@@@ -150,20 -150,19 +150,19 @@@ static const char addressbits[256] = 
  };
  
  /**
-  * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
+  * __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
   *                     block
-  * @mtd:      MTD block structure
   * @buf:      input buffer with raw data
+  * @eccsize:  data bytes per ecc step (256 or 512)
   * @code:     output buffer with ECC
   */
int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
                       unsigned char *code)
  {
        int i;
        const uint32_t *bp = (uint32_t *)buf;
        /* 256 or 512 bytes/ecc  */
-       const uint32_t eccsize_mult =
-                       (((struct nand_chip *)mtd->priv)->ecc.size) >> 8;
+       const uint32_t eccsize_mult = eccsize >> 8;
        uint32_t cur;           /* current value in buffer */
        /* rp0..rp15..rp17 are the various accumulated parities (per byte) */
        uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7;
                    (invparity[par & 0x55] << 2) |
                    (invparity[rp17] << 1) |
                    (invparity[rp16] << 0);
+ }
+ EXPORT_SYMBOL(__nand_calculate_ecc);
+ /**
+  * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
+  *                     block
+  * @mtd:      MTD block structure
+  * @buf:      input buffer with raw data
+  * @code:     output buffer with ECC
+  */
+ int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
+                      unsigned char *code)
+ {
+       __nand_calculate_ecc(buf,
+                       ((struct nand_chip *)mtd->priv)->ecc.size, code);
        return 0;
  }
  EXPORT_SYMBOL(nand_calculate_ecc);
@@@ -475,7 -490,7 +490,7 @@@ int __nand_correct_data(unsigned char *
                 *
                 * The b2 shift is there to get rid of the lowest two bits.
                 * We could also do addressbits[b2] >> 1 but for the
 -               * performace it does not make any difference
 +               * performance it does not make any difference
                 */
                if (eccsize_mult == 1)
                        byte_addr = (addressbits[b1] << 4) + addressbits[b0];
@@@ -774,7 -774,7 +774,7 @@@ static void s3c2410_nand_init_chip(stru
        chip->select_chip  = s3c2410_nand_select_chip;
        chip->chip_delay   = 50;
        chip->priv         = nmtd;
-       chip->options      = 0;
+       chip->options      = set->options;
        chip->controller   = &info->controller;
  
        switch (info->cpu_type) {
   * @info: The controller instance.
   * @nmtd: The driver version of the MTD instance.
   *
 - * This routine is called after the chip probe has succesfully completed
 + * This routine is called after the chip probe has successfully completed
   * and the relevant per-chip information updated. This call ensure that
   * we update the internal state accordingly.
   *
  #include <linux/io.h>
  
  #include <asm/mach/flash.h>
 -#include <mach/gpmc.h>
 -#include <mach/onenand.h>
 +#include <plat/gpmc.h>
 +#include <plat/onenand.h>
  #include <mach/gpio.h>
  
 -#include <mach/dma.h>
 +#include <plat/dma.h>
  
 -#include <mach/board.h>
 +#include <plat/board.h>
  
  #define DRIVER_NAME "omap2-onenand"
  
@@@ -112,10 -112,24 +112,24 @@@ static int omap2_onenand_wait(struct mt
        unsigned long timeout;
        u32 syscfg;
  
-       if (state == FL_RESETING) {
-               int i;
+       if (state == FL_RESETING || state == FL_PREPARING_ERASE ||
+           state == FL_VERIFYING_ERASE) {
+               int i = 21;
+               unsigned int intr_flags = ONENAND_INT_MASTER;
+               switch (state) {
+               case FL_RESETING:
+                       intr_flags |= ONENAND_INT_RESET;
+                       break;
+               case FL_PREPARING_ERASE:
+                       intr_flags |= ONENAND_INT_ERASE;
+                       break;
+               case FL_VERIFYING_ERASE:
+                       i = 101;
+                       break;
+               }
  
-               for (i = 0; i < 20; i++) {
+               while (--i) {
                        udelay(1);
                        intr = read_reg(c, ONENAND_REG_INTERRUPT);
                        if (intr & ONENAND_INT_MASTER)
                        wait_err("controller error", state, ctrl, intr);
                        return -EIO;
                }
-               if (!(intr & ONENAND_INT_RESET)) {
+               if ((intr & intr_flags) != intr_flags) {
                        wait_err("timeout", state, ctrl, intr);
                        return -EIO;
                }
diff --combined fs/jffs2/readinode.c
@@@ -931,7 -931,7 +931,7 @@@ static inline int read_unknown(struct j
   * Helper function for jffs2_get_inode_nodes().
   * The function detects whether more data should be read and reads it if yes.
   *
 - * Returns: 0 on succes;
 + * Returns: 0 on success;
   *        negative error code on failure.
   */
  static int read_more(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
@@@ -1284,7 -1284,7 +1284,7 @@@ static int jffs2_do_read_inode_internal
                                f->target = NULL;
                                mutex_unlock(&f->sem);
                                jffs2_do_clear_inode(c, f);
-                               return -ret;
+                               return ret;
                        }
  
                        f->target[je32_to_cpu(latest_node->csize)] = '\0';
diff --combined kernel/panic.c
@@@ -10,6 -10,7 +10,7 @@@
   */
  #include <linux/debug_locks.h>
  #include <linux/interrupt.h>
+ #include <linux/kmsg_dump.h>
  #include <linux/kallsyms.h>
  #include <linux/notifier.h>
  #include <linux/module.h>
@@@ -74,6 -75,7 +75,7 @@@ NORET_TYPE void panic(const char * fmt
        dump_stack();
  #endif
  
+       kmsg_dump(KMSG_DUMP_PANIC);
        /*
         * If we have crashed and we have a crash kernel loaded let it handle
         * everything else.
@@@ -90,8 -92,6 +92,8 @@@
  
        atomic_notifier_call_chain(&panic_notifier_list, 0, buf);
  
 +      bust_spinlocks(0);
 +
        if (!panic_blink)
                panic_blink = no_blink;
  
                mdelay(1);
                i++;
        }
 -      bust_spinlocks(0);
  }
  
  EXPORT_SYMBOL(panic);
@@@ -339,6 -340,7 +341,7 @@@ void oops_exit(void
  {
        do_oops_enter_exit();
        print_oops_end_marker();
+       kmsg_dump(KMSG_DUMP_OOPS);
  }
  
  #ifdef WANT_WARN_ON_SLOWPATH
diff --combined kernel/printk.c
@@@ -33,7 -33,7 +33,8 @@@
  #include <linux/bootmem.h>
  #include <linux/syscalls.h>
  #include <linux/kexec.h>
 +#include <linux/ratelimit.h>
+ #include <linux/kmsg_dump.h>
  
  #include <asm/uaccess.h>
  
@@@ -1377,11 -1377,11 +1378,11 @@@ late_initcall(disable_boot_consoles)
   */
  DEFINE_RATELIMIT_STATE(printk_ratelimit_state, 5 * HZ, 10);
  
 -int printk_ratelimit(void)
 +int __printk_ratelimit(const char *func)
  {
 -      return __ratelimit(&printk_ratelimit_state);
 +      return ___ratelimit(&printk_ratelimit_state, func);
  }
 -EXPORT_SYMBOL(printk_ratelimit);
 +EXPORT_SYMBOL(__printk_ratelimit);
  
  /**
   * printk_timed_ratelimit - caller-controlled printk ratelimiting
@@@ -1405,4 -1405,122 +1406,122 @@@ bool printk_timed_ratelimit(unsigned lo
        return false;
  }
  EXPORT_SYMBOL(printk_timed_ratelimit);
+ static DEFINE_SPINLOCK(dump_list_lock);
+ static LIST_HEAD(dump_list);
+ /**
+  * kmsg_dump_register - register a kernel log dumper.
+  * @dump: pointer to the kmsg_dumper structure
+  *
+  * Adds a kernel log dumper to the system. The dump callback in the
+  * structure will be called when the kernel oopses or panics and must be
+  * set. Returns zero on success and %-EINVAL or %-EBUSY otherwise.
+  */
+ int kmsg_dump_register(struct kmsg_dumper *dumper)
+ {
+       unsigned long flags;
+       int err = -EBUSY;
+       /* The dump callback needs to be set */
+       if (!dumper->dump)
+               return -EINVAL;
+       spin_lock_irqsave(&dump_list_lock, flags);
+       /* Don't allow registering multiple times */
+       if (!dumper->registered) {
+               dumper->registered = 1;
+               list_add_tail(&dumper->list, &dump_list);
+               err = 0;
+       }
+       spin_unlock_irqrestore(&dump_list_lock, flags);
+       return err;
+ }
+ EXPORT_SYMBOL_GPL(kmsg_dump_register);
+ /**
+  * kmsg_dump_unregister - unregister a kmsg dumper.
+  * @dump: pointer to the kmsg_dumper structure
+  *
+  * Removes a dump device from the system. Returns zero on success and
+  * %-EINVAL otherwise.
+  */
+ int kmsg_dump_unregister(struct kmsg_dumper *dumper)
+ {
+       unsigned long flags;
+       int err = -EINVAL;
+       spin_lock_irqsave(&dump_list_lock, flags);
+       if (dumper->registered) {
+               dumper->registered = 0;
+               list_del(&dumper->list);
+               err = 0;
+       }
+       spin_unlock_irqrestore(&dump_list_lock, flags);
+       return err;
+ }
+ EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
+ static const char const *kmsg_reasons[] = {
+       [KMSG_DUMP_OOPS]        = "oops",
+       [KMSG_DUMP_PANIC]       = "panic",
+ };
+ static const char *kmsg_to_str(enum kmsg_dump_reason reason)
+ {
+       if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0)
+               return "unknown";
+       return kmsg_reasons[reason];
+ }
+ /**
+  * kmsg_dump - dump kernel log to kernel message dumpers.
+  * @reason: the reason (oops, panic etc) for dumping
+  *
+  * Iterate through each of the dump devices and call the oops/panic
+  * callbacks with the log buffer.
+  */
+ void kmsg_dump(enum kmsg_dump_reason reason)
+ {
+       unsigned long end;
+       unsigned chars;
+       struct kmsg_dumper *dumper;
+       const char *s1, *s2;
+       unsigned long l1, l2;
+       unsigned long flags;
+       /* Theoretically, the log could move on after we do this, but
+          there's not a lot we can do about that. The new messages
+          will overwrite the start of what we dump. */
+       spin_lock_irqsave(&logbuf_lock, flags);
+       end = log_end & LOG_BUF_MASK;
+       chars = logged_chars;
+       spin_unlock_irqrestore(&logbuf_lock, flags);
+       if (logged_chars > end) {
+               s1 = log_buf + log_buf_len - logged_chars + end;
+               l1 = logged_chars - end;
+               s2 = log_buf;
+               l2 = end;
+       } else {
+               s1 = "";
+               l1 = 0;
+               s2 = log_buf + end - logged_chars;
+               l2 = logged_chars;
+       }
+       if (!spin_trylock_irqsave(&dump_list_lock, flags)) {
+               printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n",
+                               kmsg_to_str(reason));
+               return;
+       }
+       list_for_each_entry(dumper, &dump_list, list)
+               dumper->dump(dumper, reason, s1, l1, s2, l2);
+       spin_unlock_irqrestore(&dump_list_lock, flags);
+ }
  #endif