Merge branch 'for-linus/i2c' of git://git.fluff.org/bjdooks/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 24 Jan 2010 18:37:51 +0000 (10:37 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 24 Jan 2010 18:37:51 +0000 (10:37 -0800)
* 'for-linus/i2c' of git://git.fluff.org/bjdooks/linux:
  i2c: imx: call ioremap only after request_mem_region
  i2c: mxc: let time to generate stop bit

12 files changed:
arch/arm/mach-s3c6410/mach-hmt.c
drivers/mtd/maps/Kconfig
drivers/mtd/maps/pismo.c [new file with mode: 0644]
drivers/mtd/mtdoops.c
drivers/mtd/tests/mtd_readtest.c
drivers/mtd/tests/mtd_speedtest.c
drivers/mtd/tests/mtd_stresstest.c
include/linux/kmsg_dump.h
include/linux/mtd/pismo.h [new file with mode: 0644]
kernel/kexec.c
kernel/panic.c
kernel/printk.c

index cdd4b53..7619456 100644 (file)
@@ -82,7 +82,7 @@ static int hmt_bl_init(struct device *dev)
        return ret;
 }
 
-static int hmt_bl_notify(int brightness)
+static int hmt_bl_notify(struct device *dev, int brightness)
 {
        /*
         * translate from CIELUV/CIELAB L*->brightness, E.G. from
index 4c364d4..2de0cc8 100644 (file)
@@ -549,4 +549,21 @@ config MTD_VMU
          To build this as a module select M here, the module will be called
          vmu-flash.
 
+config MTD_PISMO
+       tristate "MTD discovery driver for PISMO modules"
+       depends on I2C
+       depends on ARCH_VERSATILE
+       help
+         This driver allows for discovery of PISMO modules - see
+         <http://www.pismoworld.org/>.  These are small modules containing
+         up to five memory devices (eg, SRAM, flash, DOC) described by an
+         I2C EEPROM.
+
+         This driver does not create any MTD maps itself; instead it
+         creates MTD physmap and MTD SRAM platform devices.  If you
+         enable this option, you should consider enabling MTD_PHYSMAP
+         and/or MTD_PLATRAM according to the devices on your module.
+
+         When built as a module, it will be called pismo.ko
+
 endmenu
diff --git a/drivers/mtd/maps/pismo.c b/drivers/mtd/maps/pismo.c
new file mode 100644 (file)
index 0000000..c48cad2
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * PISMO memory driver - http://www.pismoworld.org/
+ *
+ * For ARM Realview and Versatile platforms
+ *
+ * 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.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/mtd/physmap.h>
+#include <linux/mtd/plat-ram.h>
+#include <linux/mtd/pismo.h>
+
+#define PISMO_NUM_CS   5
+
+struct pismo_cs_block {
+       u8      type;
+       u8      width;
+       __le16  access;
+       __le32  size;
+       u32     reserved[2];
+       char    device[32];
+} __packed;
+
+struct pismo_eeprom {
+       struct pismo_cs_block cs[PISMO_NUM_CS];
+       char    board[15];
+       u8      sum;
+} __packed;
+
+struct pismo_mem {
+       phys_addr_t base;
+       u32     size;
+       u16     access;
+       u8      width;
+       u8      type;
+};
+
+struct pismo_data {
+       struct i2c_client       *client;
+       void                    (*vpp)(void *, int);
+       void                    *vpp_data;
+       struct platform_device  *dev[PISMO_NUM_CS];
+};
+
+/* FIXME: set_vpp could do with a better calling convention */
+static struct pismo_data *vpp_pismo;
+static DEFINE_MUTEX(pismo_mutex);
+
+static int pismo_setvpp_probe_fix(struct pismo_data *pismo)
+{
+       mutex_lock(&pismo_mutex);
+       if (vpp_pismo) {
+               mutex_unlock(&pismo_mutex);
+               kfree(pismo);
+               return -EBUSY;
+       }
+       vpp_pismo = pismo;
+       mutex_unlock(&pismo_mutex);
+       return 0;
+}
+
+static void pismo_setvpp_remove_fix(struct pismo_data *pismo)
+{
+       mutex_lock(&pismo_mutex);
+       if (vpp_pismo == pismo)
+               vpp_pismo = NULL;
+       mutex_unlock(&pismo_mutex);
+}
+
+static void pismo_set_vpp(struct map_info *map, int on)
+{
+       struct pismo_data *pismo = vpp_pismo;
+
+       pismo->vpp(pismo->vpp_data, on);
+}
+/* end of hack */
+
+
+static unsigned int __devinit pismo_width_to_bytes(unsigned int width)
+{
+       width &= 15;
+       if (width > 2)
+               return 0;
+       return 1 << width;
+}
+
+static int __devinit pismo_eeprom_read(struct i2c_client *client, void *buf,
+       u8 addr, size_t size)
+{
+       int ret;
+       struct i2c_msg msg[] = {
+               {
+                       .addr = client->addr,
+                       .len = sizeof(addr),
+                       .buf = &addr,
+               }, {
+                       .addr = client->addr,
+                       .flags = I2C_M_RD,
+                       .len = size,
+                       .buf = buf,
+               },
+       };
+
+       ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+
+       return ret == ARRAY_SIZE(msg) ? size : -EIO;
+}
+
+static int __devinit pismo_add_device(struct pismo_data *pismo, int i,
+       struct pismo_mem *region, const char *name, void *pdata, size_t psize)
+{
+       struct platform_device *dev;
+       struct resource res = { };
+       phys_addr_t base = region.base;
+       int ret;
+
+       if (base == ~0)
+               return -ENXIO;
+
+       res.start = base;
+       res.end = base + region->size - 1;
+       res.flags = IORESOURCE_MEM;
+
+       dev = platform_device_alloc(name, i);
+       if (!dev)
+               return -ENOMEM;
+       dev->dev.parent = &pismo->client->dev;
+
+       do {
+               ret = platform_device_add_resources(dev, &res, 1);
+               if (ret)
+                       break;
+
+               ret = platform_device_add_data(dev, pdata, psize);
+               if (ret)
+                       break;
+
+               ret = platform_device_add(dev);
+               if (ret)
+                       break;
+
+               pismo->dev[i] = dev;
+               return 0;
+       } while (0);
+
+       platform_device_put(dev);
+       return ret;
+}
+
+static int __devinit pismo_add_nor(struct pismo_data *pismo, int i,
+       struct pismo_mem *region)
+{
+       struct physmap_flash_data data = {
+               .width = region->width,
+       };
+
+       if (pismo->vpp)
+               data.set_vpp = pismo_set_vpp;
+
+       return pismo_add_device(pismo, i, region, "physmap-flash",
+               &data, sizeof(data));
+}
+
+static int __devinit pismo_add_sram(struct pismo_data *pismo, int i,
+       struct pismo_mem *region)
+{
+       struct platdata_mtd_ram data = {
+               .bankwidth = region->width,
+       };
+
+       return pismo_add_device(pismo, i, region, "mtd-ram",
+               &data, sizeof(data));
+}
+
+static void __devinit pismo_add_one(struct pismo_data *pismo, int i,
+       const struct pismo_cs_block *cs, phys_addr_t base)
+{
+       struct device *dev = &pismo->client->dev;
+       struct pismo_mem region;
+
+       region.base = base;
+       region.type = cs->type;
+       region.width = pismo_width_to_bytes(cs->width);
+       region.access = le16_to_cpu(cs->access);
+       region.size = le32_to_cpu(cs->size);
+
+       if (region.width == 0) {
+               dev_err(dev, "cs%u: bad width: %02x, ignoring\n", i, cs->width);
+               return;
+       }
+
+       /*
+        * FIXME: may need to the platforms memory controller here, but at
+        * the moment we assume that it has already been correctly setup.
+        * The memory controller can also tell us the base address as well.
+        */
+
+       dev_info(dev, "cs%u: %.32s: type %02x access %u00ps size %uK\n",
+               i, cs->device, region.type, region.access, region.size / 1024);
+
+       switch (region.type) {
+       case 0:
+               break;
+       case 1:
+               /* static DOC */
+               break;
+       case 2:
+               /* static NOR */
+               pismo_add_nor(pismo, i, &region);
+               break;
+       case 3:
+               /* static RAM */
+               pismo_add_sram(pismo, i, &region);
+               break;
+       }
+}
+
+static int __devexit pismo_remove(struct i2c_client *client)
+{
+       struct pismo_data *pismo = i2c_get_clientdata(client);
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pismo->dev); i++)
+               platform_device_unregister(pismo->dev[i]);
+
+       /* FIXME: set_vpp needs saner arguments */
+       pismo_setvpp_remove_fix(pismo);
+
+       kfree(pismo);
+
+       return 0;
+}
+
+static int __devinit pismo_probe(struct i2c_client *client,
+                                const struct i2c_device_id *id)
+{
+       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+       struct pismo_pdata *pdata = client->dev.platform_data;
+       struct pismo_eeprom eeprom;
+       struct pismo_data *pismo;
+       int ret, i;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
+               dev_err(&client->dev, "functionality mismatch\n");
+               return -EIO;
+       }
+
+       pismo = kzalloc(sizeof(*pismo), GFP_KERNEL);
+       if (!pismo)
+               return -ENOMEM;
+
+       /* FIXME: set_vpp needs saner arguments */
+       ret = pismo_setvpp_probe_fix(pismo);
+       if (ret)
+               return ret;
+
+       pismo->client = client;
+       if (pdata) {
+               pismo->vpp = pdata->set_vpp;
+               pismo->vpp_data = pdata->vpp_data;
+       }
+       i2c_set_clientdata(client, pismo);
+
+       ret = pismo_eeprom_read(client, &eeprom, 0, sizeof(eeprom));
+       if (ret < 0) {
+               dev_err(&client->dev, "error reading EEPROM: %d\n", ret);
+               return ret;
+       }
+
+       dev_info(&client->dev, "%.15s board found\n", eeprom.board);
+
+       for (i = 0; i < ARRAY_SIZE(eeprom.cs); i++)
+               if (eeprom.cs[i].type != 0xff)
+                       pismo_add_one(pismo, i, &eeprom.cs[i],
+                                     pdata->cs_addrs[i]);
+
+       return 0;
+}
+
+static const struct i2c_device_id pismo_id[] = {
+       { "pismo" },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, pismo_id);
+
+static struct i2c_driver pismo_driver = {
+       .driver = {
+               .name   = "pismo",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = pismo_probe,
+       .remove         = __devexit_p(pismo_remove),
+       .id_table       = pismo_id,
+};
+
+static int __init pismo_init(void)
+{
+       BUILD_BUG_ON(sizeof(struct pismo_cs_block) != 48);
+       BUILD_BUG_ON(sizeof(struct pismo_eeprom) != 256);
+
+       return i2c_add_driver(&pismo_driver);
+}
+module_init(pismo_init);
+
+static void __exit pismo_exit(void)
+{
+       i2c_del_driver(&pismo_driver);
+}
+module_exit(pismo_exit);
+
+MODULE_AUTHOR("Russell King <linux@arm.linux.org.uk>");
+MODULE_DESCRIPTION("PISMO memory driver");
+MODULE_LICENSE("GPL");
index a714ec4..92e12df 100644 (file)
@@ -322,7 +322,7 @@ static void mtdoops_do_dump(struct kmsg_dumper *dumper,
        memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
 
        /* Panics must be written immediately */
-       if (reason == KMSG_DUMP_PANIC) {
+       if (reason != KMSG_DUMP_OOPS) {
                if (!cxt->mtd->panic_write)
                        printk(KERN_ERR "mtdoops: Cannot write from panic without panic_write\n");
                else
index 79fc453..25c5dd0 100644 (file)
@@ -147,6 +147,10 @@ static int scan_for_bad_eraseblocks(void)
        }
        memset(bbt, 0 , ebcnt);
 
+       /* NOR flash does not implement block_isbad */
+       if (mtd->block_isbad == NULL)
+               return 0;
+
        printk(PRINT_PREF "scanning for bad eraseblocks\n");
        for (i = 0; i < ebcnt; ++i) {
                bbt[i] = is_block_bad(i) ? 1 : 0;
@@ -184,7 +188,7 @@ static int __init mtd_readtest_init(void)
        tmp = mtd->size;
        do_div(tmp, mtd->erasesize);
        ebcnt = tmp;
-       pgcnt = mtd->erasesize / mtd->writesize;
+       pgcnt = mtd->erasesize / pgsize;
 
        printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
               "page size %u, count of eraseblocks %u, pages per "
index 141363a..7fbb51d 100644 (file)
@@ -301,6 +301,10 @@ static int scan_for_bad_eraseblocks(void)
        }
        memset(bbt, 0 , ebcnt);
 
+       /* NOR flash does not implement block_isbad */
+       if (mtd->block_isbad == NULL)
+               goto out;
+
        printk(PRINT_PREF "scanning for bad eraseblocks\n");
        for (i = 0; i < ebcnt; ++i) {
                bbt[i] = is_block_bad(i) ? 1 : 0;
@@ -309,6 +313,7 @@ static int scan_for_bad_eraseblocks(void)
                cond_resched();
        }
        printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad);
+out:
        goodebcnt = ebcnt - bad;
        return 0;
 }
@@ -340,7 +345,7 @@ static int __init mtd_speedtest_init(void)
        tmp = mtd->size;
        do_div(tmp, mtd->erasesize);
        ebcnt = tmp;
-       pgcnt = mtd->erasesize / mtd->writesize;
+       pgcnt = mtd->erasesize / pgsize;
 
        printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
               "page size %u, count of eraseblocks %u, pages per "
index 6392047..a99d3cd 100644 (file)
@@ -227,6 +227,10 @@ static int scan_for_bad_eraseblocks(void)
        }
        memset(bbt, 0 , ebcnt);
 
