diff options
author | Eric Miao <eric.miao@linaro.org> | 2011-08-31 17:41:43 +0800 |
---|---|---|
committer | Eric Miao <eric.miao@linaro.org> | 2011-10-14 09:56:51 +0800 |
commit | 026054f9eeb4184185e7017da8ec7865e8532405 (patch) | |
tree | 9f28995dbf72367e627401219493688147ae333a | |
parent | 6863686857febe7277791f765462cff0e55107c2 (diff) |
iim: add IIM driver support
Signed-off-by: Frank Li <Frank.Li@freescale.com>
Signed-off-by: Eric Miao <eric.miao@linaro.org>
-rw-r--r-- | arch/arm/mach-mx5/Kconfig | 2 | ||||
-rw-r--r-- | arch/arm/mach-mx5/devices-imx53.h | 4 | ||||
-rw-r--r-- | arch/arm/plat-mxc/devices/Kconfig | 3 | ||||
-rw-r--r-- | arch/arm/plat-mxc/devices/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/plat-mxc/devices/platform-imx-iim.c | 59 | ||||
-rw-r--r-- | arch/arm/plat-mxc/include/mach/devices-common.h | 8 | ||||
-rw-r--r-- | arch/arm/plat-mxc/include/mach/mx53.h | 10 | ||||
-rw-r--r-- | drivers/char/Kconfig | 6 | ||||
-rw-r--r-- | drivers/char/Makefile | 2 | ||||
-rw-r--r-- | drivers/char/mxc_iim.c | 644 | ||||
-rw-r--r-- | drivers/char/mxc_iim.h | 67 | ||||
-rw-r--r-- | include/linux/fsl_devices.h | 19 |
12 files changed, 825 insertions, 0 deletions
diff --git a/arch/arm/mach-mx5/Kconfig b/arch/arm/mach-mx5/Kconfig index d03b6d2239d..419e43ef76f 100644 --- a/arch/arm/mach-mx5/Kconfig +++ b/arch/arm/mach-mx5/Kconfig @@ -31,6 +31,7 @@ config SOC_IMX51 select ARCH_MXC_AUDMUX_V2 select ARCH_HAS_CPUFREQ select ARCH_MX5 + select IMX_HAVE_PLATFORM_IMX_IIM config SOC_IMX53 bool @@ -41,6 +42,7 @@ config SOC_IMX53 select ARCH_HAS_CPUFREQ select ARCH_MX5 select ARCH_MX53 + select IMX_HAVE_PLATFORM_IMX_IIM if ARCH_MX50_SUPPORTED #comment "i.MX50 machines:" diff --git a/arch/arm/mach-mx5/devices-imx53.h b/arch/arm/mach-mx5/devices-imx53.h index f472517c0bb..73337e9a7ad 100644 --- a/arch/arm/mach-mx5/devices-imx53.h +++ b/arch/arm/mach-mx5/devices-imx53.h @@ -44,3 +44,7 @@ extern const struct imx_imx_keypad_data imx53_imx_keypad_data; extern const struct imx_srtc_data imx53_imx_srtc_data __initconst; #define imx53_add_srtc() \ imx_add_srtc(&imx53_imx_srtc_data) + +extern const struct imx_iim_data imx53_imx_iim_data __initconst; +#define imx53_add_iim(pdata) \ + imx_add_iim(&imx53_imx_iim_data, pdata) diff --git a/arch/arm/plat-mxc/devices/Kconfig b/arch/arm/plat-mxc/devices/Kconfig index 48921429899..308119a1819 100644 --- a/arch/arm/plat-mxc/devices/Kconfig +++ b/arch/arm/plat-mxc/devices/Kconfig @@ -79,3 +79,6 @@ config IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX config IMX_HAVE_PLATFORM_SPI_IMX bool + +config IMX_HAVE_PLATFORM_IMX_IIM + bool diff --git a/arch/arm/plat-mxc/devices/Makefile b/arch/arm/plat-mxc/devices/Makefile index 08c4840302b..0b86d101363 100644 --- a/arch/arm/plat-mxc/devices/Makefile +++ b/arch/arm/plat-mxc/devices/Makefile @@ -26,3 +26,4 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_RTC) += platform-mxc_rtc.o obj-$(CONFIG_IMX_HAVE_PLATFORM_MXC_W1) += platform-mxc_w1.o obj-$(CONFIG_IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX) += platform-sdhci-esdhc-imx.o obj-$(CONFIG_IMX_HAVE_PLATFORM_SPI_IMX) += platform-spi_imx.o +obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_IIM) += platform-imx-iim.o diff --git a/arch/arm/plat-mxc/devices/platform-imx-iim.c b/arch/arm/plat-mxc/devices/platform-imx-iim.c new file mode 100644 index 00000000000..74043ded3c0 --- /dev/null +++ b/arch/arm/plat-mxc/devices/platform-imx-iim.c @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <mach/hardware.h> +#include <mach/devices-common.h> + +#define imx5_iim_data_entry_single(soc) \ + { \ + .iobase = soc ## _IIM_BASE_ADDR, \ + .irq = soc ## _INT_IIM, \ + } + +#ifdef CONFIG_SOC_IMX51 +const struct imx_iim_data imx51_imx_iim_data __initconst = + imx5_iim_data_entry_single(MX51); +#endif /* ifdef CONFIG_SOC_IMX51 */ + +#ifdef CONFIG_SOC_IMX53 +const struct imx_iim_data imx53_imx_iim_data __initconst = + imx5_iim_data_entry_single(MX53); +#endif /* ifdef CONFIG_SOC_IMX53 */ + +struct platform_device *__init imx_add_iim( + const struct imx_iim_data *data, + const struct mxc_iim_platform_data *pdata) +{ + struct resource res[] = { + { + .start = data->iobase, + .end = data->iobase + SZ_16 - 1, + .flags = IORESOURCE_MEM, + }, { + .start = data->irq, + .end = data->irq, + .flags = IORESOURCE_IRQ, + }, + }; + + return imx_add_platform_device("mxc_iim", 0, + res, ARRAY_SIZE(res), pdata, sizeof(*pdata)); +} + diff --git a/arch/arm/plat-mxc/include/mach/devices-common.h b/arch/arm/plat-mxc/include/mach/devices-common.h index e4fcaee790d..37364db52fa 100644 --- a/arch/arm/plat-mxc/include/mach/devices-common.h +++ b/arch/arm/plat-mxc/include/mach/devices-common.h @@ -308,3 +308,11 @@ struct imx_srtc_data { }; struct platform_device *__init imx_add_srtc( const struct imx_srtc_data *data); + +struct imx_iim_data { + resource_size_t iobase; + resource_size_t irq; +}; +struct platform_device *__init imx_add_iim( + const struct imx_iim_data *data, + const struct mxc_iim_platform_data *pdata); diff --git a/arch/arm/plat-mxc/include/mach/mx53.h b/arch/arm/plat-mxc/include/mach/mx53.h index 5e3c3236ebf..2c4344740e1 100644 --- a/arch/arm/plat-mxc/include/mach/mx53.h +++ b/arch/arm/plat-mxc/include/mach/mx53.h @@ -337,4 +337,14 @@ #define MX53_INT_GPIO7_LOW 107 #define MX53_INT_GPIO7_HIGH 108 +/*! + * IIM bank info + */ +#define MXC_IIM_MX51_BANK_START_ADDR 0x0800 +#define MXC_IIM_MX51_BANK_END_ADDR 0x147c +#define MXC_IIM_MX53_BANK_START_ADDR 0x0800 +#define MXC_IIM_MX53_BANK_END_ADDR 0x183c +#define MXC_IIM_MX53_BANK_AREA_1_OFFSET 0xc00 +#define MXC_IIM_MX53_MAC_ADDR_OFFSET 0x24 + #endif /* ifndef __MACH_MX53_H__ */ diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 423fd56bf61..f43b6a50192 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -608,6 +608,12 @@ config RAMOOPS This enables panic and oops messages to be logged to a circular buffer in RAM where it can be read back at some later point. +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. + config MSM_SMD_PKT bool "Enable device interface for some SMD packet ports" default n diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 32762ba769c..279a6cef4cd 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -61,6 +61,8 @@ 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 index 00000000000..4d1429a95e8 --- /dev/null +++ b/drivers/char/mxc_iim.c @@ -0,0 +1,644 @@ +/* + * Copyright (C) 2009-2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#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 + + /* Clear the status bits and error bits */ + __raw_writel(0x3, &(iim_reg_base->stat)); + __raw_writel(0xfe, &(iim_reg_base->err)); + /* Generate interrupt */ + __raw_writel(0x3, &(iim_reg_base->statm)); + __raw_writel(0xfe, &(iim_reg_base->emask)); + + dev_dbg(iim_data->dev, "<= %s\n", __func__); +} + +static u32 sense_fuse(u32 bank, u32 row, u32 bit) +{ + u32 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(u32 bank, u32 row, u32 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(u32 bank, u32 row, u32 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); + } + __raw_writel(0x3, &(iim_reg_base->stat)); + 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) +{ + u32 bank, 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 = sense_fuse(bank, row, 0); + mutex_unlock(&iim_data->mutex); + dev_info(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) +{ + u32 bank; + ulong fuse_val; + u8 row; + u8 *tmp_buf = NULL; + loff_t file_pos = *f_pos; + ssize_t retval = 0; + + dev_dbg(iim_data->dev, "=> %s\n", __func__); + dev_dbg(iim_data->dev, "count: %d f_pos: %lld\n", count, file_pos); + + 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; + } + + if (count > 1 && 0 == file_pos) { + /* Check if file_pos and fuse val are in tmp_buf */ + if (sscanf(tmp_buf, "%x %x", + (s32 *)&file_pos, (s32 *)&fuse_val) < 0) { + dev_info(iim_data->dev, + "Invalid input string: %s\n", tmp_buf); + retval = -EINVAL; + goto out; + } + dev_dbg(iim_data->dev, + "file_pos: 0x%08x, fuse_val: 0x%08x\n", + (s32)file_pos, (s32)fuse_val); + } else if (1 == count) + fuse_val = tmp_buf[0]; + else { + dev_info(iim_data->dev, + "Invalid input: %s, count: %d, file pos: %d\n", + tmp_buf, count, (s32)file_pos); + retval = -EINVAL; + goto out; + } + + if (fuse_val > 0xff) { + dev_info(iim_data->dev, + "Invalid blow value: 0x%08x\n", (s32)fuse_val); + retval = -EINVAL; + goto out; + } + + if (file_pos & 0x3) { + dev_info(iim_data->dev, "file pos is not 4-byte aligned!\n"); + retval = -EINVAL; + goto out; + } + + if (file_pos < iim_data->bank_start || + file_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); + dev_info(iim_data->dev, + "file pos out of range: 0x%08x\n", (u32)file_pos); + retval = -EINVAL; + goto out; + } + + bank = (file_pos - iim_data->bank_start) >> 10; + row = ((file_pos - iim_data->bank_start) & 0x3ff) >> 2; + + dev_info(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_info(iim_data->dev, "fuses at (bank:%d, row:%d) = 0x%x\n", + bank, row, (unsigned int)fuse_val); + + retval = count; +out: + if (tmp_buf) + kfree(tmp_buf); + dev_dbg(iim_data->dev, "<= %s\n", __func__); + 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) +{ + 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); + + mxc_iim_disable_irq(); + + 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); + 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; + + iim_data = pdev->dev.platform_data; + iim_data->dev = &pdev->dev; + iim_data->name = mxc_iim_miscdev.name; + + dev_dbg(iim_data->dev, "=> %s\n", __func__); + + 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; + } + + ret = request_irq(iim_data->irq, mxc_iim_irq, IRQF_DISABLED, + iim_data->name, iim_data); + if (ret) + 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; + iim_data->virt_base = + (u32)ioremap(iim_data->reg_base, iim_data->reg_size); + + 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) +{ + free_irq(iim_data->irq, iim_data); + iounmap((void *)iim_data->virt_base); + 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 index 00000000000..0a0814562da --- /dev/null +++ b/drivers/char/mxc_iim.h @@ -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 diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index fffdf00f87b..3a45181b35e 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -120,6 +120,25 @@ struct fsl_spi_platform_data { u32 sysclk; }; +struct mxc_iim_platform_data { + const s8 *name; + u32 virt_base; + u32 reg_base; + u32 reg_end; + u32 reg_size; + u32 bank_start; + u32 bank_end; + u32 irq; + u32 action; + struct mutex mutex; + struct completion completion; + spinlock_t lock; + struct clk *clk; + struct device *dev; + void (*enable_fuse)(void); + void (*disable_fuse)(void); +}; + struct mpc8xx_pcmcia_ops { void(*hw_ctrl)(int slot, int enable); int(*voltage_set)(int slot, int vcc, int vpp); |