summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-mxs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-mxs.c')
-rw-r--r--drivers/i2c/busses/i2c-mxs.c611
1 files changed, 611 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c
new file mode 100644
index 000000000000..852b2eda5dc4
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -0,0 +1,611 @@
+/*
+ * Freescale MX28 I2C bus driver
+ * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/dmaengine.h>
+#include <mach/device.h>
+#include <mach/regs-i2c.h>
+#include <mach/system.h>
+#include <mach/hardware.h>
+
+#include "i2c-mxs.h"
+
+/* 2 for read, 1 for write */
+#define NR_DESC 3
+static struct mxs_dma_desc *desc[NR_DESC];
+static dma_addr_t i2c_buf_phys;
+static u8 *i2c_buf_virt;
+
+#define CMD_I2C_SELECT (BM_I2C_CTRL0_RETAIN_CLOCK | \
+ BM_I2C_CTRL0_PRE_SEND_START | \
+ BM_I2C_CTRL0_MASTER_MODE | \
+ BM_I2C_CTRL0_DIRECTION | \
+ BF_I2C_CTRL0_XFER_COUNT(1))
+#define CMD_I2C_WRITE (BM_I2C_CTRL0_PRE_SEND_START | \
+ BM_I2C_CTRL0_MASTER_MODE | \
+ BM_I2C_CTRL0_DIRECTION)
+#define CMD_I2C_READ (BM_I2C_CTRL0_SEND_NAK_ON_LAST | \
+ BM_I2C_CTRL0_MASTER_MODE)
+
+/* Hack for platform which does not support PioQueue Mode */
+#if !defined(HW_I2C_QUEUECMD) || \
+ !defined(HW_I2C_QUEUEDATA) || \
+ !defined(HW_I2C_QUEUECTRL_CLR) || \
+ !defined(HW_I2C_QUEUECTRL_SET)
+#warning "Pio Queue Mode *NOT* Support!"
+#define HW_I2C_QUEUECMD HW_I2C_VERSION
+#define HW_I2C_QUEUEDATA HW_I2C_VERSION
+#define HW_I2C_QUEUECTRL_SET HW_I2C_VERSION
+#define HW_I2C_QUEUECTRL_CLR HW_I2C_VERSION
+#endif
+
+static void hw_i2c_dmachan_reset(struct mxs_i2c_dev *dev)
+{
+ mxs_dma_disable(dev->dma_chan);
+ mxs_dma_reset(dev->dma_chan);
+ mxs_dma_ack_irq(dev->dma_chan);
+}
+
+static mxs_i2c_reset(struct mxs_i2c_dev *mxs_i2c)
+{
+ hw_i2c_dmachan_reset(mxs_i2c);
+ mxs_dma_enable_irq(mxs_i2c->dma_chan, 1);
+ mxs_reset_block((void __iomem *)mxs_i2c->regbase, 0);
+ __raw_writel(0x0000FF00, mxs_i2c->regbase + HW_I2C_CTRL1_SET);
+}
+
+static int hw_i2c_dma_init(struct platform_device *pdev)
+{
+ struct mxs_i2c_dev *mxs_i2c = platform_get_drvdata(pdev);
+ int i, ret;
+
+ ret = mxs_dma_request(mxs_i2c->dma_chan, &pdev->dev, "i2c");
+ if (ret)
+ return ret;
+
+ for (i = 0; i < NR_DESC; i++) {
+ desc[i] = mxs_dma_alloc_desc();
+ if (desc[i] == NULL)
+ goto err;
+ }
+
+ i2c_buf_virt = dma_alloc_coherent(&pdev->dev,
+ PAGE_SIZE, &i2c_buf_phys, GFP_KERNEL);
+ if (i2c_buf_virt == NULL)
+ goto err;
+
+ hw_i2c_dmachan_reset(mxs_i2c);
+ mxs_dma_enable_irq(mxs_i2c->dma_chan, 1);
+
+ return 0;
+
+err:
+ while (--i >= 0)
+ mxs_dma_free_desc(desc[i]);
+
+ return -ENOMEM;
+}
+
+static void hw_i2c_dma_uninit(struct platform_device *pdev)
+{
+ struct mxs_i2c_dev *mxs_i2c = platform_get_drvdata(pdev);
+ int i;
+ LIST_HEAD(list);
+
+ mxs_dma_enable_irq(mxs_i2c->dma_chan, 0);
+ mxs_dma_get_cooked(mxs_i2c->dma_chan, &list);
+ mxs_dma_disable(mxs_i2c->dma_chan);
+
+ for (i = 0; i < NR_DESC; i++)
+ mxs_dma_free_desc(desc[i]);
+
+ hw_i2c_dmachan_reset(mxs_i2c);
+
+ dma_free_coherent(&pdev->dev, PAGE_SIZE, i2c_buf_virt, i2c_buf_phys);
+
+ mxs_dma_release(mxs_i2c->dma_chan, &pdev->dev);
+}
+
+static void hw_i2c_pioq_setup_read(struct mxs_i2c_dev *dev,
+ u8 addr, void *buff, int len, int flags)
+{
+ u32 queuecmd;
+ u32 queuedata;
+
+ WARN_ONCE(len > 24, "choose DMA mode if xfer len > 24 bytes\n");
+
+ /* fill queue cmd */
+ queuecmd = CMD_I2C_SELECT;
+ __raw_writel(queuecmd, dev->regbase + HW_I2C_QUEUECMD);
+
+ /* fill data (slave addr) */
+ queuedata = (addr << 1) | I2C_READ;
+ __raw_writel(queuedata, dev->regbase + HW_I2C_DATA);
+
+ /* fill queue cmd */
+ queuecmd = CMD_I2C_READ | flags;
+ queuecmd |= BF_I2C_CTRL0_XFER_COUNT(len) | flags;
+ __raw_writel(queuecmd, dev->regbase + HW_I2C_QUEUECMD);
+
+}
+
+static void hw_i2c_dma_setup_read(u8 addr, void *buff, int len, int flags)
+{
+ if (len > (PAGE_SIZE - 4))
+ BUG();
+
+ memset(&desc[0]->cmd, 0, sizeof(desc[0]->cmd));
+ memset(&desc[1]->cmd, 0, sizeof(desc[1]->cmd));
+
+ desc[0]->cmd.cmd.bits.bytes = 1;
+ desc[0]->cmd.cmd.bits.pio_words = 1;
+ desc[0]->cmd.cmd.bits.wait4end = 1;
+ desc[0]->cmd.cmd.bits.dec_sem = 1;
+ desc[0]->cmd.cmd.bits.irq = 0;
+ desc[0]->cmd.cmd.bits.chain = 1;
+ desc[0]->cmd.cmd.bits.command = DMA_READ;
+ desc[0]->cmd.address = i2c_buf_phys;
+ desc[0]->cmd.pio_words[0] = CMD_I2C_SELECT;
+ i2c_buf_virt[0] = (addr << 1) | I2C_READ;
+
+ desc[1]->cmd.cmd.bits.bytes = len;
+ desc[1]->cmd.cmd.bits.pio_words = 1;
+ desc[1]->cmd.cmd.bits.wait4end = 1;
+ desc[1]->cmd.cmd.bits.dec_sem = 1;
+ desc[1]->cmd.cmd.bits.irq = 1;
+ desc[1]->cmd.cmd.bits.command = DMA_WRITE;
+ desc[1]->cmd.address = (u32) i2c_buf_phys + 1;
+ desc[1]->cmd.pio_words[0] = CMD_I2C_READ;
+ desc[1]->cmd.pio_words[0] |= BF_I2C_CTRL0_XFER_COUNT(len) | flags;
+}
+
+static void hw_i2c_pioq_setup_write(struct mxs_i2c_dev *dev,
+ u8 addr, void *buff, int len, int flags)
+{
+ int align_len, i;
+ u8 slaveaddr;
+ u32 queuecmd;
+ u8 *buf1;
+ u32 *buf2;
+
+ WARN_ONCE(len > 24, "choose DMA mode if xfer len > 24 bytes\n");
+
+ align_len = (len + 1 + 3) & ~3;
+
+ buf1 = (u8 *) dev->buf;
+ buf2 = (u32 *) dev->buf;
+
+ /* fill queue cmd */
+ queuecmd = CMD_I2C_WRITE;
+ queuecmd |= BF_I2C_CTRL0_XFER_COUNT(len + 1) | flags;
+ __raw_writel(queuecmd, dev->regbase + HW_I2C_QUEUECMD);
+
+ /* fill data (slave addr) */
+ slaveaddr = (addr << 1) | I2C_WRITE;
+ memcpy(buf1, &slaveaddr, 1);
+
+ memcpy(&buf1[1], buff, len);
+
+ /* fill data */
+ for (i = 0; i < align_len / 4; i++)
+ __raw_writel(*buf2++, dev->regbase + HW_I2C_DATA);
+}
+
+static void hw_i2c_dma_setup_write(u8 addr, void *buff, int len, int flags)
+{
+ memset(&desc[2]->cmd, 0, sizeof(desc[2]->cmd));
+
+ desc[2]->cmd.cmd.bits.bytes = len + 1;
+ desc[2]->cmd.cmd.bits.pio_words = 1;
+ desc[2]->cmd.cmd.bits.wait4end = 1;
+ desc[2]->cmd.cmd.bits.dec_sem = 1;
+ desc[2]->cmd.cmd.bits.irq = 1;
+ desc[2]->cmd.cmd.bits.command = DMA_READ;
+ desc[2]->cmd.address = i2c_buf_phys;
+ desc[2]->cmd.pio_words[0] = CMD_I2C_WRITE;
+ desc[2]->cmd.pio_words[0] |= BM_I2C_CTRL0_POST_SEND_STOP;
+ desc[2]->cmd.pio_words[0] |= BF_I2C_CTRL0_XFER_COUNT(len + 1) | flags;
+
+ i2c_buf_virt[0] = (addr << 1) | I2C_WRITE;
+ memcpy(&i2c_buf_virt[1], buff, len);
+}
+
+static void hw_i2c_pioq_run(struct mxs_i2c_dev *dev)
+{
+ __raw_writel(0x20, dev->regbase + HW_I2C_QUEUECTRL_SET);
+}
+
+static void hw_i2c_dma_run(struct mxs_i2c_dev *dev, int dir)
+{
+ if (dir == I2C_READ) {
+ mxs_dma_desc_append(dev->dma_chan, desc[0]);
+ mxs_dma_desc_append(dev->dma_chan, desc[1]);
+ } else
+ mxs_dma_desc_append(dev->dma_chan, desc[2]);
+
+ mxs_dma_enable(dev->dma_chan);
+}
+
+static void hw_i2c_pioq_stop(struct mxs_i2c_dev *dev)
+{
+ __raw_writel(0x20, dev->regbase + HW_I2C_QUEUECTRL_CLR);
+}
+
+static void hw_i2c_finish_read(struct mxs_i2c_dev *dev, void *buff, int len)
+{
+ int i, align_len;
+ u8 *buf1;
+ u32 *buf2;
+
+ if (dev->flags & MXS_I2C_PIOQUEUE_MODE) {
+ align_len = (len + 3) & ~3;
+
+ buf1 = (u8 *) dev->buf;
+ buf2 = (u32 *) dev->buf;
+
+ for (i = 0; i < align_len / 4; i++)
+ *buf2++ = __raw_readl(dev->regbase + HW_I2C_QUEUEDATA);
+
+ memcpy(buff, buf1, len);
+ } else
+ memcpy(buff, &i2c_buf_virt[1], len);
+}
+
+/*
+ * Low level master read/write transaction.
+ */
+static int mxs_i2c_xfer_msg(struct i2c_adapter *adap,
+ struct i2c_msg *msg, int stop)
+{
+ struct mxs_i2c_dev *dev = i2c_get_adapdata(adap);
+ int err;
+ int flags;
+
+ init_completion(&dev->cmd_complete);
+ dev->cmd_err = 0;
+
+ dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
+ msg->addr, msg->len, msg->flags, stop);
+
+ if ((msg->len == 0) || (msg->len > (PAGE_SIZE - 1)))
+ return -EINVAL;
+
+ flags = stop ? BM_I2C_CTRL0_POST_SEND_STOP : 0;
+
+ if (msg->flags & I2C_M_RD) {
+ if (dev->flags & MXS_I2C_PIOQUEUE_MODE) {
+ hw_i2c_pioq_setup_read(dev,
+ msg->addr,
+ msg->buf, msg->len, flags);
+ hw_i2c_pioq_run(dev);
+ } else {
+ hw_i2c_dma_setup_read(msg->addr,
+ msg->buf, msg->len, flags);
+
+ hw_i2c_dma_run(dev, I2C_READ);
+ }
+ } else {
+ if (dev->flags & MXS_I2C_PIOQUEUE_MODE) {
+ hw_i2c_pioq_setup_write(dev,
+ msg->addr,
+ msg->buf, msg->len, flags);
+ hw_i2c_pioq_run(dev);
+ } else {
+ hw_i2c_dma_setup_write(msg->addr,
+ msg->buf, msg->len, flags);
+ hw_i2c_dma_run(dev, I2C_WRITE);
+ }
+ }
+
+ err = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
+ msecs_to_jiffies(1000)
+ );
+ if (err <= 0) {
+ mxs_i2c_reset(dev);
+ dev_dbg(dev->dev, "controller is timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ if ((!dev->cmd_err) && (msg->flags & I2C_M_RD))
+ hw_i2c_finish_read(dev, msg->buf, msg->len);
+
+ dev_dbg(dev->dev, "Done with err=%d\n", dev->cmd_err);
+
+ return dev->cmd_err;
+}
+
+static int
+mxs_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ int i;
+ int err;
+
+ if (!msgs->len)
+ return -EINVAL;
+
+ for (i = 0; i < num; i++) {
+ err = mxs_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
+ if (err)
+ break;
+ }
+
+ if (err == 0)
+ err = num;
+
+ return err;
+}
+
+static u32 mxs_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static irqreturn_t mxs_i2c_dma_isr(int this_irq, void *dev_id)
+{
+ struct mxs_i2c_dev *mxs_i2c = dev_id;
+
+ LIST_HEAD(list);
+ mxs_dma_ack_irq(mxs_i2c->dma_chan);
+ mxs_dma_cooked(mxs_i2c->dma_chan, &list);
+
+ complete(&mxs_i2c->cmd_complete);
+
+ return IRQ_HANDLED;
+}
+
+static void mxs_i2c_task(struct work_struct *work)
+{
+ struct mxs_i2c_dev *mxs_i2c = container_of(work,
+ struct mxs_i2c_dev, work);
+ mxs_i2c_reset(mxs_i2c);
+ complete(&mxs_i2c->cmd_complete);
+}
+
+#define I2C_IRQ_MASK 0x000000FF
+static irqreturn_t mxs_i2c_isr(int this_irq, void *dev_id)
+{
+ struct mxs_i2c_dev *mxs_i2c = dev_id;
+ u32 stat;
+ u32 done_mask =
+ BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ | BM_I2C_CTRL1_BUS_FREE_IRQ;
+
+ stat = __raw_readl(mxs_i2c->regbase + HW_I2C_CTRL1) & I2C_IRQ_MASK;
+ if (!stat)
+ return IRQ_NONE;
+
+ if (stat & BM_I2C_CTRL1_NO_SLAVE_ACK_IRQ) {
+ mxs_i2c->cmd_err = -EREMOTEIO;
+ /* it takes long time to reset i2c */
+ schedule_work(&mxs_i2c->work);
+ goto done;
+ }
+
+ /* Don't care about BM_I2C_CTRL1_OVERSIZE_XFER_TERM_IRQ */
+ if (stat & (BM_I2C_CTRL1_EARLY_TERM_IRQ |
+ BM_I2C_CTRL1_MASTER_LOSS_IRQ |
+ BM_I2C_CTRL1_SLAVE_STOP_IRQ | BM_I2C_CTRL1_SLAVE_IRQ)) {
+ mxs_i2c->cmd_err = -EIO;
+ complete(&mxs_i2c->cmd_complete);
+ goto done;
+ }
+
+ if ((stat & done_mask) == done_mask &&
+ (mxs_i2c->flags & MXS_I2C_PIOQUEUE_MODE))
+
+ complete(&mxs_i2c->cmd_complete);
+
+done:
+ __raw_writel(stat, mxs_i2c->regbase + HW_I2C_CTRL1_CLR);
+ return IRQ_HANDLED;
+}
+
+static const struct i2c_algorithm mxs_i2c_algo = {
+ .master_xfer = mxs_i2c_xfer,
+ .functionality = mxs_i2c_func,
+};
+
+static int mxs_i2c_probe(struct platform_device *pdev)
+{
+ struct mxs_i2c_dev *mxs_i2c;
+ struct mxs_i2c_plat_data *pdata;
+ struct i2c_adapter *adap;
+ struct resource *res;
+ int err = 0;
+
+ mxs_i2c = kzalloc(sizeof(struct mxs_i2c_dev), GFP_KERNEL);
+ if (!mxs_i2c) {
+ dev_err(&pdev->dev, "no mem \n");
+ return -ENOMEM;
+ }
+
+ pdata = pdev->dev.platform_data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no register base resource\n");
+ err = -ENODEV;
+ goto nores;
+ }
+ mxs_i2c->regbase = (unsigned long)IO_ADDRESS(res->start);
+
+ res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no dma channel resource\n");
+ err = -ENODEV;
+ goto nores;
+ }
+ mxs_i2c->dma_chan = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no err_irq resource\n");
+ err = -ENODEV;
+ goto nores;
+ }
+ mxs_i2c->irq_err = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "no dma_irq resource\n");
+ err = -ENODEV;
+ goto nores;
+ }
+ mxs_i2c->irq_dma = res->start;
+
+ mxs_i2c->dev = &pdev->dev;
+ mxs_i2c->flags = pdata->pioqueue_mode ?
+ MXS_I2C_PIOQUEUE_MODE : MXS_I2C_DMA_MODE;
+
+ err =
+ request_irq(mxs_i2c->irq_err, mxs_i2c_isr, 0, pdev->name, mxs_i2c);
+ if (err) {
+ dev_err(&pdev->dev, "Can't get IRQ\n");
+ goto no_err_irq;
+ }
+
+ err =
+ request_irq(mxs_i2c->irq_dma, mxs_i2c_dma_isr, 0, pdev->name,
+ mxs_i2c);
+ if (err) {
+ dev_err(&pdev->dev, "Can't get IRQ\n");
+ goto no_dma_irq;
+ }
+
+ /* reset I2C module */
+ mxs_reset_block((void __iomem *)mxs_i2c->regbase, 1);
+ platform_set_drvdata(pdev, mxs_i2c);
+
+ if (mxs_i2c->flags & MXS_I2C_PIOQUEUE_MODE)
+ __raw_writel(0x04, mxs_i2c->regbase + HW_I2C_QUEUECTRL_SET);
+
+ mxs_i2c->buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (mxs_i2c->buf == NULL) {
+ dev_err(&pdev->dev, "HW Init failed\n");
+ goto init_failed;
+ } else {
+ err = hw_i2c_dma_init(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "HW Init failed\n");
+ goto init_failed;
+ }
+ }
+
+ /* Will catch all error (IRQ mask) */
+ __raw_writel(0x0000FF00, mxs_i2c->regbase + HW_I2C_CTRL1_SET);
+
+ adap = &mxs_i2c->adapter;
+ strncpy(adap->name, "MXS I2C adapter", sizeof(adap->name));
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON;
+ adap->algo = &mxs_i2c_algo;
+ adap->dev.parent = &pdev->dev;
+ adap->nr = pdev->id;
+ i2c_set_adapdata(adap, mxs_i2c);
+ err = i2c_add_numbered_adapter(adap);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add adapter\n");
+ goto no_i2c_adapter;
+
+ }
+
+ INIT_WORK(&mxs_i2c->work, mxs_i2c_task);
+
+ return 0;
+
+no_i2c_adapter:
+ __raw_writel(BM_I2C_CTRL0_SFTRST, mxs_i2c->regbase + HW_I2C_CTRL0_SET);
+
+ if (mxs_i2c->flags & MXS_I2C_DMA_MODE)
+ hw_i2c_dma_uninit(pdev);
+ else
+ kfree(mxs_i2c->buf);
+init_failed:
+ free_irq(mxs_i2c->irq_dma, mxs_i2c);
+no_dma_irq:
+ free_irq(mxs_i2c->irq_err, mxs_i2c);
+no_err_irq:
+nores:
+ kfree(mxs_i2c);
+ return err;
+}
+
+static int mxs_i2c_remove(struct platform_device *pdev)
+{
+ struct mxs_i2c_dev *mxs_i2c = platform_get_drvdata(pdev);
+ int res;
+
+ res = i2c_del_adapter(&mxs_i2c->adapter);
+ if (res)
+ return -EBUSY;
+
+ __raw_writel(BM_I2C_CTRL0_SFTRST, mxs_i2c->regbase + HW_I2C_CTRL0_SET);
+
+ if (mxs_i2c->flags & MXS_I2C_DMA_MODE)
+ hw_i2c_dma_uninit(pdev);
+ if (mxs_i2c->flags & MXS_I2C_PIOQUEUE_MODE)
+ hw_i2c_pioq_stop(mxs_i2c);
+
+ platform_set_drvdata(pdev, NULL);
+
+ free_irq(mxs_i2c->irq_err, mxs_i2c);
+ free_irq(mxs_i2c->irq_dma, mxs_i2c);
+
+ kfree(mxs_i2c->buf);
+ kfree(mxs_i2c);
+ return 0;
+}
+
+static struct platform_driver mxs_i2c_driver = {
+ .driver = {
+ .name = "mxs-i2c",
+ .owner = THIS_MODULE,
+ },
+ .probe = mxs_i2c_probe,
+ .remove = __devexit_p(mxs_i2c_remove),
+};
+
+static int __init mxs_i2c_init(void)
+{
+ return platform_driver_register(&mxs_i2c_driver);
+}
+
+subsys_initcall(mxs_i2c_init);
+
+static void __exit mxs_i2c_exit(void)
+{
+ platform_driver_unregister(&mxs_i2c_driver);
+}
+
+module_exit(mxs_i2c_exit);
+
+MODULE_AUTHOR("Embedded Alley Solutions, Inc/Freescale Inc");
+MODULE_DESCRIPTION("MXS I2C Bus Driver");
+MODULE_LICENSE("GPL");