summaryrefslogtreecommitdiff
path: root/drivers/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/Kconfig23
-rw-r--r--drivers/char/Makefile6
-rw-r--r--drivers/char/hw_random/Kconfig24
-rw-r--r--drivers/char/hw_random/Makefile2
-rw-r--r--drivers/char/hw_random/fsl-rnga.c238
-rw-r--r--drivers/char/hw_random/fsl-rngc.c372
-rw-r--r--drivers/char/imx_sim.c1497
-rw-r--r--drivers/char/mxc_iim.c161
-rw-r--r--drivers/char/mxc_si4702.c1221
-rw-r--r--drivers/char/mxs_viim.c175
10 files changed, 3718 insertions, 1 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 6a06913b01d3..bf04030c9d2b 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -431,6 +431,29 @@ config SGI_MBCS
If you have an SGI Altix with an attached SABrick
say Y or M here, otherwise say N.
+config FM_SI4702
+ tristate "SI4702 FM device driver"
+ depends on (MACH_MX31_3DS || MACH_MX35_3DS || MACH_MX37_3DS || MACH_MX51_3DS)
+ default n
+
+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 MXS_VIIM
+ tristate "MXS Virtual IIM device driver"
+ depends on ARCH_STMP3XXX
+ help
+ Support for access to MXS Virtual IIM device, most people should say N here.
+
+config IMX_SIM
+ tristate "IMX SIM support"
+ depends on (ARCH_MX51 || MACH_MX25_3DS)
+ ---help---
+ Say Y to enable the SIM driver support.
+
source "drivers/serial/Kconfig"
config UNIX98_PTYS
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 66f779ad4f4c..c711f02de8f7 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -9,6 +9,11 @@ FONTMAPFILE = cp437.uni
obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o
+obj-$(CONFIG_FM_SI4702) += mxc_si4702.o
+obj-$(CONFIG_MXC_IIM) += mxc_iim.o
+obj-$(CONFIG_MXS_VIIM) += mxs_viim.o
+obj-$(CONFIG_IMX_SIM) += imx_sim.o
+
obj-$(CONFIG_LEGACY_PTYS) += pty.o
obj-$(CONFIG_UNIX98_PTYS) += pty.o
obj-y += misc.o
@@ -97,7 +102,6 @@ obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o
obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio.o
obj-$(CONFIG_GPIO_TB0219) += tb0219.o
obj-$(CONFIG_TELCLOCK) += tlclk.o
-
obj-$(CONFIG_MWAVE) += mwave/
obj-$(CONFIG_AGP) += agp/
obj-$(CONFIG_PCMCIA) += pcmcia/
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index ce66a70184f7..108a752c9132 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -60,6 +60,30 @@ config HW_RANDOM_AMD
If unsure, say Y.
+config HW_RANDOM_FSL_RNGA
+ tristate "Freescale RNGA Random Number Generator"
+ depends on HW_RANDOM && ARCH_HAS_RNGA && !MXC_SECURITY_RNG
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on Freescale i.MX processors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fsl-rnga.
+
+ If unsure, say Y.
+
+config HW_RANDOM_FSL_RNGC
+ tristate "Freescale RNGC Random Number Generator"
+ depends on HW_RANDOM && ARCH_HAS_RNGC && !MXC_SECURITY_RNG
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on Freescale i.MX processors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called fsl-rngc.
+
+ If unsure, say Y.
+
config HW_RANDOM_GEODE
tristate "AMD Geode HW Random Number Generator support"
depends on HW_RANDOM && X86_32 && PCI
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index 676828ba8123..c6170069f998 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -7,6 +7,8 @@ rng-core-y := core.o
obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o
obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o
obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o
+obj-$(CONFIG_HW_RANDOM_FSL_RNGA) += fsl-rnga.o
+obj-$(CONFIG_HW_RANDOM_FSL_RNGC) += fsl-rngc.o
obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o
obj-$(CONFIG_HW_RANDOM_N2RNG) += n2-rng.o
n2-rng-y := n2-drv.o n2-asm.o
diff --git a/drivers/char/hw_random/fsl-rnga.c b/drivers/char/hw_random/fsl-rnga.c
new file mode 100644
index 000000000000..a5c8065604d4
--- /dev/null
+++ b/drivers/char/hw_random/fsl-rnga.c
@@ -0,0 +1,238 @@
+/*
+ * RNG driver for Freescale RNGA
+ *
+ * Copyright 2008-2009 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
+ */
+
+/*
+ * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
+ * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for the AMD 768 Random Number Generator (RNG)
+ * (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for Intel i810 Random Number Generator (RNG)
+ * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
+ * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+
+/* RNGA Registers */
+#define RNGA_CONTROL 0x00
+#define RNGA_STATUS 0x04
+#define RNGA_ENTROPY 0x08
+#define RNGA_OUTPUT_FIFO 0x0c
+#define RNGA_MODE 0x10
+#define RNGA_VERIFICATION_CONTROL 0x14
+#define RNGA_OSC_CONTROL_COUNTER 0x18
+#define RNGA_OSC1_COUNTER 0x1c
+#define RNGA_OSC2_COUNTER 0x20
+#define RNGA_OSC_COUNTER_STATUS 0x24
+
+/* RNGA Registers Range */
+#define RNG_ADDR_RANGE 0x28
+
+/* RNGA Control Register */
+#define RNGA_CONTROL_SLEEP 0x00000010
+#define RNGA_CONTROL_CLEAR_INT 0x00000008
+#define RNGA_CONTROL_MASK_INTS 0x00000004
+#define RNGA_CONTROL_HIGH_ASSURANCE 0x00000002
+#define RNGA_CONTROL_GO 0x00000001
+
+#define RNGA_STATUS_LEVEL_MASK 0x0000ff00
+
+/* RNGA Status Register */
+#define RNGA_STATUS_OSC_DEAD 0x80000000
+#define RNGA_STATUS_SLEEP 0x00000010
+#define RNGA_STATUS_ERROR_INT 0x00000008
+#define RNGA_STATUS_FIFO_UNDERFLOW 0x00000004
+#define RNGA_STATUS_LAST_READ_STATUS 0x00000002
+#define RNGA_STATUS_SECURITY_VIOLATION 0x00000001
+
+static struct platform_device *rng_dev;
+
+static int fsl_rnga_data_present(struct hwrng *rng)
+{
+ int level;
+ u32 rng_base = (u32) rng->priv;
+
+ /* how many random numbers is in FIFO? [0-16] */
+ level = ((__raw_readl(rng_base + RNGA_STATUS) &
+ RNGA_STATUS_LEVEL_MASK) >> 8);
+
+ return level > 0 ? 1 : 0;
+}
+
+static int fsl_rnga_data_read(struct hwrng *rng, u32 * data)
+{
+ int err;
+ u32 ctrl, rng_base = (u32) rng->priv;
+
+ /* retrieve a random number from FIFO */
+ *data = __raw_readl(rng_base + RNGA_OUTPUT_FIFO);
+
+ /* some error while reading this random number? */
+ err = __raw_readl(rng_base + RNGA_STATUS) & RNGA_STATUS_ERROR_INT;
+
+ /* if error: clear error interrupt, but doesn't return random number */
+ if (err) {
+ dev_dbg(&rng_dev->dev, "Error while reading random number!\n");
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+ __raw_writel(ctrl | RNGA_CONTROL_CLEAR_INT,
+ rng_base + RNGA_CONTROL);
+ return 0;
+ } else
+ return 4;
+}
+
+static int fsl_rnga_init(struct hwrng *rng)
+{
+ u32 ctrl, osc, rng_base = (u32) rng->priv;
+
+ /* wake up */
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+ __raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, rng_base + RNGA_CONTROL);
+
+ /* verify if oscillator is working */
+ osc = __raw_readl(rng_base + RNGA_STATUS);
+ if (osc & RNGA_STATUS_OSC_DEAD) {
+ dev_err(&rng_dev->dev, "RNGA Oscillator is dead!\n");
+ return -ENODEV;
+ }
+
+ /* go running */
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+ __raw_writel(ctrl | RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);
+
+ return 0;
+}
+
+static void fsl_rnga_cleanup(struct hwrng *rng)
+{
+ u32 ctrl, rng_base = (u32) rng->priv;
+
+ ctrl = __raw_readl(rng_base + RNGA_CONTROL);
+
+ /* stop rnga */
+ __raw_writel(ctrl & ~RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);
+}
+
+static struct hwrng fsl_rnga = {
+ .name = "fsl-rnga",
+ .init = fsl_rnga_init,
+ .cleanup = fsl_rnga_cleanup,
+ .data_present = fsl_rnga_data_present,
+ .data_read = fsl_rnga_data_read
+};
+
+static int __init fsl_rnga_probe(struct platform_device *pdev)
+{
+ int err = -ENODEV;
+ struct clk *clk;
+ struct resource *res, *mem;
+ void __iomem *rng_base = NULL;
+
+ if (rng_dev)
+ return -EBUSY;
+
+ clk = clk_get(NULL, "rng_clk");
+
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Could not get rng_clk!\n");
+ err = PTR_ERR(clk);
+ return err;
+ }
+
+ clk_enable(clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res)
+ return -ENOENT;
+
+ mem = request_mem_region(res->start, res->end - res->start, pdev->name);
+
+ if (mem == NULL)
+ return -EBUSY;
+
+ dev_set_drvdata(&pdev->dev, mem);
+ rng_base = ioremap(res->start, res->end - res->start);
+
+ fsl_rnga.priv = (unsigned long)rng_base;
+
+ err = hwrng_register(&fsl_rnga);
+ if (err) {
+ dev_err(&pdev->dev, "FSL RNGA registering failed (%d)\n", err);
+ return err;
+ }
+
+ rng_dev = pdev;
+
+ dev_info(&pdev->dev, "FSL RNGA Registered.\n");
+
+ return 0;
+}
+
+static int __exit fsl_rnga_remove(struct platform_device *pdev)
+{
+ struct resource *mem = dev_get_drvdata(&pdev->dev);
+ void __iomem *rng_base = (void __iomem *)fsl_rnga.priv;
+
+ hwrng_unregister(&fsl_rnga);
+
+ release_resource(mem);
+
+ iounmap(rng_base);
+
+ return 0;
+}
+
+static struct platform_driver fsl_rnga_driver = {
+ .driver = {
+ .name = "fsl_rnga",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(fsl_rnga_remove),
+};
+
+static int __init mod_init(void)
+{
+ return platform_driver_probe(&fsl_rnga_driver, fsl_rnga_probe);
+}
+
+static void __exit mod_exit(void)
+{
+ platform_driver_unregister(&fsl_rnga_driver);
+}
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("H/W RNGA driver for i.MX");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/fsl-rngc.c b/drivers/char/hw_random/fsl-rngc.c
new file mode 100644
index 000000000000..9bf78e846fa0
--- /dev/null
+++ b/drivers/char/hw_random/fsl-rngc.c
@@ -0,0 +1,372 @@
+/*
+ * RNG driver for Freescale RNGC
+ *
+ * Copyright 2008-2009 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
+ */
+
+/*
+ * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
+ * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for the AMD 768 Random Number Generator (RNG)
+ * (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for Intel i810 Random Number Generator (RNG)
+ * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
+ * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/hw_random.h>
+#include <linux/io.h>
+#include <asm/hardware.h>
+
+#define RNGC_VERSION_MAJOR3 3
+
+#define RNGC_VERSION_ID 0x0000
+#define RNGC_COMMAND 0x0004
+#define RNGC_CONTROL 0x0008
+#define RNGC_STATUS 0x000C
+#define RNGC_ERROR 0x0010
+#define RNGC_FIFO 0x0014
+#define RNGC_VERIF_CTRL 0x0020
+#define RNGC_OSC_CTRL_COUNT 0x0028
+#define RNGC_OSC_COUNT 0x002C
+#define RNGC_OSC_COUNT_STATUS 0x0030
+
+#define RNGC_VERID_ZEROS_MASK 0x0f000000
+#define RNGC_VERID_RNG_TYPE_MASK 0xf0000000
+#define RNGC_VERID_RNG_TYPE_SHIFT 28
+#define RNGC_VERID_CHIP_VERSION_MASK 0x00ff0000
+#define RNGC_VERID_CHIP_VERSION_SHIFT 16
+#define RNGC_VERID_VERSION_MAJOR_MASK 0x0000ff00
+#define RNGC_VERID_VERSION_MAJOR_SHIFT 8
+#define RNGC_VERID_VERSION_MINOR_MASK 0x000000ff
+#define RNGC_VERID_VERSION_MINOR_SHIFT 0
+
+#define RNGC_CMD_ZEROS_MASK 0xffffff8c
+#define RNGC_CMD_SW_RST 0x00000040
+#define RNGC_CMD_CLR_ERR 0x00000020
+#define RNGC_CMD_CLR_INT 0x00000010
+#define RNGC_CMD_SEED 0x00000002
+#define RNGC_CMD_SELF_TEST 0x00000001
+
+#define RNGC_CTRL_ZEROS_MASK 0xfffffc8c
+#define RNGC_CTRL_CTL_ACC 0x00000200
+#define RNGC_CTRL_VERIF_MODE 0x00000100
+#define RNGC_CTRL_MASK_ERROR 0x00000040
+
+#define RNGC_CTRL_MASK_DONE 0x00000020
+#define RNGC_CTRL_AUTO_SEED 0x00000010
+#define RNGC_CTRL_FIFO_UFLOW_MASK 0x00000003
+#define RNGC_CTRL_FIFO_UFLOW_SHIFT 0
+
+#define RNGC_CTRL_FIFO_UFLOW_ZEROS_ERROR 0
+#define RNGC_CTRL_FIFO_UFLOW_ZEROS_ERROR2 1
+#define RNGC_CTRL_FIFO_UFLOW_BUS_XFR 2
+#define RNGC_CTRL_FIFO_UFLOW_ZEROS_INTR 3
+
+#define RNGC_STATUS_ST_PF_MASK 0x00c00000
+#define RNGC_STATUS_ST_PF_SHIFT 22
+#define RNGC_STATUS_ST_PF_TRNG 0x00800000
+#define RNGC_STATUS_ST_PF_PRNG 0x00400000
+#define RNGC_STATUS_ERROR 0x00010000
+#define RNGC_STATUS_FIFO_SIZE_MASK 0x0000f000
+#define RNGC_STATUS_FIFO_SIZE_SHIFT 12
+#define RNGC_STATUS_FIFO_LEVEL_MASK 0x00000f00
+#define RNGC_STATUS_FIFO_LEVEL_SHIFT 8
+#define RNGC_STATUS_NEXT_SEED_DONE 0x00000040
+#define RNGC_STATUS_SEED_DONE 0x00000020
+#define RNGC_STATUS_ST_DONE 0x00000010
+#define RNGC_STATUS_RESEED 0x00000008
+#define RNGC_STATUS_SLEEP 0x00000004
+#define RNGC_STATUS_BUSY 0x00000002
+#define RNGC_STATUS_SEC_STATE 0x00000001
+
+#define RNGC_ERROR_STATUS_ZEROS_MASK 0xffffffc0
+#define RNGC_ERROR_STATUS_BAD_KEY 0x00000040
+#define RNGC_ERROR_STATUS_RAND_ERR 0x00000020
+#define RNGC_ERROR_STATUS_FIFO_ERR 0x00000010
+#define RNGC_ERROR_STATUS_STAT_ERR 0x00000008
+#define RNGC_ERROR_STATUS_ST_ERR 0x00000004
+#define RNGC_ERROR_STATUS_OSC_ERR 0x00000002
+#define RNGC_ERROR_STATUS_LFSR_ERR 0x00000001
+
+#define RNG_ADDR_RANGE 0x34
+
+static DECLARE_COMPLETION(rng_self_testing);
+static DECLARE_COMPLETION(rng_seed_done);
+
+static struct platform_device *rng_dev;
+
+int irq_rng;
+
+static int fsl_rngc_data_present(struct hwrng *rng)
+{
+ int level;
+ u32 rngc_base = (u32) rng->priv;
+
+ /* how many random numbers are in FIFO? [0-16] */
+ level = (__raw_readl(rngc_base + RNGC_STATUS) &
+ RNGC_STATUS_FIFO_LEVEL_MASK) >> RNGC_STATUS_FIFO_LEVEL_SHIFT;
+
+ return level > 0 ? 1 : 0;
+}
+
+static int fsl_rngc_data_read(struct hwrng *rng, u32 * data)
+{
+ int err;
+ u32 rngc_base = (u32) rng->priv;
+
+ /* retrieve a random number from FIFO */
+ *data = __raw_readl(rngc_base + RNGC_FIFO);
+
+ /* is there some error while reading this random number? */
+ err = __raw_readl(rngc_base + RNGC_STATUS) & RNGC_STATUS_ERROR;
+
+ /* if error happened doesn't return random number */
+ return err ? 0 : 4;
+}
+
+static irqreturn_t rngc_irq(int irq, void *dev)
+{
+ int handled = 0;
+ u32 rngc_base = (u32) dev;
+
+ /* is the seed creation done? */
+ if (__raw_readl(rngc_base + RNGC_STATUS) & RNGC_STATUS_SEED_DONE) {
+ complete(&rng_seed_done);
+ __raw_writel(RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR,
+ rngc_base + RNGC_COMMAND);
+ handled = 1;
+ }
+
+ /* is the self test done? */
+ if (__raw_readl(rngc_base + RNGC_STATUS) & RNGC_STATUS_ST_DONE) {
+ complete(&rng_self_testing);
+ __raw_writel(RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR,
+ rngc_base + RNGC_COMMAND);
+ handled = 1;
+ }
+
+ /* is there any error? */
+ if (__raw_readl(rngc_base + RNGC_STATUS) & RNGC_STATUS_ERROR) {
+ /* clear interrupt */
+ __raw_writel(RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR,
+ rngc_base + RNGC_COMMAND);
+ handled = 1;
+ }
+
+ return handled;
+}
+
+static int fsl_rngc_init(struct hwrng *rng)
+{
+ int err;
+ u32 cmd, ctrl, osc, rngc_base = (u32) rng->priv;
+
+ INIT_COMPLETION(rng_self_testing);
+ INIT_COMPLETION(rng_seed_done);
+
+ err = __raw_readl(rngc_base + RNGC_STATUS) & RNGC_STATUS_ERROR;
+ if (err) {
+ /* is this a bad keys error ? */
+ if (__raw_readl(rngc_base + RNGC_ERROR) &
+ RNGC_ERROR_STATUS_BAD_KEY) {
+ dev_err(&rng_dev->dev, "Can't start, Bad Keys.\n");
+ return -EIO;
+ }
+ }
+
+ /* mask all interrupts, will be unmasked soon */
+ ctrl = __raw_readl(rngc_base + RNGC_CONTROL);
+ __raw_writel(ctrl | RNGC_CTRL_MASK_DONE | RNGC_CTRL_MASK_ERROR,
+ rngc_base + RNGC_CONTROL);
+
+ /* verify if oscillator is working */
+ osc = __raw_readl(rngc_base + RNGC_ERROR);
+ if (osc & RNGC_ERROR_STATUS_OSC_ERR) {
+ dev_err(&rng_dev->dev, "RNGC Oscillator is dead!\n");
+ return -EIO;
+ }
+
+ err = request_irq(irq_rng, rngc_irq, 0, "fsl_rngc", (void *)rng->priv);
+ if (err) {
+ dev_err(&rng_dev->dev, "Can't get interrupt working.\n");
+ return -EIO;
+ }
+
+ /* do self test, repeat until get success */
+ do {
+ /* clear error */
+ cmd = __raw_readl(rngc_base + RNGC_COMMAND);
+ __raw_writel(cmd | RNGC_CMD_CLR_ERR, rngc_base + RNGC_COMMAND);
+
+ /* unmask all interrupt */
+ ctrl = __raw_readl(rngc_base + RNGC_CONTROL);
+ __raw_writel(ctrl & ~(RNGC_CTRL_MASK_DONE |
+ RNGC_CTRL_MASK_ERROR), rngc_base + RNGC_CONTROL);
+
+ /* run self test */
+ cmd = __raw_readl(rngc_base + RNGC_COMMAND);
+ __raw_writel(cmd | RNGC_CMD_SELF_TEST,
+ rngc_base + RNGC_COMMAND);
+
+ wait_for_completion(&rng_self_testing);
+
+ } while (__raw_readl(rngc_base + RNGC_ERROR) &
+ RNGC_ERROR_STATUS_ST_ERR);
+
+ /* clear interrupt. Is it really necessary here? */
+ __raw_writel(RNGC_CMD_CLR_INT | RNGC_CMD_CLR_ERR,
+ rngc_base + RNGC_COMMAND);
+
+ /* create seed, repeat while there is some statistical error */
+ do {
+ /* clear error */
+ cmd = __raw_readl(rngc_base + RNGC_COMMAND);
+ __raw_writel(cmd | RNGC_CMD_CLR_ERR, rngc_base + RNGC_COMMAND);
+
+ /* seed creation */
+ cmd = __raw_readl(rngc_base + RNGC_COMMAND);
+ __raw_writel(cmd | RNGC_CMD_SEED, rngc_base + RNGC_COMMAND);
+
+ wait_for_completion(&rng_seed_done);
+
+ } while (__raw_readl(rngc_base + RNGC_ERROR) &
+ RNGC_ERROR_STATUS_STAT_ERR);
+
+ err = __raw_readl(rngc_base + RNGC_ERROR) &
+ (RNGC_ERROR_STATUS_STAT_ERR |
+ RNGC_ERROR_STATUS_RAND_ERR |
+ RNGC_ERROR_STATUS_FIFO_ERR |
+ RNGC_ERROR_STATUS_ST_ERR |
+ RNGC_ERROR_STATUS_OSC_ERR |
+ RNGC_ERROR_STATUS_LFSR_ERR);
+
+ if (err) {
+ dev_err(&rng_dev->dev, "FSL RNGC appears inoperable.\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static struct hwrng fsl_rngc = {
+ .name = "fsl-rngc",
+ .init = fsl_rngc_init,
+ .data_present = fsl_rngc_data_present,
+ .data_read = fsl_rngc_data_read
+};
+
+static int __init fsl_rngc_probe(struct platform_device *pdev)
+{
+ int err = -ENODEV;
+ struct clk *clk;
+ struct resource *res, *mem;
+ void __iomem *rngc_base = NULL;
+
+ if (rng_dev)
+ return -EBUSY;
+
+ clk = clk_get(NULL, "rng_clk");
+
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Can not get rng_clk\n");
+ err = PTR_ERR(clk);
+ return err;
+ }
+
+ clk_enable(clk);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res)
+ return -ENOENT;
+
+ mem = request_mem_region(res->start, res->end - res->start, pdev->name);
+
+ if (mem == NULL)
+ return -EBUSY;
+
+ dev_set_drvdata(&pdev->dev, mem);
+ rngc_base = ioremap(res->start, res->end - res->start);
+
+ fsl_rngc.priv = (unsigned long)rngc_base;
+
+ irq_rng = platform_get_irq(pdev, 0);
+
+ err = hwrng_register(&fsl_rngc);
+ if (err) {
+ dev_err(&pdev->dev, "FSL RNGC registering failed (%d)\n", err);
+ return err;
+ }
+
+ rng_dev = pdev;
+
+ dev_info(&pdev->dev, "FSL RNGC Registered.\n");
+
+ return 0;
+}
+
+static int __exit fsl_rngc_remove(struct platform_device *pdev)
+{
+ struct resource *mem = dev_get_drvdata(&pdev->dev);
+ void __iomem *rngc_base = (void __iomem *)fsl_rngc.priv;
+
+ hwrng_unregister(&fsl_rngc);
+
+ release_resource(mem);
+
+ iounmap(rngc_base);
+
+ return 0;
+}
+
+static struct platform_driver fsl_rngc_driver = {
+ .driver = {
+ .name = "fsl_rngc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(fsl_rngc_remove),
+};
+
+static int __init mod_init(void)
+{
+ return platform_driver_probe(&fsl_rngc_driver, fsl_rngc_probe);
+}
+
+static void __exit mod_exit(void)
+{
+ platform_driver_unregister(&fsl_rngc_driver);
+}
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("H/W RNGC driver for i.MX");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/imx_sim.c b/drivers/char/imx_sim.c
new file mode 100644
index 000000000000..4d10f11e8012
--- /dev/null
+++ b/drivers/char/imx_sim.c
@@ -0,0 +1,1497 @@
+/*
+ * Copyright 2008-2009 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
+ */
+
+/*!
+ * @file mxc_sim.c
+ *
+ * @brief Driver for Freescale IMX SIM interface
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+
+#include <linux/sched.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/clk.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mxc_sim_interface.h>
+
+#include <asm/io.h>
+#include <mach/hardware.h>
+
+#define SIM_INTERNAL_CLK 0
+#define SIM_RFU -1
+
+/* Default communication parameters: FI=372, DI=1, PI1=5V, II=50mA, WWT=10 */
+#define SIM_PARAM_DEFAULT { 0, 1, 1, 5, 1, 0, 0, 0, 10 }
+
+/* Transmit and receive buffer sizes */
+#define SIM_XMT_BUFFER_SIZE 256
+#define SIM_RCV_BUFFER_SIZE 256
+
+/* Interface character references */
+#define SIM_IFC_TXI(letter, number) (letter + number * 4)
+#define SIM_IFC_TA1 SIM_IFC_TXI(0, 0)
+#define SIM_IFC_TB1 SIM_IFC_TXI(0, 1)
+#define SIM_IFC_TC1 SIM_IFC_TXI(0, 2)
+#define SIM_IFC_TD1 SIM_IFC_TXI(0, 3)
+#define SIM_IFC_TA2 SIM_IFC_TXI(1, 0)
+#define SIM_IFC_TB2 SIM_IFC_TXI(1, 1)
+#define SIM_IFC_TC2 SIM_IFC_TXI(1, 2)
+#define SIM_IFC_TD2 SIM_IFC_TXI(1, 3)
+#define SIM_IFC_TA3 SIM_IFC_TXI(2, 0)
+#define SIM_IFC_TB3 SIM_IFC_TXI(2, 1)
+#define SIM_IFC_TC3 SIM_IFC_TXI(2, 2)
+#define SIM_IFC_TD3 SIM_IFC_TXI(2, 3)
+#define SIM_IFC_TA4 SIM_IFC_TXI(3, 0)
+#define SIM_IFC_TB4 SIM_IFC_TXI(3, 1)
+#define SIM_IFC_TC4 SIM_IFC_TXI(3, 2)
+#define SIM_IFC_TD4 SIM_IFC_TXI(3, 3)
+
+/* ATR and OPS states */
+#define SIM_STATE_REMOVED 0
+#define SIM_STATE_OPERATIONAL_IDLE 1
+#define SIM_STATE_OPERATIONAL_COMMAND 2
+#define SIM_STATE_OPERATIONAL_RESPONSE 3
+#define SIM_STATE_OPERATIONAL_STATUS1 4
+#define SIM_STATE_OPERATIONAL_STATUS2 5
+#define SIM_STATE_OPERATIONAL_PTS 6
+#define SIM_STATE_DETECTED_ATR_T0 7
+#define SIM_STATE_DETECTED_ATR_TS 8
+#define SIM_STATE_DETECTED_ATR_TXI 9
+#define SIM_STATE_DETECTED_ATR_THB 10
+#define SIM_STATE_DETECTED_ATR_TCK 11
+
+/* Definitions of the offset of the SIM hardware registers */
+#define PORT1_CNTL 0x00 /* 00 */
+#define SETUP 0x04 /* 04 */
+#define PORT1_DETECT 0x08 /* 08 */
+#define PORT1_XMT_BUF 0x0C /* 0c */
+#define PORT1_RCV_BUF 0x10 /* 10 */
+#define PORT0_CNTL 0x14 /* 14 */
+#define CNTL 0x18 /* 18 */
+#define CLK_PRESCALER 0x1C /* 1c */
+#define RCV_THRESHOLD 0x20 /* 20 */
+#define ENABLE 0x24 /* 24 */
+#define XMT_STATUS 0x28 /* 28 */
+#define RCV_STATUS 0x2C /* 2c */
+#define INT_MASK 0x30 /* 30 */
+#define PORTO_XMT_BUF 0x34 /* 34 */
+#define PORT0_RCV_BUF 0x38 /* 38 */
+#define PORT0_DETECT 0x3C /* 3c */
+#define DATA_FORMAT 0x40 /* 40 */
+#define XMT_THRESHOLD 0x44 /* 44 */
+#define GUARD_CNTL 0x48 /* 48 */
+#define OD_CONFIG 0x4C /* 4c */
+#define RESET_CNTL 0x50 /* 50 */
+#define CHAR_WAIT 0x54 /* 54 */
+#define GPCNT 0x58 /* 58 */
+#define DIVISOR 0x5C /* 5c */
+#define BWT 0x60 /* 60 */
+#define BGT 0x64 /* 64 */
+#define BWT_H 0x68 /* 68 */
+#define XMT_FIFO_STAT 0x6C /* 6c */
+#define RCV_FIFO_CNT 0x70 /* 70 */
+#define RCV_FIFO_WPTR 0x74 /* 74 */
+#define RCV_FIFO_RPTR 0x78 /* 78 */
+
+/* SIM port[0|1]_cntl register bits */
+#define SIM_PORT_CNTL_SFPD (1<<7)
+#define SIM_PORT_CNTL_3VOLT (1<<6)
+#define SIM_PORT_CNTL_SCSP (1<<5)
+#define SIM_PORT_CNTL_SCEN (1<<4)
+#define SIM_PORT_CNTL_SRST (1<<3)
+#define SIM_PORT_CNTL_STEN (1<<2)
+#define SIM_PORT_CNTL_SVEN (1<<1)
+#define SIM_PORT_CNTL_SAPD (1<<0)
+
+/* SIM od_config register bits */
+#define SIM_OD_CONFIG_OD_P1 (1<<1)
+#define SIM_OD_CONFIG_OD_P0 (1<<0)
+
+/* SIM enable register bits */
+#define SIM_ENABLE_XMTEN (1<<1)
+#define SIM_ENABLE_RCVEN (1<<0)
+
+/* SIM int_mask register bits */
+#define SIM_INT_MASK_RFEM (1<<13)
+#define SIM_INT_MASK_BGTM (1<<12)
+#define SIM_INT_MASK_BWTM (1<<11)
+#define SIM_INT_MASK_RTM (1<<10)
+#define SIM_INT_MASK_CWTM (1<<9)
+#define SIM_INT_MASK_GPCM (1<<8)
+#define SIM_INT_MASK_TDTFM (1<<7)
+#define SIM_INT_MASK_TFOM (1<<6)
+#define SIM_INT_MASK_XTM (1<<5)
+#define SIM_INT_MASK_TFEIM (1<<4)
+#define SIM_INT_MASK_ETCIM (1<<3)
+#define SIM_INT_MASK_OIM (1<<2)
+#define SIM_INT_MASK_TCIM (1<<1)
+#define SIM_INT_MASK_RIM (1<<0)
+
+/* SIM xmt_status register bits */
+#define SIM_XMT_STATUS_GPCNT (1<<8)
+#define SIM_XMT_STATUS_TDTF (1<<7)
+#define SIM_XMT_STATUS_TFO (1<<6)
+#define SIM_XMT_STATUS_TC (1<<5)
+#define SIM_XMT_STATUS_ETC (1<<4)
+#define SIM_XMT_STATUS_TFE (1<<3)
+#define SIM_XMT_STATUS_XTE (1<<0)
+
+/* SIM rcv_status register bits */
+#define SIM_RCV_STATUS_BGT (1<<11)
+#define SIM_RCV_STATUS_BWT (1<<10)
+#define SIM_RCV_STATUS_RTE (1<<9)
+#define SIM_RCV_STATUS_CWT (1<<8)
+#define SIM_RCV_STATUS_CRCOK (1<<7)
+#define SIM_RCV_STATUS_LRCOK (1<<6)
+#define SIM_RCV_STATUS_RDRF (1<<5)
+#define SIM_RCV_STATUS_RFD (1<<4)
+#define SIM_RCV_STATUS_RFE (1<<1)
+#define SIM_RCV_STATUS_OEF (1<<0)
+
+/* SIM cntl register bits */
+#define SIM_CNTL_BWTEN (1<<15)
+#define SIM_CNTL_XMT_CRC_LRC (1<<14)
+#define SIM_CNTL_CRCEN (1<<13)
+#define SIM_CNTL_LRCEN (1<<12)
+#define SIM_CNTL_CWTEN (1<<11)
+#define SIM_CNTL_SAMPLE12 (1<<4)
+#define SIM_CNTL_ONACK (1<<3)
+#define SIM_CNTL_ANACK (1<<2)
+#define SIM_CNTL_ICM (1<<1)
+#define SIM_CNTL_GPCNT_CLK_SEL(x) ((x&0x03)<<9)
+#define SIM_CNTL_GPCNT_CLK_SEL_MASK (0x03<<9)
+#define SIM_CNTL_BAUD_SEL(x) ((x&0x07)<<6)
+#define SIM_CNTL_BAUD_SEL_MASK (0x07<<6)
+
+/* SIM rcv_threshold register bits */
+#define SIM_RCV_THRESHOLD_RTH(x) ((x&0x0f)<<9)
+#define SIM_RCV_THRESHOLD_RTH_MASK (0x0f<<9)
+#define SIM_RCV_THRESHOLD_RDT(x) ((x&0x1ff)<<0)
+#define SIM_RCV_THRESHOLD_RDT_MASK (0x1ff<<0)
+
+/* SIM xmt_threshold register bits */
+#define SIM_XMT_THRESHOLD_XTH(x) ((x&0x0f)<<4)
+#define SIM_XMT_THRESHOLD_XTH_MASK (0x0f<<4)
+#define SIM_XMT_THRESHOLD_TDT(x) ((x&0x0f)<<0)
+#define SIM_XMT_THRESHOLD_TDT_MASK (0x0f<<0)
+
+/* SIM guard_cntl register bits */
+#define SIM_GUARD_CNTL_RCVR11 (1<<8)
+#define SIM_GIARD_CNTL_GETU(x) (x&0xff)
+#define SIM_GIARD_CNTL_GETU_MASK (0xff)
+
+/* SIM port[0|]_detect register bits */
+#define SIM_PORT_DETECT_SPDS (1<<3)
+#define SIM_PORT_DETECT_SPDP (1<<2)
+#define SIM_PORT_DETECT_SDI (1<<1)
+#define SIM_PORT_DETECT_SDIM (1<<0)
+
+/* END of REGS definitions */
+
+/* ATR parser data (the parser state is stored in the main device structure) */
+typedef struct {
+ uint8_t T0; /* ATR T0 */
+ uint8_t TS; /* ATR TS */
+ /* ATR TA1, TB1, TC1, TD1, TB1, ... , TD4 */
+ uint8_t TXI[16];
+ uint8_t THB[15]; /* ATR historical bytes */
+ uint8_t TCK; /* ATR checksum */
+ uint16_t ifc_valid; /* valid interface characters */
+ uint8_t ifc_current_valid; /* calid ifcs in the current batch */
+ uint8_t cnt; /* number of current batch */
+ uint8_t num_hb; /* number of historical bytes */
+} sim_atrparser_t;
+
+/* Main SIM driver structure */
+typedef struct {
+ /* card inserted = 1, ATR received = 2, card removed = 0 */
+ int present;
+ /* current ATR or OPS state */
+ int state;
+ /* current power state */
+ int power;
+ /* error code occured during transfer */
+ int errval;
+ struct clk *clk; /* Clock id */
+ uint8_t clk_flag;
+ struct resource *res; /* IO map memory */
+ void __iomem *ioaddr; /* Mapped address */
+ int ipb_irq; /* sim ipb IRQ num */
+ int dat_irq; /* sim dat IRQ num */
+ /* parser for incoming ATR stream */
+ sim_atrparser_t atrparser;
+ /* raw ATR stream received */
+ sim_atr_t atr;
+ /* communication parameters according to ATR */
+ sim_param_t param_atr;
+ /* current communication parameters */
+ sim_param_t param;
+ /* current TPDU or PTS transfer */
+ sim_xfer_t xfer;
+ /* transfer is on the way = 1, idle = 2 */
+ int xfer_ongoing;
+ /* remaining bytes to transmit for the current transfer */
+ int xmt_remaining;
+ /* transmit position */
+ int xmt_pos;
+ /* receive position / number of bytes received */
+ int rcv_count;
+ uint8_t rcv_buffer[SIM_RCV_BUFFER_SIZE];
+ uint8_t xmt_buffer[SIM_XMT_BUFFER_SIZE];
+ /* transfer completion notifier */
+ struct completion xfer_done;
+ /* async notifier for card and ATR detection */
+ struct fasync_struct *fasync;
+ /* Platform specific data */
+ struct mxc_sim_platform_data *plat_data;
+} sim_t;
+
+static int sim_param_F[] = {
+ SIM_INTERNAL_CLK, 372, 558, 744, 1116, 1488, 1860, SIM_RFU,
+ SIM_RFU, 512, 768, 1024, 1536, 2048, SIM_RFU, SIM_RFU
+};
+
+static int sim_param_D[] = {
+ SIM_RFU, 64 * 1, 64 * 2, 64 * 4, 64 * 8, 64 * 16, SIM_RFU, SIM_RFU,
+ SIM_RFU, SIM_RFU, 64 * 1 / 2, 64 * 1 / 4, 64 * 1 / 8, 64 * 1 / 16,
+ 64 * 1 / 32, 64 * 1 / 64
+};
+
+static struct miscdevice sim_dev;
+
+/* Function: sim_calc_param
+ *
+ * Description: determine register values depending on communication parameters
+ *
+ * Parameters:
+ * uint32_t fi ATR frequency multiplier index
+ * uint32_t di ATR frequency divider index
+ * uint32_t* ptr_divisor location to store divisor result
+ * uint32_t* ptr_sample12 location to store sample12 result
+ *
+ * Return Values:
+ * SIM_OK calculation finished without errors
+ * -SIM_E_PARAM_DIVISOR_RANGE calculated divisor > 255
+ * -SIM_E_PARAM_FBYD_NOTDIVBY8OR12 F/D not divisable by 12 (as required)
+ * -SIM_E_PARAM_FBYD_WITHFRACTION F/D has a remainder
+ * -SIM_E_PARAM_DI_INVALID frequency multiplyer index not supported
+ * -SIM_E_PARAM_FI_INVALID frequency divider index not supported
+ */
+
+static int sim_calc_param(uint32_t fi, uint32_t di, uint32_t *ptr_divisor,
+ uint32_t *ptr_sample12)
+{
+ int32_t errval = SIM_OK;
+ int32_t f = sim_param_F[fi];
+ int32_t d = sim_param_D[di];
+ int32_t stage2_fra = (64 * f) % d;
+ int32_t stage2_div = (64 * f) / d;
+ uint32_t sample12 = 1;
+ uint32_t divisor = 31;
+
+ pr_debug("%s entering.\n", __func__);
+ if ((f > 0) || (d > 0)) {
+ if (stage2_fra == 0) {
+ if ((stage2_div % 12) == 0) {
+ sample12 = 1;
+ divisor = stage2_div / 12;
+ } else if ((stage2_div % 8) == 0) {
+ sample12 = 0;
+ divisor = stage2_div / 8;
+ } else
+ sample12 = -1;
+ if (sample12 >= 0) {
+ if (divisor < 256) {
+ pr_debug("fi=%i", fi);
+ pr_debug("di=%i", di);
+ pr_debug("f=%i", f);
+ pr_debug("d=%i/64", d);
+ pr_debug("div=%i", stage2_div);
+ pr_debug("divisor=%i", divisor);
+ pr_debug("sample12=%i\n", sample12);
+
+ *ptr_divisor = divisor;
+ *ptr_sample12 = sample12;
+ errval = SIM_OK;
+ } else
+ errval = -SIM_E_PARAM_DIVISOR_RANGE;
+ } else
+ errval = -SIM_E_PARAM_FBYD_NOTDIVBY8OR12;
+ } else
+ errval = -SIM_E_PARAM_FBYD_WITHFRACTION;
+ } else
+ errval = -SIM_E_PARAM_FI_INVALID;
+
+ return errval;
+};
+
+/* Function: sim_set_param
+ *
+ * Description: apply communication parameters (setup devisor and sample12)
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ * sim_param_t* param pointer to communication parameters
+ *
+ * Return Values:
+ * see function sim_calc_param
+ */
+
+static int sim_set_param(sim_t *sim, sim_param_t *param)
+{
+ uint32_t divisor, sample12, reg_data;
+ int errval;
+
+ pr_debug("%s entering.\n", __func__);
+ errval = sim_calc_param(param->FI, param->DI, &divisor, &sample12);
+ if (errval == SIM_OK) {
+ __raw_writel(divisor, sim->ioaddr + DIVISOR);
+ if (sample12) {
+ reg_data = __raw_readl(sim->ioaddr + CNTL);
+ reg_data |= SIM_CNTL_SAMPLE12;
+ __raw_writel(reg_data, sim->ioaddr + CNTL);
+ } else {
+ reg_data = __raw_readl(sim->ioaddr + CNTL);
+ reg_data &= ~SIM_CNTL_SAMPLE12;
+ __raw_writel(reg_data, sim->ioaddr + CNTL);
+ }
+ }
+
+ return errval;
+};
+
+/* Function: sim_atr_received
+ *
+ * Description: this function is called whenever a valid ATR has been received.
+ * It determines the communication parameters from the ATR received and notifies
+ * the user space application with SIGIO.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_atr_received(sim_t *sim)
+{
+ sim_param_t param_default = SIM_PARAM_DEFAULT;
+ sim->param_atr = param_default;
+
+ pr_debug("%s entering.\n", __func__);
+ if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TA1))) {
+ sim->param_atr.FI = sim->atrparser.TXI[SIM_IFC_TA1] >> 4;
+ sim->param_atr.DI = sim->atrparser.TXI[SIM_IFC_TA1] & 0x0f;
+ }
+ if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TB1))) {
+ sim->param_atr.PI1 = (sim->atrparser.TXI[SIM_IFC_TB1] >> 4)
+ & 0x07;
+ sim->param_atr.II = sim->atrparser.TXI[SIM_IFC_TB1] & 0x07f;
+ }
+ if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TC1)))
+ sim->param_atr.N = sim->atrparser.TXI[SIM_IFC_TC1];
+
+ if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TD1)))
+ sim->param_atr.T = sim->atrparser.TXI[SIM_IFC_TD1] & 0x0f;
+
+ if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TB2)))
+ sim->param_atr.PI2 = sim->atrparser.TXI[SIM_IFC_TB2];
+
+ if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TC2)))
+ sim->param_atr.WWT = sim->atrparser.TXI[SIM_IFC_TC2];
+
+ if (sim->fasync)
+ kill_fasync(&sim->fasync, SIGIO, POLL_IN);
+
+};
+
+/* Function: sim_xmt_fill
+ *
+ * Description: fill the transmit FIFO until the FIFO is full or
+ * the end of the transmission has been reached.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_xmt_fill(sim_t *sim)
+{
+ uint32_t reg_data;
+ int bytesleft;
+
+ reg_data = __raw_readl(sim->ioaddr + XMT_FIFO_STAT);
+ bytesleft = 16 - ((reg_data >> 8) & 0x0f);
+
+ pr_debug("txfill: remaining=%i bytesleft=%i\n",
+ sim->xmt_remaining, bytesleft);
+ if (bytesleft > sim->xmt_remaining)
+ bytesleft = sim->xmt_remaining;
+
+ sim->xmt_remaining -= bytesleft;
+ for (; bytesleft > 0; bytesleft--) {
+ __raw_writel(sim->xmt_buffer[sim->xmt_pos],
+ sim->ioaddr + PORT1_XMT_BUF);
+ sim->xmt_pos++;
+ };
+/* FIXME: optimization - keep filling until fifo full */
+};
+
+/* Function: sim_xmt_start
+ *
+ * Description: initiate a transfer
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ * int pos position in the xfer transmit buffer
+ * int count number of bytes to be transmitted
+ */
+
+static void sim_xmt_start(sim_t *sim, int pos, int count)
+{
+ uint32_t reg_data;
+
+ pr_debug("tx\n");
+ sim->xmt_remaining = count;
+ sim->xmt_pos = pos;
+ sim_xmt_fill(sim);
+
+ if (sim->xmt_remaining) {
+ reg_data = __raw_readl(sim->ioaddr + INT_MASK);
+ reg_data &= ~SIM_INT_MASK_TDTFM;
+ __raw_writel(reg_data, sim->ioaddr + INT_MASK);
+ } else {
+ reg_data = __raw_readl(sim->ioaddr + INT_MASK);
+ reg_data &= ~SIM_INT_MASK_TCIM;
+ __raw_writel(reg_data, sim->ioaddr + INT_MASK);
+ __raw_writel(SIM_XMT_STATUS_TC | SIM_XMT_STATUS_TDTF,
+ sim->ioaddr + XMT_STATUS);
+ reg_data = __raw_readl(sim->ioaddr + ENABLE);
+ reg_data |= SIM_ENABLE_XMTEN;
+ __raw_writel(reg_data, sim->ioaddr + ENABLE);
+ }
+};
+
+/* Function: sim_atr_add
+ *
+ * Description: add a byte to the raw ATR string
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ * uint8_t data byte to be added
+ */
+
+static void sim_atr_add(sim_t *sim, uint8_t data)
+{
+ pr_debug("%s entering.\n", __func__);
+ if (sim->atr.size < SIM_ATR_LENGTH_MAX)
+ sim->atr.t[sim->atr.size++] = data;
+ else
+ printk(KERN_ERR "sim.c: ATR received is too big!\n");
+};
+
+/* Function: sim_fsm
+ *
+ * Description: main finite state machine running in ISR context.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ * uint8_t data byte received
+ */
+
+static void sim_fsm(sim_t *sim, uint16_t data)
+{
+ uint32_t temp, i = 0;
+ switch (sim->state) {
+
+ pr_debug("%s stat is %d \n", __func__, sim->state);
+ /* OPS FSM */
+
+ case SIM_STATE_OPERATIONAL_IDLE:
+ printk(KERN_INFO "data received unexpectidly (%04x)\n", data);
+ break;
+
+ case SIM_STATE_OPERATIONAL_COMMAND:
+ if (data == sim->xmt_buffer[1]) {
+ if (sim->xfer.rcv_length) {
+ sim->state = SIM_STATE_OPERATIONAL_RESPONSE;
+ } else {
+ sim->state = SIM_STATE_OPERATIONAL_STATUS1;
+ if (sim->xfer.xmt_length > 5)
+ sim_xmt_start(sim, 5,
+ sim->xfer.xmt_length - 5);
+ };
+ } else if (((data & 0xf0) == 0x60) | ((data & 0xf0) == 0x90)) {
+ sim->xfer.sw1 = data;
+ sim->state = SIM_STATE_OPERATIONAL_STATUS2;
+ } else {
+ sim->errval = -SIM_E_NACK;
+ complete(&sim->xfer_done);
+ };
+ break;
+
+ case SIM_STATE_OPERATIONAL_RESPONSE:
+ sim->rcv_buffer[sim->rcv_count] = data;
+ sim->rcv_count++;
+ if (sim->rcv_count == sim->xfer.rcv_length)
+ sim->state = SIM_STATE_OPERATIONAL_STATUS1;
+ break;
+
+ case SIM_STATE_OPERATIONAL_STATUS1:
+ sim->xfer.sw1 = data;
+ sim->state = SIM_STATE_OPERATIONAL_STATUS2;
+ break;
+
+ case SIM_STATE_OPERATIONAL_STATUS2:
+ sim->xfer.sw2 = data;
+ sim->state = SIM_STATE_OPERATIONAL_IDLE;
+ complete(&sim->xfer_done);
+ break;
+
+ case SIM_STATE_OPERATIONAL_PTS:
+ sim->rcv_buffer[sim->rcv_count] = data;
+ sim->rcv_count++;
+ if (sim->rcv_count == sim->xfer.rcv_length)
+ sim->state = SIM_STATE_OPERATIONAL_IDLE;
+ break;
+
+ /* ATR FSM */
+
+ case SIM_STATE_DETECTED_ATR_T0:
+ sim_atr_add(sim, data);
+ pr_debug("T0 %02x\n", data);
+ sim->atrparser.T0 = data;
+ sim->state = SIM_STATE_DETECTED_ATR_TS;
+ break;
+
+ case SIM_STATE_DETECTED_ATR_TS:
+ sim_atr_add(sim, data);
+ pr_debug("TS %02x\n", data);
+ sim->atrparser.TS = data;
+ if (data & 0xf0) {
+ sim->atrparser.ifc_current_valid = (data >> 4) & 0x0f;
+ sim->atrparser.num_hb = data & 0x0f;
+ sim->atrparser.ifc_valid = 0;
+ sim->state = SIM_STATE_DETECTED_ATR_TXI;
+ sim->atrparser.cnt = 0;
+ } else {
+ goto sim_fsm_atr_thb;
+ };
+ break;
+
+ case SIM_STATE_DETECTED_ATR_TXI:
+ sim_atr_add(sim, data);
+ i = ffs(sim->atrparser.ifc_current_valid) - 1;
+ pr_debug("T%c%i %02x\n", 'A' + i, sim->atrparser.cnt + 1, data);
+ sim->atrparser.TXI[SIM_IFC_TXI(i, sim->atrparser.cnt)] = data;
+ sim->atrparser.ifc_valid |= 1 << SIM_IFC_TXI(i,
+ sim->atrparser.
+ cnt);
+ sim->atrparser.ifc_current_valid &= ~(1 << i);
+
+ if (sim->atrparser.ifc_current_valid == 0) {
+ if (i == 3) {
+ sim->atrparser.ifc_current_valid = (data >> 4)
+ & 0x0f;
+ sim->atrparser.cnt++;
+
+ if (sim->atrparser.cnt >= 4) {
+ /* error */
+ printk(KERN_ERR "ERROR !\n");
+ break;
+ };
+
+ if (sim->atrparser.ifc_current_valid == 0)
+ goto sim_fsm_atr_thb;
+ } else {
+sim_fsm_atr_thb:
+ if (sim->atrparser.num_hb) {
+ sim->state = SIM_STATE_DETECTED_ATR_THB;
+ sim->atrparser.cnt = 0;
+ } else {
+ goto sim_fsm_atr_tck;
+ };
+ };
+ };
+ break;
+
+ case SIM_STATE_DETECTED_ATR_THB:
+ sim_atr_add(sim, data);
+ pr_debug("THB%i %02x\n", i, data);
+ sim->atrparser.THB[sim->atrparser.cnt] = data;
+ sim->atrparser.cnt++;
+
+ if (sim->atrparser.cnt == sim->atrparser.num_hb) {
+sim_fsm_atr_tck:
+ i = sim->atrparser.ifc_valid & (1 << (SIM_IFC_TD1));
+ temp = sim->atrparser.TXI[SIM_IFC_TD1] & 0x0f;
+ if ((i && temp) == SIM_PROTOCOL_T1)
+ sim->state = SIM_STATE_DETECTED_ATR_TCK;
+ else
+ goto sim_fsm_atr_received;
+ };
+ break;
+
+ case SIM_STATE_DETECTED_ATR_TCK:
+ sim_atr_add(sim, data);
+ /* checksum not required for T=0 */
+ sim->atrparser.TCK = data;
+sim_fsm_atr_received:
+ sim->state = SIM_STATE_OPERATIONAL_IDLE;
+ sim->present = SIM_PRESENT_OPERATIONAL;
+ sim_atr_received(sim);
+ break;
+ };
+};
+
+/* Function: sim_irq_handler
+ *
+ * Description: interrupt service routine.
+ *
+ * Parameters:
+ * int irq interrupt number
+ * void *dev_id pointer to SIM device handler
+ *
+ * Return values:
+ * IRQ_HANDLED OS specific
+ */
+
+static irqreturn_t sim_irq_handler(int irq, void *dev_id)
+{
+ uint32_t reg_data, reg_data0, reg_data1;
+
+ sim_t *sim = (sim_t *) dev_id;
+
+ pr_debug("%s entering\n", __func__);
+
+ reg_data0 = __raw_readl(sim->ioaddr + XMT_STATUS);
+ reg_data1 = __raw_readl(sim->ioaddr + INT_MASK);
+ if ((reg_data0 & SIM_XMT_STATUS_TC)
+ && (!(reg_data1 & SIM_INT_MASK_TCIM))) {
+ pr_debug("TC_IRQ\n");
+ __raw_writel(SIM_XMT_STATUS_TC, sim->ioaddr + XMT_STATUS);
+ reg_data = __raw_readl(sim->ioaddr + INT_MASK);
+ reg_data |= SIM_INT_MASK_TCIM;
+ __raw_writel(reg_data, sim->ioaddr + INT_MASK);
+ reg_data = __raw_readl(sim->ioaddr + ENABLE);
+ reg_data &= ~SIM_ENABLE_XMTEN;
+ __raw_writel(reg_data, sim->ioaddr + ENABLE);
+ };
+
+ reg_data0 = __raw_readl(sim->ioaddr + XMT_STATUS);
+ reg_data1 = __raw_readl(sim->ioaddr + INT_MASK);
+ if ((reg_data0 & SIM_XMT_STATUS_TDTF)
+ && (!(reg_data1 & SIM_INT_MASK_TDTFM))) {
+ pr_debug("TDTF_IRQ\n");
+ __raw_writel(SIM_XMT_STATUS_TDTF, sim->ioaddr + XMT_STATUS);
+ sim_xmt_fill(sim);
+
+ if (sim->xmt_remaining == 0) {
+ __raw_writel(SIM_XMT_STATUS_TC,
+ sim->ioaddr + XMT_STATUS);
+ reg_data = __raw_readl(sim->ioaddr + INT_MASK);
+ reg_data &= ~SIM_INT_MASK_TCIM;
+ reg_data |= SIM_INT_MASK_TDTFM;
+ __raw_writel(reg_data, sim->ioaddr + INT_MASK);
+ };
+ };
+
+ reg_data0 = __raw_readl(sim->ioaddr + RCV_STATUS);
+ reg_data1 = __raw_readl(sim->ioaddr + INT_MASK);
+ if ((reg_data0 & SIM_RCV_STATUS_RDRF)
+ && (!(reg_data1 & SIM_INT_MASK_RIM))) {
+ pr_debug("%s RDRF_IRQ\n", __func__);
+ __raw_writel(SIM_RCV_STATUS_RDRF, sim->ioaddr + RCV_STATUS);
+
+ while (__raw_readl(sim->ioaddr + RCV_FIFO_CNT)) {
+ uint32_t data;
+ data = __raw_readl(sim->ioaddr + PORT1_RCV_BUF);
+ pr_debug("RX = %02x state = %i\n", data, sim->state);
+ if (data & 0x700) {
+ if (sim->xfer_ongoing) {
+ /* error */
+ printk(KERN_ERR "ERROR !\n");
+ return IRQ_HANDLED;
+ };
+ } else
+ sim_fsm(sim, data);
+ };
+ };
+
+ reg_data0 = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ if (reg_data0 & SIM_PORT_DETECT_SDI) {
+ pr_debug("%s PD_IRQ\n", __func__);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data |= SIM_PORT_DETECT_SDI;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+
+ reg_data0 = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ if (reg_data0 & SIM_PORT_DETECT_SPDP) {
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data &= ~SIM_PORT_DETECT_SPDS;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+
+ if (sim->present != SIM_PRESENT_REMOVED) {
+ pr_debug("Removed sim card\n");
+ sim->present = SIM_PRESENT_REMOVED;
+ sim->state = SIM_STATE_REMOVED;
+
+ if (sim->fasync)
+ kill_fasync(&sim->fasync,
+ SIGIO, POLL_IN);
+ };
+ } else {
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data |= SIM_PORT_DETECT_SPDS;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+
+ if (sim->present == SIM_PRESENT_REMOVED) {
+ pr_debug("Inserted sim card\n");
+ sim->state = SIM_STATE_DETECTED_ATR_T0;
+ sim->present = SIM_PRESENT_DETECTED;
+
+ if (sim->fasync)
+ kill_fasync(&sim->fasync,
+ SIGIO, POLL_IN);
+ };
+ };
+ };
+
+ return IRQ_HANDLED;
+};
+
+/* Function: sim_power_on
+ *
+ * Description: run the power on sequence
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_power_on(sim_t *sim)
+{
+ uint32_t reg_data;
+
+ /* power on sequence */
+ pr_debug("%s Powering on the sim port.\n", __func__);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data |= SIM_PORT_CNTL_SVEN;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ msleep(10);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data |= SIM_PORT_CNTL_SCEN;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ msleep(10);
+ reg_data = SIM_RCV_THRESHOLD_RTH(0) | SIM_RCV_THRESHOLD_RDT(1);
+ __raw_writel(reg_data, sim->ioaddr + RCV_THRESHOLD);
+ __raw_writel(SIM_RCV_STATUS_RDRF, sim->ioaddr + RCV_STATUS);
+ reg_data = __raw_readl(sim->ioaddr + INT_MASK);
+ reg_data &= ~SIM_INT_MASK_RIM;
+ __raw_writel(reg_data, sim->ioaddr + INT_MASK);
+ __raw_writel(31, sim->ioaddr + DIVISOR);
+ reg_data = __raw_readl(sim->ioaddr + CNTL);
+ reg_data |= SIM_CNTL_SAMPLE12;
+ __raw_writel(reg_data, sim->ioaddr + CNTL);
+ reg_data = __raw_readl(sim->ioaddr + ENABLE);
+ reg_data |= SIM_ENABLE_RCVEN;
+ __raw_writel(reg_data, sim->ioaddr + ENABLE);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data |= SIM_PORT_CNTL_SRST;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ pr_debug("%s port0_ctl is 0x%x.\n", __func__,
+ __raw_readl(sim->ioaddr + PORT0_CNTL));
+ sim->power = SIM_POWER_ON;
+};
+
+/* Function: sim_power_off
+ *
+ * Description: run the power off sequence
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_power_off(sim_t *sim)
+{
+ uint32_t reg_data;
+
+ pr_debug("%s entering.\n", __func__);
+ /* sim_power_off sequence */
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data &= ~SIM_PORT_CNTL_SCEN;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ reg_data = __raw_readl(sim->ioaddr + ENABLE);
+ reg_data &= ~SIM_ENABLE_RCVEN;
+ __raw_writel(reg_data, sim->ioaddr + ENABLE);
+ reg_data = __raw_readl(sim->ioaddr + INT_MASK);
+ reg_data |= SIM_INT_MASK_RIM;
+ __raw_writel(reg_data, sim->ioaddr + INT_MASK);
+ __raw_writel(0, sim->ioaddr + RCV_THRESHOLD);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data &= ~SIM_PORT_CNTL_SRST;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data &= ~SIM_PORT_CNTL_SVEN;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ sim->power = SIM_POWER_OFF;
+};
+
+/* Function: sim_start
+ *
+ * Description: ramp up the SIM interface
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_start(sim_t *sim)
+{
+ uint32_t reg_data, clk_rate, clk_div = 0;
+
+ pr_debug("%s entering.\n", __func__);
+ /* Configuring SIM for Operation */
+ reg_data = SIM_XMT_THRESHOLD_XTH(0) | SIM_XMT_THRESHOLD_TDT(4);
+ __raw_writel(reg_data, sim->ioaddr + XMT_THRESHOLD);
+ __raw_writel(0, sim->ioaddr + SETUP);
+ /* ~ 4 MHz */
+ clk_rate = clk_get_rate(sim->clk);
+ clk_div = clk_rate / sim->plat_data->clk_rate;
+ if (clk_rate % sim->plat_data->clk_rate)
+ clk_div++;
+ pr_debug("%s prescaler is 0x%x.\n", __func__, clk_div);
+ __raw_writel(clk_div, sim->ioaddr + CLK_PRESCALER);
+
+ reg_data = SIM_CNTL_GPCNT_CLK_SEL(0) | SIM_CNTL_BAUD_SEL(7)
+ | SIM_CNTL_SAMPLE12 | SIM_CNTL_ANACK | SIM_CNTL_ICM;
+ __raw_writel(reg_data, sim->ioaddr + CNTL);
+ __raw_writel(31, sim->ioaddr + DIVISOR);
+ reg_data = __raw_readl(sim->ioaddr + OD_CONFIG);
+ reg_data |= SIM_OD_CONFIG_OD_P0;
+ __raw_writel(reg_data, sim->ioaddr + OD_CONFIG);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data |= SIM_PORT_CNTL_3VOLT | SIM_PORT_CNTL_STEN;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+
+ /* presense detect */
+ pr_debug("%s p0_det is 0x%x \n", __func__,
+ __raw_readl(sim->ioaddr + PORT0_DETECT));
+ if (__raw_readl(sim->ioaddr + PORT0_DETECT) & SIM_PORT_DETECT_SPDP) {
+ pr_debug("%s card removed \n", __func__);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data &= ~SIM_PORT_DETECT_SPDS;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+ sim->present = SIM_PRESENT_REMOVED;
+ sim->state = SIM_STATE_REMOVED;
+ } else {
+ pr_debug("%s card inserted \n", __func__);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data |= SIM_PORT_DETECT_SPDS;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+ sim->present = SIM_PRESENT_DETECTED;
+ sim->state = SIM_STATE_DETECTED_ATR_T0;
+ };
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data |= SIM_PORT_DETECT_SDI;
+ reg_data &= ~SIM_PORT_DETECT_SDIM;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+
+ /*
+ * Since there is no PD0 layout on MX51, assume
+ * that there is a SIM card in slot defaulty.
+ * */
+ if (0 == (sim->plat_data->detect)) {
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ reg_data |= SIM_PORT_DETECT_SPDS;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
+ sim->present = SIM_PRESENT_DETECTED;
+ sim->state = SIM_STATE_DETECTED_ATR_T0;
+ }
+
+ if (sim->present == SIM_PRESENT_DETECTED)
+ sim_power_on(sim);
+
+};
+
+/* Function: sim_stop
+ *
+ * Description: shut down the SIM interface
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_stop(sim_t *sim)
+{
+ pr_debug("%s entering.\n", __func__);
+ __raw_writel(0, sim->ioaddr + SETUP);
+ __raw_writel(0, sim->ioaddr + ENABLE);
+ __raw_writel(0, sim->ioaddr + PORT0_CNTL);
+ __raw_writel(0x06, sim->ioaddr + CNTL);
+ __raw_writel(0, sim->ioaddr + CLK_PRESCALER);
+ __raw_writel(0, sim->ioaddr + SETUP);
+ __raw_writel(0, sim->ioaddr + OD_CONFIG);
+ __raw_writel(0, sim->ioaddr + XMT_THRESHOLD);
+ __raw_writel(0xb8, sim->ioaddr + XMT_STATUS);
+ __raw_writel(4, sim->ioaddr + RESET_CNTL);
+ mdelay(1);
+};
+
+/* Function: sim_data_reset
+ *
+ * Description: reset a SIM structure to default values
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_data_reset(sim_t *sim)
+{
+ sim_param_t param_default = SIM_PARAM_DEFAULT;
+ sim->present = SIM_PRESENT_REMOVED;
+ sim->state = SIM_STATE_REMOVED;
+ sim->power = SIM_POWER_OFF;
+ sim->errval = SIM_OK;
+ memset(&sim->atrparser, 0, sizeof(sim->atrparser));
+ memset(&sim->atr, 0, sizeof(sim->atr));
+ sim->param_atr = param_default;
+ memset(&sim->param, 0, sizeof(sim->param));
+ memset(&sim->xfer, 0, sizeof(sim->xfer));
+ sim->xfer_ongoing = 0;
+ sim->xmt_remaining = 0;
+ sim->xmt_pos = 0;
+ sim->rcv_count = 0;
+ memset(sim->rcv_buffer, 0, SIM_RCV_BUFFER_SIZE);
+ memset(sim->xmt_buffer, 0, SIM_XMT_BUFFER_SIZE);
+};
+
+/* Function: sim_cold_reset
+ *
+ * Description: cold reset the SIM interface, including card
+ * power down and interface hardware reset.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_cold_reset(sim_t *sim)
+{
+ pr_debug("%s entering.\n", __func__);
+ if (sim->present != SIM_PRESENT_REMOVED) {
+ sim_power_off(sim);
+ sim_stop(sim);
+ sim_data_reset(sim);
+ sim->state = SIM_STATE_DETECTED_ATR_T0;
+ sim->present = SIM_PRESENT_DETECTED;
+ msleep(50);
+ sim_start(sim);
+ sim_power_on(sim);
+ };
+};
+
+/* Function: sim_warm_reset
+ *
+ * Description: warm reset the SIM interface: just invoke the
+ * reset signal and reset the SIM structure for the interface.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static void sim_warm_reset(sim_t *sim)
+{
+ uint32_t reg_data;
+
+ pr_debug("%s entering.\n", __func__);
+ if (sim->present != SIM_PRESENT_REMOVED) {
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data |= SIM_PORT_CNTL_SRST;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ sim_data_reset(sim);
+ msleep(50);
+ reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
+ reg_data &= ~SIM_PORT_CNTL_SRST;
+ __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
+ };
+};
+
+/* Function: sim_card_lock
+ *
+ * Description: physically lock the SIM card.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static int sim_card_lock(sim_t *sim)
+{
+ int errval;
+
+ pr_debug("%s entering.\n", __func__);
+ /* place holder for true physcial locking */
+ if (sim->present != SIM_PRESENT_REMOVED)
+ errval = SIM_OK;
+ else
+ errval = -SIM_E_NOCARD;
+ return errval;
+};
+
+/* Function: sim_card_eject
+ *
+ * Description: physically unlock and eject the SIM card.
+ *
+ * Parameters:
+ * sim_t* sim pointer to SIM device handler
+ */
+
+static int sim_card_eject(sim_t *sim)
+{
+ int errval;
+
+ pr_debug("%s entering.\n", __func__);
+ /* place holder for true physcial locking */
+ if (sim->present != SIM_PRESENT_REMOVED)
+ errval = SIM_OK;
+ else
+ errval = -SIM_E_NOCARD;
+ return errval;
+};
+
+/* Function: sim_ioctl
+ *
+ * Description: handle ioctl calls
+ *
+ * Parameters: OS specific
+ */
+
+static int sim_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret, errval = SIM_OK;
+ unsigned long timeout;
+
+ sim_t *sim = (sim_t *) file->private_data;
+
+ pr_debug("%s entering.\n", __func__);
+ switch (cmd) {
+ pr_debug("ioctl cmd %d is issued...\n", cmd);
+
+ case SIM_IOCTL_GET_ATR:
+ if (sim->present != SIM_PRESENT_OPERATIONAL) {
+ errval = -SIM_E_NOCARD;
+ break;
+ };
+ ret = copy_to_user((sim_atr_t *) arg, &sim->atr,
+ sizeof(sim_atr_t));
+ if (ret)
+ errval = -SIM_E_ACCESS;
+ break;
+
+ case SIM_IOCTL_GET_PARAM_ATR:
+ if (sim->present != SIM_PRESENT_OPERATIONAL) {
+ errval = -SIM_E_NOCARD;
+ break;
+ };
+ ret = copy_to_user((sim_param_t *) arg, &sim->param_atr,
+ sizeof(sim_param_t));
+ if (ret)
+ errval = -SIM_E_ACCESS;
+ break;
+
+ case SIM_IOCTL_GET_PARAM:
+ ret = copy_to_user((sim_param_t *) arg, &sim->param,
+ sizeof(sim_param_t));
+ if (ret)
+ errval = -SIM_E_ACCESS;
+ break;
+
+ case SIM_IOCTL_SET_PARAM:
+ ret = copy_from_user(&sim->param, (sim_param_t *) arg,
+ sizeof(sim_param_t));
+ if (ret)
+ errval = -SIM_E_ACCESS;
+ else
+ errval = sim_set_param(sim, &sim->param);
+ break;
+
+ case SIM_IOCTL_POWER_ON:
+ if (sim->power == SIM_POWER_ON) {
+ errval = -SIM_E_POWERED_ON;
+ break;
+ };
+ sim_power_on(sim);
+ break;
+
+ case SIM_IOCTL_POWER_OFF:
+ if (sim->power == SIM_POWER_OFF) {
+ errval = -SIM_E_POWERED_OFF;
+ break;
+ };
+ sim_power_off(sim);
+ break;
+
+ case SIM_IOCTL_COLD_RESET:
+ if (sim->power == SIM_POWER_OFF) {
+ errval = -SIM_E_POWERED_OFF;
+ break;
+ };
+ sim_cold_reset(sim);
+ break;
+
+ case SIM_IOCTL_WARM_RESET:
+ sim_warm_reset(sim);
+ if (sim->power == SIM_POWER_OFF) {
+ errval = -SIM_E_POWERED_OFF;
+ break;
+ };
+ break;
+
+ case SIM_IOCTL_XFER:
+ if (sim->present != SIM_PRESENT_OPERATIONAL) {
+ errval = -SIM_E_NOCARD;
+ break;
+ };
+
+ ret = copy_from_user(&sim->xfer, (sim_xfer_t *) arg,
+ sizeof(sim_xfer_t));
+ if (ret) {
+ errval = -SIM_E_ACCESS;
+ break;
+ };
+
+ ret = copy_from_user(sim->xmt_buffer, sim->xfer.xmt_buffer,
+ sim->xfer.xmt_length);
+ if (ret) {
+ errval = -SIM_E_ACCESS;
+ break;
+ };
+
+ sim->rcv_count = 0;
+ sim->xfer.sw1 = 0;
+ sim->xfer.sw2 = 0;
+
+ if (sim->xfer.type == SIM_XFER_TYPE_TPDU) {
+ if (sim->xfer.xmt_length < 5) {
+ errval = -SIM_E_TPDUSHORT;
+ break;
+ }
+ sim->state = SIM_STATE_OPERATIONAL_COMMAND;
+ } else if (sim->xfer.type == SIM_XFER_TYPE_PTS) {
+ if (sim->xfer.xmt_length == 0) {
+ errval = -SIM_E_PTSEMPTY;
+ break;
+ }
+ sim->state = SIM_STATE_OPERATIONAL_PTS;
+ } else {
+ errval = -SIM_E_INVALIDXFERTYPE;
+ break;
+ };
+
+ if (sim->xfer.xmt_length > SIM_XMT_BUFFER_SIZE) {
+ errval = -SIM_E_INVALIDXMTLENGTH;
+ break;
+ };
+
+ if (sim->xfer.rcv_length > SIM_XMT_BUFFER_SIZE) {
+ errval = -SIM_E_INVALIDRCVLENGTH;
+ break;
+ };
+
+ sim->errval = 0;
+ sim->xfer_ongoing = 1;
+ init_completion(&sim->xfer_done);
+ sim_xmt_start(sim, 0, 5);
+ timeout =
+ wait_for_completion_interruptible_timeout(&sim->xfer_done,
+ sim->xfer.
+ timeout);
+ sim->xfer_ongoing = 0;
+
+ if (sim->errval) {
+ errval = sim->errval;
+ break;
+ };
+
+ if (timeout == 0) {
+ errval = -SIM_E_TIMEOUT;
+ break;
+ }
+
+ ret = copy_to_user(sim->xfer.rcv_buffer, sim->rcv_buffer,
+ sim->xfer.rcv_length);
+ if (ret) {
+ errval = -SIM_E_ACCESS;
+ break;
+ };
+
+ ret = copy_to_user((sim_xfer_t *) arg, &sim->xfer,
+ sizeof(sim_xfer_t));
+ if (ret)
+ errval = -SIM_E_ACCESS;
+ break;
+
+ case SIM_IOCTL_GET_PRESENSE:
+ if (put_user(sim->present, (int *)arg))
+ errval = -SIM_E_ACCESS;
+ break;
+
+ case SIM_IOCTL_CARD_LOCK:
+ errval = sim_card_lock(sim);
+ break;
+
+ case SIM_IOCTL_CARD_EJECT:
+ errval = sim_card_eject(sim);
+ break;
+
+ };
+
+ return errval;
+};
+
+/* Function: sim_fasync
+ *
+ * Description: async handler
+ *
+ * Parameters: OS specific
+ */
+
+static int sim_fasync(int fd, struct file *file, int mode)
+{
+ sim_t *sim = (sim_t *) file->private_data;
+ pr_debug("%s entering.\n", __func__);
+ return fasync_helper(fd, file, mode, &sim->fasync);
+}
+
+/* Function: sim_open
+ *
+ * Description: ramp up interface when being opened
+ *
+ * Parameters: OS specific
+ */
+
+static int sim_open(struct inode *inode, struct file *file)
+{
+ int errval = SIM_OK;
+
+ sim_t *sim = dev_get_drvdata(sim_dev.parent);
+ file->private_data = sim;
+
+ pr_debug("%s entering.\n", __func__);
+ if (!sim->ioaddr) {
+ errval = -ENOMEM;
+ return errval;
+ }
+
+ if (!(sim->clk_flag)) {
+ pr_debug("\n%s enable the clock\n", __func__);
+ clk_enable(sim->clk);
+ sim->clk_flag = 1;
+ }
+
+ sim_start(sim);
+
+ return errval;
+};
+
+/* Function: sim_release
+ *
+ * Description: shut down interface when being closed
+ *
+ * Parameters: OS specific
+ */
+
+static int sim_release(struct inode *inode, struct file *file)
+{
+ uint32_t reg_data;
+
+ sim_t *sim = (sim_t *) file->private_data;
+
+ pr_debug("%s entering.\n", __func__);
+ if (sim->clk_flag) {
+ pr_debug("\n%s disable the clock\n", __func__);
+ clk_disable(sim->clk);
+ sim->clk_flag = 0;
+ }
+
+ /* disable presense detection */
+ reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
+ __raw_writel(reg_data | SIM_PORT_DETECT_SDIM,
+ sim->ioaddr + PORT0_DETECT);
+
+ if (sim->present != SIM_PRESENT_REMOVED) {
+ sim_power_off(sim);
+ if (sim->fasync)
+ kill_fasync(&sim->fasync, SIGIO, POLL_IN);
+ };
+
+ sim_stop(sim);
+
+ sim_fasync(-1, file, 0);
+
+ pr_debug("exit\n");
+ return 0;
+};
+
+static const struct file_operations sim_fops = {
+ .open = sim_open,
+ .ioctl = sim_ioctl,
+ .fasync = sim_fasync,
+ .release = sim_release
+};
+
+static struct miscdevice sim_dev = {
+ MISC_DYNAMIC_MINOR,
+ "mxc_sim",
+ &sim_fops
+};
+
+/*****************************************************************************\
+ * *
+ * Driver init/exit *
+ * *
+\*****************************************************************************/
+
+static int sim_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct mxc_sim_platform_data *sim_plat = pdev->dev.platform_data;
+
+ sim_t *sim = kzalloc(sizeof(sim_t), GFP_KERNEL);
+
+ if (sim == 0) {
+ ret = -ENOMEM;
+ printk(KERN_ERR "Can't get the MEMORY\n");
+ return ret;
+ };
+
+ BUG_ON(pdev == NULL);
+
+ sim->plat_data = sim_plat;
+ sim->clk_flag = 0;
+
+ sim->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!sim->res) {
+ ret = -ENOMEM;
+ printk(KERN_ERR "Can't get the MEMORY\n");
+ goto out;
+ }
+
+ /* request the sim clk and sim_serial_clk */
+ sim->clk = clk_get(NULL, sim->plat_data->clock_sim);
+ if (IS_ERR(sim->clk)) {
+ ret = PTR_ERR(sim->clk);
+ printk(KERN_ERR "Get CLK ERROR !\n");
+ goto out;
+ }
+ pr_debug("sim clock:%lu\n", clk_get_rate(sim->clk));
+
+ sim->ipb_irq = platform_get_irq(pdev, 0);
+ sim->dat_irq = platform_get_irq(pdev, 1);
+ if (!(sim->ipb_irq | sim->dat_irq)) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ if (!request_mem_region(sim->res->start,
+ sim->res->end -
+ sim->res->start + 1, pdev->name)) {
+ printk(KERN_ERR "request_mem_region failed\n");
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ sim->ioaddr = (void *)ioremap(sim->res->start, sim->res->end -
+ sim->res->start + 1);
+ if (sim->ipb_irq)
+ ret = request_irq(sim->ipb_irq, sim_irq_handler,
+ 0, "mxc_sim_ipb", sim);
+ if (sim->dat_irq)
+ ret |= request_irq(sim->dat_irq, sim_irq_handler,
+ 0, "mxc_sim_dat", sim);
+
+ if (ret) {
+ printk(KERN_ERR "Can't get the irq\n");
+ goto out2;
+ };
+
+ platform_set_drvdata(pdev, sim);
+ sim_dev.parent = &(pdev->dev);
+
+ misc_register(&sim_dev);
+
+ return ret;
+out2:
+ if (sim->ipb_irq)
+ free_irq(sim->ipb_irq, sim);
+ if (sim->dat_irq)
+ free_irq(sim->dat_irq, sim);
+ release_mem_region(sim->res->start,
+ sim->res->end - sim->res->start + 1);
+out1:
+ clk_put(sim->clk);
+out:
+ kfree(sim);
+ return ret;
+}
+
+static int sim_remove(struct platform_device *pdev)
+{
+ sim_t *sim = platform_get_drvdata(pdev);
+
+ clk_put(sim->clk);
+
+ if (sim->ipb_irq)
+ free_irq(sim->ipb_irq, sim);
+ if (sim->dat_irq)
+ free_irq(sim->dat_irq, sim);
+
+ iounmap(sim->ioaddr);
+
+ kfree(sim);
+ release_mem_region(sim->res->start,
+ sim->res->end - sim->res->start + 1);
+
+ misc_deregister(&sim_dev);
+ return 0;
+}
+
+static struct platform_driver sim_driver = {
+ .driver = {
+ .name = "mxc_sim",
+ },
+ .probe = sim_probe,
+ .remove = sim_remove,
+ .suspend = NULL,
+ .resume = NULL,
+};
+
+static int __init sim_drv_init(void)
+{
+ return platform_driver_register(&sim_driver);
+}
+
+static void __exit sim_drv_exit(void)
+{
+ platform_driver_unregister(&sim_driver);
+}
+
+module_init(sim_drv_init);
+module_exit(sim_drv_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC SIM Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/mxc_iim.c b/drivers/char/mxc_iim.c
new file mode 100644
index 000000000000..b407d34759a9
--- /dev/null
+++ b/drivers/char/mxc_iim.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2009 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/platform_device.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/clk.h>
+#include <linux/miscdevice.h>
+
+static unsigned long iim_reg_base, iim_reg_end, iim_reg_size;
+static struct clk *iim_clk;
+static struct device *iim_dev;
+
+/*!
+ * MXC IIM interface - memory map function
+ * This function maps 4KB 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)
+{
+ 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_reg_base >> PAGE_SHIFT,
+ iim_reg_size,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ 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)
+{
+ iim_clk = clk_get(NULL, "iim_clk");
+ if (IS_ERR(iim_clk)) {
+ dev_err(iim_dev, "No IIM clock defined\n");
+ return -ENODEV;
+ }
+ clk_enable(iim_clk);
+
+ 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_clk);
+ clk_put(iim_clk);
+ return 0;
+}
+
+static const struct file_operations mxc_iim_fops = {
+ .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 int mxc_iim_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+
+ iim_dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (IS_ERR(res)) {
+ dev_err(iim_dev, "Unable to get IIM resource\n");
+ return -ENODEV;
+ }
+
+ iim_reg_base = res->start;
+ iim_reg_end = res->end;
+ iim_reg_size = iim_reg_end - iim_reg_base + 1;
+
+ ret = misc_register(&mxc_iim_miscdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int 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_si4702.c b/drivers/char/mxc_si4702.c
new file mode 100644
index 000000000000..d9fcce6cb439
--- /dev/null
+++ b/drivers/char/mxc_si4702.c
@@ -0,0 +1,1221 @@
+/*
+ * linux/drivers/char/mxc_si4702.c
+ *
+ * Copyright 2008-2009 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/cdev.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/mxc_si4702.h>
+#include <asm/uaccess.h>
+#include <mach/hardware.h>
+
+#define SI4702_DEV_NAME "si4702"
+#define DEV_MAJOR 0 /* this could be module param */
+#define DEV_MINOR 0
+#define DEV_BASE_MINOR 0
+#define DEV_MINOR_COUNT 256
+#define SI4702_I2C_ADDR 0x10 /* 7bits I2C address */
+#define DELAY_WAIT 0xffff /* loop_counter max value */
+/* register define */
+#define SI4702_DEVICEID 0x00
+#define SI4702_CHIPID 0x01
+#define SI4702_POWERCFG 0x02
+#define SI4702_CHANNEL 0x03
+#define SI4702_SYSCONFIG1 0x04
+#define SI4702_SYSCONFIG2 0x05
+#define SI4702_SYSCONFIG3 0x06
+#define SI4702_TEST1 0x07
+#define SI4702_TEST2 0x08
+#define SI4702_B00TCONFIG 0x09
+#define SI4702_STATUSRSSI 0x0A
+#define SI4702_READCHAN 0x0B
+#define SI4702_REG_NUM 0x10
+#define SI4702_REG_BYTE (SI4702_REG_NUM * 2)
+#define SI4702_DEVICE_ID 0x1242
+#define SI4702_RW_REG_NUM (SI4702_STATUSRSSI - SI4702_POWERCFG)
+#define SI4702_RW_OFFSET \
+ (SI4702_REG_NUM - SI4702_STATUSRSSI + SI4702_POWERCFG)
+
+#define SI4702_SPACE_MASK 0x0030
+#define SI4702_SPACE_200K 0x0
+#define SI4702_SPACE_100K 0x10
+#define SI4702_SPACE_50K 0x20
+
+#define SI4702_BAND_MASK 0x00c0
+#define SI4702_BAND_LSB 6
+
+#define SI4702_SEEKTH_MASK 0xff00
+#define SI4702_SEEKTH_LSB 8
+
+#define SI4702_SNR_MASK 0x00f0
+#define SI4702_SNR_LSB 4
+
+#define SI4702_CNT_MASK 0x000f
+#define SI4702_CNT_LSB 0
+
+#define SI4702_VOL_MASK 0x000f
+#define SI4702_VOL_LSB 0
+
+#define SI4702_CHAN_MASK 0x03ff
+#define SI4702_TUNE_BIT 0x8000
+#define SI4702_STC_BIT 0x4000
+#define SI4702_DMUTE_BIT 0x4000
+#define SI4702_SEEKUP_BIT 0x0200
+#define SI4702_SEEK_BIT 0x0100
+#define SI4702_SF_BIT 0x2000
+#define SI4702_ENABLE_BIT 0x0001
+#define SI4702_DISABLE_BIT 0x0040
+
+enum {
+ BAND_USA = 0,
+ BAND_JAP_W,
+ BAND_JAP
+};
+
+struct si4702_info {
+ int min_band;
+ int max_band;
+ int space;
+ int volume;
+ int channel;
+ int mute;
+};
+
+struct si4702_drvdata {
+ struct regulator *vio;
+ struct regulator *vdd;
+ struct class *radio_class;
+ struct si4702_info info;
+ /*by default, dev major is zero, and it's alloc dynamicaly. */
+ int major;
+ int minor;
+ struct cdev *cdev;
+ int count; /* open count */
+ struct i2c_client *client;
+ unsigned char reg_rw_buf[SI4702_REG_BYTE];
+ struct mxc_fm_platform_data *plat_data;
+};
+
+static struct si4702_drvdata *si4702_drvdata;
+
+DEFINE_SPINLOCK(count_lock);
+
+#ifdef DEBUG
+static void si4702_dump_reg(void)
+{
+ int i, j;
+ unsigned char *reg_rw_buf;
+
+ if (NULL == si4702_drvdata)
+ return;
+
+ reg_rw_buf = si4702_drvdata->reg_rw_buf;
+
+ for (i = 0; i < 10; i++) {
+ j = i * 2 + 12;
+ pr_debug("reg[%02d] = %04x\n", i,
+ ((reg_rw_buf[j] << 8) & 0xFF00) +
+ (reg_rw_buf[j + 1] & 0x00FF));
+ }
+ for (; i < 16; i++) {
+ j = (i - 10) * 2;
+ pr_debug("reg[%02d] = %04x\n", i,
+ ((reg_rw_buf[j] << 8) & 0xFF00) +
+ (reg_rw_buf[j + 1] & 0x00FF));
+ }
+}
+#else
+static void si4702_dump_reg(void)
+{
+}
+#endif /* DEBUG */
+
+/*
+ *check the si4702 spec for the read/write concequence.
+ *
+ *0 2 A F0 A F
+ *-------------------------------
+ * buf:0 2 A F
+ */
+#define REG_to_BUF(reg) (((reg >= 0) && (reg < SI4702_STATUSRSSI))?\
+ (reg - SI4702_STATUSRSSI + SI4702_REG_NUM):\
+ ((reg >= SI4702_STATUSRSSI) && (reg < SI4702_REG_NUM))?\
+ (reg - SI4702_STATUSRSSI) : -1)
+
+static int si4702_read_reg(const int reg, u16 *value)
+{
+ int ret, index;
+ unsigned char *reg_rw_buf;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ reg_rw_buf = si4702_drvdata->reg_rw_buf;
+
+ index = REG_to_BUF(reg);
+
+ if (-1 == index)
+ return -1;
+
+ ret =
+ i2c_master_recv(si4702_drvdata->client, reg_rw_buf,
+ SI4702_REG_BYTE);
+
+ *value = (reg_rw_buf[index * 2] << 8) & 0xFF00;
+ *value |= reg_rw_buf[index * 2 + 1] & 0x00FF;
+
+ return ret < 0 ? ret : 0;
+}
+
+static int si4702_write_reg(const int reg, const u16 value)
+{
+ int index, ret;
+ unsigned char *reg_rw_buf;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ reg_rw_buf = si4702_drvdata->reg_rw_buf;
+
+ index = REG_to_BUF(reg);
+
+ if (-1 == index)
+ return -1;
+
+ reg_rw_buf[index * 2] = (value & 0xFF00) >> 8;
+ reg_rw_buf[index * 2 + 1] = value & 0x00FF;
+
+ ret = i2c_master_send(si4702_drvdata->client,
+ &reg_rw_buf[SI4702_RW_OFFSET * 2],
+ (SI4702_STATUSRSSI - SI4702_POWERCFG) * 2);
+ return ret < 0 ? ret : 0;
+}
+
+static void si4702_gpio_get(void)
+{
+ if (NULL == si4702_drvdata)
+ return;
+
+ si4702_drvdata->plat_data->gpio_get();
+}
+
+static void si4702_gpio_put(void)
+{
+ if (NULL == si4702_drvdata)
+ return;
+
+ si4702_drvdata->plat_data->gpio_put();
+}
+
+static void si4702_reset(void)
+{
+ if (NULL == si4702_drvdata)
+ return;
+
+ si4702_drvdata->plat_data->reset();
+}
+
+static void si4702_clock_en(int flag)
+{
+ if (NULL == si4702_drvdata)
+ return;
+
+ si4702_drvdata->plat_data->clock_ctl(flag);
+}
+
+static int si4702_id_detect(struct i2c_client *client)
+{
+ int ret, index;
+ unsigned int ID = 0;
+ unsigned char reg_rw_buf[SI4702_REG_BYTE];
+
+ si4702_gpio_get();
+ si4702_reset();
+ si4702_clock_en(1);
+
+ ret = i2c_master_recv(client, (char *)reg_rw_buf, SI4702_REG_BYTE);
+
+ si4702_gpio_put();
+
+ if (ret < 0)
+ return ret;
+
+ index = REG_to_BUF(SI4702_DEVICEID);
+ if (index < 0)
+ return index;
+
+ ID = (reg_rw_buf[index * 2] << 8) & 0xFF00;
+ ID |= reg_rw_buf[index * 2 + 1] & 0x00FF;
+
+ return ID;
+}
+
+/* valid args 50/100/200 */
+static int si4702_set_space(int space)
+{
+ u16 reg;
+ int ret;
+ struct si4702_info *info;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ ret = si4702_read_reg(SI4702_SYSCONFIG2, &reg);
+ if (ret == -1)
+ return ret;
+
+ reg &= ~SI4702_SPACE_MASK;
+ switch (space) {
+ case 50:
+ reg |= SI4702_SPACE_50K;
+ break;
+ case 100:
+ reg |= SI4702_SPACE_100K;
+ break;
+ case 200:
+ ret |= SI4702_SPACE_200K;
+ break;
+ default:
+ return -1;
+ }
+
+ ret = si4702_write_reg(SI4702_SYSCONFIG2, reg);
+ if (ret == -1)
+ return ret;
+
+ info = &si4702_drvdata->info;
+ info->space = space;
+ return 0;
+}
+
+static int si4702_set_band_range(int band)
+{
+ u16 reg;
+ int ret, band_min, band_max;
+ struct si4702_info *info;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ switch (band) {
+ case BAND_USA:
+ band_min = 87500;
+ band_max = 108000;
+ break;
+ case BAND_JAP_W:
+ band_min = 76000;
+ band_max = 108000;
+ break;
+ case BAND_JAP:
+ band_min = 76000;
+ band_max = 90000;
+ break;
+ default:
+ return -1;
+ }
+
+ ret = si4702_read_reg(SI4702_SYSCONFIG2, &reg);
+ if (ret == -1)
+ return ret;
+
+ reg = (reg & ~SI4702_BAND_MASK)
+ | ((band << SI4702_BAND_LSB) & SI4702_BAND_MASK);
+ ret = si4702_write_reg(SI4702_SYSCONFIG2, reg);
+ if (ret == -1)
+ return ret;
+
+ info = &si4702_drvdata->info;
+ info->min_band = band_min;
+ info->max_band = band_max;
+ return 0;
+}
+
+static int si4702_set_seekth(u8 seekth)
+{
+ u16 reg;
+ int ret;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ ret = si4702_read_reg(SI4702_SYSCONFIG2, &reg);
+ if (ret == -1)
+ return ret;
+
+ reg =
+ (reg & ~SI4702_SEEKTH_MASK) | ((seekth << SI4702_SEEKTH_LSB) &
+ SI4702_SEEKTH_MASK);
+ ret = si4702_write_reg(SI4702_SYSCONFIG2, reg);
+ if (ret == -1)
+ return ret;
+
+ return 0;
+}
+
+static int si4702_set_sksnr(u8 sksnr)
+{
+ u16 reg;
+ int ret;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ ret = si4702_read_reg(SI4702_SYSCONFIG3, &reg);
+ if (ret == -1)
+ return ret;
+
+ reg =
+ (reg & ~SI4702_SNR_MASK) | ((sksnr << SI4702_SNR_LSB) &
+ SI4702_SNR_MASK);
+ ret = si4702_write_reg(SI4702_SYSCONFIG3, reg);
+ if (ret == -1)
+ return ret;
+
+ return 0;
+}
+
+static int si4702_set_skcnt(u8 skcnt)
+{
+ u16 reg;
+ int ret;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ ret = si4702_read_reg(SI4702_SYSCONFIG3, &reg);
+ if (ret == -1)
+ return ret;
+
+ reg = (reg & ~SI4702_CNT_MASK) | (skcnt & SI4702_CNT_MASK);
+ ret = si4702_write_reg(SI4702_SYSCONFIG3, reg);
+ if (ret == -1)
+ return ret;
+
+ return 0;
+}
+
+static int si4702_set_vol(int vol)
+{
+ u16 reg;
+ int ret;
+ struct si4702_info *info;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ ret = si4702_read_reg(SI4702_SYSCONFIG2, &reg);
+ if (ret == -1)
+ return ret;
+
+ reg = (reg & ~SI4702_VOL_MASK) | (vol & SI4702_VOL_MASK);
+ ret = si4702_write_reg(SI4702_SYSCONFIG2, reg);
+ if (ret == -1)
+ return ret;
+
+ info = &si4702_drvdata->info;
+ info->volume = vol;
+
+ return 0;
+}
+
+static u8 si4702_channel_select(u32 freq)
+{
+ u16 loop_counter = 0;
+ s16 channel;
+ u16 si4702_reg_data;
+ u8 error_ind = 0;
+ struct i2c_client *client;
+ struct si4702_info *info;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ info = &si4702_drvdata->info;
+ client = si4702_drvdata->client;
+
+ dev_info(&client->dev, "Input frequnce is %d\n", freq);
+ if (freq < 76000 || freq > 108000) {
+ dev_err(&client->dev, "Input frequnce is invalid\n");
+ return -1;
+ }
+ /* convert freq to channel */
+ channel = (freq - info->min_band) / info->space;
+
+ si4702_reg_data = SI4702_TUNE_BIT | (channel & SI4702_CHAN_MASK);
+ /* set channel */
+ error_ind = si4702_write_reg(SI4702_CHANNEL, si4702_reg_data);
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to set channel\n");
+ return -1;
+ }
+ dev_info(&client->dev, "Set channel to %d\n", channel);
+
+ /* wait for STC == 1 */
+ do {
+ error_ind =
+ si4702_read_reg(SI4702_STATUSRSSI, &si4702_reg_data);
+
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to read setted STC\n");
+ return -1;
+ }
+ if ((si4702_reg_data & SI4702_STC_BIT) != 0)
+ break;
+ } while (++loop_counter < DELAY_WAIT);
+
+ /* check loop_counter */
+ if (loop_counter >= DELAY_WAIT) {
+ dev_err(&client->dev, "Can't wait for STC bit set");
+ return -1;
+ }
+ dev_info(&client->dev, "loop counter %d\n", loop_counter);
+
+ loop_counter = 0;
+ /* clear tune bit */
+ error_ind = si4702_write_reg(SI4702_CHANNEL, 0);
+
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to set stop tune\n");
+ return -1;
+ }
+
+ /* wait for STC == 0 */
+ do {
+ error_ind =
+ si4702_read_reg(SI4702_STATUSRSSI, &si4702_reg_data);
+
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to set read STC\n");
+ return -1;
+ }
+ if ((si4702_reg_data & SI4702_STC_BIT) == 0)
+ break;
+ } while (++loop_counter < DELAY_WAIT);
+
+ /* check loop_counter */
+ if (loop_counter >= DELAY_WAIT) {
+ dev_err(&client->dev, "Can't wait for STC bit clean");
+ return -1;
+ }
+ dev_info(&client->dev, "loop counter %d\n", loop_counter);
+
+ /* read RSSI */
+ error_ind = si4702_read_reg(SI4702_READCHAN, &si4702_reg_data);
+
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to read RSSI\n");
+ return -1;
+ }
+
+ channel = si4702_reg_data & SI4702_CHAN_MASK;
+ dev_info(&client->dev, "seek finish: channel(%d)\n", channel);
+
+ return 0;
+}
+
+static s32 si4702_channel_seek(s16 dir)
+{
+ u16 loop_counter = 0;
+ u16 si4702_reg_data, reg_power_cfg;
+ u8 error_ind = 0;
+ u32 channel, freq;
+ struct i2c_client *client;
+ struct si4702_info *info;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ info = &si4702_drvdata->info;
+ client = si4702_drvdata->client;
+
+ error_ind = si4702_read_reg(SI4702_POWERCFG, &reg_power_cfg);
+
+ if (info->mute) {
+ /* check disable mute */
+ reg_power_cfg &= ~SI4702_DMUTE_BIT;
+ } else {
+ reg_power_cfg |= SI4702_DMUTE_BIT;
+ }
+
+ if (dir) {
+ reg_power_cfg |= SI4702_SEEKUP_BIT;
+ } else {
+ reg_power_cfg &= ~SI4702_SEEKUP_BIT;
+ }
+ /* start seek */
+ reg_power_cfg |= SI4702_SEEK_BIT;
+ error_ind = si4702_write_reg(SI4702_POWERCFG, reg_power_cfg);
+
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to set seek start bit\n");
+ return -1;
+ }
+
+ /* wait STC == 1 */
+ do {
+ error_ind =
+ si4702_read_reg(SI4702_STATUSRSSI, &si4702_reg_data);
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to read STC bit\n");
+ return -1;
+ }
+
+ if ((si4702_reg_data & SI4702_STC_BIT) != 0)
+ break;
+ } while (++loop_counter < DELAY_WAIT);
+
+ /* clear seek bit */
+ reg_power_cfg &= ~SI4702_SEEK_BIT;
+ error_ind = si4702_write_reg(SI4702_POWERCFG, reg_power_cfg);
+ if (error_ind) {
+ dev_err(&client->dev, "Failed to stop seek\n");
+ return -1;
+ }
+
+ if (loop_counter >= DELAY_WAIT) {
+ dev_err(&client->dev, "Can't wait for STC bit set\n");
+ return -1;
+ }
+
+ /* check whether SF==1 (seek failed bit) */
+ if ((si4702_reg_data & SI4702_SF_BIT) != 0) {
+ dev_err(&client->dev, "Failed to seek any channel\n");
+ return -1;
+ }
+
+ loop_counter = 0;
+ /* wait STC == 0 */
+ do {
+ error_ind =
+ si4702_read_reg(SI4702_STATUSRSSI, &si4702_reg_data);
+
+ if (error_ind) {
+ dev_err(&client->dev,
+ "Failed to wait STC bit to clear\n");
+ return -1;
+ }
+ if ((si4702_reg_data & SI4702_STC_BIT) == 0)
+ break;
+ } while (++loop_counter < DELAY_WAIT);
+
+ /* check loop_counter */
+ if (loop_counter >= DELAY_WAIT) {
+ dev_err(&client->dev, "Can't wait for STC bit clean");
+ return -1;
+ }
+
+ error_ind = si4702_read_reg(SI4702_READCHAN, &si4702_reg_data);
+
+ if (error_ind) {
+ dev_err(&client->dev, "I2C simulate failed\n");
+ return -1;
+ }
+
+ channel = si4702_reg_data & SI4702_CHAN_MASK;
+ freq = channel * info->space + info->min_band;
+ dev_err(&client->dev,
+ "seek finish: channel(%d), freq(%dKHz)\n", channel, freq);
+
+ return 0;
+}
+
+static int si4702_startup(void)
+{
+ u16 magic = 0, id;
+ struct i2c_client *client;
+ struct mxc_fm_platform_data *data;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ if (si4702_drvdata->vio)
+ regulator_enable(si4702_drvdata->vio);
+ if (si4702_drvdata->vdd)
+ regulator_enable(si4702_drvdata->vdd);
+ data = si4702_drvdata->plat_data;
+ client = si4702_drvdata->client;
+
+ /* read prior to write, otherwise write op will fail */
+ si4702_read_reg(SI4702_DEVICEID, &id);
+ dev_err(&client->dev, "si4702: DEVICEID: 0x%x\n", id);
+
+ si4702_clock_en(1);
+ msleep(100);
+
+ /* disable mute, stereo, seek down, powerup */
+ si4702_write_reg(SI4702_POWERCFG, SI4702_DMUTE_BIT | SI4702_ENABLE_BIT);
+ msleep(500);
+ si4702_read_reg(SI4702_TEST1, &magic);
+ if (magic != 0x3C04)
+ dev_err(&client->dev, "magic number 0x%x.\n", magic);
+ /* close tune, set channel to 0 */
+ si4702_write_reg(SI4702_CHANNEL, 0);
+ /* disable interrupt, disable GPIO */
+ si4702_write_reg(SI4702_SYSCONFIG1, 0);
+ /* set volume to middle level */
+ si4702_set_vol(0xf);
+
+ si4702_set_space(data->space);
+ si4702_set_band_range(data->band);
+ si4702_set_seekth(data->seekth);
+ si4702_set_skcnt(data->skcnt);
+ si4702_set_sksnr(data->sksnr);
+
+ return 0;
+}
+
+static void si4702_shutdown(void)
+{
+ if (NULL == si4702_drvdata)
+ return;
+
+ si4702_write_reg(SI4702_POWERCFG, SI4702_DMUTE_BIT |
+ SI4702_ENABLE_BIT | SI4702_DISABLE_BIT);
+ msleep(100);
+ si4702_clock_en(0);
+
+ if (si4702_drvdata->vdd)
+ regulator_disable(si4702_drvdata->vdd);
+ if (si4702_drvdata->vio)
+ regulator_disable(si4702_drvdata->vio);
+}
+
+enum {
+ FM_STARTUP = 0,
+ FM_SHUTDOWN,
+ FM_RESET,
+ FM_VOLUP,
+ FM_VOLDOWN,
+ FM_SEEK_UP,
+ FM_SEEK_DOWN,
+ FM_MUTEON,
+ FM_MUTEDIS,
+ FM_SEL,
+ FM_SEEKTH,
+ FM_DL,
+ FM_CTL_MAX
+};
+
+static const char *const fm_control[FM_CTL_MAX] = {
+ [FM_STARTUP] = "start",
+ [FM_SHUTDOWN] = "halt",
+ [FM_RESET] = "reset",
+ [FM_VOLUP] = "volup",
+ [FM_VOLDOWN] = "voldown",
+ [FM_SEEK_UP] = "seeku",
+ [FM_SEEK_DOWN] = "seekd",
+ [FM_MUTEON] = "mute",
+ [FM_MUTEDIS] = "muted",
+ [FM_SEL] = "select",
+ [FM_SEEKTH] = "seekth",
+ [FM_DL] = "delay"
+};
+
+static int cmd(unsigned int index, int arg)
+{
+ struct i2c_client *client;
+ struct mxc_fm_platform_data *plat_data;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ client = si4702_drvdata->client;
+ plat_data = si4702_drvdata->plat_data;
+
+ switch (index) {
+ case FM_SHUTDOWN:
+ dev_err(&client->dev, "FM_SHUTDOWN\n");
+ si4702_shutdown();
+ break;
+ case FM_STARTUP:
+ dev_err(&client->dev, "FM_STARTUP\n");
+ si4702_reset();
+ si4702_startup();
+ break;
+ case FM_RESET:
+ dev_err(&client->dev, "FM_RESET\n");
+ si4702_reset();
+ break;
+ case FM_SEEK_DOWN:
+ dev_err(&client->dev, "SEEK DOWN\n");
+ si4702_channel_seek(0);
+ break;
+ case FM_SEEK_UP:
+ dev_err(&client->dev, "SEEK UP\n");
+ si4702_channel_seek(1);
+ break;
+ case FM_SEL:
+ dev_err(&client->dev, "select %d\n", arg * 100);
+ si4702_channel_select(arg * 100);
+ break;
+ case FM_SEEKTH:
+ dev_err(&client->dev, "seekth = %d\n", arg);
+ si4702_set_seekth(arg);
+ break;
+ case FM_DL:
+ dev_err(&client->dev, "delay = %d\n", arg);
+ break;
+ default:
+ dev_err(&client->dev, "error command\n");
+ break;
+ }
+ return 0;
+}
+
+static ssize_t si4702_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct si4702_drvdata *drv_data = dev_get_drvdata(dev);
+ u16 device_id;
+
+ dev_err(&(drv_data->client->dev), "si4702 show\n");
+ si4702_read_reg(SI4702_DEVICEID, &device_id);
+ pr_info("device id %x\n", device_id);
+ si4702_dump_reg();
+ return 0;
+}
+
+static ssize_t si4702_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int state = 0;
+ const char *const *s;
+ char *p = NULL;
+ int error;
+ int len, arg = 0;
+ struct si4702_drvdata *drv_data = dev_get_drvdata(dev);
+ struct i2c_client *client = drv_data->client;
+
+ dev_err(&client->dev, "si4702 store %d\n", count);
+
+ p = memchr(buf, ' ', count);
+ if (p) {
+ len = p - buf;
+ *p = '\0';
+ } else
+ len = count;
+
+ len -= 1;
+ dev_err(&client->dev, "cmd %s\n", buf);
+
+ for (s = &fm_control[state]; state < FM_CTL_MAX; s++, state++) {
+ if (*s && !strncmp(buf, *s, len)) {
+ break;
+ }
+ }
+ if (state < FM_CTL_MAX && *s) {
+ if (p)
+ arg = simple_strtoul(p + 1, NULL, 0);
+ dev_err(&client->dev, "arg = %d\n", arg);
+ error = cmd(state, arg);
+ } else {
+ dev_err(&client->dev, "error cmd\n");
+ error = -EINVAL;
+ }
+
+ return error ? error : count;
+}
+
+static int ioctl_si4702(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int mute = 0;
+ u16 data;
+ int error;
+ u8 volume;
+ unsigned int freq;
+ int dir;
+ struct i2c_client *client;
+ struct si4702_info *info;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ info = &si4702_drvdata->info;
+ client = si4702_drvdata->client;
+
+ dev_err(&client->dev, "ioctl, cmd: 0x%x, arg: 0x%lx\n", cmd, arg);
+
+ switch (cmd) {
+ case SI4702_SETVOLUME:
+ /* get volume from user */
+ if (copy_from_user(&volume, argp, sizeof(u8))) {
+
+ dev_err(&client->dev,
+ "ioctl, copy volume value from user failed\n");
+ return -EFAULT;
+ }
+ dev_err(&client->dev, "volume %d\n", volume);
+ /* refill the register value */
+ volume &= 0x0f;
+ if (info->mute)
+ error = si4702_write_reg(SI4702_POWERCFG, 0x0001);
+ else
+ error = si4702_write_reg(SI4702_POWERCFG, 0x4001);
+
+ error = si4702_write_reg(SI4702_CHANNEL, 0);
+ error = si4702_write_reg(SI4702_SYSCONFIG1, 0);
+ error = si4702_write_reg(SI4702_SYSCONFIG2, 0x0f10 | volume);
+ if (error) {
+ dev_err(&client->dev, "ioctl, set volume failed\n");
+ return -EFAULT;
+ }
+ /* renew the device info */
+ info->volume = volume;
+
+ break;
+ case SI4702_GETVOLUME:
+ /* just copy volume value to user */
+ if (copy_to_user(argp, &(info->volume), sizeof(unsigned int))) {
+ dev_err(&client->dev, "ioctl, copy to user failed\n");
+ return -EFAULT;
+ }
+ break;
+ case SI4702_MUTEON:
+ mute = 1;
+ case SI4702_MUTEOFF:
+ if (mute) {
+ /* enable mute */
+ si4702_read_reg(SI4702_POWERCFG, &data);
+ data &= 0x00FF;
+ error = si4702_write_reg(SI4702_POWERCFG, data);
+ } else {
+ si4702_read_reg(SI4702_POWERCFG, &data);
+ data &= 0x00FF;
+ data |= 0x4000;
+ error = si4702_write_reg(SI4702_POWERCFG, data);
+ }
+ if (error) {
+ dev_err(&client->dev, "ioctl, set mute failed\n");
+ return -EFAULT;
+ }
+ break;
+ case SI4702_SELECT:
+ if (copy_from_user(&freq, argp, sizeof(unsigned int))) {
+
+ dev_err(&client->dev,
+ "ioctl, copy frequence from user failed\n");
+ return -EFAULT;
+ }
+ /* check frequence */
+ if (freq > info->max_band || freq < info->min_band) {
+ dev_err(&client->dev,
+ "the frequence select is out of band\n");
+ return -EINVAL;
+ }
+ if (si4702_channel_select(freq)) {
+ dev_err(&client->dev,
+ "ioctl, failed to select channel\n");
+ return -EFAULT;
+ }
+ break;
+ case SI4702_SEEK:
+ if (copy_from_user(&dir, argp, sizeof(int))) {
+
+ dev_err(&client->dev, "ioctl, copy from user failed\n");
+ return -EFAULT;
+ }
+ /* get seeked channel */
+ dir = si4702_channel_seek(dir);
+ if (dir == -1) {
+ return -EAGAIN;
+ } else if (dir == -2) {
+ return -EFAULT;
+ }
+ if (copy_to_user(argp, &dir, sizeof(int))) {
+
+ dev_err(&client->dev,
+ "ioctl, copy seek frequnce to user failed\n");
+ return -EFAULT;
+ }
+ break;
+ default:
+ dev_err(&client->dev, "SI4702: Invalid ioctl command\n");
+ return -EINVAL;
+
+ }
+ return 0;
+}
+
+static int open_si4702(struct inode *inode, struct file *file)
+{
+ struct i2c_client *client;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ client = si4702_drvdata->client;
+
+ spin_lock(&count_lock);
+ if (si4702_drvdata->count != 0) {
+ dev_err(&client->dev, "device has been open already\n");
+ spin_unlock(&count_lock);
+ return -EBUSY;
+ }
+ si4702_drvdata->count++;
+ spin_unlock(&count_lock);
+
+ /* request and active GPIO */
+ si4702_gpio_get();
+ /* reset the si4702 from it's reset pin */
+ si4702_reset();
+
+ /* startup si4702 */
+ if (si4702_startup()) {
+ spin_lock(&count_lock);
+ si4702_drvdata->count--;
+ spin_unlock(&count_lock);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int release_si4702(struct inode *inode, struct file *file)
+{
+ struct i2c_client *client;
+
+ if (NULL == si4702_drvdata)
+ return -1;
+
+ client = si4702_drvdata->client;
+
+ dev_err(&client->dev, "release\n");
+ /* software shutdown */
+ si4702_shutdown();
+ /* inactive, free GPIO, cut power */
+ si4702_gpio_put();
+
+ spin_lock(&count_lock);
+ si4702_drvdata->count--;
+ spin_unlock(&count_lock);
+
+ return 0;
+}
+
+static int si4702_suspend(struct i2c_client *client, pm_message_t state)
+{
+ return 0;
+}
+
+static int si4702_resume(struct i2c_client *client)
+{
+ return 0;
+}
+
+static struct device_attribute si4702_dev_attr = {
+ .attr = {
+ .name = "si4702_ctl",
+ .mode = S_IRUSR | S_IWUSR,
+ },
+ .show = si4702_show,
+ .store = si4702_store,
+};
+
+static struct file_operations si4702_fops = {
+ .owner = THIS_MODULE,
+ .open = open_si4702,
+ .release = release_si4702,
+ .ioctl = ioctl_si4702,
+};
+
+static int __devinit si4702_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct mxc_fm_platform_data *plat_data;
+ struct si4702_drvdata *drv_data;
+ struct device *dev;
+
+ dev_info(&client->dev, "si4702 device probe process start.\n");
+
+ plat_data = (struct mxc_fm_platform_data *)client->dev.platform_data;
+ if (plat_data == NULL) {
+ dev_err(&client->dev, "lack of platform data!\n");
+ return -ENODEV;
+ }
+
+ drv_data = kmalloc(sizeof(struct si4702_drvdata), GFP_KERNEL);
+ if (drv_data == NULL) {
+ dev_err(&client->dev, "lack of kernel memory!\n");
+ return -ENOMEM;
+ }
+ memset(drv_data, 0, sizeof(struct si4702_drvdata));
+ drv_data->plat_data = plat_data;
+ drv_data->major = DEV_MAJOR;
+ drv_data->minor = DEV_MINOR;
+ drv_data->count = 0;
+
+ /*enable power supply */
+ if (plat_data->reg_vio != NULL) {
+ drv_data->vio = regulator_get(&client->dev, plat_data->reg_vio);
+ if (drv_data->vio == ERR_PTR(-ENOENT))
+ goto free_drv_data;
+ regulator_enable(drv_data->vio);
+ }
+
+ /* here, we assume that vio and vdd are not the same */
+ if (plat_data->reg_vdd != NULL) {
+ drv_data->vdd = regulator_get(&client->dev, plat_data->reg_vdd);
+ if (drv_data->vdd == ERR_PTR(-ENOENT))
+ goto disable_vio;
+ regulator_enable(drv_data->vdd);
+ }
+
+ /*attach client and check device id */
+ if (SI4702_DEVICE_ID != si4702_id_detect(client)) {
+ dev_err(&client->dev, "id wrong.\n");
+ goto disable_vdd;
+ }
+ dev_info(&client->dev, "chip id %x detect.\n", SI4702_DEVICE_ID);
+ drv_data->client = client;
+
+ /*user interface begain */
+ /*create device file in sysfs as a user interface,
+ * also for debug support */
+ ret = device_create_file(&client->dev, &si4702_dev_attr);
+ if (ret) {
+ dev_err(&client->dev, "create device file failed!\n");
+ goto gpio_put; /* shall i use some meanful error code? */
+ }
+
+ /*create a char dev for application code access */
+ if (drv_data->major) {
+ ret = register_chrdev(drv_data->major, "si4702", &si4702_fops);;
+ } else {
+ ret = register_chrdev(0, "si4702", &si4702_fops);
+ }
+
+ if (drv_data->major == 0)
+ drv_data->major = ret;
+
+ /* create class and device for udev information */
+ drv_data->radio_class = class_create(THIS_MODULE, "radio");
+ if (IS_ERR(drv_data->radio_class)) {
+ dev_err(&client->dev, "SI4702: failed to create radio class\n");
+ goto char_dev_remove;
+ }
+
+ dev = device_create(drv_data->radio_class, NULL,
+ MKDEV(drv_data->major, drv_data->minor), NULL, "si4702");
+ if (IS_ERR(dev)) {
+ dev_err(&client->dev,
+ "SI4702: failed to create radio class device\n");
+ goto class_remove;
+ }
+ /*User interface end */
+ dev_set_drvdata(&client->dev, drv_data);
+ si4702_drvdata = drv_data;
+
+ si4702_gpio_get();
+ dev_info(&client->dev, "si4702 device probe successfully.\n");
+ si4702_shutdown();
+
+ return 0;
+
+class_remove:
+ class_destroy(drv_data->radio_class);
+char_dev_remove:
+ unregister_chrdev(drv_data->major, "si4702");
+ device_remove_file(&client->dev, &si4702_dev_attr);
+gpio_put:
+ si4702_gpio_put();
+disable_vdd:
+ if (plat_data->reg_vdd) {
+ regulator_disable(drv_data->vdd);
+ regulator_put(drv_data->vdd);
+ }
+disable_vio:
+ if (plat_data->reg_vio) {
+ regulator_disable(drv_data->vio);
+ regulator_put(drv_data->vio);
+ }
+
+free_drv_data:
+ kfree(drv_data);
+
+ return -ENODEV;
+}
+
+static int __devexit si4702_remove(struct i2c_client *client)
+{
+ struct mxc_fm_platform_data *plat_data;
+ struct si4702_drvdata *drv_data = dev_get_drvdata(&client->dev);
+
+ plat_data = (struct mxc_fm_platform_data *)client->dev.platform_data;
+
+ device_destroy(drv_data->radio_class,
+ MKDEV(drv_data->major, drv_data->minor));
+ class_destroy(drv_data->radio_class);
+
+ unregister_chrdev(drv_data->major, "si4702");
+ device_remove_file(&client->dev, &si4702_dev_attr);
+ si4702_gpio_put();
+
+ if (plat_data->reg_vdd)
+ regulator_put(drv_data->vdd);
+
+ if (plat_data->reg_vio)
+ regulator_put(drv_data->vio);
+
+ kfree(si4702_drvdata);
+ si4702_drvdata = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id si4702_id[] = {
+ {"si4702", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, si4702_id);
+
+static struct i2c_driver i2c_si4702_driver = {
+ .driver = {
+ .name = "si4702",
+ },
+ .probe = si4702_probe,
+ .remove = si4702_remove,
+ .suspend = si4702_suspend,
+ .resume = si4702_resume,
+ .id_table = si4702_id,
+};
+
+static int __init init_si4702(void)
+{
+ /*add to i2c driver */
+ pr_info("add si4702 i2c driver\n");
+ return i2c_add_driver(&i2c_si4702_driver);
+}
+
+static void __exit exit_si4702(void)
+{
+ i2c_del_driver(&i2c_si4702_driver);
+}
+
+module_init(init_si4702);
+module_exit(exit_si4702);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("SI4702 FM driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/mxs_viim.c b/drivers/char/mxs_viim.c
new file mode 100644
index 000000000000..2510afa6c13e
--- /dev/null
+++ b/drivers/char/mxs_viim.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2009 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/platform_device.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+
+static unsigned long iim_reg_base0, iim_reg_end0, iim_reg_size0;
+static unsigned long iim_reg_base1, iim_reg_end1, iim_reg_size1;
+static struct device *iim_dev;
+
+/*!
+ * MXS Virtual IIM interface - memory map function
+ * This function maps one page size VIIM registers from VIIM base address0
+ * if the size of the required virtual memory space is less than or equal to
+ * one page size, otherwise this function will also map one page size VIIM
+ * registers from VIIM base address1.
+ *
+ * @param file struct file *
+ * @param vma structure vm_area_struct *
+ *
+ * @return Return 0 on success or negative error code on error
+ */
+static int mxs_viim_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ size_t size = vma->vm_end - vma->vm_start;
+
+ 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_reg_base0 >> PAGE_SHIFT,
+ iim_reg_size0,
+ vma->vm_page_prot))
+ return -EAGAIN;
+
+ if (size > iim_reg_size0) {
+ if (remap_pfn_range(vma,
+ vma->vm_start + iim_reg_size0,
+ iim_reg_base1 >> PAGE_SHIFT,
+ iim_reg_size1,
+ vma->vm_page_prot))
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+/*!
+ * MXS Virtual 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 mxs_viim_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+/*!
+ * MXS Virtual 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 mxs_viim_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static const struct file_operations mxs_viim_fops = {
+ .mmap = mxs_viim_mmap,
+ .open = mxs_viim_open,
+ .release = mxs_viim_release,
+};
+
+static struct miscdevice mxs_viim_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mxs_viim",
+ .fops = &mxs_viim_fops,
+};
+
+/*!
+ * This function is called by the driver framework to get virtual iim base/end
+ * address and register iim misc device.
+ *
+ * @param dev The device structure for Virtual IIM passed in by the
+ * driver framework.
+ *
+ * @return Returns 0 on success or negative error code on error
+ */
+static int mxs_viim_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int ret;
+
+ iim_dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (IS_ERR(res)) {
+ dev_err(iim_dev, "Unable to get Virtual IIM resource 0\n");
+ return -ENODEV;
+ }
+
+ iim_reg_base0 = res->start;
+ iim_reg_end0 = res->end;
+ iim_reg_size0 = iim_reg_end0 - iim_reg_base0 + 1;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (IS_ERR(res)) {
+ dev_err(iim_dev, "Unable to get Virtual IIM resource 1\n");
+ return -ENODEV;
+ }
+
+ iim_reg_base1 = res->start;
+ iim_reg_end1 = res->end;
+ iim_reg_size1 = iim_reg_end1 - iim_reg_base1 + 1;
+
+ ret = misc_register(&mxs_viim_miscdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mxs_viim_remove(struct platform_device *pdev)
+{
+ misc_deregister(&mxs_viim_miscdev);
+ return 0;
+}
+
+static struct platform_driver mxs_viim_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mxs_viim",
+ },
+ .probe = mxs_viim_probe,
+ .remove = mxs_viim_remove,
+};
+
+static int __init mxs_viim_dev_init(void)
+{
+ return platform_driver_register(&mxs_viim_driver);
+}
+
+static void __exit mxs_viim_dev_cleanup(void)
+{
+ platform_driver_unregister(&mxs_viim_driver);
+}
+
+module_init(mxs_viim_dev_init);
+module_exit(mxs_viim_dev_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXS Virtual IIM driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(MISC_DYNAMIC_MINOR);