+       /* NOR flash does not implement block_isbad */
+       if (mtd->block_isbad == NULL)
+               return 0;
+
        printk(PRINT_PREF "scanning for bad eraseblocks\n");
        for (i = 0; i < ebcnt; ++i) {
                bbt[i] = is_block_bad(i) ? 1 : 0;
@@ -265,7 +269,7 @@ static int __init mtd_stresstest_init(void)
        tmp = mtd->size;
        do_div(tmp, mtd->erasesize);
        ebcnt = tmp;
-       pgcnt = mtd->erasesize / mtd->writesize;
+       pgcnt = mtd->erasesize / pgsize;
 
        printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
               "page size %u, count of eraseblocks %u, pages per "
index e32aa26..24b4414 100644 (file)
@@ -17,6 +17,7 @@
 enum kmsg_dump_reason {
        KMSG_DUMP_OOPS,
        KMSG_DUMP_PANIC,
+       KMSG_DUMP_KEXEC,
 };
 
 /**
diff --git a/include/linux/mtd/pismo.h b/include/linux/mtd/pismo.h
new file mode 100644 (file)
index 0000000..8dfb7e1
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * PISMO memory driver - http://www.pismoworld.org/
+ *
+ * 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.
+ */
+#ifndef __LINUX_MTD_PISMO_H
+#define __LINUX_MTD_PISMO_H
+
+struct pismo_pdata {
+       void                    (*set_vpp)(void *, int);
+       void                    *vpp_data;
+       phys_addr_t             cs_addrs[5];
+};
+
+#endif
index a9a93d9..ef077fb 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/console.h>
 #include <linux/vmalloc.h>
 #include <linux/swap.h>
+#include <linux/kmsg_dump.h>
 
 #include <asm/page.h>
 #include <asm/uaccess.h>
@@ -1074,6 +1075,9 @@ void crash_kexec(struct pt_regs *regs)
        if (mutex_trylock(&kexec_mutex)) {
                if (kexec_crash_image) {
                        struct pt_regs fixed_regs;
+
+                       kmsg_dump(KMSG_DUMP_KEXEC);
+
                        crash_setup_regs(&fixed_regs, regs);
                        crash_save_vmcoreinfo();
                        machine_crash_shutdown(&fixed_regs);
index 5827f7b..c787333 100644 (file)
@@ -75,7 +75,6 @@ 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.
@@ -83,6 +82,8 @@ NORET_TYPE void panic(const char * fmt, ...)
         */
        crash_kexec(NULL);
 
+       kmsg_dump(KMSG_DUMP_PANIC);
+
        /*
         * Note smp_send_stop is the usual smp shutdown function, which
         * unfortunately means it may not be hardened to work in a panic
index 17463ca..1751c45 100644 (file)
@@ -1467,6 +1467,7 @@ EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
 static const char const *kmsg_reasons[] = {
        [KMSG_DUMP_OOPS]        = "oops",
        [KMSG_DUMP_PANIC]       = "panic",
+       [KMSG_DUMP_KEXEC]       = "kexec",
 };
 
 static const char *kmsg_to_str(enum kmsg_dump_reason reason)