* [PATCH, RFC] Freescale STMP: i2c driver
@ 2009-06-03 19:59 dmitry pervushin
[not found] ` <1244059155.4074.19.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>
0 siblings, 1 reply; 11+ messages in thread
From: dmitry pervushin @ 2009-06-03 19:59 UTC (permalink / raw)
To: Ben Dooks; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA
Hello Ben & i2c people,
Here is the patch to support I2C bus on Freescale STMP378x platform; any
comments are welcome
Signed-off-by: Dmitrij Frasenyak <d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
Signed-off-by: dmitry pervishin <dpervushin-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
---
drivers/i2c/busses/Kconfig | 7
drivers/i2c/busses/Makefile | 1
drivers/i2c/busses/i2c-stmp378x.c | 440 ++++++++++++++++++++++++++++++++++++++
3 files changed, 448 insertions(+)
Index: linux-2.6-arm/drivers/i2c/busses/Kconfig
===================================================================
--- linux-2.6-arm.orig/drivers/i2c/busses/Kconfig
+++ linux-2.6-arm/drivers/i2c/busses/Kconfig
@@ -513,6 +513,13 @@ config I2C_SIMTEC
This driver can also be built as a module. If so, the module
will be called i2c-simtec.
+config I2C_STMP378X
+ tristate "STMP378x I2C adapter"
+ depends on ARCH_STMP378X
+ help
+ Include support of I2C adapter on Freescale STMP378x board; to build
+ the driver as a module, say M here - module will be called i2c-stmp378x.
+
config I2C_VERSATILE
tristate "ARM Versatile/Realview I2C bus support"
depends on ARCH_VERSATILE || ARCH_REALVIEW
Index: linux-2.6-arm/drivers/i2c/busses/Makefile
===================================================================
--- linux-2.6-arm.orig/drivers/i2c/busses/Makefile
+++ linux-2.6-arm/drivers/i2c/busses/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
+obj-$(CONFIG_I2C_STMP378X) += i2c-stmp378x.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
# External I2C/SMBus adapter drivers
Index: linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
===================================================================
--- /dev/null
+++ linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
@@ -0,0 +1,440 @@
+/*
+ * Freescale STMP378X I2C bus driver
+ *
+ * Author: Dmitrij Frasenyak <sed-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, 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/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/clk.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+
+#include <mach/platform.h>
+#include <mach/stmp3xxx.h>
+#include <mach/dma.h>
+#include <mach/regs-i2c.h>
+#include <mach/regs-apbx.h>
+
+static const u32 I2C_READ = 1,
+ I2C_WRITE = 0;
+
+static const struct stmp3xxx_dma_command cmd_i2c_select = {
+ .cmd = BF(1, APBX_CHn_CMD_XFER_COUNT) |
+ BF(1, APBX_CHn_CMD_CMDWORDS) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_CHAIN |
+ BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
+ .pio_words = {
+ [0] = BM_I2C_CTRL0_RETAIN_CLOCK |
+ BM_I2C_CTRL0_PRE_SEND_START |
+ BM_I2C_CTRL0_MASTER_MODE |
+ BM_I2C_CTRL0_DIRECTION |
+ BF(1, I2C_CTRL0_XFER_COUNT),
+ },
+};
+
+static const struct stmp3xxx_dma_command cmd_i2c_write = {
+ .cmd = BM_APBX_CHn_CMD_SEMAPHORE |
+ BF(1, APBX_CHn_CMD_CMDWORDS) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_IRQONCMPLT |
+ BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
+ .pio_words = {
+ [0] = BM_I2C_CTRL0_PRE_SEND_START |
+ BM_I2C_CTRL0_MASTER_MODE |
+ BM_I2C_CTRL0_DIRECTION,
+ },
+};
+
+static const struct stmp3xxx_dma_command cmd_i2c_read = {
+ .cmd = BM_APBX_CHn_CMD_SEMAPHORE |
+ BF(1, APBX_CHn_CMD_CMDWORDS) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_IRQONCMPLT |
+ BF(BV_APBX_CHn_CMD_COMMAND__DMA_WRITE, APBX_CHn_CMD_COMMAND),
+ .pio_words = {
+ [0] = BM_I2C_CTRL0_SEND_NAK_ON_LAST |
+ BM_I2C_CTRL0_MASTER_MODE,
+ },
+};
+
+struct stmp378x_i2c_dev {
+ struct device *dev;
+ int irq;
+ struct completion cmd_complete;
+ u32 cmd_err;
+ struct i2c_adapter adapter;
+ void __iomem *io;
+ unsigned dma;
+
+ dma_addr_t bufp;
+ unsigned char *bufv;
+
+ struct stmp3xxx_dma_descriptor i2c_dma_read[2],
+ i2c_dma_write;
+};
+
+static int stmp378x_i2c_init(struct stmp378x_i2c_dev *dev)
+{
+ int err;
+
+ err = stmp3xxx_dma_request(dev->dma, dev->dev, dev_name(dev->dev));
+ if (err)
+ goto out;
+ stmp3xxx_reset_block(dev->io, true);
+
+ dev->bufv = dma_alloc_coherent(dev->dev, PAGE_SIZE,
+ &dev->bufp, GFP_KERNEL);
+ if (dma_mapping_error(dev->dev, dev->bufp))
+ goto out_dma;
+
+ err = stmp3xxx_request_pin_group(dev->dev->platform_data,
+ dev_name(dev->dev));
+ if (err)
+ goto out_free;
+
+ err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[0]);
+ if (err)
+ goto out_rd0;
+ err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[1]);
+ if (err)
+ goto out_rd1;
+ err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_write);
+ if (err)
+ goto out_wr;
+
+ stmp3xxx_dma_reset_channel(dev->dma);
+ stmp3xxx_dma_clear_interrupt(dev->dma);
+ stmp3xxx_dma_enable_interrupt(dev->dma);
+
+ return 0;
+/*
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
+*/
+out_wr:
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
+out_rd1:
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
+out_rd0:
+ stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
+out_free:
+ dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
+out_dma:
+ stmp3xxx_dma_release(dev->dma);
+out:
+ return err;
+}
+
+static void stmp378x_i2c_release(struct stmp378x_i2c_dev *dev)
+{
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
+ dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
+ stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
+ stmp3xxx_dma_release(dev->dma);
+}
+/*
+ * Low level master read/write transaction.
+ */
+static int stmp378x_i2c_xfer_msg(struct i2c_adapter *adap,
+ struct i2c_msg *msg, int stop)
+{
+ struct stmp378x_i2c_dev *dev = i2c_get_adapdata(adap);
+ int err;
+ struct stmp3xxx_dma_descriptor *c, *r;
+
+ init_completion(&dev->cmd_complete);
+ dev->cmd_err = 0;
+
+ dev_dbg(dev->dev, "start XFER\n");
+ 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;
+
+ if (msg->flags & I2C_M_RD) {
+
+ c = &dev->i2c_dma_read[0];
+ r = &dev->i2c_dma_read[1];
+
+ /* first, setup the SELECT command */
+ memcpy(c->command, &cmd_i2c_select, sizeof(cmd_i2c_select));
+ c->command->buf_ptr = dev->bufp;
+ dev->bufv[0] = msg->addr | I2C_READ;
+
+ /* then, setup the READ command */
+ memcpy(r->command, &cmd_i2c_read, sizeof(cmd_i2c_read));
+ r->command->cmd |= BF(msg->len, APBX_CHn_CMD_XFER_COUNT);
+ r->command->pio_words[0] |= BF(msg->len, I2C_CTRL0_XFER_COUNT);
+ if (stop)
+ r->command->pio_words[0] |= BM_I2C_CTRL0_POST_SEND_STOP;
+ /*
+ * yes, STMP controller can DMA transfer the data from any
+ * byte boundary
+ */
+ r->command->buf_ptr = dev->bufp + 1;
+
+ /* and chain them together */
+ c->command->next = r->handle;
+
+ } else {
+
+ c = &dev->i2c_dma_write;
+ memcpy(c->command, &cmd_i2c_write, sizeof(cmd_i2c_write));
+ c->command->cmd |= BF(msg->len + 1, APBX_CHn_CMD_XFER_COUNT);
+ c->command->pio_words[0] |=
+ BF(msg->len + 1, I2C_CTRL0_XFER_COUNT);
+ if (stop)
+ c->command->pio_words[0] |=
+ BM_I2C_CTRL0_POST_SEND_STOP;
+ dev->bufv[0] = msg->addr | I2C_WRITE;
+ memcpy(dev->bufv + 1, msg->buf, msg->len);
+ }
+
+ stmp3xxx_dma_go(dev->dma, c, 1);
+
+ err = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
+ msecs_to_jiffies(1000));
+
+ if (err < 0) {
+ dev_dbg(dev->dev, "controler is timed out\n");
+ return -ETIMEDOUT;
+ }
+ if (!dev->cmd_err && (msg->flags & I2C_M_RD))
+ memcpy(msg->buf, dev->bufv + 1, msg->len);
+
+ dev_dbg(dev->dev, "done with err=%d\n", dev->cmd_err);
+
+ return dev->cmd_err;
+}
+
+
+static int
+stmp378x_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 = stmp378x_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
+ if (err)
+ return err;
+ }
+ return num;
+}
+
+static u32
+stmp378x_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+#define I2C_IRQ_MASK 0x000000FF
+static irqreturn_t
+stmp378x_i2c_isr(int this_irq, void *dev_id)
+{
+ struct stmp378x_i2c_dev *dev = dev_id;
+ u32 stat;
+ u32 done_mask = BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ |
+ BM_I2C_CTRL1_BUS_FREE_IRQ ;
+
+ stat = readl(HW_I2C_CTRL1 + dev->io) & I2C_IRQ_MASK;
+ if (!stat)
+ return IRQ_NONE;
+
+ if (stat & BM_I2C_CTRL1_NO_SLAVE_ACK_IRQ) {
+ dev->cmd_err = -EREMOTEIO;
+
+ /*
+ * Stop DMA
+ * Clear NAK
+ */
+ stmp3xxx_setl(BM_I2C_CTRL1_CLR_GOT_A_NAK,
+ HW_I2C_CTRL1 + dev->io);
+ stmp3xxx_dma_reset_channel(dev->dma);
+ stmp3xxx_dma_clear_interrupt(dev->dma);
+
+ complete(&dev->cmd_complete);
+
+ 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)) {
+ dev->cmd_err = -EIO;
+ complete(&dev->cmd_complete);
+ goto done;
+ }
+
+ if ((stat & done_mask) == done_mask)
+ complete(&dev->cmd_complete);
+
+done:
+ stmp3xxx_clearl(stat, dev->io + HW_I2C_CTRL1);
+ return IRQ_HANDLED;
+}
+
+static const struct i2c_algorithm stmp378x_i2c_algo = {
+ .master_xfer = stmp378x_i2c_xfer,
+ .functionality = stmp378x_i2c_func,
+};
+
+
+static int __devinit
+stmp378x_i2c_probe(struct platform_device *pdev)
+{
+ struct stmp378x_i2c_dev *dev;
+ struct i2c_adapter *adap;
+ struct resource *mem, *dma;
+ int err = 0;
+
+ /* NOTE: driver uses the static register mapping */
+ dev = kzalloc(sizeof(struct stmp378x_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ dev_err(&pdev->dev, "no mem \n");
+ return -ENOMEM;
+ }
+
+ dev->irq = platform_get_irq(pdev, 0); /* Error */
+ if (dev->irq < 0) {
+ dev_err(&pdev->dev, "no err_irq resource\n");
+ err = -ENODEV;
+ goto nores;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no memory window\n");
+ err = -ENODEV;
+ goto nores;
+ }
+
+ dma = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dma) {
+ dev_err(&pdev->dev, "no dma channel\n");
+ err = -ENODEV;
+ goto nores;
+ }
+ dev->dma = dma->start;
+
+ dev->dev = &pdev->dev;
+ dev->io = ioremap(mem->start, resource_size(mem));
+ if (!dev->io) {
+ dev_err(&pdev->dev, "cannot ioremap\n");
+ err = -ENXIO;
+ goto nores;
+ }
+
+ err = request_irq(dev->irq, stmp378x_i2c_isr, 0, pdev->name, dev);
+ if (err) {
+ dev_err(&pdev->dev, "Can't get IRQ\n");
+ goto no_err_irq;
+ }
+
+ err = stmp378x_i2c_init(dev);
+ if (err) {
+ dev_err(&pdev->dev, "HW Init failed\n");
+ goto init_failed;
+ }
+
+ stmp3xxx_setl(0xFF00, dev->io + HW_I2C_CTRL1);
+
+ adap = &dev->adapter;
+ i2c_set_adapdata(adap, dev);
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON;
+ strncpy(adap->name, "378x I2C adapter", sizeof(adap->name));
+ adap->algo = &stmp378x_i2c_algo;
+ adap->dev.parent = &pdev->dev;
+
+ adap->nr = pdev->id;
+ err = i2c_add_numbered_adapter(adap);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add adapter\n");
+ goto no_i2c_adapter;
+
+ }
+
+ return 0;
+
+no_i2c_adapter:
+ stmp378x_i2c_release(dev);
+init_failed:
+ free_irq(dev->irq, dev);
+no_err_irq:
+ iounmap(dev->io);
+nores:
+ kfree(dev);
+ return err;
+}
+
+static int __devexit
+stmp378x_i2c_remove(struct platform_device *pdev)
+{
+ struct stmp378x_i2c_dev *dev = platform_get_drvdata(pdev);
+ int res;
+
+ res = i2c_del_adapter(&dev->adapter);
+ if (res)
+ return -EBUSY;
+
+ stmp378x_i2c_release(dev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ free_irq(dev->irq, dev);
+ iounmap(dev->io);
+
+ kfree(dev);
+ return 0;
+}
+
+static struct platform_driver stmp378x_i2c_driver = {
+ .probe = stmp378x_i2c_probe,
+ .remove = __devexit_p(stmp378x_i2c_remove),
+ .driver = {
+ .name = "i2c_stmp",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init stmp378x_i2c_init_driver(void)
+{
+ return platform_driver_register(&stmp378x_i2c_driver);
+}
+module_init(stmp378x_i2c_init_driver);
+
+static void __exit stmp378x_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&stmp378x_i2c_driver);
+}
+module_exit(stmp378x_i2c_exit_driver);
+
+MODULE_AUTHOR("d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org");
+MODULE_DESCRIPTION("I2C for Freescale STMP378x");
+MODULE_LICENSE("GPL");
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH, RFC] Freescale STMP: i2c driver
[not found] ` <1244059155.4074.19.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>
@ 2009-06-08 22:50 ` Ben Dooks
[not found] ` <20090608225034.GA20446-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
2009-06-15 8:19 ` Ben Dooks
1 sibling, 1 reply; 11+ messages in thread
From: Ben Dooks @ 2009-06-08 22:50 UTC (permalink / raw)
To: dmitry pervushin; +Cc: Ben Dooks, linux-i2c-u79uwXL29TY76Z2rM5mHXA
On Wed, Jun 03, 2009 at 11:59:15PM +0400, dmitry pervushin wrote:
> Hello Ben & i2c people,
>
> Here is the patch to support I2C bus on Freescale STMP378x platform; any
> comments are welcome
>
> Signed-off-by: Dmitrij Frasenyak <d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
should really have a from Dmitrij Frasenyak line as he is the original
authour and you're just pushing it, IIRC.
> Signed-off-by: dmitry pervishin <dpervushin-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
>
> ---
> drivers/i2c/busses/Kconfig | 7
> drivers/i2c/busses/Makefile | 1
> drivers/i2c/busses/i2c-stmp378x.c | 440 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 448 insertions(+)
>
> Index: linux-2.6-arm/drivers/i2c/busses/Kconfig
> ===================================================================
> --- linux-2.6-arm.orig/drivers/i2c/busses/Kconfig
> +++ linux-2.6-arm/drivers/i2c/busses/Kconfig
> @@ -513,6 +513,13 @@ config I2C_SIMTEC
> This driver can also be built as a module. If so, the module
> will be called i2c-simtec.
>
> +config I2C_STMP378X
> + tristate "STMP378x I2C adapter"
> + depends on ARCH_STMP378X
> + help
> + Include support of I2C adapter on Freescale STMP378x board; to build
> + the driver as a module, say M here - module will be called i2c-stmp378x.
> +
> config I2C_VERSATILE
> tristate "ARM Versatile/Realview I2C bus support"
> depends on ARCH_VERSATILE || ARCH_REALVIEW
> Index: linux-2.6-arm/drivers/i2c/busses/Makefile
> ===================================================================
> --- linux-2.6-arm.orig/drivers/i2c/busses/Makefile
> +++ linux-2.6-arm/drivers/i2c/busses/Makefile
> @@ -48,6 +48,7 @@ obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
> obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
> obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
> obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
> +obj-$(CONFIG_I2C_STMP378X) += i2c-stmp378x.o
> obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
>
> # External I2C/SMBus adapter drivers
> Index: linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
> @@ -0,0 +1,440 @@
> +/*
> + * Freescale STMP378X I2C bus driver
> + *
> + * Author: Dmitrij Frasenyak <sed-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
> + *
> + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
> + * Copyright 2008 Embedded Alley Solutions, 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/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/clk.h>
> +#include <linux/io.h>
> +#include <linux/dma-mapping.h>
> +
> +#include <mach/platform.h>
> +#include <mach/stmp3xxx.h>
> +#include <mach/dma.h>
> +#include <mach/regs-i2c.h>
> +#include <mach/regs-apbx.h>
> +
> +static const u32 I2C_READ = 1,
> + I2C_WRITE = 0;
do you really want to be defining things with a prefix of I2C, that
might end up clashing with the i2c core?
> +static const struct stmp3xxx_dma_command cmd_i2c_select = {
> + .cmd = BF(1, APBX_CHn_CMD_XFER_COUNT) |
> + BF(1, APBX_CHn_CMD_CMDWORDS) |
> + BM_APBX_CHn_CMD_WAIT4ENDCMD |
> + BM_APBX_CHn_CMD_CHAIN |
> + BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
what is BF() ?
> + .pio_words = {
> + [0] = BM_I2C_CTRL0_RETAIN_CLOCK |
> + BM_I2C_CTRL0_PRE_SEND_START |
> + BM_I2C_CTRL0_MASTER_MODE |
> + BM_I2C_CTRL0_DIRECTION |
> + BF(1, I2C_CTRL0_XFER_COUNT),
> + },
> +};
> +
> +static const struct stmp3xxx_dma_command cmd_i2c_write = {
> + .cmd = BM_APBX_CHn_CMD_SEMAPHORE |
> + BF(1, APBX_CHn_CMD_CMDWORDS) |
> + BM_APBX_CHn_CMD_WAIT4ENDCMD |
> + BM_APBX_CHn_CMD_IRQONCMPLT |
> + BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
> + .pio_words = {
> + [0] = BM_I2C_CTRL0_PRE_SEND_START |
> + BM_I2C_CTRL0_MASTER_MODE |
> + BM_I2C_CTRL0_DIRECTION,
> + },
> +};
> +
> +static const struct stmp3xxx_dma_command cmd_i2c_read = {
> + .cmd = BM_APBX_CHn_CMD_SEMAPHORE |
> + BF(1, APBX_CHn_CMD_CMDWORDS) |
> + BM_APBX_CHn_CMD_WAIT4ENDCMD |
> + BM_APBX_CHn_CMD_IRQONCMPLT |
> + BF(BV_APBX_CHn_CMD_COMMAND__DMA_WRITE, APBX_CHn_CMD_COMMAND),
> + .pio_words = {
> + [0] = BM_I2C_CTRL0_SEND_NAK_ON_LAST |
> + BM_I2C_CTRL0_MASTER_MODE,
> + },
> +};
> +
> +struct stmp378x_i2c_dev {
> + struct device *dev;
> + int irq;
> + struct completion cmd_complete;
> + u32 cmd_err;
> + struct i2c_adapter adapter;
> + void __iomem *io;
> + unsigned dma;
> +
> + dma_addr_t bufp;
> + unsigned char *bufv;
> +
> + struct stmp3xxx_dma_descriptor i2c_dma_read[2],
> + i2c_dma_write;
> +};
would be nice to see some kerneldoc descriptions for the fields in
this structure.
> +static int stmp378x_i2c_init(struct stmp378x_i2c_dev *dev)
> +{
> + int err;
> +
> + err = stmp3xxx_dma_request(dev->dma, dev->dev, dev_name(dev->dev));
> + if (err)
> + goto out;
> + stmp3xxx_reset_block(dev->io, true);
> +
> + dev->bufv = dma_alloc_coherent(dev->dev, PAGE_SIZE,
> + &dev->bufp, GFP_KERNEL);
> + if (dma_mapping_error(dev->dev, dev->bufp))
> + goto out_dma;
hmm, doesn't dma_alloc_coherent() fail with an NULL buffer return?
> + err = stmp3xxx_request_pin_group(dev->dev->platform_data,
> + dev_name(dev->dev));
> + if (err)
> + goto out_free;
> +
> + err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[0]);
> + if (err)
> + goto out_rd0;
> + err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[1]);
> + if (err)
> + goto out_rd1;
> + err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_write);
> + if (err)
> + goto out_wr;
> +
> + stmp3xxx_dma_reset_channel(dev->dma);
> + stmp3xxx_dma_clear_interrupt(dev->dma);
> + stmp3xxx_dma_enable_interrupt(dev->dma);
> +
> + return 0;
> +/*
> + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
> +*/
> +out_wr:
> + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
> +out_rd1:
> + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
> +out_rd0:
> + stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
> +out_free:
> + dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
> +out_dma:
> + stmp3xxx_dma_release(dev->dma);
> +out:
> + return err;
> +}
> +
> +static void stmp378x_i2c_release(struct stmp378x_i2c_dev *dev)
> +{
> + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
> + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
> + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
> + dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
> + stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
> + stmp3xxx_dma_release(dev->dma);
> +}
> +/*
> + * Low level master read/write transaction.
> + */
> +static int stmp378x_i2c_xfer_msg(struct i2c_adapter *adap,
> + struct i2c_msg *msg, int stop)
> +{
> + struct stmp378x_i2c_dev *dev = i2c_get_adapdata(adap);
> + int err;
> + struct stmp3xxx_dma_descriptor *c, *r;
> +
> + init_completion(&dev->cmd_complete);
> + dev->cmd_err = 0;
> +
> + dev_dbg(dev->dev, "start XFER\n");
> + 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;
> +
> + if (msg->flags & I2C_M_RD) {
> +
> + c = &dev->i2c_dma_read[0];
> + r = &dev->i2c_dma_read[1];
> +
> + /* first, setup the SELECT command */
> + memcpy(c->command, &cmd_i2c_select, sizeof(cmd_i2c_select));
> + c->command->buf_ptr = dev->bufp;
> + dev->bufv[0] = msg->addr | I2C_READ;
> +
> + /* then, setup the READ command */
> + memcpy(r->command, &cmd_i2c_read, sizeof(cmd_i2c_read));
> + r->command->cmd |= BF(msg->len, APBX_CHn_CMD_XFER_COUNT);
> + r->command->pio_words[0] |= BF(msg->len, I2C_CTRL0_XFER_COUNT);
> + if (stop)
> + r->command->pio_words[0] |= BM_I2C_CTRL0_POST_SEND_STOP;
> + /*
> + * yes, STMP controller can DMA transfer the data from any
> + * byte boundary
> + */
> + r->command->buf_ptr = dev->bufp + 1;
> +
> + /* and chain them together */
> + c->command->next = r->handle;
> +
> + } else {
> +
> + c = &dev->i2c_dma_write;
> + memcpy(c->command, &cmd_i2c_write, sizeof(cmd_i2c_write));
> + c->command->cmd |= BF(msg->len + 1, APBX_CHn_CMD_XFER_COUNT);
> + c->command->pio_words[0] |=
> + BF(msg->len + 1, I2C_CTRL0_XFER_COUNT);
> + if (stop)
> + c->command->pio_words[0] |=
> + BM_I2C_CTRL0_POST_SEND_STOP;
> + dev->bufv[0] = msg->addr | I2C_WRITE;
> + memcpy(dev->bufv + 1, msg->buf, msg->len);
> + }
> +
> + stmp3xxx_dma_go(dev->dma, c, 1);
> +
> + err = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
> + msecs_to_jiffies(1000));
> +
> + if (err < 0) {
> + dev_dbg(dev->dev, "controler is timed out\n");
> + return -ETIMEDOUT;
> + }
> + if (!dev->cmd_err && (msg->flags & I2C_M_RD))
> + memcpy(msg->buf, dev->bufv + 1, msg->len);
> +
> + dev_dbg(dev->dev, "done with err=%d\n", dev->cmd_err);
> +
> + return dev->cmd_err;
> +}
> +
> +
> +static int
> +stmp378x_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 = stmp378x_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
> + if (err)
> + return err;
> + }
> + return num;
> +}
> +
> +static u32
> +stmp378x_i2c_func(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
> +}
> +
> +#define I2C_IRQ_MASK 0x000000FF
> +static irqreturn_t
> +stmp378x_i2c_isr(int this_irq, void *dev_id)
> +{
> + struct stmp378x_i2c_dev *dev = dev_id;
> + u32 stat;
> + u32 done_mask = BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ |
> + BM_I2C_CTRL1_BUS_FREE_IRQ ;
> +
> + stat = readl(HW_I2C_CTRL1 + dev->io) & I2C_IRQ_MASK;
> + if (!stat)
> + return IRQ_NONE;
> +
> + if (stat & BM_I2C_CTRL1_NO_SLAVE_ACK_IRQ) {
> + dev->cmd_err = -EREMOTEIO;
> +
> + /*
> + * Stop DMA
> + * Clear NAK
> + */
> + stmp3xxx_setl(BM_I2C_CTRL1_CLR_GOT_A_NAK,
> + HW_I2C_CTRL1 + dev->io);
> + stmp3xxx_dma_reset_channel(dev->dma);
> + stmp3xxx_dma_clear_interrupt(dev->dma);
> +
> + complete(&dev->cmd_complete);
> +
> + 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)) {
> + dev->cmd_err = -EIO;
> + complete(&dev->cmd_complete);
> + goto done;
> + }
> +
> + if ((stat & done_mask) == done_mask)
> + complete(&dev->cmd_complete);
> +
> +done:
> + stmp3xxx_clearl(stat, dev->io + HW_I2C_CTRL1);
> + return IRQ_HANDLED;
> +}
> +
> +static const struct i2c_algorithm stmp378x_i2c_algo = {
> + .master_xfer = stmp378x_i2c_xfer,
> + .functionality = stmp378x_i2c_func,
> +};
> +
> +
> +static int __devinit
> +stmp378x_i2c_probe(struct platform_device *pdev)
> +{
> + struct stmp378x_i2c_dev *dev;
> + struct i2c_adapter *adap;
> + struct resource *mem, *dma;
> + int err = 0;
> +
> + /* NOTE: driver uses the static register mapping */
> + dev = kzalloc(sizeof(struct stmp378x_i2c_dev), GFP_KERNEL);
> + if (!dev) {
> + dev_err(&pdev->dev, "no mem \n");
> + return -ENOMEM;
> + }
> +
> + dev->irq = platform_get_irq(pdev, 0); /* Error */
> + if (dev->irq < 0) {
> + dev_err(&pdev->dev, "no err_irq resource\n");
> + err = -ENODEV;
> + goto nores;
> + }
-ENODEV isn't what you want here, you'll end up losing errors in the
higher level.
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem) {
> + dev_err(&pdev->dev, "no memory window\n");
> + err = -ENODEV;
> + goto nores;
> + }
> +
> + dma = platform_get_resource(pdev, IORESOURCE_DMA, 0);
> + if (!dma) {
> + dev_err(&pdev->dev, "no dma channel\n");
> + err = -ENODEV;
> + goto nores;
> + }
> + dev->dma = dma->start;
> +
> + dev->dev = &pdev->dev;
> + dev->io = ioremap(mem->start, resource_size(mem));
> + if (!dev->io) {
> + dev_err(&pdev->dev, "cannot ioremap\n");
> + err = -ENXIO;
> + goto nores;
> + }
> +
> + err = request_irq(dev->irq, stmp378x_i2c_isr, 0, pdev->name, dev);
> + if (err) {
> + dev_err(&pdev->dev, "Can't get IRQ\n");
> + goto no_err_irq;
> + }
> +
> + err = stmp378x_i2c_init(dev);
> + if (err) {
> + dev_err(&pdev->dev, "HW Init failed\n");
> + goto init_failed;
> + }
> +
> + stmp3xxx_setl(0xFF00, dev->io + HW_I2C_CTRL1);
> +
> + adap = &dev->adapter;
> + i2c_set_adapdata(adap, dev);
> + adap->owner = THIS_MODULE;
> + adap->class = I2C_CLASS_HWMON;
is this the correct class?
> + strncpy(adap->name, "378x I2C adapter", sizeof(adap->name));
> + adap->algo = &stmp378x_i2c_algo;
> + adap->dev.parent = &pdev->dev;
> +
> + adap->nr = pdev->id;
> + err = i2c_add_numbered_adapter(adap);
> + if (err) {
> + dev_err(&pdev->dev, "Failed to add adapter\n");
> + goto no_i2c_adapter;
> +
> + }
> +
> + return 0;
> +
> +no_i2c_adapter:
> + stmp378x_i2c_release(dev);
> +init_failed:
> + free_irq(dev->irq, dev);
> +no_err_irq:
> + iounmap(dev->io);
> +nores:
> + kfree(dev);
> + return err;
> +}
> +
> +static int __devexit
> +stmp378x_i2c_remove(struct platform_device *pdev)
> +{
> + struct stmp378x_i2c_dev *dev = platform_get_drvdata(pdev);
> + int res;
> +
> + res = i2c_del_adapter(&dev->adapter);
> + if (res)
> + return -EBUSY;
> +
> + stmp378x_i2c_release(dev);
> +
> + platform_set_drvdata(pdev, NULL);
> +
> + free_irq(dev->irq, dev);
> + iounmap(dev->io);
> +
> + kfree(dev);
> + return 0;
> +}
> +
> +static struct platform_driver stmp378x_i2c_driver = {
> + .probe = stmp378x_i2c_probe,
> + .remove = __devexit_p(stmp378x_i2c_remove),
> + .driver = {
> + .name = "i2c_stmp",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init stmp378x_i2c_init_driver(void)
> +{
> + return platform_driver_register(&stmp378x_i2c_driver);
> +}
> +module_init(stmp378x_i2c_init_driver);
> +
> +static void __exit stmp378x_i2c_exit_driver(void)
> +{
> + platform_driver_unregister(&stmp378x_i2c_driver);
> +}
> +module_exit(stmp378x_i2c_exit_driver);
> +
> +MODULE_AUTHOR("d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org");
> +MODULE_DESCRIPTION("I2C for Freescale STMP378x");
> +MODULE_LICENSE("GPL");
no module alias for autoloading?
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Ben (ben-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH, RFC] Freescale STMP: i2c driver
[not found] ` <20090608225034.GA20446-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
@ 2009-06-09 7:46 ` Jean Delvare
2009-06-16 16:25 ` dmitry pervushin
1 sibling, 0 replies; 11+ messages in thread
From: Jean Delvare @ 2009-06-09 7:46 UTC (permalink / raw)
To: dmitry pervushin; +Cc: Ben Dooks, linux-i2c-u79uwXL29TY76Z2rM5mHXA
On Mon, 8 Jun 2009 23:50:34 +0100, Ben Dooks wrote:
> On Wed, Jun 03, 2009 at 11:59:15PM +0400, dmitry pervushin wrote:
> > +static const u32 I2C_READ = 1,
> > + I2C_WRITE = 0;
>
> do you really want to be defining things with a prefix of I2C, that
> might end up clashing with the i2c core?
Definitely not. Please just use I2C_SMBUS_READ and I2C_SMBUS_WRITE
which are already defined by <linux/i2c.h>.
(One might argue these are unfortunate names, but that's what we have
at the moment so let's just use it.)
--
Jean Delvare
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH, RFC] Freescale STMP: i2c driver
[not found] ` <1244059155.4074.19.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>
2009-06-08 22:50 ` Ben Dooks
@ 2009-06-15 8:19 ` Ben Dooks
[not found] ` <20090615081938.GA19878-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
1 sibling, 1 reply; 11+ messages in thread
From: Ben Dooks @ 2009-06-15 8:19 UTC (permalink / raw)
To: dmitry pervushin; +Cc: Ben Dooks, linux-i2c-u79uwXL29TY76Z2rM5mHXA
any chance of an reply or update driver for inclusion?
--
Ben (ben-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH, RFC] Freescale STMP: i2c driver
[not found] ` <20090608225034.GA20446-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
2009-06-09 7:46 ` Jean Delvare
@ 2009-06-16 16:25 ` dmitry pervushin
[not found] ` <1245169539.32549.12.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>
1 sibling, 1 reply; 11+ messages in thread
From: dmitry pervushin @ 2009-06-16 16:25 UTC (permalink / raw)
To: Ben Dooks; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA
Hello Ben,
I am sending the updated patch in next message, and this message is just
to answer your questions/concerns.\
[...]
> > +#include <mach/regs-apbx.h>
> > +
> > +static const u32 I2C_READ = 1,
> > + I2C_WRITE = 0;
>
> do you really want to be defining things with a prefix of I2C, thatB
> might end up clashing with the i2c core?
Fixed, changed to I2C_SMBUS_READ/WRITE as Jean Delvare proposed.
>
> > +static const struct stmp3xxx_dma_command cmd_i2c_select = {
> > + .cmd = BF(1, APBX_CHn_CMD_XFER_COUNT) |
> > + BF(1, APBX_CHn_CMD_CMDWORDS) |
> > + BM_APBX_CHn_CMD_WAIT4ENDCMD |
> > + BM_APBX_CHn_CMD_CHAIN |
> > + BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
>
> what is BF() ?
it is the macro from arch/arm/plat-stmp3xxx/include/mach/platform.h,
abbreviated "bitfield" :) For example, BF(1, APBX_CHn_CMD_CMDWORDS) is
expanded like
((1 << BP_APBX_CHn_CMD_CMDWORDS) & BM_APBX_CHn_CMD_CMDWORDS).
BP_xxx stuff means bitfield position, BM_xxx - bitfield mask.
>> + dev->bufv = dma_alloc_coherent(dev->dev, PAGE_SIZE,
>> + &dev->bufp, GFP_KERNEL);>
>> + if (dma_mapping_error(dev->dev, dev->bufp))
>> + goto out_dma;
> hmm, doesn't dma_alloc_coherent() fail with an NULL buffer return?
Fixed; but using dma_mapping_error seems to be more accurate method to
me. 0 might be valid dma address on some architecture. Again, for the
time being there is no difference to check dma_mapping_error or plain
compare with NULL...
[skipped]
> > + adap = &dev->adapter;
> > + i2c_set_adapdata(adap, dev);
> > + adap->owner = THIS_MODULE;
> > + adap->class = I2C_CLASS_HWMON;
>
> is this the correct class?
Mmm.. I changed this to I2C_CLASS_TV_DIGITAL since the only I2C user is
digital radio daughterboard. Any objections?
>
> > + strncpy(adap->name, "378x I2C adapter", sizeof(adap->name));
> > + adap->algo = &stmp378x_i2c_algo;
> > + adap->dev.parent = &pdev->dev;
> > +
> > + adap->nr = pdev->id;
> > + err = i2c_add_numbered_adapter(adap);
> > + if (err) {
> > + dev_err(&pdev->dev, "Failed to add adapter\n");
> > + goto no_i2c_adapter;
> > +
> > + }
> > +
> > + return 0;
> > +
> > +no_i2c_adapter:
> > + stmp378x_i2c_release(dev);
> > +init_failed:
> > + free_irq(dev->irq, dev);
> > +no_err_irq:
> > + iounmap(dev->io);
> > +nores:
> > + kfree(dev);
> > + return err;
> > +}
> > +
> > +static int __devexit
> > +stmp378x_i2c_remove(struct platform_device *pdev)
> > +{
> > + struct stmp378x_i2c_dev *dev = platform_get_drvdata(pdev);
> > + int res;
> > +
> > + res = i2c_del_adapter(&dev->adapter);
> > + if (res)
> > + return -EBUSY;
> > +
> > + stmp378x_i2c_release(dev);
> > +
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + free_irq(dev->irq, dev);
> > + iounmap(dev->io);
> > +
> > + kfree(dev);
> > + return 0;
> > +}
> > +
> > +static struct platform_driver stmp378x_i2c_driver = {
> > + .probe = stmp378x_i2c_probe,
> > + .remove = __devexit_p(stmp378x_i2c_remove),
> > + .driver = {
> > + .name = "i2c_stmp",
> > + .owner = THIS_MODULE,
> > + },
> > +};
> > +
> > +static int __init stmp378x_i2c_init_driver(void)
> > +{
> > + return platform_driver_register(&stmp378x_i2c_driver);
> > +}
> > +module_init(stmp378x_i2c_init_driver);
> > +
> > +static void __exit stmp378x_i2c_exit_driver(void)
> > +{
> > + platform_driver_unregister(&stmp378x_i2c_driver);
> > +}
> > +module_exit(stmp378x_i2c_exit_driver);
> > +
> > +MODULE_AUTHOR("d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org");
> > +MODULE_DESCRIPTION("I2C for Freescale STMP378x");
> > +MODULE_LICENSE("GPL");
>
> no module alias for autoloading?
Added.
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
> > the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH, RFC] Freescale STMP: i2c driver
[not found] ` <20090615081938.GA19878-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
@ 2009-06-16 16:31 ` dmitry pervushin
[not found] ` <1245169913.32549.19.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>
0 siblings, 1 reply; 11+ messages in thread
From: dmitry pervushin @ 2009-06-16 16:31 UTC (permalink / raw)
To: Ben Dooks; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA
On Mon, 2009-06-15 at 09:19 +0100, Ben Dooks wrote:
> any chance of an reply or update driver for inclusion?
Here it is:
From: Dmitrij Frasenyak <d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
Signed-off-by: dmitry pervushin <dpervushin-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
Add I2C driver for Freescale STMP boards
---
drivers/i2c/busses/Kconfig | 7
drivers/i2c/busses/Makefile | 1
drivers/i2c/busses/i2c-stmp378x.c | 448 ++++++++++++++++++++++++++++++++++++++
3 files changed, 456 insertions(+)
Index: linux-2.6-arm/drivers/i2c/busses/Kconfig
===================================================================
--- linux-2.6-arm.orig/drivers/i2c/busses/Kconfig
+++ linux-2.6-arm/drivers/i2c/busses/Kconfig
@@ -513,6 +513,13 @@ config I2C_SIMTEC
This driver can also be built as a module. If so, the module
will be called i2c-simtec.
+config I2C_STMP378X
+ tristate "STMP378x I2C adapter"
+ depends on ARCH_STMP378X
+ help
+ Include support of I2C adapter on Freescale STMP378x board; to build
+ the driver as a module, say M here - module will be called i2c-stmp378x.
+
config I2C_VERSATILE
tristate "ARM Versatile/Realview I2C bus support"
depends on ARCH_VERSATILE || ARCH_REALVIEW
Index: linux-2.6-arm/drivers/i2c/busses/Makefile
===================================================================
--- linux-2.6-arm.orig/drivers/i2c/busses/Makefile
+++ linux-2.6-arm/drivers/i2c/busses/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
+obj-$(CONFIG_I2C_STMP378X) += i2c-stmp378x.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
# External I2C/SMBus adapter drivers
Index: linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
===================================================================
--- /dev/null
+++ linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
@@ -0,0 +1,448 @@
+/*
+ * Freescale STMP378X I2C bus driver
+ *
+ * Author: Dmitrij Frasenyak <sed-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, 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/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/clk.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+
+#include <mach/platform.h>
+#include <mach/stmp3xxx.h>
+#include <mach/dma.h>
+#include <mach/regs-i2c.h>
+#include <mach/regs-apbx.h>
+
+static const struct stmp3xxx_dma_command cmd_i2c_select = {
+ .cmd = BF(1, APBX_CHn_CMD_XFER_COUNT) |
+ BF(1, APBX_CHn_CMD_CMDWORDS) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_CHAIN |
+ BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
+ .pio_words = {
+ [0] = BM_I2C_CTRL0_RETAIN_CLOCK |
+ BM_I2C_CTRL0_PRE_SEND_START |
+ BM_I2C_CTRL0_MASTER_MODE |
+ BM_I2C_CTRL0_DIRECTION |
+ BF(1, I2C_CTRL0_XFER_COUNT),
+ },
+};
+
+static const struct stmp3xxx_dma_command cmd_i2c_write = {
+ .cmd = BM_APBX_CHn_CMD_SEMAPHORE |
+ BF(1, APBX_CHn_CMD_CMDWORDS) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_IRQONCMPLT |
+ BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
+ .pio_words = {
+ [0] = BM_I2C_CTRL0_PRE_SEND_START |
+ BM_I2C_CTRL0_MASTER_MODE |
+ BM_I2C_CTRL0_DIRECTION,
+ },
+};
+
+static const struct stmp3xxx_dma_command cmd_i2c_read = {
+ .cmd = BM_APBX_CHn_CMD_SEMAPHORE |
+ BF(1, APBX_CHn_CMD_CMDWORDS) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_IRQONCMPLT |
+ BF(BV_APBX_CHn_CMD_COMMAND__DMA_WRITE, APBX_CHn_CMD_COMMAND),
+ .pio_words = {
+ [0] = BM_I2C_CTRL0_SEND_NAK_ON_LAST |
+ BM_I2C_CTRL0_MASTER_MODE,
+ },
+};
+
+/**
+ * struct stmp378x_i2c_dev - STMP3xxx I2C adapter data
+ * @dev: the associated platform_device
+ * @cmd_complete: the completion structure indicating that message has been
+ * transferred
+ * @cmd_err: current error code
+ * @adapter: the adapter we implement
+ * @io: io address
+ * @irq: irq number
+ * @dma: dma channel
+ * @bufv: pointer to the dma buffer
+ * @bufp: physical address of the dma buffer
+ * @i2c_dma_read: "read" dma chain (read request and then write)
+ * @i2c_dma_write: "write" dma chain (the single combined write request)
+ */
+struct stmp378x_i2c_dev {
+ struct device *dev;
+ int irq;
+ struct completion cmd_complete;
+ u32 cmd_err;
+ struct i2c_adapter adapter;
+ void __iomem *io;
+ unsigned dma;
+
+ dma_addr_t bufp;
+ unsigned char *bufv;
+
+ struct stmp3xxx_dma_descriptor i2c_dma_read[2],
+ i2c_dma_write;
+};
+
+static int stmp378x_i2c_init(struct stmp378x_i2c_dev *dev)
+{
+ int err;
+
+ err = stmp3xxx_dma_request(dev->dma, dev->dev, dev_name(dev->dev));
+ if (err)
+ goto out;
+ stmp3xxx_reset_block(dev->io, true);
+
+ dev->bufv = dma_alloc_coherent(dev->dev, PAGE_SIZE,
+ &dev->bufp, GFP_KERNEL);
+ if (!dev->bufv)
+ goto out_dma;
+
+ err = stmp3xxx_request_pin_group(dev->dev->platform_data,
+ dev_name(dev->dev));
+ if (err)
+ goto out_free;
+
+ err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[0]);
+ if (err)
+ goto out_rd0;
+ err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[1]);
+ if (err)
+ goto out_rd1;
+ err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_write);
+ if (err)
+ goto out_wr;
+
+ stmp3xxx_dma_reset_channel(dev->dma);
+ stmp3xxx_dma_clear_interrupt(dev->dma);
+ stmp3xxx_dma_enable_interrupt(dev->dma);
+
+ return 0;
+/*
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
+*/
+out_wr:
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
+out_rd1:
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
+out_rd0:
+ stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
+out_free:
+ dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
+out_dma:
+ stmp3xxx_dma_release(dev->dma);
+out:
+ return err;
+}
+
+static void stmp378x_i2c_release(struct stmp378x_i2c_dev *dev)
+{
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
+ dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
+ stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
+ stmp3xxx_dma_release(dev->dma);
+}
+/*
+ * Low level master read/write transaction.
+ */
+static int stmp378x_i2c_xfer_msg(struct i2c_adapter *adap,
+ struct i2c_msg *msg, int stop)
+{
+ struct stmp378x_i2c_dev *dev = i2c_get_adapdata(adap);
+ int err;
+ struct stmp3xxx_dma_descriptor *c, *r;
+
+ init_completion(&dev->cmd_complete);
+ dev->cmd_err = 0;
+
+ if ((msg->len == 0) || (msg->len > (PAGE_SIZE - 1)))
+ return -EINVAL;
+
+ if (msg->flags & I2C_M_RD) {
+
+ c = &dev->i2c_dma_read[0];
+ r = &dev->i2c_dma_read[1];
+
+ /* first, setup the SELECT command */
+ memcpy(c->command, &cmd_i2c_select, sizeof(cmd_i2c_select));
+ c->command->buf_ptr = dev->bufp;
+ dev->bufv[0] = msg->addr | I2C_SMBUS_READ;
+
+ /* then, setup the READ command */
+ memcpy(r->command, &cmd_i2c_read, sizeof(cmd_i2c_read));
+ r->command->cmd |= BF(msg->len, APBX_CHn_CMD_XFER_COUNT);
+ r->command->pio_words[0] |= BF(msg->len, I2C_CTRL0_XFER_COUNT);
+ if (stop)
+ r->command->pio_words[0] |= BM_I2C_CTRL0_POST_SEND_STOP;
+ /*
+ * yes, STMP controller can DMA transfer the data from any
+ * byte boundary
+ */
+ r->command->buf_ptr = dev->bufp + 1;
+
+ /* and chain them together */
+ c->command->next = r->handle;
+
+ } else {
+
+ c = &dev->i2c_dma_write;
+ memcpy(c->command, &cmd_i2c_write, sizeof(cmd_i2c_write));
+ c->command->cmd |= BF(msg->len + 1, APBX_CHn_CMD_XFER_COUNT);
+ c->command->pio_words[0] |=
+ BF(msg->len + 1, I2C_CTRL0_XFER_COUNT);
+ if (stop)
+ c->command->pio_words[0] |=
+ BM_I2C_CTRL0_POST_SEND_STOP;
+ dev->bufv[0] = msg->addr | I2C_SMBUS_WRITE;
+ memcpy(dev->bufv + 1, msg->buf, msg->len);
+ }
+
+ stmp3xxx_dma_go(dev->dma, c, 1);
+
+ err = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
+ msecs_to_jiffies(1000));
+
+ if (err < 0) {
+ dev_dbg(dev->dev, "controler is timed out\n");
+ return -ETIMEDOUT;
+ }
+ if (!dev->cmd_err && (msg->flags & I2C_M_RD))
+ memcpy(msg->buf, dev->bufv + 1, msg->len);
+
+ dev_dbg(dev->dev, "done with err=%d\n", dev->cmd_err);
+
+ return dev->cmd_err;
+}
+
+
+static int
+stmp378x_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 = stmp378x_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
+ if (err)
+ return err;
+ }
+ return num;
+}
+
+static u32
+stmp378x_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+#define I2C_IRQ_MASK 0x000000FF
+static irqreturn_t
+stmp378x_i2c_isr(int this_irq, void *dev_id)
+{
+ struct stmp378x_i2c_dev *dev = dev_id;
+ u32 stat;
+ u32 done_mask = BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ |
+ BM_I2C_CTRL1_BUS_FREE_IRQ ;
+
+ stat = readl(HW_I2C_CTRL1 + dev->io) & I2C_IRQ_MASK;
+ if (!stat)
+ return IRQ_NONE;
+
+ if (stat & BM_I2C_CTRL1_NO_SLAVE_ACK_IRQ) {
+ dev->cmd_err = -EREMOTEIO;
+
+ /*
+ * Stop DMA
+ * Clear NAK
+ */
+ stmp3xxx_setl(BM_I2C_CTRL1_CLR_GOT_A_NAK,
+ HW_I2C_CTRL1 + dev->io);
+ stmp3xxx_dma_reset_channel(dev->dma);
+ stmp3xxx_dma_clear_interrupt(dev->dma);
+
+ complete(&dev->cmd_complete);
+
+ 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)) {
+ dev->cmd_err = -EIO;
+ complete(&dev->cmd_complete);
+ goto done;
+ }
+
+ if ((stat & done_mask) == done_mask)
+ complete(&dev->cmd_complete);
+
+done:
+ stmp3xxx_clearl(stat, dev->io + HW_I2C_CTRL1);
+ return IRQ_HANDLED;
+}
+
+static const struct i2c_algorithm stmp378x_i2c_algo = {
+ .master_xfer = stmp378x_i2c_xfer,
+ .functionality = stmp378x_i2c_func,
+};
+
+
+static int __devinit
+stmp378x_i2c_probe(struct platform_device *pdev)
+{
+ struct stmp378x_i2c_dev *dev;
+ struct i2c_adapter *adap;
+ struct resource *mem, *dma;
+ int err = 0;
+
+ dev = kzalloc(sizeof(struct stmp378x_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ dev_err(&pdev->dev, "no mem \n");
+ return -ENOMEM;
+ }
+
+ dev->irq = platform_get_irq(pdev, 0); /* Error */
+ if (dev->irq < 0) {
+ dev_err(&pdev->dev, "no err_irq resource\n");
+ err = dev->irq;
+ goto nores;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no memory window\n");
+ err = -ENODEV;
+ goto nores;
+ }
+
+ dma = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dma) {
+ dev_err(&pdev->dev, "no dma channel\n");
+ err = -ENODEV;
+ goto nores;
+ }
+ dev->dma = dma->start;
+
+ dev->dev = &pdev->dev;
+ dev->io = ioremap(mem->start, resource_size(mem));
+ if (!dev->io) {
+ dev_err(&pdev->dev, "cannot ioremap\n");
+ err = -ENXIO;
+ goto nores;
+ }
+
+ err = request_irq(dev->irq, stmp378x_i2c_isr, 0, pdev->name, dev);
+ if (err) {
+ dev_err(&pdev->dev, "Can't get IRQ\n");
+ goto no_err_irq;
+ }
+
+ err = stmp378x_i2c_init(dev);
+ if (err) {
+ dev_err(&pdev->dev, "HW Init failed\n");
+ goto init_failed;
+ }
+
+ stmp3xxx_setl(0xFF00, dev->io + HW_I2C_CTRL1);
+
+ adap = &dev->adapter;
+ i2c_set_adapdata(adap, dev);
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_TV_DIGITAL;
+ strncpy(adap->name, "378x I2C adapter", sizeof(adap->name));
+ adap->algo = &stmp378x_i2c_algo;
+ adap->dev.parent = &pdev->dev;
+
+ adap->nr = pdev->id;
+ err = i2c_add_numbered_adapter(adap);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add adapter\n");
+ goto no_i2c_adapter;
+
+ }
+
+ return 0;
+
+no_i2c_adapter:
+ stmp378x_i2c_release(dev);
+init_failed:
+ free_irq(dev->irq, dev);
+no_err_irq:
+ iounmap(dev->io);
+nores:
+ kfree(dev);
+ return err;
+}
+
+static int __devexit
+stmp378x_i2c_remove(struct platform_device *pdev)
+{
+ struct stmp378x_i2c_dev *dev = platform_get_drvdata(pdev);
+ int res;
+
+ res = i2c_del_adapter(&dev->adapter);
+ if (res)
+ return -EBUSY;
+
+ stmp378x_i2c_release(dev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ free_irq(dev->irq, dev);
+ iounmap(dev->io);
+
+ kfree(dev);
+ return 0;
+}
+
+static struct platform_driver stmp378x_i2c_driver = {
+ .probe = stmp378x_i2c_probe,
+ .remove = __devexit_p(stmp378x_i2c_remove),
+ .driver = {
+ .name = "i2c_stmp3xxx",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init stmp378x_i2c_init_driver(void)
+{
+ return platform_driver_register(&stmp378x_i2c_driver);
+}
+module_init(stmp378x_i2c_init_driver);
+
+static void __exit stmp378x_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&stmp378x_i2c_driver);
+}
+module_exit(stmp378x_i2c_exit_driver);
+
+MODULE_AUTHOR("d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org");
+MODULE_DESCRIPTION("I2C for Freescale STMP378x");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:i2c_stmp3xxx");
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH, RFC] Freescale STMP: i2c driver
[not found] ` <1245169913.32549.19.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>
@ 2009-06-22 0:47 ` Ben Dooks
[not found] ` <20090622004741.GB9006-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
0 siblings, 1 reply; 11+ messages in thread
From: Ben Dooks @ 2009-06-22 0:47 UTC (permalink / raw)
To: dmitry pervushin; +Cc: Ben Dooks, linux-i2c-u79uwXL29TY76Z2rM5mHXA
On Tue, Jun 16, 2009 at 08:31:53PM +0400, dmitry pervushin wrote:
> On Mon, 2009-06-15 at 09:19 +0100, Ben Dooks wrote:
> > any chance of an reply or update driver for inclusion?
> Here it is:
>
> From: Dmitrij Frasenyak <d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
> Signed-off-by: dmitry pervushin <dpervushin-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
>
> Add I2C driver for Freescale STMP boards
Sorry, forgot to come back and sort this one out.
The From: field is fine, but the first signed-off-by should also be
from Dimirij as well (to say that as the creator he is accepting that
this is suitable for inclusion - I do hope you've both read at-least
section 12 of Documentation/SubmittingPatches).
The signed-off-by should go after the description text, and must have
Dimirij's Signed-off-By, and it would be nice to have yours in there
too.
> ---
> drivers/i2c/busses/Kconfig | 7
> drivers/i2c/busses/Makefile | 1
> drivers/i2c/busses/i2c-stmp378x.c | 448 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 456 insertions(+)
>
> Index: linux-2.6-arm/drivers/i2c/busses/Kconfig
> ===================================================================
> --- linux-2.6-arm.orig/drivers/i2c/busses/Kconfig
> +++ linux-2.6-arm/drivers/i2c/busses/Kconfig
> @@ -513,6 +513,13 @@ config I2C_SIMTEC
> This driver can also be built as a module. If so, the module
> will be called i2c-simtec.
>
> +config I2C_STMP378X
> + tristate "STMP378x I2C adapter"
> + depends on ARCH_STMP378X
> + help
> + Include support of I2C adapter on Freescale STMP378x board; to build
> + the driver as a module, say M here - module will be called i2c-stmp378x.
> +
> config I2C_VERSATILE
> tristate "ARM Versatile/Realview I2C bus support"
> depends on ARCH_VERSATILE || ARCH_REALVIEW
> Index: linux-2.6-arm/drivers/i2c/busses/Makefile
> ===================================================================
> --- linux-2.6-arm.orig/drivers/i2c/busses/Makefile
> +++ linux-2.6-arm/drivers/i2c/busses/Makefile
> @@ -48,6 +48,7 @@ obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
> obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
> obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
> obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
> +obj-$(CONFIG_I2C_STMP378X) += i2c-stmp378x.o
> obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
>
> # External I2C/SMBus adapter drivers
> Index: linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
> @@ -0,0 +1,448 @@
> +/*
> + * Freescale STMP378X I2C bus driver
> + *
> + * Author: Dmitrij Frasenyak <sed-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
> + *
> + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
> + * Copyright 2008 Embedded Alley Solutions, 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/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/clk.h>
> +#include <linux/io.h>
> +#include <linux/dma-mapping.h>
> +
> +#include <mach/platform.h>
> +#include <mach/stmp3xxx.h>
> +#include <mach/dma.h>
> +#include <mach/regs-i2c.h>
> +#include <mach/regs-apbx.h>
> +
> +static const struct stmp3xxx_dma_command cmd_i2c_select = {
> + .cmd = BF(1, APBX_CHn_CMD_XFER_COUNT) |
> + BF(1, APBX_CHn_CMD_CMDWORDS) |
> + BM_APBX_CHn_CMD_WAIT4ENDCMD |
> + BM_APBX_CHn_CMD_CHAIN |
> + BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
> + .pio_words = {
> + [0] = BM_I2C_CTRL0_RETAIN_CLOCK |
> + BM_I2C_CTRL0_PRE_SEND_START |
> + BM_I2C_CTRL0_MASTER_MODE |
> + BM_I2C_CTRL0_DIRECTION |
> + BF(1, I2C_CTRL0_XFER_COUNT),
> + },
> +};
> +
> +static const struct stmp3xxx_dma_command cmd_i2c_write = {
> + .cmd = BM_APBX_CHn_CMD_SEMAPHORE |
> + BF(1, APBX_CHn_CMD_CMDWORDS) |
> + BM_APBX_CHn_CMD_WAIT4ENDCMD |
> + BM_APBX_CHn_CMD_IRQONCMPLT |
> + BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
> + .pio_words = {
> + [0] = BM_I2C_CTRL0_PRE_SEND_START |
> + BM_I2C_CTRL0_MASTER_MODE |
> + BM_I2C_CTRL0_DIRECTION,
> + },
> +};
> +
> +static const struct stmp3xxx_dma_command cmd_i2c_read = {
> + .cmd = BM_APBX_CHn_CMD_SEMAPHORE |
> + BF(1, APBX_CHn_CMD_CMDWORDS) |
> + BM_APBX_CHn_CMD_WAIT4ENDCMD |
> + BM_APBX_CHn_CMD_IRQONCMPLT |
> + BF(BV_APBX_CHn_CMD_COMMAND__DMA_WRITE, APBX_CHn_CMD_COMMAND),
> + .pio_words = {
> + [0] = BM_I2C_CTRL0_SEND_NAK_ON_LAST |
> + BM_I2C_CTRL0_MASTER_MODE,
> + },
> +};
> +
> +/**
> + * struct stmp378x_i2c_dev - STMP3xxx I2C adapter data
> + * @dev: the associated platform_device
> + * @cmd_complete: the completion structure indicating that message has been
> + * transferred
> + * @cmd_err: current error code
> + * @adapter: the adapter we implement
> + * @io: io address
> + * @irq: irq number
> + * @dma: dma channel
> + * @bufv: pointer to the dma buffer
> + * @bufp: physical address of the dma buffer
> + * @i2c_dma_read: "read" dma chain (read request and then write)
> + * @i2c_dma_write: "write" dma chain (the single combined write request)
> + */
> +struct stmp378x_i2c_dev {
> + struct device *dev;
> + int irq;
> + struct completion cmd_complete;
> + u32 cmd_err;
> + struct i2c_adapter adapter;
> + void __iomem *io;
> + unsigned dma;
> +
> + dma_addr_t bufp;
> + unsigned char *bufv;
> +
> + struct stmp3xxx_dma_descriptor i2c_dma_read[2],
> + i2c_dma_write;
> +};
> +
> +static int stmp378x_i2c_init(struct stmp378x_i2c_dev *dev)
> +{
> + int err;
> +
> + err = stmp3xxx_dma_request(dev->dma, dev->dev, dev_name(dev->dev));
> + if (err)
> + goto out;
> + stmp3xxx_reset_block(dev->io, true);
> +
> + dev->bufv = dma_alloc_coherent(dev->dev, PAGE_SIZE,
> + &dev->bufp, GFP_KERNEL);
> + if (!dev->bufv)
> + goto out_dma;
> +
> + err = stmp3xxx_request_pin_group(dev->dev->platform_data,
> + dev_name(dev->dev));
> + if (err)
> + goto out_free;
> +
> + err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[0]);
> + if (err)
> + goto out_rd0;
> + err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[1]);
> + if (err)
> + goto out_rd1;
> + err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_write);
> + if (err)
> + goto out_wr;
> +
> + stmp3xxx_dma_reset_channel(dev->dma);
> + stmp3xxx_dma_clear_interrupt(dev->dma);
> + stmp3xxx_dma_enable_interrupt(dev->dma);
> +
> + return 0;
> +/*
> + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
> +*/
> +out_wr:
> + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
> +out_rd1:
> + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
> +out_rd0:
> + stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
> +out_free:
> + dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
> +out_dma:
> + stmp3xxx_dma_release(dev->dma);
> +out:
> + return err;
> +}
> +
> +static void stmp378x_i2c_release(struct stmp378x_i2c_dev *dev)
> +{
> + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
> + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
> + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
> + dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
> + stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
> + stmp3xxx_dma_release(dev->dma);
> +}
> +/*
> + * Low level master read/write transaction.
> + */
> +static int stmp378x_i2c_xfer_msg(struct i2c_adapter *adap,
> + struct i2c_msg *msg, int stop)
> +{
> + struct stmp378x_i2c_dev *dev = i2c_get_adapdata(adap);
> + int err;
> + struct stmp3xxx_dma_descriptor *c, *r;
> +
> + init_completion(&dev->cmd_complete);
> + dev->cmd_err = 0;
> +
> + if ((msg->len == 0) || (msg->len > (PAGE_SIZE - 1)))
> + return -EINVAL;
> +
> + if (msg->flags & I2C_M_RD) {
> +
> + c = &dev->i2c_dma_read[0];
> + r = &dev->i2c_dma_read[1];
> +
> + /* first, setup the SELECT command */
> + memcpy(c->command, &cmd_i2c_select, sizeof(cmd_i2c_select));
> + c->command->buf_ptr = dev->bufp;
> + dev->bufv[0] = msg->addr | I2C_SMBUS_READ;
> +
> + /* then, setup the READ command */
> + memcpy(r->command, &cmd_i2c_read, sizeof(cmd_i2c_read));
> + r->command->cmd |= BF(msg->len, APBX_CHn_CMD_XFER_COUNT);
> + r->command->pio_words[0] |= BF(msg->len, I2C_CTRL0_XFER_COUNT);
> + if (stop)
> + r->command->pio_words[0] |= BM_I2C_CTRL0_POST_SEND_STOP;
> + /*
> + * yes, STMP controller can DMA transfer the data from any
> + * byte boundary
> + */
> + r->command->buf_ptr = dev->bufp + 1;
> +
> + /* and chain them together */
> + c->command->next = r->handle;
> +
> + } else {
> +
> + c = &dev->i2c_dma_write;
> + memcpy(c->command, &cmd_i2c_write, sizeof(cmd_i2c_write));
> + c->command->cmd |= BF(msg->len + 1, APBX_CHn_CMD_XFER_COUNT);
> + c->command->pio_words[0] |=
> + BF(msg->len + 1, I2C_CTRL0_XFER_COUNT);
> + if (stop)
> + c->command->pio_words[0] |=
> + BM_I2C_CTRL0_POST_SEND_STOP;
> + dev->bufv[0] = msg->addr | I2C_SMBUS_WRITE;
> + memcpy(dev->bufv + 1, msg->buf, msg->len);
> + }
> +
> + stmp3xxx_dma_go(dev->dma, c, 1);
> +
> + err = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
> + msecs_to_jiffies(1000));
> +
> + if (err < 0) {
> + dev_dbg(dev->dev, "controler is timed out\n");
> + return -ETIMEDOUT;
> + }
> + if (!dev->cmd_err && (msg->flags & I2C_M_RD))
> + memcpy(msg->buf, dev->bufv + 1, msg->len);
> +
> + dev_dbg(dev->dev, "done with err=%d\n", dev->cmd_err);
> +
> + return dev->cmd_err;
> +}
> +
> +
> +static int
> +stmp378x_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 = stmp378x_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
> + if (err)
> + return err;
> + }
> + return num;
> +}
> +
> +static u32
> +stmp378x_i2c_func(struct i2c_adapter *adap)
> +{
> + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
> +}
> +
> +#define I2C_IRQ_MASK 0x000000FF
> +static irqreturn_t
> +stmp378x_i2c_isr(int this_irq, void *dev_id)
> +{
> + struct stmp378x_i2c_dev *dev = dev_id;
> + u32 stat;
> + u32 done_mask = BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ |
> + BM_I2C_CTRL1_BUS_FREE_IRQ ;
> +
> + stat = readl(HW_I2C_CTRL1 + dev->io) & I2C_IRQ_MASK;
> + if (!stat)
> + return IRQ_NONE;
> +
> + if (stat & BM_I2C_CTRL1_NO_SLAVE_ACK_IRQ) {
> + dev->cmd_err = -EREMOTEIO;
> +
> + /*
> + * Stop DMA
> + * Clear NAK
> + */
> + stmp3xxx_setl(BM_I2C_CTRL1_CLR_GOT_A_NAK,
> + HW_I2C_CTRL1 + dev->io);
> + stmp3xxx_dma_reset_channel(dev->dma);
> + stmp3xxx_dma_clear_interrupt(dev->dma);
> +
> + complete(&dev->cmd_complete);
> +
> + 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)) {
> + dev->cmd_err = -EIO;
> + complete(&dev->cmd_complete);
> + goto done;
> + }
> +
> + if ((stat & done_mask) == done_mask)
> + complete(&dev->cmd_complete);
> +
> +done:
> + stmp3xxx_clearl(stat, dev->io + HW_I2C_CTRL1);
> + return IRQ_HANDLED;
> +}
> +
> +static const struct i2c_algorithm stmp378x_i2c_algo = {
> + .master_xfer = stmp378x_i2c_xfer,
> + .functionality = stmp378x_i2c_func,
> +};
> +
> +
> +static int __devinit
> +stmp378x_i2c_probe(struct platform_device *pdev)
> +{
> + struct stmp378x_i2c_dev *dev;
> + struct i2c_adapter *adap;
> + struct resource *mem, *dma;
> + int err = 0;
> +
> + dev = kzalloc(sizeof(struct stmp378x_i2c_dev), GFP_KERNEL);
> + if (!dev) {
> + dev_err(&pdev->dev, "no mem \n");
> + return -ENOMEM;
> + }
> +
> + dev->irq = platform_get_irq(pdev, 0); /* Error */
> + if (dev->irq < 0) {
> + dev_err(&pdev->dev, "no err_irq resource\n");
> + err = dev->irq;
> + goto nores;
> + }
> +
> + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem) {
> + dev_err(&pdev->dev, "no memory window\n");
> + err = -ENODEV;
> + goto nores;
> + }
> +
> + dma = platform_get_resource(pdev, IORESOURCE_DMA, 0);
> + if (!dma) {
> + dev_err(&pdev->dev, "no dma channel\n");
> + err = -ENODEV;
> + goto nores;
> + }
> + dev->dma = dma->start;
> +
> + dev->dev = &pdev->dev;
> + dev->io = ioremap(mem->start, resource_size(mem));
> + if (!dev->io) {
> + dev_err(&pdev->dev, "cannot ioremap\n");
> + err = -ENXIO;
> + goto nores;
> + }
> +
> + err = request_irq(dev->irq, stmp378x_i2c_isr, 0, pdev->name, dev);
> + if (err) {
> + dev_err(&pdev->dev, "Can't get IRQ\n");
> + goto no_err_irq;
> + }
> +
> + err = stmp378x_i2c_init(dev);
> + if (err) {
> + dev_err(&pdev->dev, "HW Init failed\n");
> + goto init_failed;
> + }
> +
> + stmp3xxx_setl(0xFF00, dev->io + HW_I2C_CTRL1);
> +
> + adap = &dev->adapter;
> + i2c_set_adapdata(adap, dev);
> + adap->owner = THIS_MODULE;
> + adap->class = I2C_CLASS_TV_DIGITAL;
> + strncpy(adap->name, "378x I2C adapter", sizeof(adap->name));
> + adap->algo = &stmp378x_i2c_algo;
> + adap->dev.parent = &pdev->dev;
> +
> + adap->nr = pdev->id;
> + err = i2c_add_numbered_adapter(adap);
> + if (err) {
> + dev_err(&pdev->dev, "Failed to add adapter\n");
> + goto no_i2c_adapter;
> +
> + }
> +
> + return 0;
> +
> +no_i2c_adapter:
> + stmp378x_i2c_release(dev);
> +init_failed:
> + free_irq(dev->irq, dev);
> +no_err_irq:
> + iounmap(dev->io);
> +nores:
> + kfree(dev);
> + return err;
> +}
> +
> +static int __devexit
> +stmp378x_i2c_remove(struct platform_device *pdev)
> +{
> + struct stmp378x_i2c_dev *dev = platform_get_drvdata(pdev);
> + int res;
> +
> + res = i2c_del_adapter(&dev->adapter);
> + if (res)
> + return -EBUSY;
> +
> + stmp378x_i2c_release(dev);
> +
> + platform_set_drvdata(pdev, NULL);
> +
> + free_irq(dev->irq, dev);
> + iounmap(dev->io);
> +
> + kfree(dev);
> + return 0;
> +}
> +
> +static struct platform_driver stmp378x_i2c_driver = {
> + .probe = stmp378x_i2c_probe,
> + .remove = __devexit_p(stmp378x_i2c_remove),
> + .driver = {
> + .name = "i2c_stmp3xxx",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init stmp378x_i2c_init_driver(void)
> +{
> + return platform_driver_register(&stmp378x_i2c_driver);
> +}
> +module_init(stmp378x_i2c_init_driver);
> +
> +static void __exit stmp378x_i2c_exit_driver(void)
> +{
> + platform_driver_unregister(&stmp378x_i2c_driver);
> +}
> +module_exit(stmp378x_i2c_exit_driver);
> +
> +MODULE_AUTHOR("d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org");
> +MODULE_DESCRIPTION("I2C for Freescale STMP378x");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:i2c_stmp3xxx");
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Ben (ben-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH, RFC] Freescale STMP: i2c driver
[not found] ` <1245169539.32549.12.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>
@ 2009-06-22 10:51 ` Ben Dooks
[not found] ` <20090622105148.GC9006-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
0 siblings, 1 reply; 11+ messages in thread
From: Ben Dooks @ 2009-06-22 10:51 UTC (permalink / raw)
To: dmitry pervushin; +Cc: Ben Dooks, linux-i2c-u79uwXL29TY76Z2rM5mHXA
On Tue, Jun 16, 2009 at 08:25:39PM +0400, dmitry pervushin wrote:
> Hello Ben,
>
> I am sending the updated patch in next message, and this message is just
> to answer your questions/concerns.\
>
> [...]
> > > +#include <mach/regs-apbx.h>
> > > +
> > > +static const u32 I2C_READ = 1,
> > > + I2C_WRITE = 0;
> >
> > do you really want to be defining things with a prefix of I2C, thatB
> > might end up clashing with the i2c core?
> Fixed, changed to I2C_SMBUS_READ/WRITE as Jean Delvare proposed.
> >
> > > +static const struct stmp3xxx_dma_command cmd_i2c_select = {
> > > + .cmd = BF(1, APBX_CHn_CMD_XFER_COUNT) |
> > > + BF(1, APBX_CHn_CMD_CMDWORDS) |
> > > + BM_APBX_CHn_CMD_WAIT4ENDCMD |
> > > + BM_APBX_CHn_CMD_CHAIN |
> > > + BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
> >
> > what is BF() ?
> it is the macro from arch/arm/plat-stmp3xxx/include/mach/platform.h,
> abbreviated "bitfield" :) For example, BF(1, APBX_CHn_CMD_CMDWORDS) is
> expanded like
> ((1 << BP_APBX_CHn_CMD_CMDWORDS) & BM_APBX_CHn_CMD_CMDWORDS).
> BP_xxx stuff means bitfield position, BM_xxx - bitfield mask.
yuck. I don't like people hiding thinks in macros for what is very
little gain, especially when an extant constant is being used!
--
Ben (ben-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH, RFC] Freescale STMP: i2c driver
[not found] ` <20090622004741.GB9006-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
@ 2009-06-22 13:39 ` dmitry pervushin
2009-06-22 13:42 ` [PATCH, RFC] Freescale STMP: i2c driver, updated with correct signed-off lines dmitry pervushin
1 sibling, 0 replies; 11+ messages in thread
From: dmitry pervushin @ 2009-06-22 13:39 UTC (permalink / raw)
To: Ben Dooks; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA
On Mon, 2009-06-22 at 01:47 +0100, Ben Dooks wrote:
> On Tue, Jun 16, 2009 at 08:31:53PM +0400, dmitry pervushin wrote:
> > On Mon, 2009-06-15 at 09:19 +0100, Ben Dooks wrote:
> > > any chance of an reply or update driver for inclusion?
> > Here it is:
> >
> > From: Dmitrij Frasenyak <d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
> > Signed-off-by: dmitry pervushin <dpervushin-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
> >
> > Add I2C driver for Freescale STMP boards
From: Dmitrij Frasenyak <d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
Signed-off-by: Dmitrij Frasenyak <d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
Signed-off-by: dmitry pervushin <dpervushin-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
>
> > ---
> > drivers/i2c/busses/Kconfig | 7
> > drivers/i2c/busses/Makefile | 1
> > drivers/i2c/busses/i2c-stmp378x.c | 448 ++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 456 insertions(+)
> >
> > Index: linux-2.6-arm/drivers/i2c/busses/Kconfig
> > ===================================================================
> > --- linux-2.6-arm.orig/drivers/i2c/busses/Kconfig
> > +++ linux-2.6-arm/drivers/i2c/busses/Kconfig
> > @@ -513,6 +513,13 @@ config I2C_SIMTEC
> > This driver can also be built as a module. If so, the module
> > will be called i2c-simtec.
> >
> > +config I2C_STMP378X
> > + tristate "STMP378x I2C adapter"
> > + depends on ARCH_STMP378X
> > + help
> > + Include support of I2C adapter on Freescale STMP378x board; to build
> > + the driver as a module, say M here - module will be called i2c-stmp378x.
> > +
> > config I2C_VERSATILE
> > tristate "ARM Versatile/Realview I2C bus support"
> > depends on ARCH_VERSATILE || ARCH_REALVIEW
> > Index: linux-2.6-arm/drivers/i2c/busses/Makefile
> > ===================================================================
> > --- linux-2.6-arm.orig/drivers/i2c/busses/Makefile
> > +++ linux-2.6-arm/drivers/i2c/busses/Makefile
> > @@ -48,6 +48,7 @@ obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
> > obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
> > obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
> > obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
> > +obj-$(CONFIG_I2C_STMP378X) += i2c-stmp378x.o
> > obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
> >
> > # External I2C/SMBus adapter drivers
> > Index: linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
> > ===================================================================
> > --- /dev/null
> > +++ linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
> > @@ -0,0 +1,448 @@
> > +/*
> > + * Freescale STMP378X I2C bus driver
> > + *
> > + * Author: Dmitrij Frasenyak <sed-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
> > + *
> > + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
> > + * Copyright 2008 Embedded Alley Solutions, 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/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/clk.h>
> > +#include <linux/io.h>
> > +#include <linux/dma-mapping.h>
> > +
> > +#include <mach/platform.h>
> > +#include <mach/stmp3xxx.h>
> > +#include <mach/dma.h>
> > +#include <mach/regs-i2c.h>
> > +#include <mach/regs-apbx.h>
> > +
> > +static const struct stmp3xxx_dma_command cmd_i2c_select = {
> > + .cmd = BF(1, APBX_CHn_CMD_XFER_COUNT) |
> > + BF(1, APBX_CHn_CMD_CMDWORDS) |
> > + BM_APBX_CHn_CMD_WAIT4ENDCMD |
> > + BM_APBX_CHn_CMD_CHAIN |
> > + BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
> > + .pio_words = {
> > + [0] = BM_I2C_CTRL0_RETAIN_CLOCK |
> > + BM_I2C_CTRL0_PRE_SEND_START |
> > + BM_I2C_CTRL0_MASTER_MODE |
> > + BM_I2C_CTRL0_DIRECTION |
> > + BF(1, I2C_CTRL0_XFER_COUNT),
> > + },
> > +};
> > +
> > +static const struct stmp3xxx_dma_command cmd_i2c_write = {
> > + .cmd = BM_APBX_CHn_CMD_SEMAPHORE |
> > + BF(1, APBX_CHn_CMD_CMDWORDS) |
> > + BM_APBX_CHn_CMD_WAIT4ENDCMD |
> > + BM_APBX_CHn_CMD_IRQONCMPLT |
> > + BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
> > + .pio_words = {
> > + [0] = BM_I2C_CTRL0_PRE_SEND_START |
> > + BM_I2C_CTRL0_MASTER_MODE |
> > + BM_I2C_CTRL0_DIRECTION,
> > + },
> > +};
> > +
> > +static const struct stmp3xxx_dma_command cmd_i2c_read = {
> > + .cmd = BM_APBX_CHn_CMD_SEMAPHORE |
> > + BF(1, APBX_CHn_CMD_CMDWORDS) |
> > + BM_APBX_CHn_CMD_WAIT4ENDCMD |
> > + BM_APBX_CHn_CMD_IRQONCMPLT |
> > + BF(BV_APBX_CHn_CMD_COMMAND__DMA_WRITE, APBX_CHn_CMD_COMMAND),
> > + .pio_words = {
> > + [0] = BM_I2C_CTRL0_SEND_NAK_ON_LAST |
> > + BM_I2C_CTRL0_MASTER_MODE,
> > + },
> > +};
> > +
> > +/**
> > + * struct stmp378x_i2c_dev - STMP3xxx I2C adapter data
> > + * @dev: the associated platform_device
> > + * @cmd_complete: the completion structure indicating that message has been
> > + * transferred
> > + * @cmd_err: current error code
> > + * @adapter: the adapter we implement
> > + * @io: io address
> > + * @irq: irq number
> > + * @dma: dma channel
> > + * @bufv: pointer to the dma buffer
> > + * @bufp: physical address of the dma buffer
> > + * @i2c_dma_read: "read" dma chain (read request and then write)
> > + * @i2c_dma_write: "write" dma chain (the single combined write request)
> > + */
> > +struct stmp378x_i2c_dev {
> > + struct device *dev;
> > + int irq;
> > + struct completion cmd_complete;
> > + u32 cmd_err;
> > + struct i2c_adapter adapter;
> > + void __iomem *io;
> > + unsigned dma;
> > +
> > + dma_addr_t bufp;
> > + unsigned char *bufv;
> > +
> > + struct stmp3xxx_dma_descriptor i2c_dma_read[2],
> > + i2c_dma_write;
> > +};
> > +
> > +static int stmp378x_i2c_init(struct stmp378x_i2c_dev *dev)
> > +{
> > + int err;
> > +
> > + err = stmp3xxx_dma_request(dev->dma, dev->dev, dev_name(dev->dev));
> > + if (err)
> > + goto out;
> > + stmp3xxx_reset_block(dev->io, true);
> > +
> > + dev->bufv = dma_alloc_coherent(dev->dev, PAGE_SIZE,
> > + &dev->bufp, GFP_KERNEL);
> > + if (!dev->bufv)
> > + goto out_dma;
> > +
> > + err = stmp3xxx_request_pin_group(dev->dev->platform_data,
> > + dev_name(dev->dev));
> > + if (err)
> > + goto out_free;
> > +
> > + err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[0]);
> > + if (err)
> > + goto out_rd0;
> > + err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[1]);
> > + if (err)
> > + goto out_rd1;
> > + err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_write);
> > + if (err)
> > + goto out_wr;
> > +
> > + stmp3xxx_dma_reset_channel(dev->dma);
> > + stmp3xxx_dma_clear_interrupt(dev->dma);
> > + stmp3xxx_dma_enable_interrupt(dev->dma);
> > +
> > + return 0;
> > +/*
> > + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
> > +*/
> > +out_wr:
> > + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
> > +out_rd1:
> > + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
> > +out_rd0:
> > + stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
> > +out_free:
> > + dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
> > +out_dma:
> > + stmp3xxx_dma_release(dev->dma);
> > +out:
> > + return err;
> > +}
> > +
> > +static void stmp378x_i2c_release(struct stmp378x_i2c_dev *dev)
> > +{
> > + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
> > + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
> > + stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
> > + dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
> > + stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
> > + stmp3xxx_dma_release(dev->dma);
> > +}
> > +/*
> > + * Low level master read/write transaction.
> > + */
> > +static int stmp378x_i2c_xfer_msg(struct i2c_adapter *adap,
> > + struct i2c_msg *msg, int stop)
> > +{
> > + struct stmp378x_i2c_dev *dev = i2c_get_adapdata(adap);
> > + int err;
> > + struct stmp3xxx_dma_descriptor *c, *r;
> > +
> > + init_completion(&dev->cmd_complete);
> > + dev->cmd_err = 0;
> > +
> > + if ((msg->len == 0) || (msg->len > (PAGE_SIZE - 1)))
> > + return -EINVAL;
> > +
> > + if (msg->flags & I2C_M_RD) {
> > +
> > + c = &dev->i2c_dma_read[0];
> > + r = &dev->i2c_dma_read[1];
> > +
> > + /* first, setup the SELECT command */
> > + memcpy(c->command, &cmd_i2c_select, sizeof(cmd_i2c_select));
> > + c->command->buf_ptr = dev->bufp;
> > + dev->bufv[0] = msg->addr | I2C_SMBUS_READ;
> > +
> > + /* then, setup the READ command */
> > + memcpy(r->command, &cmd_i2c_read, sizeof(cmd_i2c_read));
> > + r->command->cmd |= BF(msg->len, APBX_CHn_CMD_XFER_COUNT);
> > + r->command->pio_words[0] |= BF(msg->len, I2C_CTRL0_XFER_COUNT);
> > + if (stop)
> > + r->command->pio_words[0] |= BM_I2C_CTRL0_POST_SEND_STOP;
> > + /*
> > + * yes, STMP controller can DMA transfer the data from any
> > + * byte boundary
> > + */
> > + r->command->buf_ptr = dev->bufp + 1;
> > +
> > + /* and chain them together */
> > + c->command->next = r->handle;
> > +
> > + } else {
> > +
> > + c = &dev->i2c_dma_write;
> > + memcpy(c->command, &cmd_i2c_write, sizeof(cmd_i2c_write));
> > + c->command->cmd |= BF(msg->len + 1, APBX_CHn_CMD_XFER_COUNT);
> > + c->command->pio_words[0] |=
> > + BF(msg->len + 1, I2C_CTRL0_XFER_COUNT);
> > + if (stop)
> > + c->command->pio_words[0] |=
> > + BM_I2C_CTRL0_POST_SEND_STOP;
> > + dev->bufv[0] = msg->addr | I2C_SMBUS_WRITE;
> > + memcpy(dev->bufv + 1, msg->buf, msg->len);
> > + }
> > +
> > + stmp3xxx_dma_go(dev->dma, c, 1);
> > +
> > + err = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
> > + msecs_to_jiffies(1000));
> > +
> > + if (err < 0) {
> > + dev_dbg(dev->dev, "controler is timed out\n");
> > + return -ETIMEDOUT;
> > + }
> > + if (!dev->cmd_err && (msg->flags & I2C_M_RD))
> > + memcpy(msg->buf, dev->bufv + 1, msg->len);
> > +
> > + dev_dbg(dev->dev, "done with err=%d\n", dev->cmd_err);
> > +
> > + return dev->cmd_err;
> > +}
> > +
> > +
> > +static int
> > +stmp378x_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 = stmp378x_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
> > + if (err)
> > + return err;
> > + }
> > + return num;
> > +}
> > +
> > +static u32
> > +stmp378x_i2c_func(struct i2c_adapter *adap)
> > +{
> > + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
> > +}
> > +
> > +#define I2C_IRQ_MASK 0x000000FF
> > +static irqreturn_t
> > +stmp378x_i2c_isr(int this_irq, void *dev_id)
> > +{
> > + struct stmp378x_i2c_dev *dev = dev_id;
> > + u32 stat;
> > + u32 done_mask = BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ |
> > + BM_I2C_CTRL1_BUS_FREE_IRQ ;
> > +
> > + stat = readl(HW_I2C_CTRL1 + dev->io) & I2C_IRQ_MASK;
> > + if (!stat)
> > + return IRQ_NONE;
> > +
> > + if (stat & BM_I2C_CTRL1_NO_SLAVE_ACK_IRQ) {
> > + dev->cmd_err = -EREMOTEIO;
> > +
> > + /*
> > + * Stop DMA
> > + * Clear NAK
> > + */
> > + stmp3xxx_setl(BM_I2C_CTRL1_CLR_GOT_A_NAK,
> > + HW_I2C_CTRL1 + dev->io);
> > + stmp3xxx_dma_reset_channel(dev->dma);
> > + stmp3xxx_dma_clear_interrupt(dev->dma);
> > +
> > + complete(&dev->cmd_complete);
> > +
> > + 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)) {
> > + dev->cmd_err = -EIO;
> > + complete(&dev->cmd_complete);
> > + goto done;
> > + }
> > +
> > + if ((stat & done_mask) == done_mask)
> > + complete(&dev->cmd_complete);
> > +
> > +done:
> > + stmp3xxx_clearl(stat, dev->io + HW_I2C_CTRL1);
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static const struct i2c_algorithm stmp378x_i2c_algo = {
> > + .master_xfer = stmp378x_i2c_xfer,
> > + .functionality = stmp378x_i2c_func,
> > +};
> > +
> > +
> > +static int __devinit
> > +stmp378x_i2c_probe(struct platform_device *pdev)
> > +{
> > + struct stmp378x_i2c_dev *dev;
> > + struct i2c_adapter *adap;
> > + struct resource *mem, *dma;
> > + int err = 0;
> > +
> > + dev = kzalloc(sizeof(struct stmp378x_i2c_dev), GFP_KERNEL);
> > + if (!dev) {
> > + dev_err(&pdev->dev, "no mem \n");
> > + return -ENOMEM;
> > + }
> > +
> > + dev->irq = platform_get_irq(pdev, 0); /* Error */
> > + if (dev->irq < 0) {
> > + dev_err(&pdev->dev, "no err_irq resource\n");
> > + err = dev->irq;
> > + goto nores;
> > + }
> > +
> > + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > + if (!mem) {
> > + dev_err(&pdev->dev, "no memory window\n");
> > + err = -ENODEV;
> > + goto nores;
> > + }
> > +
> > + dma = platform_get_resource(pdev, IORESOURCE_DMA, 0);
> > + if (!dma) {
> > + dev_err(&pdev->dev, "no dma channel\n");
> > + err = -ENODEV;
> > + goto nores;
> > + }
> > + dev->dma = dma->start;
> > +
> > + dev->dev = &pdev->dev;
> > + dev->io = ioremap(mem->start, resource_size(mem));
> > + if (!dev->io) {
> > + dev_err(&pdev->dev, "cannot ioremap\n");
> > + err = -ENXIO;
> > + goto nores;
> > + }
> > +
> > + err = request_irq(dev->irq, stmp378x_i2c_isr, 0, pdev->name, dev);
> > + if (err) {
> > + dev_err(&pdev->dev, "Can't get IRQ\n");
> > + goto no_err_irq;
> > + }
> > +
> > + err = stmp378x_i2c_init(dev);
> > + if (err) {
> > + dev_err(&pdev->dev, "HW Init failed\n");
> > + goto init_failed;
> > + }
> > +
> > + stmp3xxx_setl(0xFF00, dev->io + HW_I2C_CTRL1);
> > +
> > + adap = &dev->adapter;
> > + i2c_set_adapdata(adap, dev);
> > + adap->owner = THIS_MODULE;
> > + adap->class = I2C_CLASS_TV_DIGITAL;
> > + strncpy(adap->name, "378x I2C adapter", sizeof(adap->name));
> > + adap->algo = &stmp378x_i2c_algo;
> > + adap->dev.parent = &pdev->dev;
> > +
> > + adap->nr = pdev->id;
> > + err = i2c_add_numbered_adapter(adap);
> > + if (err) {
> > + dev_err(&pdev->dev, "Failed to add adapter\n");
> > + goto no_i2c_adapter;
> > +
> > + }
> > +
> > + return 0;
> > +
> > +no_i2c_adapter:
> > + stmp378x_i2c_release(dev);
> > +init_failed:
> > + free_irq(dev->irq, dev);
> > +no_err_irq:
> > + iounmap(dev->io);
> > +nores:
> > + kfree(dev);
> > + return err;
> > +}
> > +
> > +static int __devexit
> > +stmp378x_i2c_remove(struct platform_device *pdev)
> > +{
> > + struct stmp378x_i2c_dev *dev = platform_get_drvdata(pdev);
> > + int res;
> > +
> > + res = i2c_del_adapter(&dev->adapter);
> > + if (res)
> > + return -EBUSY;
> > +
> > + stmp378x_i2c_release(dev);
> > +
> > + platform_set_drvdata(pdev, NULL);
> > +
> > + free_irq(dev->irq, dev);
> > + iounmap(dev->io);
> > +
> > + kfree(dev);
> > + return 0;
> > +}
> > +
> > +static struct platform_driver stmp378x_i2c_driver = {
> > + .probe = stmp378x_i2c_probe,
> > + .remove = __devexit_p(stmp378x_i2c_remove),
> > + .driver = {
> > + .name = "i2c_stmp3xxx",
> > + .owner = THIS_MODULE,
> > + },
> > +};
> > +
> > +static int __init stmp378x_i2c_init_driver(void)
> > +{
> > + return platform_driver_register(&stmp378x_i2c_driver);
> > +}
> > +module_init(stmp378x_i2c_init_driver);
> > +
> > +static void __exit stmp378x_i2c_exit_driver(void)
> > +{
> > + platform_driver_unregister(&stmp378x_i2c_driver);
> > +}
> > +module_exit(stmp378x_i2c_exit_driver);
> > +
> > +MODULE_AUTHOR("d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org");
> > +MODULE_DESCRIPTION("I2C for Freescale STMP378x");
> > +MODULE_LICENSE("GPL");
> > +MODULE_ALIAS("platform:i2c_stmp3xxx");
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
> > the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH, RFC] Freescale STMP: i2c driver
[not found] ` <20090622105148.GC9006-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
@ 2009-06-22 13:41 ` dmitry pervushin
0 siblings, 0 replies; 11+ messages in thread
From: dmitry pervushin @ 2009-06-22 13:41 UTC (permalink / raw)
To: Ben Dooks; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA
On Mon, 2009-06-22 at 11:51 +0100, Ben Dooks wrote:
> On Tue, Jun 16, 2009 at 08:25:39PM +0400, dmitry pervushin wrote:
> > Hello Ben,
> >
> > I am sending the updated patch in next message, and this message is just
> > to answer your questions/concerns.\
> >
> > [...]
> > > > +#include <mach/regs-apbx.h>
> > > > +
> > > > +static const u32 I2C_READ = 1,
> > > > + I2C_WRITE = 0;
> > >
> > > do you really want to be defining things with a prefix of I2C, thatB
> > > might end up clashing with the i2c core?
> > Fixed, changed to I2C_SMBUS_READ/WRITE as Jean Delvare proposed.
> > >
> > > > +static const struct stmp3xxx_dma_command cmd_i2c_select = {
> > > > + .cmd = BF(1, APBX_CHn_CMD_XFER_COUNT) |
> > > > + BF(1, APBX_CHn_CMD_CMDWORDS) |
> > > > + BM_APBX_CHn_CMD_WAIT4ENDCMD |
> > > > + BM_APBX_CHn_CMD_CHAIN |
> > > > + BF(BV_APBX_CHn_CMD_COMMAND__DMA_READ, APBX_CHn_CMD_COMMAND),
> > >
> > > what is BF() ?
> > it is the macro from arch/arm/plat-stmp3xxx/include/mach/platform.h,
> > abbreviated "bitfield" :) For example, BF(1, APBX_CHn_CMD_CMDWORDS) is
> > expanded like
> > ((1 << BP_APBX_CHn_CMD_CMDWORDS) & BM_APBX_CHn_CMD_CMDWORDS).
> > BP_xxx stuff means bitfield position, BM_xxx - bitfield mask.
>
> yuck. I don't like people hiding thinks in macros for what is very
> little gain, especially when an extant constant is being used!
I hope that your personal preferences do not mix with patches merging process :)
However.. the updated patch is in the next message, again.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH, RFC] Freescale STMP: i2c driver, updated with correct signed-off lines
[not found] ` <20090622004741.GB9006-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
2009-06-22 13:39 ` dmitry pervushin
@ 2009-06-22 13:42 ` dmitry pervushin
1 sibling, 0 replies; 11+ messages in thread
From: dmitry pervushin @ 2009-06-22 13:42 UTC (permalink / raw)
To: Ben Dooks; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA
On Mon, 2009-06-22 at 01:47 +0100, Ben Dooks wrote:
> On Tue, Jun 16, 2009 at 08:31:53PM +0400, dmitry pervushin wrote:
> > On Mon, 2009-06-15 at 09:19 +0100, Ben Dooks wrote:
> > > any chance of an reply or update driver for inclusion?
> > Here it is:
Add I2C driver for Freescale STMP boards
From: Dmitrij Frasenyak <d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
Signed-off-by: Dmitrij Frasenyak <d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
Signed-off-by: dmitry pervushin <dpervushin-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
---
drivers/i2c/busses/Kconfig | 7
drivers/i2c/busses/Makefile | 1
drivers/i2c/busses/i2c-stmp378x.c | 450 ++++++++++++++++++++++++++++++++++++++
3 files changed, 458 insertions(+)
Index: linux-2.6-arm/drivers/i2c/busses/Kconfig
===================================================================
--- linux-2.6-arm.orig/drivers/i2c/busses/Kconfig
+++ linux-2.6-arm/drivers/i2c/busses/Kconfig
@@ -513,6 +513,13 @@ config I2C_SIMTEC
This driver can also be built as a module. If so, the module
will be called i2c-simtec.
+config I2C_STMP378X
+ tristate "STMP378x I2C adapter"
+ depends on ARCH_STMP378X
+ help
+ Include support of I2C adapter on Freescale STMP378x board; to build
+ the driver as a module, say M here - module will be called i2c-stmp378x.
+
config I2C_VERSATILE
tristate "ARM Versatile/Realview I2C bus support"
depends on ARCH_VERSATILE || ARCH_REALVIEW
Index: linux-2.6-arm/drivers/i2c/busses/Makefile
===================================================================
--- linux-2.6-arm.orig/drivers/i2c/busses/Makefile
+++ linux-2.6-arm/drivers/i2c/busses/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
+obj-$(CONFIG_I2C_STMP378X) += i2c-stmp378x.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
# External I2C/SMBus adapter drivers
Index: linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
===================================================================
--- /dev/null
+++ linux-2.6-arm/drivers/i2c/busses/i2c-stmp378x.c
@@ -0,0 +1,450 @@
+/*
+ * Freescale STMP378X I2C bus driver
+ *
+ * Author: Dmitrij Frasenyak <sed-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, 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/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/clk.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+
+#include <mach/platform.h>
+#include <mach/stmp3xxx.h>
+#include <mach/dma.h>
+#include <mach/regs-i2c.h>
+#include <mach/regs-apbx.h>
+
+static const struct stmp3xxx_dma_command cmd_i2c_select = {
+ .cmd = (1 << BP_APBX_CHn_CMD_XFER_COUNT) |
+ (1 << BP_APBX_CHn_CMD_CMDWORDS) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_CHAIN |
+ (BV_APBX_CHn_CMD_COMMAND__DMA_READ << BP_APBX_CHn_CMD_COMMAND),
+ .pio_words = {
+ [0] = BM_I2C_CTRL0_RETAIN_CLOCK |
+ BM_I2C_CTRL0_PRE_SEND_START |
+ BM_I2C_CTRL0_MASTER_MODE |
+ BM_I2C_CTRL0_DIRECTION |
+ (1 << BP_I2C_CTRL0_XFER_COUNT),
+ },
+};
+
+static const struct stmp3xxx_dma_command cmd_i2c_write = {
+ .cmd = BM_APBX_CHn_CMD_SEMAPHORE |
+ (1 << BP_APBX_CHn_CMD_CMDWORDS) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_IRQONCMPLT |
+ (BV_APBX_CHn_CMD_COMMAND__DMA_READ << BP_APBX_CHn_CMD_COMMAND),
+ .pio_words = {
+ [0] = BM_I2C_CTRL0_PRE_SEND_START |
+ BM_I2C_CTRL0_MASTER_MODE |
+ BM_I2C_CTRL0_DIRECTION,
+ },
+};
+
+static const struct stmp3xxx_dma_command cmd_i2c_read = {
+ .cmd = BM_APBX_CHn_CMD_SEMAPHORE |
+ (1 << BP_APBX_CHn_CMD_CMDWORDS) |
+ BM_APBX_CHn_CMD_WAIT4ENDCMD |
+ BM_APBX_CHn_CMD_IRQONCMPLT |
+ (BV_APBX_CHn_CMD_COMMAND__DMA_WRITE << BP_APBX_CHn_CMD_COMMAND),
+ .pio_words = {
+ [0] = BM_I2C_CTRL0_SEND_NAK_ON_LAST |
+ BM_I2C_CTRL0_MASTER_MODE,
+ },
+};
+
+/**
+ * struct stmp378x_i2c_dev - STMP3xxx I2C adapter data
+ * @dev: the associated platform_device
+ * @cmd_complete: the completion structure indicating that message has been
+ * transferred
+ * @cmd_err: current error code
+ * @adapter: the adapter we implement
+ * @io: io address
+ * @irq: irq number
+ * @dma: dma channel
+ * @bufv: pointer to the dma buffer
+ * @bufp: physical address of the dma buffer
+ * @i2c_dma_read: "read" dma chain (read request and then write)
+ * @i2c_dma_write: "write" dma chain (the single combined write request)
+ */
+struct stmp378x_i2c_dev {
+ struct device *dev;
+ int irq;
+ struct completion cmd_complete;
+ u32 cmd_err;
+ struct i2c_adapter adapter;
+ void __iomem *io;
+ unsigned dma;
+
+ dma_addr_t bufp;
+ unsigned char *bufv;
+
+ struct stmp3xxx_dma_descriptor i2c_dma_read[2],
+ i2c_dma_write;
+};
+
+static int stmp378x_i2c_init(struct stmp378x_i2c_dev *dev)
+{
+ int err;
+
+ err = stmp3xxx_dma_request(dev->dma, dev->dev, dev_name(dev->dev));
+ if (err)
+ goto out;
+ stmp3xxx_reset_block(dev->io, true);
+
+ dev->bufv = dma_alloc_coherent(dev->dev, PAGE_SIZE,
+ &dev->bufp, GFP_KERNEL);
+ if (!dev->bufv)
+ goto out_dma;
+
+ err = stmp3xxx_request_pin_group(dev->dev->platform_data,
+ dev_name(dev->dev));
+ if (err)
+ goto out_free;
+
+ err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[0]);
+ if (err)
+ goto out_rd0;
+ err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_read[1]);
+ if (err)
+ goto out_rd1;
+ err = stmp3xxx_dma_allocate_command(dev->dma, &dev->i2c_dma_write);
+ if (err)
+ goto out_wr;
+
+ stmp3xxx_dma_reset_channel(dev->dma);
+ stmp3xxx_dma_clear_interrupt(dev->dma);
+ stmp3xxx_dma_enable_interrupt(dev->dma);
+
+ return 0;
+/*
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
+*/
+out_wr:
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
+out_rd1:
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
+out_rd0:
+ stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
+out_free:
+ dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
+out_dma:
+ stmp3xxx_dma_release(dev->dma);
+out:
+ return err;
+}
+
+static void stmp378x_i2c_release(struct stmp378x_i2c_dev *dev)
+{
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_write);
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[1]);
+ stmp3xxx_dma_free_command(dev->dma, &dev->i2c_dma_read[0]);
+ dma_free_coherent(dev->dev, PAGE_SIZE, dev->bufv, dev->bufp);
+ stmp3xxx_release_pin_group(dev->dev->platform_data, dev_name(dev->dev));
+ stmp3xxx_dma_release(dev->dma);
+}
+/*
+ * Low level master read/write transaction.
+ */
+static int stmp378x_i2c_xfer_msg(struct i2c_adapter *adap,
+ struct i2c_msg *msg, int stop)
+{
+ struct stmp378x_i2c_dev *dev = i2c_get_adapdata(adap);
+ int err;
+ struct stmp3xxx_dma_descriptor *c, *r;
+
+ init_completion(&dev->cmd_complete);
+ dev->cmd_err = 0;
+
+ if ((msg->len == 0) || (msg->len > (PAGE_SIZE - 1)))
+ return -EINVAL;
+
+ if (msg->flags & I2C_M_RD) {
+
+ c = &dev->i2c_dma_read[0];
+ r = &dev->i2c_dma_read[1];
+
+ /* first, setup the SELECT command */
+ memcpy(c->command, &cmd_i2c_select, sizeof(cmd_i2c_select));
+ c->command->buf_ptr = dev->bufp;
+ dev->bufv[0] = msg->addr | I2C_SMBUS_READ;
+
+ /* then, setup the READ command */
+ memcpy(r->command, &cmd_i2c_read, sizeof(cmd_i2c_read));
+ r->command->cmd |= (msg->len << BP_APBX_CHn_CMD_XFER_COUNT);
+ r->command->pio_words[0] |=
+ (msg->len << BP_I2C_CTRL0_XFER_COUNT);
+ if (stop)
+ r->command->pio_words[0] |= BM_I2C_CTRL0_POST_SEND_STOP;
+ /*
+ * yes, STMP controller can DMA transfer the data from any
+ * byte boundary
+ */
+ r->command->buf_ptr = dev->bufp + 1;
+
+ /* and chain them together */
+ c->command->next = r->handle;
+
+ } else {
+
+ c = &dev->i2c_dma_write;
+ memcpy(c->command, &cmd_i2c_write, sizeof(cmd_i2c_write));
+ c->command->cmd |=
+ ((msg->len + 1) << BP_APBX_CHn_CMD_XFER_COUNT);
+ c->command->pio_words[0] |=
+ ((msg->len + 1) << BP_I2C_CTRL0_XFER_COUNT);
+ if (stop)
+ c->command->pio_words[0] |=
+ BM_I2C_CTRL0_POST_SEND_STOP;
+ dev->bufv[0] = msg->addr | I2C_SMBUS_WRITE;
+ memcpy(dev->bufv + 1, msg->buf, msg->len);
+ }
+
+ stmp3xxx_dma_go(dev->dma, c, 1);
+
+ err = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
+ msecs_to_jiffies(1000));
+
+ if (err < 0) {
+ dev_dbg(dev->dev, "controler is timed out\n");
+ return -ETIMEDOUT;
+ }
+ if (!dev->cmd_err && (msg->flags & I2C_M_RD))
+ memcpy(msg->buf, dev->bufv + 1, msg->len);
+
+ dev_dbg(dev->dev, "done with err=%d\n", dev->cmd_err);
+
+ return dev->cmd_err;
+}
+
+
+static int
+stmp378x_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 = stmp378x_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
+ if (err)
+ return err;
+ }
+ return num;
+}
+
+static u32
+stmp378x_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+#define I2C_IRQ_MASK 0x000000FF
+static irqreturn_t
+stmp378x_i2c_isr(int this_irq, void *dev_id)
+{
+ struct stmp378x_i2c_dev *dev = dev_id;
+ u32 stat;
+ u32 done_mask = BM_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ |
+ BM_I2C_CTRL1_BUS_FREE_IRQ ;
+
+ stat = readl(HW_I2C_CTRL1 + dev->io) & I2C_IRQ_MASK;
+ if (!stat)
+ return IRQ_NONE;
+
+ if (stat & BM_I2C_CTRL1_NO_SLAVE_ACK_IRQ) {
+ dev->cmd_err = -EREMOTEIO;
+
+ /*
+ * Stop DMA
+ * Clear NAK
+ */
+ stmp3xxx_setl(BM_I2C_CTRL1_CLR_GOT_A_NAK,
+ HW_I2C_CTRL1 + dev->io);
+ stmp3xxx_dma_reset_channel(dev->dma);
+ stmp3xxx_dma_clear_interrupt(dev->dma);
+
+ complete(&dev->cmd_complete);
+
+ 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)) {
+ dev->cmd_err = -EIO;
+ complete(&dev->cmd_complete);
+ goto done;
+ }
+
+ if ((stat & done_mask) == done_mask)
+ complete(&dev->cmd_complete);
+
+done:
+ stmp3xxx_clearl(stat, dev->io + HW_I2C_CTRL1);
+ return IRQ_HANDLED;
+}
+
+static const struct i2c_algorithm stmp378x_i2c_algo = {
+ .master_xfer = stmp378x_i2c_xfer,
+ .functionality = stmp378x_i2c_func,
+};
+
+
+static int __devinit
+stmp378x_i2c_probe(struct platform_device *pdev)
+{
+ struct stmp378x_i2c_dev *dev;
+ struct i2c_adapter *adap;
+ struct resource *mem, *dma;
+ int err = 0;
+
+ dev = kzalloc(sizeof(struct stmp378x_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ dev_err(&pdev->dev, "no mem \n");
+ return -ENOMEM;
+ }
+
+ dev->irq = platform_get_irq(pdev, 0); /* Error */
+ if (dev->irq < 0) {
+ dev_err(&pdev->dev, "no err_irq resource\n");
+ err = dev->irq;
+ goto nores;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no memory window\n");
+ err = -ENODEV;
+ goto nores;
+ }
+
+ dma = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!dma) {
+ dev_err(&pdev->dev, "no dma channel\n");
+ err = -ENODEV;
+ goto nores;
+ }
+ dev->dma = dma->start;
+
+ dev->dev = &pdev->dev;
+ dev->io = ioremap(mem->start, resource_size(mem));
+ if (!dev->io) {
+ dev_err(&pdev->dev, "cannot ioremap\n");
+ err = -ENXIO;
+ goto nores;
+ }
+
+ err = request_irq(dev->irq, stmp378x_i2c_isr, 0, pdev->name, dev);
+ if (err) {
+ dev_err(&pdev->dev, "Can't get IRQ\n");
+ goto no_err_irq;
+ }
+
+ err = stmp378x_i2c_init(dev);
+ if (err) {
+ dev_err(&pdev->dev, "HW Init failed\n");
+ goto init_failed;
+ }
+
+ stmp3xxx_setl(0xFF00, dev->io + HW_I2C_CTRL1);
+
+ adap = &dev->adapter;
+ i2c_set_adapdata(adap, dev);
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_TV_DIGITAL;
+ strncpy(adap->name, "378x I2C adapter", sizeof(adap->name));
+ adap->algo = &stmp378x_i2c_algo;
+ adap->dev.parent = &pdev->dev;
+
+ adap->nr = pdev->id;
+ err = i2c_add_numbered_adapter(adap);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add adapter\n");
+ goto no_i2c_adapter;
+
+ }
+
+ return 0;
+
+no_i2c_adapter:
+ stmp378x_i2c_release(dev);
+init_failed:
+ free_irq(dev->irq, dev);
+no_err_irq:
+ iounmap(dev->io);
+nores:
+ kfree(dev);
+ return err;
+}
+
+static int __devexit
+stmp378x_i2c_remove(struct platform_device *pdev)
+{
+ struct stmp378x_i2c_dev *dev = platform_get_drvdata(pdev);
+ int res;
+
+ res = i2c_del_adapter(&dev->adapter);
+ if (res)
+ return -EBUSY;
+
+ stmp378x_i2c_release(dev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ free_irq(dev->irq, dev);
+ iounmap(dev->io);
+
+ kfree(dev);
+ return 0;
+}
+
+static struct platform_driver stmp378x_i2c_driver = {
+ .probe = stmp378x_i2c_probe,
+ .remove = __devexit_p(stmp378x_i2c_remove),
+ .driver = {
+ .name = "i2c_stmp3xxx",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init stmp378x_i2c_init_driver(void)
+{
+ return platform_driver_register(&stmp378x_i2c_driver);
+}
+module_init(stmp378x_i2c_init_driver);
+
+static void __exit stmp378x_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&stmp378x_i2c_driver);
+}
+module_exit(stmp378x_i2c_exit_driver);
+
+MODULE_AUTHOR("d.frasenyak-L1vi/lXTdtvkgf6YlCu6wwC/G2K4zDHf@public.gmane.org");
+MODULE_DESCRIPTION("I2C for Freescale STMP378x");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:i2c_stmp3xxx");
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2009-06-22 13:42 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-06-03 19:59 [PATCH, RFC] Freescale STMP: i2c driver dmitry pervushin
[not found] ` <1244059155.4074.19.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>
2009-06-08 22:50 ` Ben Dooks
[not found] ` <20090608225034.GA20446-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
2009-06-09 7:46 ` Jean Delvare
2009-06-16 16:25 ` dmitry pervushin
[not found] ` <1245169539.32549.12.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>
2009-06-22 10:51 ` Ben Dooks
[not found] ` <20090622105148.GC9006-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
2009-06-22 13:41 ` dmitry pervushin
2009-06-15 8:19 ` Ben Dooks
[not found] ` <20090615081938.GA19878-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
2009-06-16 16:31 ` dmitry pervushin
[not found] ` <1245169913.32549.19.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>
2009-06-22 0:47 ` Ben Dooks
[not found] ` <20090622004741.GB9006-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>
2009-06-22 13:39 ` dmitry pervushin
2009-06-22 13:42 ` [PATCH, RFC] Freescale STMP: i2c driver, updated with correct signed-off lines dmitry pervushin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).