* [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[parent not found: <1244059155.4074.19.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>]
* 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
[parent not found: <20090608225034.GA20446-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>]
* 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] ` <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
[parent not found: <1245169539.32549.12.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>]
* 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
[parent not found: <20090622105148.GC9006-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>]
* 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 [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
[parent not found: <20090615081938.GA19878-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>]
* 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
[parent not found: <1245169913.32549.19.camel-GL0tbJR47UuzLY3athcO2A@public.gmane.org>]
* 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
[parent not found: <20090622004741.GB9006-elnMNo+KYs3pIgCt6eIbzw@public.gmane.org>]
* 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, 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 an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.