* [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 a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).