]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00141217-9 iim: add iim driver support
authorFrank Li <Frank.Li@freescale.com>
Mon, 14 Mar 2011 02:37:22 +0000 (10:37 +0800)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:32:31 +0000 (08:32 +0200)
add iim driver for 2.6.38

Signed-off-by: Frank Li <Frank.Li@freescale.com>
drivers/char/Kconfig
drivers/char/Makefile
drivers/char/mxc_iim.c [new file with mode: 0644]
drivers/char/mxc_iim.h [new file with mode: 0644]

index 49502bc5360aa70fe83d455388b3dde0efaa284c..bdefe09362372c6ac255570c32214e2bd381bb27 100644 (file)
@@ -616,5 +616,11 @@ config MSM_SMD_PKT
          Enables userspace clients to read and write to some packet SMD
          ports via device interface for MSM chipset.
 
+config MXC_IIM
+       tristate "MXC IIM device driver"
+       depends on ARCH_MXC
+       help
+         Support for access to MXC IIM device, most people should say N here.
+
 endmenu
 
index 7a00672bd85de7b7256855653bce7d3c04b0b33b..caaf67a94c57d973b2228229aa3d05d4f08e1793 100644 (file)
@@ -61,5 +61,7 @@ obj-$(CONFIG_TCG_TPM)         += tpm/
 obj-$(CONFIG_PS3_FLASH)                += ps3flash.o
 obj-$(CONFIG_RAMOOPS)          += ramoops.o
 
+obj-$(CONFIG_MXC_IIM)           += mxc_iim.o
+
 obj-$(CONFIG_JS_RTC)           += js-rtc.o
 js-rtc-y = rtc.o
