diff options
author | Roshni Shah <shah.roshni@yahoo.com> | 2011-03-14 06:49:42 -0400 |
---|---|---|
committer | Justin Waters <justin.waters@timesys.com> | 2012-03-02 16:59:46 -0500 |
commit | 2731b2eadeaa141e6f305fa8086106608112bbaa (patch) | |
tree | 2702d45bac84073cd580ccb1bd3eafb9a000d3b6 /drivers/i2c | |
parent | 6d23f5084c975be637f7d748db82116bf84d3872 (diff) |
Add support for the i.MX53 QSB2.6.35.3-mx53-early-201103141049
This patch seems to have originated from the 11.01.00 release
from Freescale, which is no longer available except through the
gitweb interface from Freescale.
http://opensource.freescale.com/git?p=imx/linux-2.6-imx.git;a=commit;h=27fdf7bae11978d21e8aba09bb635f49b07edd4a
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/Kconfig | 58 | ||||
-rw-r--r-- | drivers/i2c/busses/Makefile | 4 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-mxs.c | 611 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-mxs.h | 42 | ||||
-rw-r--r-- | drivers/i2c/busses/mxc_i2c.c | 808 | ||||
-rw-r--r-- | drivers/i2c/busses/mxc_i2c_hs.c | 553 | ||||
-rw-r--r-- | drivers/i2c/busses/mxc_i2c_hs_reg.h | 97 | ||||
-rw-r--r-- | drivers/i2c/busses/mxc_i2c_reg.h | 40 |
8 files changed, 2213 insertions, 0 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index bceafbfa7268..d72d0f0ccb44 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -431,6 +431,64 @@ config I2C_MPC This driver can also be built as a module. If so, the module will be called i2c-mpc. +config I2C_MXC + tristate "MXC I2C support" + depends on I2C && ARCH_MXC + help + If you say yes to this option, support will be included for Freescale + MXC I2C modules. + + This driver can also be built as a module. + +config I2C_MXC_HS + tristate "MXC HIGH SPEED I2C support" + depends on I2C && ARCH_MXC + help + If you say yes to this option, support will be included for Freescale + MXC HIGH SPEED I2C modules. + + This driver can also be built as a module. + +config I2C_MXS + tristate "MXS I2C Support" + depends on I2C && ARCH_MXS + help + If you say yes to this option, support will be included for Freescale + MXS I2C modules. + +config I2C_MXS_SELECT0 + bool "Enable I2C0 module" + default y + depends on I2C_MXS + help + Enable MXS I2C0 Module + +config I2C_MXS_SELECT0_PIOQUEUE_MODE + tristate "MXS I2C0 PIOQUEUE MODE Support" + depends on (I2C_MXS_SELECT0 && !ARCH_MX23) + help + say yes if you are sure transfer length is eqaul to or less than 24 bytes. + Otherwise say no to use DMA mode by default. + +config I2C_MXS_SELECT1 + bool "Enable I2C1 module" + depends on (I2C_MXS && !ARCH_MX23) + help + Enable MXS I2C1 Module + +config I2C_MXS_SELECT1_PIOQUEUE_MODE + tristate "MXS I2C1 PIOQUEUE MODE Support" + depends on I2C_MXS_SELECT1 + help + say yes if you are sure transfer length is eqaul to or less than 24 bytes. + Otherwise say no to use DMA mode by default. + +config I2C_STMP378X + tristate "STMP378x I2C adapter" + depends on MACH_STMP378X + help + TBD + config I2C_MV64XXX tristate "Marvell mv64xxx I2C Controller" depends on (MV64X60 || PLAT_ORION) && EXPERIMENTAL diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 936880bd1dc5..0b25b7baa449 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -74,6 +74,10 @@ obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o obj-$(CONFIG_I2C_STUB) += i2c-stub.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o +obj-$(CONFIG_I2C_MXC) += mxc_i2c.o +obj-$(CONFIG_I2C_MXC_HS) += mxc_i2c_hs.o +obj-$(CONFIG_I2C_STMP378X) += i2c-stmp378x.o +obj-$(CONFIG_I2C_MXS) += i2c-mxs.o ifeq ($(CONFIG_I2C_DEBUG_BUS),y) EXTRA_CFLAGS += -DDEBUG 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"); diff --git a/drivers/i2c/busses/i2c-mxs.h b/drivers/i2c/busses/i2c-mxs.h new file mode 100644 index 000000000000..1a35385b793b --- /dev/null +++ b/drivers/i2c/busses/i2c-mxs.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#ifndef _I2C_H +#define _I2C_H + +#define I2C_READ 1 +#define I2C_WRITE 0 + +struct mxs_i2c_dev { + struct device *dev; + void *buf; + unsigned long regbase; + u32 flags; +#define MXS_I2C_DMA_MODE 0x1 +#define MXS_I2C_PIOQUEUE_MODE 0x2 + int dma_chan; + int irq_dma; + int irq_err; + struct completion cmd_complete; + u32 cmd_err; + struct i2c_adapter adapter; + spinlock_t lock; + wait_queue_head_t queue; + struct work_struct work; +}; +#endif diff --git a/drivers/i2c/busses/mxc_i2c.c b/drivers/i2c/busses/mxc_i2c.c new file mode 100644 index 000000000000..b47f9db4b476 --- /dev/null +++ b/drivers/i2c/busses/mxc_i2c.c @@ -0,0 +1,808 @@ +/* + * Copyright (C) 2004-2010 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_i2c.c + * + * @brief Driver for the Freescale Semiconductor MXC I2C buses. + * + * Based on i2c driver algorithm for PCF8584 adapters + * + * @ingroup MXCI2C + */ + +/* + * Include Files + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/fsl_devices.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <asm/irq.h> +#include "mxc_i2c_reg.h" + +/*! + * In case the MXC device has multiple I2C modules, this structure is used to + * store information specific to each I2C module. + */ +typedef struct { + /*! + * This structure is used to identify the physical i2c bus along with + * the access algorithms necessary to access it. + */ + struct i2c_adapter adap; + + /*! + * This waitqueue is used to wait for the data transfer to complete. + */ + wait_queue_head_t wq; + + /*! + * The base address of the I2C device. + */ + void __iomem *membase; + + /*! + * The interrupt number used by the I2C device. + */ + int irq; + + /*! + * The default clock divider value to be used. + */ + unsigned int clkdiv; + + /*! + * The clock source for the device. + */ + struct clk *clk; + + /*! + * The current power state of the device + */ + bool low_power; + + /*! + * Boolean to indicate if data was transferred + */ + bool transfer_done; + + /*! + * Boolean to indicate if we received an ACK for the data transmitted + */ + bool tx_success; +} mxc_i2c_device; + +struct clk_div_table { + int reg_value; + int div; +}; + +static const struct clk_div_table i2c_clk_table[] = { + {0x20, 22}, {0x21, 24}, {0x22, 26}, {0x23, 28}, + {0, 30}, {1, 32}, {0x24, 32}, {2, 36}, + {0x25, 36}, {0x26, 40}, {3, 42}, {0x27, 44}, + {4, 48}, {0x28, 48}, {5, 52}, {0x29, 56}, + {6, 60}, {0x2A, 64}, {7, 72}, {0x2B, 72}, + {8, 80}, {0x2C, 80}, {9, 88}, {0x2D, 96}, + {0xA, 104}, {0x2E, 112}, {0xB, 128}, {0x2F, 128}, + {0xC, 144}, {0xD, 160}, {0x30, 160}, {0xE, 192}, + {0x31, 192}, {0x32, 224}, {0xF, 240}, {0x33, 256}, + {0x10, 288}, {0x11, 320}, {0x34, 320}, {0x12, 384}, + {0x35, 384}, {0x36, 448}, {0x13, 480}, {0x37, 512}, + {0x14, 576}, {0x15, 640}, {0x38, 640}, {0x16, 768}, + {0x39, 768}, {0x3A, 896}, {0x17, 960}, {0x3B, 1024}, + {0x18, 1152}, {0x19, 1280}, {0x3C, 1280}, {0x1A, 1536}, + {0x3D, 1536}, {0x3E, 1792}, {0x1B, 1920}, {0x3F, 2048}, + {0x1C, 2304}, {0x1D, 2560}, {0x1E, 3072}, {0x1F, 3840}, + {0, 0} +}; + +extern void gpio_i2c_active(int i2c_num); +extern void gpio_i2c_inactive(int i2c_num); + +/*! + * Transmit a \b STOP signal to the slave device. + * + * @param dev the mxc i2c structure used to get to the right i2c device + */ +static void mxc_i2c_stop(mxc_i2c_device *dev) +{ + unsigned int cr, sr; + int retry = 16; + + cr = readw(dev->membase + MXC_I2CR); + cr &= ~(MXC_I2CR_MSTA | MXC_I2CR_MTX); + writew(cr, dev->membase + MXC_I2CR); + + /* Wait till the Bus Busy bit is reset */ + sr = readw(dev->membase + MXC_I2SR); + while (retry-- && ((sr & MXC_I2SR_IBB))) { + udelay(3); + sr = readw(dev->membase + MXC_I2SR); + } + if (retry <= 0) + dev_err(&dev->adap.dev, "Could not set I2C Bus Busy bit" + " to zero.\n"); +} + +/*! + * Wait for the transmission of the data byte to complete. This function waits + * till we get a signal from the interrupt service routine indicating completion + * of the address cycle or we time out. + * + * @param dev the mxc i2c structure used to get to the right i2c device + * @param trans_flag transfer flag + * + * + * @return The function returns 0 on success or -1 if an ack was not received + */ + +static int mxc_i2c_wait_for_tc(mxc_i2c_device *dev, int trans_flag) +{ + int retry = 16; + + while (retry-- && !dev->transfer_done) { + wait_event_interruptible_timeout(dev->wq, + dev->transfer_done, + dev->adap.timeout); + } + dev->transfer_done = false; + + if (retry <= 0) { + /* Unable to send data */ + dev_err(&dev->adap.dev, "Data not transmitted\n"); + return -1; + } + + if (!dev->tx_success) { + /* An ACK was not received for transmitted byte */ + dev_err(&dev->adap.dev, "ACK not received \n"); + return -1; + } + + return 0; +} + +/*! + * Transmit a \b START signal to the slave device. + * + * @param dev the mxc i2c structure used to get to the right i2c device + * @param *msg pointer to a message structure that contains the slave + * address + * + * @return The function returns EBUSY on failure, 0 on success. + */ +static int mxc_i2c_start(mxc_i2c_device *dev, struct i2c_msg *msg) +{ + volatile unsigned int cr, sr; + unsigned int addr_trans; + int retry = 16; + + /* + * Set the slave address and the requested transfer mode + * in the data register + */ + addr_trans = msg->addr << 1; + if (msg->flags & I2C_M_RD) { + addr_trans |= 0x01; + } + + /* Set the Master bit */ + cr = readw(dev->membase + MXC_I2CR); + cr |= MXC_I2CR_MSTA; + writew(cr, dev->membase + MXC_I2CR); + + /* Wait till the Bus Busy bit is set */ + sr = readw(dev->membase + MXC_I2SR); + while (retry-- && (!(sr & MXC_I2SR_IBB))) { + udelay(3); + sr = readw(dev->membase + MXC_I2SR); + } + if (retry <= 0) { + dev_err(&dev->adap.dev, "Could not grab Bus ownership\n"); + return -EBUSY; + } + + /* Set the Transmit bit */ + cr = readw(dev->membase + MXC_I2CR); + cr |= MXC_I2CR_MTX; + writew(cr, dev->membase + MXC_I2CR); + + writew(addr_trans, dev->membase + MXC_I2DR); + return 0; +} + +/*! + * Transmit a \b REPEAT START to the slave device + * + * @param dev the mxc i2c structure used to get to the right i2c device + * @param *msg pointer to a message structure that contains the slave + * address + */ +static int mxc_i2c_repstart(mxc_i2c_device *dev, struct i2c_msg *msg) +{ + volatile unsigned int cr, sr; + unsigned int addr_trans; + int retry = 16; + + /* + * Set the slave address and the requested transfer mode + * in the data register + */ + addr_trans = msg->addr << 1; + if (msg->flags & I2C_M_RD) { + addr_trans |= 0x01; + } + cr = readw(dev->membase + MXC_I2CR); + cr |= MXC_I2CR_RSTA; + writew(cr, dev->membase + MXC_I2CR); + /* Wait till the Bus Busy bit is set */ + sr = readw(dev->membase + MXC_I2SR); + while (retry-- && (!(sr & MXC_I2SR_IBB))) { + udelay(3); + sr = readw(dev->membase + MXC_I2SR); + } + if (retry <= 0) { + dev_err(&dev->adap.dev, "Could not grab Bus ownership\n"); + return -EBUSY; + } + writew(addr_trans, dev->membase + MXC_I2DR); + return 0; +} + +/*! + * Read the received data. The function waits till data is available or times + * out. Generates a stop signal if this is the last message to be received. + * Sends an ack for all the bytes received except the last byte. + * + * @param dev the mxc i2c structure used to get to the right i2c device + * @param *msg pointer to a message structure that contains the slave + * address and a pointer to the receive buffer + * @param last indicates that this is the last message to be received + * @param addr_comp flag indicates that we just finished the address cycle + * + * @return The function returns the number of bytes read or -1 on time out. + */ +static int mxc_i2c_readbytes(mxc_i2c_device *dev, struct i2c_msg *msg, + int last, int addr_comp) +{ + int i; + char *buf = msg->buf; + int len = msg->len; + volatile unsigned int cr; + + cr = readw(dev->membase + MXC_I2CR); + /* + * Clear MTX to switch to receive mode. + */ + cr &= ~MXC_I2CR_MTX; + /* + * Clear the TXAK bit to gen an ack when receiving only one byte. + */ + if (len == 1) { + cr |= MXC_I2CR_TXAK; + } else { + cr &= ~MXC_I2CR_TXAK; + } + writew(cr, dev->membase + MXC_I2CR); + /* + * Dummy read only at the end of an address cycle + */ + if (addr_comp > 0) { + readw(dev->membase + MXC_I2DR); + } + + for (i = 0; i < len; i++) { + /* + * Wait for data transmission to complete + */ + if (mxc_i2c_wait_for_tc(dev, msg->flags)) { + mxc_i2c_stop(dev); + return -1; + } + /* + * Do not generate an ACK for the last byte + */ + if (i == (len - 2)) { + cr = readw(dev->membase + MXC_I2CR); + cr |= MXC_I2CR_TXAK; + writew(cr, dev->membase + MXC_I2CR); + } else if (i == (len - 1)) { + if (last) { + mxc_i2c_stop(dev); + } + } + /* Read the data */ + *buf++ = readw(dev->membase + MXC_I2DR); + } + + return i; +} + +/*! + * Write the data to the data register. Generates a stop signal if this is + * the last message to be sent or if no ack was received for the data sent. + * + * @param dev the mxc i2c structure used to get to the right i2c device + * @param *msg pointer to a message structure that contains the slave + * address and data to be sent + * @param last indicates that this is the last message to be received + * + * @return The function returns the number of bytes written or -1 on time out + * or if no ack was received for the data that was sent. + */ +static int mxc_i2c_writebytes(mxc_i2c_device *dev, struct i2c_msg *msg, + int last) +{ + int i; + char *buf = msg->buf; + int len = msg->len; + volatile unsigned int cr; + + cr = readw(dev->membase + MXC_I2CR); + /* Set MTX to switch to transmit mode */ + cr |= MXC_I2CR_MTX; + writew(cr, dev->membase + MXC_I2CR); + + for (i = 0; i < len; i++) { + /* + * Write the data + */ + writew(*buf++, dev->membase + MXC_I2DR); + if (mxc_i2c_wait_for_tc(dev, msg->flags)) { + mxc_i2c_stop(dev); + return -1; + } + } + if (last > 0) { + mxc_i2c_stop(dev); + } + + return i; +} + +/*! + * Function enables the I2C module and initializes the registers. + * + * @param dev the mxc i2c structure used to get to the right i2c device + * @param trans_flag transfer flag + */ +static void mxc_i2c_module_en(mxc_i2c_device *dev, int trans_flag) +{ + clk_enable(dev->clk); + /* Set the frequency divider */ + writew(dev->clkdiv, dev->membase + MXC_IFDR); + /* Clear the status register */ + writew(0x0, dev->membase + MXC_I2SR); + /* Enable I2C and its interrupts */ + writew(MXC_I2CR_IEN, dev->membase + MXC_I2CR); + writew(MXC_I2CR_IEN | MXC_I2CR_IIEN, dev->membase + MXC_I2CR); +} + +/*! + * Disables the I2C module. + * + * @param dev the mxc i2c structure used to get to the right i2c device + */ +static void mxc_i2c_module_dis(mxc_i2c_device *dev) +{ + writew(0x0, dev->membase + MXC_I2CR); + clk_disable(dev->clk); +} + +/*! + * The function is registered in the adapter structure. It is called when an MXC + * driver wishes to transfer data to a device connected to the I2C device. + * + * @param adap adapter structure for the MXC i2c device + * @param msgs[] array of messages to be transferred to the device + * @param num number of messages to be transferred to the device + * + * @return The function returns the number of messages transferred, + * \b -EREMOTEIO on I2C failure and a 0 if the num argument is + * less than 0. + */ +static int mxc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + mxc_i2c_device *dev = (mxc_i2c_device *) (i2c_get_adapdata(adap)); + int i, ret = 0, addr_comp = 0; + volatile unsigned int sr; + int retry = 5; + + if (dev->low_power) { + dev_err(&dev->adap.dev, "I2C Device in low power mode\n"); + return -EREMOTEIO; + } + + if (num < 1) { + return 0; + } + + mxc_i2c_module_en(dev, msgs[0].flags); + sr = readw(dev->membase + MXC_I2SR); + /* + * Check bus state + */ + + while ((sr & MXC_I2SR_IBB) && retry--) { + udelay(5); + sr = readw(dev->membase + MXC_I2SR); + } + + if ((sr & MXC_I2SR_IBB) && retry < 0) { + mxc_i2c_module_dis(dev); + dev_err(&dev->adap.dev, "Bus busy\n"); + return -EREMOTEIO; + } + + dev->transfer_done = false; + dev->tx_success = false; + for (i = 0; i < num && ret >= 0; i++) { + addr_comp = 0; + /* + * Send the slave address and transfer direction in the + * address cycle + */ + if (i == 0) { + /* + * Send a start or repeat start signal + */ + if (mxc_i2c_start(dev, &msgs[0])) + return -EREMOTEIO; + /* Wait for the address cycle to complete */ + if (mxc_i2c_wait_for_tc(dev, msgs[0].flags)) { + mxc_i2c_stop(dev); + mxc_i2c_module_dis(dev); + return -EREMOTEIO; + } + addr_comp = 1; + } else { + /* + * Generate repeat start only if required i.e the address + * changed or the transfer direction changed + */ + if ((msgs[i].addr != msgs[i - 1].addr) || + ((msgs[i].flags & I2C_M_RD) != + (msgs[i - 1].flags & I2C_M_RD))) { + mxc_i2c_repstart(dev, &msgs[i]); + /* Wait for the address cycle to complete */ + if (mxc_i2c_wait_for_tc(dev, msgs[i].flags)) { + mxc_i2c_stop(dev); + mxc_i2c_module_dis(dev); + return -EREMOTEIO; + } + addr_comp = 1; + } + } + + /* Transfer the data */ + if (msgs[i].flags & I2C_M_RD) { + /* Read the data */ + ret = mxc_i2c_readbytes(dev, &msgs[i], (i + 1 == num), + addr_comp); + if (ret < 0) { + dev_err(&dev->adap.dev, "mxc_i2c_readbytes:" + " fail.\n"); + break; + } + } else { + /* Write the data */ + ret = mxc_i2c_writebytes(dev, &msgs[i], (i + 1 == num)); + if (ret < 0) { + dev_err(&dev->adap.dev, "mxc_i2c_writebytes:" + " fail.\n"); + break; + } + } + } + + mxc_i2c_module_dis(dev); + /* + * Decrease by 1 as we do not want Start message to be included in + * the count + */ + return (i < 0 ? ret : i); +} + +/*! + * Returns the i2c functionality supported by this driver. + * + * @param adap adapter structure for this i2c device + * + * @return Returns the functionality that is supported. + */ +static u32 mxc_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +/*! + * Stores the pointers for the i2c algorithm functions. The algorithm functions + * is used by the i2c bus driver to talk to the i2c bus + */ +static struct i2c_algorithm mxc_i2c_algorithm = { + .master_xfer = mxc_i2c_xfer, + .functionality = mxc_i2c_func +}; + +/*! + * Interrupt Service Routine. It signals to the process about the data transfer + * completion. Also sets a flag if bus arbitration is lost. + * @param irq the interrupt number + * @param dev_id driver private data + * + * @return The function returns \b IRQ_HANDLED. + */ +static irqreturn_t mxc_i2c_handler(int irq, void *dev_id) +{ + mxc_i2c_device *dev = dev_id; + volatile unsigned int sr, cr; + + sr = readw(dev->membase + MXC_I2SR); + cr = readw(dev->membase + MXC_I2CR); + + /* + * Clear the interrupt bit + */ + writew(0x0, dev->membase + MXC_I2SR); + + if (sr & MXC_I2SR_IAL) { + dev_err(&dev->adap.dev, "Bus Arbitration lost\n"); + } else { + /* Interrupt due byte transfer completion */ + dev->tx_success = true; + /* Check if RXAK is received in Transmit mode */ + if ((cr & MXC_I2CR_MTX) && (sr & MXC_I2SR_RXAK)) { + dev->tx_success = false; + } + dev->transfer_done = true; + wake_up_interruptible(&dev->wq); + } + + return IRQ_HANDLED; +} + +/*! + * This function is called to put the I2C adapter in a low power state. Refer to the + * document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device structure used to give information on which I2C + * to suspend + * @param state the power state the device is entering + * + * @return The function returns 0 on success and -1 on failure. + */ +static int mxci2c_suspend_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + mxc_i2c_device *mxcdev = platform_get_drvdata(pdev); + volatile unsigned int sr = 0; + + if (mxcdev == NULL) { + return -1; + } + + /* Prevent further calls to be processed */ + mxcdev->low_power = true; + /* Wait till we finish the current transfer */ + sr = readw(mxcdev->membase + MXC_I2SR); + while (sr & MXC_I2SR_IBB) { + msleep(10); + sr = readw(mxcdev->membase + MXC_I2SR); + } + gpio_i2c_inactive(mxcdev->adap.id); + + return 0; +} + +/*! + * This function is called to bring the I2C adapter back from a low power state. Refer + * to the document driver-model/driver.txt in the kernel source tree for more + * information. + * + * @param pdev the device structure used to give information on which I2C + * to resume + * + * @return The function returns 0 on success and -1 on failure + */ +static int mxci2c_resume_noirq(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + mxc_i2c_device *mxcdev = platform_get_drvdata(pdev); + + if (mxcdev == NULL) + return -1; + + mxcdev->low_power = false; + gpio_i2c_active(mxcdev->adap.id); + + return 0; +} + +/*! + * This function is called during the driver binding process. + * + * @param pdev the device structure used to store device specific + * information that is used by the suspend, resume and remove + * functions + * + * @return The function always returns 0. + */ +static int mxci2c_probe(struct platform_device *pdev) +{ + mxc_i2c_device *mxc_i2c; + struct mxc_i2c_platform_data *i2c_plat_data = pdev->dev.platform_data; + struct resource *res; + int id = pdev->id; + u32 clk_freq; + int ret = 0; + int i; + + mxc_i2c = kzalloc(sizeof(mxc_i2c_device), GFP_KERNEL); + if (!mxc_i2c) { + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + ret = -ENODEV; + goto err1; + } + mxc_i2c->membase = ioremap(res->start, res->end - res->start + 1); + + /* + * Request the I2C interrupt + */ + mxc_i2c->irq = platform_get_irq(pdev, 0); + if (mxc_i2c->irq < 0) { + ret = mxc_i2c->irq; + goto err2; + } + + ret = request_irq(mxc_i2c->irq, mxc_i2c_handler, + 0, pdev->name, mxc_i2c); + if (ret < 0) { + goto err2; + } + + init_waitqueue_head(&mxc_i2c->wq); + + mxc_i2c->low_power = false; + + gpio_i2c_active(id); + + mxc_i2c->clk = clk_get(&pdev->dev, "i2c_clk"); + clk_freq = clk_get_rate(mxc_i2c->clk); + mxc_i2c->clkdiv = -1; + if (i2c_plat_data->i2c_clk) { + /* Calculate divider and round up any fractional part */ + int div = (clk_freq + i2c_plat_data->i2c_clk - 1) / + i2c_plat_data->i2c_clk; + for (i = 0; i2c_clk_table[i].div != 0; i++) { + if (i2c_clk_table[i].div >= div) { + mxc_i2c->clkdiv = i2c_clk_table[i].reg_value; + break; + } + } + } + if (mxc_i2c->clkdiv == -1) { + i--; + mxc_i2c->clkdiv = 0x1F; /* Use max divider */ + } + dev_dbg(&pdev->dev, "i2c speed is %d/%d = %d bps, reg val = 0x%02X\n", + clk_freq, i2c_clk_table[i].div, + clk_freq / i2c_clk_table[i].div, mxc_i2c->clkdiv); + + /* + * Set the adapter information + */ + strlcpy(mxc_i2c->adap.name, pdev->name, 48); + mxc_i2c->adap.id = mxc_i2c->adap.nr = id; + mxc_i2c->adap.algo = &mxc_i2c_algorithm; + mxc_i2c->adap.timeout = 1; + platform_set_drvdata(pdev, mxc_i2c); + i2c_set_adapdata(&mxc_i2c->adap, mxc_i2c); + ret = i2c_add_numbered_adapter(&mxc_i2c->adap); + if (ret < 0) + goto err3; + + printk(KERN_INFO "MXC I2C driver\n"); + return 0; + + err3: + free_irq(mxc_i2c->irq, mxc_i2c); + gpio_i2c_inactive(id); + err2: + iounmap(mxc_i2c->membase); + err1: + dev_err(&pdev->dev, "failed to probe i2c adapter\n"); + kfree(mxc_i2c); + return ret; +} + +/*! + * Dissociates the driver from the I2C device. + * + * @param pdev the device structure used to give information on which I2C + * to remove + * + * @return The function always returns 0. + */ +static int mxci2c_remove(struct platform_device *pdev) +{ + mxc_i2c_device *mxc_i2c = platform_get_drvdata(pdev); + int id = pdev->id; + + free_irq(mxc_i2c->irq, mxc_i2c); + i2c_del_adapter(&mxc_i2c->adap); + gpio_i2c_inactive(id); + clk_put(mxc_i2c->clk); + platform_set_drvdata(pdev, NULL); + iounmap(mxc_i2c->membase); + kfree(mxc_i2c); + return 0; +} + +static struct dev_pm_ops mxci2c_dev_pm_ops = { + .suspend_noirq = mxci2c_suspend_noirq, + .resume_noirq = mxci2c_resume_noirq, +}; + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct platform_driver mxci2c_driver = { + .driver = { + .name = "mxc_i2c", + .owner = THIS_MODULE, + .pm = &mxci2c_dev_pm_ops, + }, + .probe = mxci2c_probe, + .remove = mxci2c_remove, +}; + +/*! + * Function requests the interrupts and registers the i2c adapter structures. + * + * @return The function returns 0 on success and a non-zero value on failure. + */ +static int __init mxc_i2c_init(void) +{ + /* Register the device driver structure. */ + return platform_driver_register(&mxci2c_driver); +} + +/*! + * This function is used to cleanup all resources before the driver exits. + */ +static void __exit mxc_i2c_exit(void) +{ + platform_driver_unregister(&mxci2c_driver); +} + +subsys_initcall(mxc_i2c_init); +module_exit(mxc_i2c_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/mxc_i2c_hs.c b/drivers/i2c/busses/mxc_i2c_hs.c new file mode 100644 index 000000000000..60262741cd54 --- /dev/null +++ b/drivers/i2c/busses/mxc_i2c_hs.c @@ -0,0 +1,553 @@ +/* + * Copyright (C) 2008-2010 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/kernel.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/fsl_devices.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <asm/irq.h> +#include "mxc_i2c_hs_reg.h" + +typedef struct { + struct device *dev; + + void __iomem *reg_base_virt; + unsigned long reg_base_phy; + int irq; + unsigned int speed; + struct clk *ipg_clk; + struct clk *serial_clk; + bool low_power; + + struct i2c_msg *msg; + int index; +} mxc_i2c_hs; + +struct clk_div_table { + int reg_value; + int div; +}; + +static const struct clk_div_table i2c_clk_table[] = { + {0x0, 16}, {0x1, 18}, {0x2, 20}, {0x3, 22}, + {0x20, 24}, {0x21, 26}, {0x22, 28}, {0x23, 30}, + {0x4, 32}, {0x5, 36}, {0x6, 40}, {0x7, 44}, + {0x24, 48}, {0x25, 52}, {0x26, 56}, {0x27, 60}, + {0x8, 64}, {0x9, 72}, {0xa, 80}, {0xb, 88}, + {0x28, 96}, {0x29, 104}, {0x2a, 112}, {0x2b, 120}, + {0xc, 128}, {0xd, 144}, {0xe, 160}, {0xf, 176}, + {0x2c, 192}, {0x2d, 208}, {0x2e, 224}, {0x2f, 240}, + {0x10, 256}, {0x11, 288}, {0x12, 320}, {0x13, 352}, + {0x30, 384}, {0x31, 416}, {0x32, 448}, {0x33, 480}, + {0x14, 512}, {0x15, 576}, {0x16, 640}, {0x17, 704}, + {0x34, 768}, {0x35, 832}, {0x36, 896}, {0x37, 960}, + {0x18, 1024}, {0x19, 1152}, {0x1a, 1280}, {0x1b, 1408}, + {0x38, 1536}, {0x39, 1664}, {0x3a, 1792}, {0x3b, 1920}, + {0x1c, 2048}, {0x1d, 2304}, {0x1e, 2560}, {0x1f, 2816}, + {0x3c, 3072}, {0x3d, 3328}, {0x3E, 3584}, {0x3F, 3840}, + {-1, -1} +}; + +static struct i2c_adapter *adap; + +extern void gpio_i2c_hs_inactive(void); +extern void gpio_i2c_hs_active(void); + +static u16 reg_read(mxc_i2c_hs *i2c_hs, u32 reg_offset) +{ + return __raw_readw(i2c_hs->reg_base_virt + reg_offset); +} + +static void reg_write(mxc_i2c_hs *i2c_hs, u32 reg_offset, u16 data) +{ + __raw_writew(data, i2c_hs->reg_base_virt + reg_offset); +} + +static void reg_set_mask(mxc_i2c_hs *i2c_hs, u32 reg_offset, u16 mask) +{ + u16 value; + + value = reg_read(i2c_hs, reg_offset); + value |= mask; + reg_write(i2c_hs, reg_offset, value); +} +static void reg_clear_mask(mxc_i2c_hs *i2c_hs, u32 reg_offset, u16 mask) +{ + u16 value; + + value = reg_read(i2c_hs, reg_offset); + value &= ~mask; + reg_write(i2c_hs, reg_offset, value); +} + +static void mxci2c_hs_set_div(mxc_i2c_hs *i2c_hs) +{ + unsigned long clk_freq; + int i; + int div = -1;; + + clk_freq = clk_get_rate(i2c_hs->serial_clk); + if (i2c_hs->speed) { + div = (clk_freq + i2c_hs->speed - 1) / i2c_hs->speed; + for (i = 0; i2c_clk_table[i].div >= 0; i++) { + if (i2c_clk_table[i].div >= div) { + div = i2c_clk_table[i].reg_value; + reg_write(i2c_hs, HIFSFDR, div); + break; + } + } + } +} + +static int mxci2c_hs_enable(mxc_i2c_hs *i2c_hs) +{ + gpio_i2c_hs_active(); + clk_enable(i2c_hs->ipg_clk); + clk_enable(i2c_hs->serial_clk); + mxci2c_hs_set_div(i2c_hs); + reg_write(i2c_hs, HICR, reg_read(i2c_hs, HICR) | HICR_HIEN); + + return 0; +} + +static int mxci2c_hs_disable(mxc_i2c_hs *i2c_hs) +{ + reg_write(i2c_hs, HICR, reg_read(i2c_hs, HICR) & (~HICR_HIEN)); + clk_disable(i2c_hs->ipg_clk); + clk_disable(i2c_hs->serial_clk); + + return 0; +} + +static int mxci2c_hs_bus_busy(mxc_i2c_hs *i2c_hs) +{ + u16 value; + int retry = 1000; + + while (retry--) { + value = reg_read(i2c_hs, HISR); + if (value & HISR_HIBB) { + udelay(1); + } else { + break; + } + } + + if (retry <= 0) { + dev_dbg(NULL, "%s: Bus Busy!\n", __func__); + return 1; + } else { + return 0; + } +} + +static int mxci2c_hs_start(mxc_i2c_hs *i2c_hs, int repeat_start, u16 address) +{ + u16 mask; + int ret = 0; + + mxci2c_hs_bus_busy(i2c_hs); + + /*7 bit address */ + reg_clear_mask(i2c_hs, HICR, HICR_ADDR_MODE); + + /*send start */ + if (repeat_start) + mask = HICR_RSTA; + else + mask = HICR_MSTA; + reg_set_mask(i2c_hs, HICR, mask); + + return ret; +} + +static int mxci2c_hs_stop(mxc_i2c_hs *i2c_hs) +{ + reg_clear_mask(i2c_hs, HICR, HICR_MSTA); + reg_clear_mask(i2c_hs, HICR, HICR_HIIEN); + + return 0; +} + +static int mxci2c_wait_writefifo(mxc_i2c_hs *i2c_hs) +{ + int i, num, left; + int retry, ret = 0; + + retry = 10000; + while (retry--) { + udelay(10); + if (reg_read(i2c_hs, HISR) & (HISR_TDE | HISR_TDC_ZERO)) { + if (i2c_hs->index < i2c_hs->msg->len) { + left = i2c_hs->msg->len - i2c_hs->index; + num = + (left > + HITFR_MAX_COUNT) ? HITFR_MAX_COUNT : left; + for (i = 0; i < num; i++) { + reg_write(i2c_hs, HITDR, + i2c_hs->msg->buf[i2c_hs-> + index + i]); + } + i2c_hs->index += num; + } else { + if (reg_read(i2c_hs, HISR) & HISR_TDC_ZERO) { + msleep(1); + break; + } + } + } + } + + if (retry <= 0) { + printk(KERN_ERR "%s:wait error\n", __func__); + ret = -1; + } + + return ret; +} + +static int mxci2c_wait_readfifo(mxc_i2c_hs *i2c_hs) +{ + int i, num, left; + int retry, ret = 0; + u16 value; + + retry = 10000; + while (retry--) { + udelay(10); + value = reg_read(i2c_hs, HISR); + if (value & (HISR_RDF | HISR_RDC_ZERO)) { + if (i2c_hs->index < i2c_hs->msg->len) { + left = i2c_hs->msg->len - i2c_hs->index; + num = + (left > + HITFR_MAX_COUNT) ? HITFR_MAX_COUNT : left; + for (i = 0; i < num; i++) { + i2c_hs->msg->buf[i2c_hs->index + i] = + reg_read(i2c_hs, HIRDR); + } + i2c_hs->index += num; + } else { + if (value & HISR_RDC_ZERO) { + break; + } + } + } + } + + if (retry <= 0) { + printk(KERN_ERR "%s:wait error\n", __func__); + ret = -1; + } + + return ret; +} + +static int mxci2c_hs_read(mxc_i2c_hs *i2c_hs, int repeat_start, + struct i2c_msg *msg) +{ + int ret; + + if (msg->len > HIRDCR_MAX_COUNT) { + printk(KERN_ERR "%s: error: msg too long, max longth 256\n", + __func__); + return -1; + } + + ret = 0; + i2c_hs->msg = msg; + i2c_hs->index = 0; + + /*set address */ + reg_write(i2c_hs, HIMADR, HIMADR_LSB_ADR(msg->addr)); + + /*receive mode */ + reg_clear_mask(i2c_hs, HICR, HICR_MTX); + + reg_clear_mask(i2c_hs, HICR, HICR_HIIEN); + + /*FIFO*/ reg_set_mask(i2c_hs, HIRFR, HIRFR_RFEN | HIRFR_RFWM(7)); + reg_set_mask(i2c_hs, HIRFR, HIRFR_RFLSH); + + /*TDCR*/ + reg_write(i2c_hs, HIRDCR, HIRDCR_RDC_EN | HIRDCR_RDC(msg->len)); + + mxci2c_hs_start(i2c_hs, repeat_start, msg->addr); + + ret = mxci2c_wait_readfifo(i2c_hs); + + if (ret < 0) + return ret; + else + return msg->len; +} + +static int mxci2c_hs_write(mxc_i2c_hs *i2c_hs, int repeat_start, + struct i2c_msg *msg) +{ + int ret, i; + + if (msg->len > HITDCR_MAX_COUNT) { + printk(KERN_ERR "%s: error: msg too long, max longth 256\n", + __func__); + return -1; + } + + ret = 0; + i2c_hs->msg = msg; + i2c_hs->index = 0; + + /*set address */ + reg_write(i2c_hs, HIMADR, HIMADR_LSB_ADR(msg->addr)); + + /*transmit mode */ + reg_set_mask(i2c_hs, HICR, HICR_MTX); + + reg_clear_mask(i2c_hs, HICR, HICR_HIIEN); + + /* TDCR */ + reg_write(i2c_hs, HITDCR, HITDCR_TDC_EN | HITDCR_TDC(msg->len)); + + /* FIFO */ + reg_set_mask(i2c_hs, HITFR, HITFR_TFEN); + reg_set_mask(i2c_hs, HITFR, HITFR_TFLSH); + + if (msg->len > HITFR_MAX_COUNT) + i2c_hs->index = HITFR_MAX_COUNT; + else { + i2c_hs->index = msg->len; + } + + for (i = 0; i < i2c_hs->index; i++) { + reg_write(i2c_hs, HITDR, msg->buf[i]); + } + + mxci2c_hs_start(i2c_hs, repeat_start, msg->addr); + + ret = mxci2c_wait_writefifo(i2c_hs); + + if (ret < 0) + return ret; + else + return msg->len; +} + +static int mxci2c_hs_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + int i; + int ret = -EIO; + + mxc_i2c_hs *i2c_hs = (mxc_i2c_hs *) (i2c_get_adapdata(adap)); + + if (i2c_hs->low_power) { + dev_err(&adap->dev, "I2C Device in low power mode\n"); + return -EREMOTEIO; + } + + if (num < 1) { + return 0; + } + + mxci2c_hs_enable(i2c_hs); + + for (i = 0; i < num; i++) { + if (msgs[i].flags & I2C_M_RD) { + ret = mxci2c_hs_read(i2c_hs, 0, &msgs[i]); + if (ret < 0) + break; + } else { + ret = mxci2c_hs_write(i2c_hs, 0, &msgs[i]); + if (ret < 0) + break; + } + mxci2c_hs_stop(i2c_hs); + } + mxci2c_hs_stop(i2c_hs); + + mxci2c_hs_disable(i2c_hs); + + if (ret < 0) + return ret; + + return i; +} + +static u32 mxci2c_hs_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +/*! + * Stores the pointers for the i2c algorithm functions. The algorithm functions + * is used by the i2c bus driver to talk to the i2c bus + */ +static struct i2c_algorithm mxci2c_hs_algorithm = { + .master_xfer = mxci2c_hs_xfer, + .functionality = mxci2c_hs_func +}; + +static int mxci2c_hs_probe(struct platform_device *pdev) +{ + mxc_i2c_hs *i2c_hs; + struct mxc_i2c_platform_data *i2c_plat_data = pdev->dev.platform_data; + struct resource *res; + int id = pdev->id; + int ret = 0; + + i2c_hs = kzalloc(sizeof(mxc_i2c_hs), GFP_KERNEL); + if (!i2c_hs) { + return -ENOMEM; + } + + i2c_hs->dev = &pdev->dev; + + i2c_hs->speed = i2c_plat_data->i2c_clk; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + ret = -ENODEV; + goto err1; + } + i2c_hs->reg_base_virt = ioremap(res->start, res->end - res->start + 1); + i2c_hs->reg_base_phy = res->start; + + i2c_hs->ipg_clk = clk_get(&pdev->dev, "hsi2c_clk"); + i2c_hs->serial_clk = clk_get(&pdev->dev, "hsi2c_serial_clk"); + + /* + * Request the I2C interrupt + */ + i2c_hs->irq = platform_get_irq(pdev, 0); + if (i2c_hs->irq < 0) { + ret = i2c_hs->irq; + goto err1; + } + + i2c_hs->low_power = false; + + /* + * Set the adapter information + */ + adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); + if (!adap) { + ret = -ENODEV; + goto err1; + } + strlcpy(adap->name, pdev->name, 48); + adap->id = adap->nr = id; + adap->algo = &mxci2c_hs_algorithm; + adap->timeout = 1; + platform_set_drvdata(pdev, i2c_hs); + i2c_set_adapdata(adap, i2c_hs); + ret = i2c_add_numbered_adapter(adap); + if (ret < 0) { + goto err2; + } + + printk(KERN_INFO "MXC HS I2C driver\n"); + return 0; + + err2: + kfree(adap); + err1: + dev_err(&pdev->dev, "failed to probe high speed i2c adapter\n"); + iounmap(i2c_hs->reg_base_virt); + kfree(i2c_hs); + return ret; +} + +static int mxci2c_hs_suspend(struct platform_device *pdev, pm_message_t state) +{ + mxc_i2c_hs *i2c_hs = platform_get_drvdata(pdev); + + if (i2c_hs == NULL) { + return -1; + } + + /* Prevent further calls to be processed */ + i2c_hs->low_power = true; + + gpio_i2c_hs_inactive(); + + return 0; +} + +static int mxci2c_hs_resume(struct platform_device *pdev) +{ + mxc_i2c_hs *i2c_hs = platform_get_drvdata(pdev); + + if (i2c_hs == NULL) + return -1; + + i2c_hs->low_power = false; + gpio_i2c_hs_active(); + + return 0; +} + +static int mxci2c_hs_remove(struct platform_device *pdev) +{ + mxc_i2c_hs *i2c_hs = platform_get_drvdata(pdev); + + i2c_del_adapter(adap); + gpio_i2c_hs_inactive(); + platform_set_drvdata(pdev, NULL); + iounmap(i2c_hs->reg_base_virt); + kfree(i2c_hs); + return 0; +} + +static struct platform_driver mxci2c_hs_driver = { + .driver = { + .name = "mxc_i2c_hs", + .owner = THIS_MODULE, + }, + .probe = mxci2c_hs_probe, + .remove = mxci2c_hs_remove, + .suspend = mxci2c_hs_suspend, + .resume = mxci2c_hs_resume, +}; + +/*! + * Function requests the interrupts and registers the i2c adapter structures. + * + * @return The function returns 0 on success and a non-zero value on failure. + */ +static int __init mxci2c_hs_init(void) +{ + /* Register the device driver structure. */ + return platform_driver_register(&mxci2c_hs_driver); +} + +/*! + * This function is used to cleanup all resources before the driver exits. + */ +static void __exit mxci2c_hs_exit(void) +{ + platform_driver_unregister(&mxci2c_hs_driver); +} + +subsys_initcall(mxci2c_hs_init); +module_exit(mxci2c_hs_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC HIGH SPEED I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/mxc_i2c_hs_reg.h b/drivers/i2c/busses/mxc_i2c_hs_reg.h new file mode 100644 index 000000000000..5a017cb17765 --- /dev/null +++ b/drivers/i2c/busses/mxc_i2c_hs_reg.h @@ -0,0 +1,97 @@ +/* + * Copyright 2008-2010 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 + */ +#ifndef __MXC_I2C_HS_REG_H__ +#define __MXC_I2C_HS_REG_H__ + +#define HISADR 0x00 + +#define HIMADR 0x04 +#define HIMADR_LSB_ADR(x) ((x) << 1) +#define HIMADR_MSB_ADR(x) (((x) & 0x7) << 8) + +#define HICR 0x08 +#define HICR_HIEN 0x1 +#define HICR_DMA_EN_RX 0x2 +#define HICR_DMA_EN_TR 0x4 +#define HICR_RSTA 0x8 +#define HICR_TXAK 0x10 +#define HICR_MTX 0x20 +#define HICR_MSTA 0x40 +#define HICR_HIIEN 0x80 +#define HICR_ADDR_MODE 0x100 +#define HICR_MST_CODE(x) (((x)&0x7) << 9) +#define HICR_HSM_EN 0x1000 +#define HICR_SAMC(x) (((x)&0x3) << 13) +#define SAMC_7_10 0 +#define SMAC_7 1 +#define SMAC_10 2 + +#define HISR 0x0c +#define HISR_RDF 0x1 +#define HISR_TDE 0x2 +#define HISR_HIAAS 0x4 +#define HISR_HIAL 0x8 +#define HISR_BTD 0x10 +#define HISR_RDC_ZERO 0x20 +#define HISR_TDC_ZERO 0x40 +#define HISR_RXAK 0x80 +#define HISR_HIBB 0x100 +#define HISR_SRW 0x200 +#define HISR_SADDR_MODE 0x400 +#define HISR_SHS_MODE 0x800 + +#define HIIMR 0x10 +#define HIIMR_RDF 0x1 +#define HIIMR_TDE 0x2 +#define HIIMR_AAS 0x4 +#define HIIMR_AL 0x8 +#define HIIMR_BTD 0x10 +#define HIIMR_RDC 0x20 +#define HIIMR_TDC 0x40 +#define HIIMR_RXAK 0x80 + +#define HITDR 0x14 + +#define HIRDR 0x18 + +#define HIFSFDR 0x1c + +#define HIHSFDR 0x20 + +#define HITFR 0x24 +#define HITFR_TFEN 0x1 +#define HITFR_TFLSH 0x2 +#define HITFR_TFWM(x) (((x) & 0x7) << 2) +#define HITFR_TFC(x) (((x) >> 8) & 0xF) +#define HITFR_MAX_COUNT 8 + +#define HIRFR 0x28 +#define HIRFR_RFEN 0x1 +#define HIRFR_RFLSH 0x2 +#define HIRFR_RFWM(x) (((x) & 0x7) << 2) +#define HIRFR_RFC(x) (((x) >> 8) & 0xF) +#define HIRFR_MAX_COUNT 8 + +#define HITDCR 0x2c +#define HITDCR_TDC(x) ((x) & 0xFF) +#define HITDCR_TDC_EN 0x100 +#define HITDCR_TDC_RSTA 0x200 +#define HITDCR_MAX_COUNT 0xFF + +#define HIRDCR 0x30 +#define HIRDCR_RDC(x) ((x) & 0xFF) +#define HIRDCR_RDC_EN 0x100 +#define HIRDCR_RDC_RSTA 0x200 +#define HIRDCR_MAX_COUNT 0xFF + +#endif diff --git a/drivers/i2c/busses/mxc_i2c_reg.h b/drivers/i2c/busses/mxc_i2c_reg.h new file mode 100644 index 000000000000..2f81036c6626 --- /dev/null +++ b/drivers/i2c/busses/mxc_i2c_reg.h @@ -0,0 +1,40 @@ +/* + * Copyright 2004-2010 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 + */ +#ifndef __MXC_I2C_REG_H__ +#define __MXC_I2C_REG_H__ + +/* Address offsets of the I2C registers */ +#define MXC_IADR 0x00 /* Address Register */ +#define MXC_IFDR 0x04 /* Freq div register */ +#define MXC_I2CR 0x08 /* Control regsiter */ +#define MXC_I2SR 0x0C /* Status register */ +#define MXC_I2DR 0x10 /* Data I/O register */ + +/* Bit definitions of I2CR */ +#define MXC_I2CR_IEN 0x0080 +#define MXC_I2CR_IIEN 0x0040 +#define MXC_I2CR_MSTA 0x0020 +#define MXC_I2CR_MTX 0x0010 +#define MXC_I2CR_TXAK 0x0008 +#define MXC_I2CR_RSTA 0x0004 + +/* Bit definitions of I2SR */ +#define MXC_I2SR_ICF 0x0080 +#define MXC_I2SR_IAAS 0x0040 +#define MXC_I2SR_IBB 0x0020 +#define MXC_I2SR_IAL 0x0010 +#define MXC_I2SR_SRW 0x0004 +#define MXC_I2SR_IIF 0x0002 +#define MXC_I2SR_RXAK 0x0001 + +#endif /* __MXC_I2C_REG_H__ */ |