diff --git a/drivers/char/mxc_iim.c b/drivers/char/mxc_iim.c
new file mode 100644 (file)
index 0000000..77132fe
--- /dev/null
@@ -0,0 +1,611 @@
+/*
+ * Copyright 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include "mxc_iim.h"
+
+static struct mxc_iim_platform_data *iim_data;
+
+
+#ifdef MXC_IIM_DEBUG
+static inline void dump_reg(void)
+{
+       struct iim_regs *iim_reg_base =
+               (struct iim_regs *)iim_data->virt_base;
+
+       dev_dbg(iim_data->dev, "stat: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->stat));
+       dev_dbg(iim_data->dev, "statm: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->statm));
+       dev_dbg(iim_data->dev, "err: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->err));
+       dev_dbg(iim_data->dev, "emask: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->emask));
+       dev_dbg(iim_data->dev, "fctl: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->fctl));
+       dev_dbg(iim_data->dev, "ua: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->ua));
+       dev_dbg(iim_data->dev, "la: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->la));
+       dev_dbg(iim_data->dev, "sdat: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->sdat));
+       dev_dbg(iim_data->dev, "prev: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->prev));
+       dev_dbg(iim_data->dev, "srev: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->srev));
+       dev_dbg(iim_data->dev, "preg_p: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->preg_p));
+       dev_dbg(iim_data->dev, "scs0: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->scs0));
+       dev_dbg(iim_data->dev, "scs1: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->scs1));
+       dev_dbg(iim_data->dev, "scs2: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->scs2));
+       dev_dbg(iim_data->dev, "scs3: 0x%08x\n",
+                       __raw_readl(&iim_reg_base->scs3));
+}
+#endif
+
+static inline void mxc_iim_disable_irq(void)
+{
+       struct iim_regs *iim_reg_base = (struct iim_regs *)iim_data->virt_base;
+
+       dev_dbg(iim_data->dev, "=> %s\n", __func__);
+
+       __raw_writel(0x0, &(iim_reg_base->statm));
+       __raw_writel(0x0, &(iim_reg_base->emask));
+
+       dev_dbg(iim_data->dev, "<= %s\n", __func__);
+}
+
+static inline void fuse_op_start(void)
+{
+       struct iim_regs *iim_reg_base = (struct iim_regs *)iim_data->virt_base;
+
+       dev_dbg(iim_data->dev, "=> %s\n", __func__);
+
+#ifdef MXC_IIM_DEBUG
+       dump_reg();
+#endif
+
+       /* Generate interrupt */
+       __raw_writel(0x3, &(iim_reg_base->statm));
+       __raw_writel(0xfe, &(iim_reg_base->emask));
+       /* Clear the status bits and error bits */
+       __raw_writel(0x3, &(iim_reg_base->stat));
+       __raw_writel(0xfe, &(iim_reg_base->err));
+
+       dev_dbg(iim_data->dev, "<= %s\n", __func__);
+}
+
+static u32 sense_fuse(s32 bank, s32 row, s32 bit)
+{
+       s32 addr, addr_l, addr_h;
+       s32 err = 0;
+       struct iim_regs *iim_reg_base = (struct iim_regs *)iim_data->virt_base;
+
+       dev_dbg(iim_data->dev, "=> %s\n", __func__);
+
+       init_completion(&(iim_data->completion));
+
+       iim_data->action = POLL_FUSE_SNSD;
+
+       fuse_op_start();
+
+       addr = ((bank << 11) | (row << 3) | (bit & 0x7));
+       /* Set IIM Program Upper Address */
+       addr_h = (addr >> 8) & 0x000000FF;
+       /* Set IIM Program Lower Address */
+       addr_l = (addr & 0x000000FF);
+
+       dev_dbg(iim_data->dev, "%s: addr_h=0x%x, addr_l=0x%x\n",
+                       __func__, addr_h, addr_l);
+       __raw_writel(addr_h, &(iim_reg_base->ua));
+       __raw_writel(addr_l, &(iim_reg_base->la));
+
+       /* Start sensing */
+#ifdef MXC_IIM_DEBUG
+       dump_reg();
+#endif
+       __raw_writel(0x8, &(iim_reg_base->fctl));
+
+       err = wait_for_completion_timeout(&(iim_data->completion),
+                                       msecs_to_jiffies(1000));
+       err = (!err) ? -ETIMEDOUT : 0;
+       if (err)
+               dev_dbg(iim_data->dev, "Sense timeout!");
+
+       dev_dbg(iim_data->dev, "<= %s\n", __func__);
+
+       return __raw_readl(&(iim_reg_base->sdat));
+}
+
+/* Blow fuses based on the bank, row and bit positions (all 0-based)
+*/
+static s32 fuse_blow_bit(s32 bank, s32 row, s32 bit)
+{
+       int addr, addr_l, addr_h, err;
+       struct iim_regs *iim_reg_base = (struct iim_regs *)iim_data->virt_base;
+
+       dev_dbg(iim_data->dev, "=> %s\n", __func__);
+
+       init_completion(&iim_data->completion);
+
+       iim_data->action = POLL_FUSE_PRGD;
+
+       fuse_op_start();
+
+       /* Disable IIM Program Protect */
+       __raw_writel(0xaa, &(iim_reg_base->preg_p));
+
+       addr = ((bank << 11) | (row << 3) | (bit & 0x7));
+       /* Set IIM Program Upper Address */
+       addr_h = (addr >> 8) & 0x000000FF;
+       /* Set IIM Program Lower Address */
+       addr_l = (addr & 0x000000FF);
+
+       dev_dbg(iim_data->dev, "blowing addr_h=0x%x, addr_l=0x%x\n",
+               addr_h, addr_l);
+
+       __raw_writel(addr_h, &(iim_reg_base->ua));
+       __raw_writel(addr_l, &(iim_reg_base->la));
+
+       /* Start Programming */
+#ifdef MXC_IIM_DEBUG
+       dump_reg();
+#endif
+       __raw_writel(0x31, &(iim_reg_base->fctl));
+       err = wait_for_completion_timeout(&(iim_data->completion),
+                                       msecs_to_jiffies(1000));
+       err = (!err) ? -ETIMEDOUT : 0;
+       if (err)
+               dev_dbg(iim_data->dev, "Fuse timeout!\n");
+
+       /* Enable IIM Program Protect */
+       __raw_writel(0x0, &(iim_reg_base->preg_p));
+
+       dev_dbg(iim_data->dev, "<= %s\n", __func__);
+
+       return err;
+}
+
+static void fuse_blow_row(s32 bank, s32 row, s32 value)
+{
+       u32 i;
+
+       dev_dbg(iim_data->dev, "=> %s\n", __func__);
+
+       /* Enable fuse blown */
+       if (iim_data->enable_fuse)
+               iim_data->enable_fuse();
+
+       for (i = 0; i < 8; i++) {
+               if (((value >> i) & 0x1) == 0)
+                       continue;
+       if (fuse_blow_bit(bank, row, i) != 0) {
+               dev_dbg(iim_data->dev, "fuse_blow_bit(bank: %d, row: %d, "
+                               "bit: %d failed\n",
+                               bank, row, i);
+               }
+       }
+
+       if (iim_data->disable_fuse)
+               iim_data->disable_fuse();
+
+       dev_dbg(iim_data->dev, "<= %s\n", __func__);
+}
+
+static irqreturn_t mxc_iim_irq(int irq, void *dev_id)
+{
+       u32 status, error, rtn = 0;
+       ulong flags;
+       struct iim_regs *iim_reg_base = (struct iim_regs *)iim_data->virt_base;
+
+       dev_dbg(iim_data->dev, "=> %s\n", __func__);
+
+       spin_lock_irqsave(&iim_data->lock, flags);
+
+       mxc_iim_disable_irq();
+#ifdef MXC_IIM_DEBUG
+       dump_reg();
+#endif
+       if (iim_data->action != POLL_FUSE_PRGD &&
+               iim_data->action != POLL_FUSE_SNSD) {
+               dev_dbg(iim_data->dev, "%s(%d) invalid operation\n",
+                       __func__, iim_data->action);
+               rtn = 1;
+               goto out;
+       }
+
+       /* Test for successful write */
+       status = readl(&(iim_reg_base->stat));
+       error = readl(&(iim_reg_base->err));
+
+       if ((status & iim_data->action) != 0 && \
+               (error & (iim_data->action >> IIM_ERR_SHIFT)) == 0) {
+               if (error) {
+                       printk(KERN_NOTICE "Even though the operation"
+                               "seems successful...\n");
+                       printk(KERN_NOTICE "There are some error(s) "
+                               "at addr=0x%x: 0x%x\n",
+                               (u32)&(iim_reg_base->err), error);
+               }
+               rtn = 0;
+               goto out;
+       }
+       printk(KERN_NOTICE "%s(%d) failed\n", __func__, iim_data->action);
+       printk(KERN_NOTICE "status address=0x%x, value=0x%x\n",
+               (u32)&(iim_reg_base->stat), status);
+       printk(KERN_NOTICE "There are some error(s) at addr=0x%x: 0x%x\n",
+               (u32)&(iim_reg_base->err), error);
+#ifdef MXC_IIM_DEBUG
+       dump_reg();
+#endif
+
+out:
+       complete(&(iim_data->completion));
+       spin_unlock_irqrestore(&iim_data->lock, flags);
+
+       dev_dbg(iim_data->dev, "<= %s\n", __func__);
+
+       return IRQ_RETVAL(rtn);
+}
+
+static loff_t mxc_iim_llseek(struct file *filp, loff_t off, int whence)
+{
+       loff_t newpos;
+
+       dev_dbg(iim_data->dev, "=> %s\n", __func__);
+       dev_dbg(iim_data->dev, "off: %lld, whence: %d\n", off, whence);
+
+       if (off < iim_data->bank_start ||
+               off > iim_data->bank_end) {
+               dev_dbg(iim_data->dev, "invalid seek offset: %lld\n", off);
+               goto invald_arg_out;
+       }
+
+       switch (whence) {
+       case 0: /* SEEK_SET */
+               newpos = off;
+               break;
+       case 1: /* SEEK_CUR */
+               newpos = filp->f_pos + off;
+               break;
+       case 2: /* SEEK_END */
+               newpos = iim_data->bank_end + off;
+               break;
+       default: /* can't happen */
+               return -EINVAL;
+
+       }
+
+       if (newpos < 0)
+               return -EINVAL;
+       filp->f_pos = newpos;
+
+       dev_dbg(iim_data->dev, "newpos: %lld\n", newpos);
+
+       dev_dbg(iim_data->dev, "<= %s\n", __func__);
+
+       return newpos;
+invald_arg_out:
+       return -EINVAL;
+}
+
+static ssize_t mxc_iim_read(struct file *filp, char __user *buf,
+                       size_t count, loff_t *f_pos)
+{
+       s32 bank;
+       s8 row, fuse_val;
+       ssize_t retval = 0;
+
+       dev_dbg(iim_data->dev, "=> %s\n", __func__);
+       dev_dbg(iim_data->dev, "count: %d f_pos: %lld\n", count, *f_pos);
+
+       if (1 != count)
+               goto invald_arg_out;
+       if (*f_pos & 0x3)
+               goto invald_arg_out;
+       if (*f_pos < iim_data->bank_start ||
+               *f_pos > iim_data->bank_end) {
+               dev_dbg(iim_data->dev, "bank_start: 0x%08x, bank_end: 0x%08x\n",
+                       iim_data->bank_start, iim_data->bank_end);
+               goto out;
+       }
+
+       bank = (*f_pos - iim_data->bank_start) >> 10;
+       row  = ((*f_pos - iim_data->bank_start) & 0x3ff) >> 2;
+
+       dev_dbg(iim_data->dev, "Read fuse at bank:%d row:%d\n",
+               bank, row);
+       mutex_lock(&iim_data->mutex);
+       fuse_val = (s8)sense_fuse(bank, row, 0);
+       mutex_unlock(&iim_data->mutex);
+       dev_dbg(iim_data->dev, "fuses at (bank:%d, row:%d) = 0x%x\n",
+               bank, row, fuse_val);
+       if (copy_to_user(buf, &fuse_val, count)) {
+               retval = -EFAULT;
+               goto out;
+       }
+
+out:
+       retval = count;
+       dev_dbg(iim_data->dev, "<= %s\n", __func__);
+       return retval;
+invald_arg_out:
+       retval = -EINVAL;
+       return retval;
+}
+
+static ssize_t mxc_iim_write(struct file *filp, const char __user *buf,
+                       size_t count, loff_t *f_pos)
+{
+       s32 bank;
+       ulong fuse_val;
+       s8 row;
+       s8 *tmp_buf = NULL;
+       ssize_t retval = 0;
+
+       dev_dbg(iim_data->dev, "=> %s\n", __func__);
+       dev_dbg(iim_data->dev, "count: %d f_pos: %lld\n", count, *f_pos);
+
+       if (*f_pos & 0x3)
+               goto invald_arg_out;
+       if (*f_pos < iim_data->bank_start ||
+               *f_pos > iim_data->bank_end) {
+               dev_dbg(iim_data->dev, "bank_start: 0x%08x, bank_end: 0x%08x\n",
+                       iim_data->bank_start, iim_data->bank_end);
+               goto invald_arg_out;
+       }
+
+       tmp_buf = kmalloc(count + 1, GFP_KERNEL);
+       if (unlikely(!tmp_buf)) {
+               retval = -ENOMEM;
+               goto out;
+       }
+       memset(tmp_buf, 0, count + 1);
+       if (copy_from_user(tmp_buf, buf, count)) {
+               retval = -EFAULT;
+               goto out;
+       }
+
+       bank = (*f_pos - iim_data->bank_start) >> 10;
+       row  = ((*f_pos - iim_data->bank_start) & 0x3ff) >> 2;
+       if (strict_strtoul(tmp_buf, 16, &fuse_val)) {
+               dev_dbg(iim_data->dev, "Invalid value parameter: %s\n",
+                               tmp_buf);
+               goto invald_arg_out;
+       }
+       if (unlikely(fuse_val > 0xff)) {
+               dev_dbg(iim_data->dev, "Invalid value for fuse: %d\n",
+                               (unsigned int)fuse_val);
+               goto invald_arg_out;
+       }
+
+       dev_dbg(iim_data->dev, "Blowing fuse at bank:%d row:%d value:%d\n",
+                       bank, row, (int)fuse_val);
+       mutex_lock(&iim_data->mutex);
+       fuse_blow_row(bank, row, fuse_val);
+       fuse_val = sense_fuse(bank, row, 0);
+       mutex_unlock(&iim_data->mutex);
+       dev_dbg(iim_data->dev, "fuses at (bank:%d, row:%d) = 0x%x\n",
+               bank, row, (unsigned int)fuse_val);
+
+       retval = count;
+out:
+       dev_dbg(iim_data->dev, "<= %s\n", __func__);
+       return retval;
+invald_arg_out:
+       retval = -EINVAL;
+       return retval;
+}
+
+/*!
+ * MXC IIM interface - memory map function
+ * This function maps IIM registers from IIM base address.
+ *
+ * @param file      struct file *
+ * @param vma       structure vm_area_struct *
+ *
+ * @return          Return 0 on success or negative error code on error
+ */
+static int mxc_iim_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       dev_dbg(iim_data->dev, "=> %s\n", __func__);
+
+       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+       /* Remap-pfn-range will mark the range VM_IO and VM_RESERVED */
+       if (remap_pfn_range(vma,
+                           vma->vm_start,
+                           iim_data->reg_base >> PAGE_SHIFT,
+                           vma->vm_end - vma->vm_start,
+                           vma->vm_page_prot))
+               return -EAGAIN;
+
+       dev_dbg(iim_data->dev, "<= %s\n", __func__);
+
+       return 0;
+}
+
+/*!
+ * MXC IIM interface - open function
+ *
+ * @param inode             struct inode *
+ * @param filp      struct file *
+ *
+ * @return          Return 0 on success or negative error code on error
+ */
+static int mxc_iim_open(struct inode *inode, struct file *filp)
+{
+       int ret;
+
+       dev_dbg(iim_data->dev, "=> %s\n", __func__);
+
+       dev_dbg(iim_data->dev, "iim_data addr: 0x%08x\n", (u32)iim_data);
+
+       iim_data->clk = clk_get(NULL, "iim_clk");
+       if (IS_ERR(iim_data->clk)) {
+               dev_err(iim_data->dev, "No IIM clock defined\n");
+               return -ENODEV;
+       }
+       clk_enable(iim_data->clk);
+
+       iim_data->virt_base =
+               (u32)ioremap(iim_data->reg_base, iim_data->reg_size);
+
+       mxc_iim_disable_irq();
+       ret = request_irq(iim_data->irq, mxc_iim_irq, IRQF_DISABLED,
+                               iim_data->name, iim_data);
+       if (ret)
+               return ret;
+
+       dev_dbg(iim_data->dev, "<= %s\n", __func__);
+
+       return 0;
+}
+
+/*!
+ * MXC IIM interface - release function
+ *
+ * @param inode             struct inode *
+ * @param filp      struct file *
+ *
+ * @return          Return 0 on success or negative error code on error
+ */
+static int mxc_iim_release(struct inode *inode, struct file *filp)
+{
+       clk_disable(iim_data->clk);
+       clk_put(iim_data->clk);
+       free_irq(iim_data->irq, iim_data);
+       iounmap((void *)iim_data->virt_base);
+       return 0;
+}
+
+static const struct file_operations mxc_iim_fops = {
+       .read = mxc_iim_read,
+       .write = mxc_iim_write,
+       .llseek = mxc_iim_llseek,
+       .mmap = mxc_iim_mmap,
+       .open = mxc_iim_open,
+       .release = mxc_iim_release,
+};
+
+static struct miscdevice mxc_iim_miscdev = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "mxc_iim",
+       .fops = &mxc_iim_fops,
+};
+
+/*!
+ * This function is called by the driver framework to get iim base/end address
+ * and register iim misc device.
+ *
+ * @param      dev     The device structure for IIM passed in by the driver
+ *                     framework.
+ *
+ * @return      Returns 0 on success or negative error code on error
+ */
+static __devinit int mxc_iim_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int ret;
+
+       dev_dbg(iim_data->dev, "=> %s\n", __func__);
+
+       iim_data = pdev->dev.platform_data;
+       iim_data->dev = &pdev->dev;
+       iim_data->name = mxc_iim_miscdev.name;
+
+       dev_dbg(iim_data->dev, "iim_data addr: 0x%08x "
+               "bank_start: 0x%04x bank_end: 0x%04x "
+               "enable_fuse: 0x%08x disable_fuse: 0x%08x\n",
+               (u32)iim_data,
+               iim_data->bank_start, iim_data->bank_end,
+               (u32)iim_data->enable_fuse,
+               (u32)iim_data->disable_fuse);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (IS_ERR(res)) {
+               dev_err(iim_data->dev, "Unable to get IIM resource\n");
+               return -ENODEV;
+       }
+
+       iim_data->irq = platform_get_irq(pdev, 0);
+       dev_dbg(iim_data->dev, "irq number: %d\n", iim_data->irq);
+       if (!iim_data->irq) {
+               ret = -ENOMEM;
+               return ret;
+       }
+
+       iim_data->reg_base = res->start;
+       iim_data->reg_end = res->end;
+       iim_data->reg_size =
+               iim_data->reg_end - iim_data->reg_base + 1;
+
+       mutex_init(&(iim_data->mutex));
+       spin_lock_init(&(iim_data->lock));
+
+       ret = misc_register(&mxc_iim_miscdev);
+       if (ret)
+               return ret;
+
+       dev_dbg(iim_data->dev, "<= %s\n", __func__);
+
+       return 0;
+}
+
+static int __devexit mxc_iim_remove(struct platform_device *pdev)
+{
+       misc_deregister(&mxc_iim_miscdev);
+       return 0;
+}
+
+static struct platform_driver mxc_iim_driver = {
+       .driver = {
+                  .owner = THIS_MODULE,
+                  .name = "mxc_iim",
+                  },
+       .probe = mxc_iim_probe,
+       .remove = mxc_iim_remove,
+};
+
+static int __init mxc_iim_dev_init(void)
+{
+       return platform_driver_register(&mxc_iim_driver);
+}
+
+static void __exit mxc_iim_dev_cleanup(void)
+{
+       platform_driver_unregister(&mxc_iim_driver);
+}
+
+module_init(mxc_iim_dev_init);
+module_exit(mxc_iim_dev_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC IIM driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR);
diff --git a/drivers/char/mxc_iim.h b/drivers/char/mxc_iim.h
new file mode 100644 (file)
index 0000000..0a08145
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010-2011 Freescale Semiconductor, Inc.
+ *
+ * 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
+ */
+
+#ifndef __IMX_IIM_H__
+#define __IMX_IIM_H__
+
+#include <linux/mutex.h>
+#include <linux/cdev.h>
+
+/* IIM Control Registers */
+struct iim_regs {
+       u32 stat;
+       u32 statm;
+       u32 err;
+       u32 emask;
+       u32 fctl;
+       u32 ua;
+       u32 la;
+       u32 sdat;
+       u32 prev;
+       u32 srev;
+       u32 preg_p;
+       u32 scs0;
+       u32 scs1;
+       u32 scs2;
+       u32 scs3;
+};
+
+#define IIM_STAT_BUSY   (1 << 7)
+#define IIM_STAT_PRGD   (1 << 1)
+#define IIM_STAT_SNSD   (1 << 0)
+
+#define IIM_ERR_PRGE    (1 << 7)
+#define IIM_ERR_WPE     (1 << 6)
+#define IIM_ERR_OPE     (1 << 5)
+#define IIM_ERR_RPE     (1 << 4)
+#define IIM_ERR_WLRE    (1 << 3)
+#define IIM_ERR_SNSE    (1 << 2)
+#define IIM_ERR_PARITYE (1 << 1)
+
+#define IIM_PROD_REV_SH         3
+#define IIM_PROD_REV_LEN        5
+#define IIM_SREV_REV_SH         4
+#define IIM_SREV_REV_LEN        4
+#define PROD_SIGNATURE_MX51     0x1
+
+#define IIM_ERR_SHIFT       8
+#define POLL_FUSE_PRGD      (IIM_STAT_PRGD | (IIM_ERR_PRGE << IIM_ERR_SHIFT))
+#define POLL_FUSE_SNSD      (IIM_STAT_SNSD | (IIM_ERR_SNSE << IIM_ERR_SHIFT))
+
+#endif