linux-i2c.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] m68knommu: driver for Freescale Coldfire I2C controller.
@ 2010-01-11  0:00 Steven King
  2010-01-11  8:37 ` Jean Delvare
  0 siblings, 1 reply; 7+ messages in thread
From: Steven King @ 2010-01-11  0:00 UTC (permalink / raw)
  To: gerg; +Cc: khali, ben-linux, linux-i2c, linux-kernel, uclinux-dev

Add support for the I2C controller used on Freescale/Motorola Coldfire MCUs. 

Signed-off-by: Steven King <sfking@fdwdc.com>

 drivers/i2c/busses/Kconfig   |   11 +
 drivers/i2c/busses/Makefile  |    1 +
 drivers/i2c/busses/i2c-mcf.c |  463 
++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 475 insertions(+), 0 deletions(-)

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 5f318ce..6caf7a0 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -429,6 +429,17 @@ config I2C_MPC
 	  This driver can also be built as a module.  If so, the module
 	  will be called i2c-mpc.
 
+config I2C_MCF
+	tristate "Freescale Coldfire I2C driver"
+	depends on (M5206 || M5206e || M520x || M523x || M5249 || M527x || M528x || 
M5307 || M532x || M5407)
+	help
+	  This driver supports the I2C interface availible on some Freescale
+	  Coldfire processors (M520x, M523x, M5249, M5271, M5275, M528x,
+	  M5307, M532x, M5407).
+
+	  This driver can be built as a module.  If so, the module
+	  will be called i2c-mcf.
+
 config I2C_MV64XXX
 	tristate "Marvell mv64xxx I2C Controller"
 	depends on (MV64X60 || PLAT_ORION) && EXPERIMENTAL
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 302c551..c71349e 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_I2C_IMX)		+= i2c-imx.o
 obj-$(CONFIG_I2C_IOP3XX)	+= i2c-iop3xx.o
 obj-$(CONFIG_I2C_IXP2000)	+= i2c-ixp2000.o
 obj-$(CONFIG_I2C_MPC)		+= i2c-mpc.o
+obj-$(CONFIG_I2C_MCF)		+= i2c-mcf.o
 obj-$(CONFIG_I2C_MV64XXX)	+= i2c-mv64xxx.o
 obj-$(CONFIG_I2C_OCORES)	+= i2c-ocores.o
 obj-$(CONFIG_I2C_OMAP)		+= i2c-omap.o
diff --git a/drivers/i2c/busses/i2c-mcf.c b/drivers/i2c/busses/i2c-mcf.c
new file mode 100644
index 0000000..27f0d0b
--- /dev/null
+++ b/drivers/i2c/busses/i2c-mcf.c
@@ -0,0 +1,463 @@
+/* Freescale/Motorola Coldfire I2C driver.
+ *
+ * Copyright 2010 Steven King <sfking@fdwdc.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+
+#include <asm/mcfi2c.h>
+
+#define	DRIVER_NAME "mcfi2c"
+
+#define	MCFI2C_ADR			0x00
+#define	MCFI2C_FDR			0x04
+#define	MCFI2C_CR			0x08
+#define		MCFI2C_CR_IEN		0x80
+#define		MCFI2C_CR_IIEN		0x40
+#define		MCFI2C_CR_MSTA		0x20
+#define		MCFI2C_CR_MTX		0x10
+#define		MCFI2C_CR_TXAK		0x08
+#define		MCFI2C_CR_RSTA		0x04
+#define	MCFI2C_DR			0x10
+#define	MCFI2C_SR			0x0C
+#define		MCFI2C_SR_ICF		0x80
+#define		MCFI2C_SR_IAAS		0x40
+#define		MCFI2C_SR_IBB		0x20
+#define		MCFI2C_SR_IAL		0x10
+#define		MCFI2C_SR_SRW		0x04
+#define		MCFI2C_SR_IIF		0x02
+#define		MCFI2C_SR_RXAK		0x01
+
+#define	DEFAULT_I2C_BUS_SPEED		100000
+
+struct mcfi2c {
+	struct i2c_adapter	adapter;
+	void __iomem		*iobase;
+	int			irq;
+	struct clk		*clk;
+	struct completion	completion;
+};
+
+static u8 mcfi2c_rd_cr(struct mcfi2c *mcfi2c)
+{
+	return readb(mcfi2c->iobase + MCFI2C_CR);
+}
+
+static void mcfi2c_wr_cr(struct mcfi2c *mcfi2c, u8 val)
+{
+	writeb(val, mcfi2c->iobase + MCFI2C_CR);
+}
+
+static u8 mcfi2c_rd_sr(struct mcfi2c *mcfi2c)
+{
+	return readb(mcfi2c->iobase + MCFI2C_SR);
+}
+
+static void mcfi2c_wr_sr(struct mcfi2c *mcfi2c, u8 val)
+{
+	writeb(val, mcfi2c->iobase + MCFI2C_SR);
+}
+
+static u8 mcfi2c_rd_dr(struct mcfi2c *mcfi2c)
+{
+	return readb(mcfi2c->iobase + MCFI2C_DR);
+}
+
+static void mcfi2c_wr_dr(struct mcfi2c *mcfi2c, u8 val)
+{
+	writeb(val, mcfi2c->iobase + MCFI2C_DR);
+}
+
+static void mcfi2c_start(struct mcfi2c *mcfi2c)
+{
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
+		     MCFI2C_CR_MTX);
+}
+
+static void mcfi2c_repeat_start(struct mcfi2c *mcfi2c)
+{
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
+		     MCFI2C_CR_MTX | MCFI2C_CR_RSTA);
+}
+
+static void mcfi2c_stop(struct mcfi2c *mcfi2c)
+{
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
+}
+
+static void mcfi2c_tx_ack(struct mcfi2c *mcfi2c)
+{
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA);
+}
+
+static void mcfi2c_tx_nak(struct mcfi2c *mcfi2c)
+{
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
+		     MCFI2C_CR_TXAK);
+}
+
+static irqreturn_t mcfi2c_irq_handler(int this_irq, void *dev_id)
+{
+	struct mcfi2c *mcfi2c = dev_id;
+
+	/* clear interrupt */
+	mcfi2c_wr_sr(mcfi2c, 0);
+	complete(&mcfi2c->completion);
+
+	return IRQ_HANDLED;
+}
+
+static void mcfi2c_reset(struct mcfi2c *mcfi2c)
+{
+	mcfi2c_wr_cr(mcfi2c, 0);
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_MSTA);
+	mcfi2c_rd_dr(mcfi2c);
+	mcfi2c_wr_sr(mcfi2c, 0);
+	mcfi2c_wr_cr(mcfi2c, 0);
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
+}
+
+static void mcfi2c_wait_for_bus_idle(struct mcfi2c *mcfi2c)
+{
+	if (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB) {
+		unsigned long timeout = jiffies + HZ / 2;
+		do {
+			cond_resched();
+			if (time_after(jiffies, timeout)) {
+				mcfi2c_reset(mcfi2c);
+				break;
+			}
+		} while (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB);
+	}
+}
+
+static int mcfi2c_wait_for_bus_busy(struct mcfi2c *mcfi2c)
+{
+	u8 sr;
+	while (!((sr = mcfi2c_rd_sr(mcfi2c)) & MCFI2C_SR_IBB))
+		if (sr & MCFI2C_SR_IAL) {
+			mcfi2c_reset(mcfi2c);
+			return -EIO;
+		}
+	return 0;
+}
+
+static int mcfi2c_xmit(struct mcfi2c *mcfi2c, u16 addr, u16 flags, u8 *buf,
+		       u16 len, int timeout, int more)
+{
+	if (!(mcfi2c_rd_cr(mcfi2c) & MCFI2C_CR_MSTA)) {
+		mcfi2c_wait_for_bus_idle(mcfi2c);
+
+		INIT_COMPLETION(mcfi2c->completion);
+		mcfi2c_start(mcfi2c);
+
+		if (mcfi2c_wait_for_bus_busy(mcfi2c))
+			return -EIO;
+	}
+
+	mcfi2c_wr_dr(mcfi2c, (addr << 1) | (flags & I2C_M_RD));
+	while (wait_for_completion_timeout(&mcfi2c->completion, timeout)) {
+		u8 sr = mcfi2c_rd_sr(mcfi2c);
+		if (sr & MCFI2C_SR_IAL) {
+			mcfi2c_wr_sr(mcfi2c, ~MCFI2C_SR_IAL);
+			return -EIO;
+		} else if (mcfi2c_rd_cr(mcfi2c) & MCFI2C_CR_MTX) {
+			if (sr & MCFI2C_SR_RXAK) {
+				mcfi2c_stop(mcfi2c);
+				return -EIO;
+			} else if (flags & I2C_M_RD) {
+				if (len > 1)
+					mcfi2c_tx_ack(mcfi2c);
+				else
+					mcfi2c_tx_nak(mcfi2c);
+				/* dummy read */
+				mcfi2c_rd_dr(mcfi2c);
+			} else if (len--) {
+				mcfi2c_wr_dr(mcfi2c, *buf++);
+			} else {
+				if (more)
+					mcfi2c_repeat_start(mcfi2c);
+				else
+					mcfi2c_stop(mcfi2c);
+				return 0;
+			}
+		} else if (--len) {
+			if (!(len > 1))
+				mcfi2c_tx_nak(mcfi2c);
+			*buf++ = mcfi2c_rd_dr(mcfi2c);
+		} else {
+			if (more)
+				mcfi2c_repeat_start(mcfi2c);
+			else
+				mcfi2c_stop(mcfi2c);
+			*buf++ = mcfi2c_rd_dr(mcfi2c);
+			return 0;
+		}
+	}
+	mcfi2c_stop(mcfi2c);
+
+	return -ETIMEDOUT;
+}
+
+static int mcfi2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
+		int num)
+{
+	struct mcfi2c *mcfi2c = i2c_get_adapdata(adapter);
+	int cnt = 0;
+	int status;
+	int retries;
+
+	while (num--) {
+		retries = adapter->retries;
+		if (msgs->flags & ~I2C_M_RD)
+			return -EINVAL;
+		do {
+			status = mcfi2c_xmit(mcfi2c, msgs->addr, msgs->flags,
+					     msgs->buf, msgs->len,
+					     adapter->timeout, num);
+		} while (status && retries--);
+		if (status)
+			return status;
+		++cnt;
+		++msgs;
+	}
+
+	return cnt;
+}
+
+static u32 mcfi2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm mcfi2c_algo = {
+	.master_xfer	= mcfi2c_xfer,
+	.functionality	= mcfi2c_func,
+};
+
+static const u16 mcfi2c_fdr[] = {
+	  28,   30,   34,   40,   44,   48,   56,   68,
+	  80,   88,  104,  128,  144,  160,  192,  240,
+	 288,  320,  384,  480,  576,  640,  768,  960,
+	1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840,
+	  20,   22,   24,   26,   28,   32,   36,   40,
+	  48,   56,   64,   72,   80,   96,  112,  128,
+	 160,  192,  224,  256,  320,  384,  448,  512,
+	 640,  768,  896, 1024, 1280, 1536, 1792, 2048
+};
+
+static u8 __devinit mcfi2c_calc_fdr(struct mcfi2c *mcfi2c,
+				    struct mcfi2c_platform_data *pdata)
+{
+	u32 bitrate = (pdata && pdata->bitrate) ?
+			pdata->bitrate : DEFAULT_I2C_BUS_SPEED;
+	int div = clk_get_rate(mcfi2c->clk)/bitrate;
+	int r = 0, i = 0;
+
+	do
+		if (abs(mcfi2c_fdr[i] - div) < abs(mcfi2c_fdr[r] - div))
+			r = i;
+	while (++i < ARRAY_SIZE(mcfi2c_fdr));
+
+	return r;
+}
+
+static int __devinit mcfi2c_probe(struct platform_device *pdev)
+{
+	struct mcfi2c *mcfi2c;
+	struct resource *res;
+	int status;
+
+	mcfi2c = kzalloc(sizeof(*mcfi2c), GFP_KERNEL);
+	if (!mcfi2c) {
+		dev_dbg(&pdev->dev, "kzalloc failed\n");
+
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_dbg(&pdev->dev, "platform_get_resource failed\n");
+		status = -ENXIO;
+		goto fail0;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+		dev_dbg(&pdev->dev, "request_mem_region failed\n");
+		status = -EBUSY;
+		goto fail0;
+	}
+
+	mcfi2c->iobase = ioremap(res->start, resource_size(res));
+	if (!mcfi2c->iobase) {
+		dev_dbg(&pdev->dev, "ioremap failed\n");
+		status = -ENOMEM;
+		goto fail1;
+	}
+
+	mcfi2c->irq = platform_get_irq(pdev, 0);
+	if (mcfi2c->irq < 0) {
+		dev_dbg(&pdev->dev, "platform_get_irq failed\n");
+		status = -ENXIO;
+		goto fail2;
+	}
+	status = request_irq(mcfi2c->irq, mcfi2c_irq_handler, IRQF_DISABLED,
+			     pdev->name, mcfi2c);
+	if (status) {
+		dev_dbg(&pdev->dev, "request_irq failed\n");
+		goto fail2;
+	}
+
+	mcfi2c->clk = clk_get(&pdev->dev, "i2c_clk");
+	if (IS_ERR(mcfi2c->clk)) {
+		dev_dbg(&pdev->dev, "clk_get failed\n");
+		status = PTR_ERR(mcfi2c->clk);
+		goto fail3;
+	}
+	clk_enable(mcfi2c->clk);
+
+	platform_set_drvdata(pdev, mcfi2c);
+
+	init_completion(&mcfi2c->completion);
+
+	writeb(mcfi2c_calc_fdr(mcfi2c, pdev->dev.platform_data),
+	       mcfi2c->iobase + MCFI2C_FDR);
+
+	writeb(0x00, mcfi2c->iobase + MCFI2C_ADR);
+
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
+
+	/* if the bus busy (IBB) is set, reset the controller */
+	if (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB)
+		mcfi2c_reset(mcfi2c);
+
+	mcfi2c->adapter.algo		= &mcfi2c_algo;
+	mcfi2c->adapter.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD;
+	mcfi2c->adapter.dev.parent	= &pdev->dev;
+	mcfi2c->adapter.nr		= pdev->id;
+	mcfi2c->adapter.retries		= 2;
+	snprintf(mcfi2c->adapter.name, sizeof(mcfi2c->adapter.name),
+			DRIVER_NAME ".%d", pdev->id);
+
+	i2c_set_adapdata(&mcfi2c->adapter, mcfi2c);
+
+	status = i2c_add_numbered_adapter(&mcfi2c->adapter);
+	if (status < 0) {
+		dev_dbg(&pdev->dev, "i2c_add_numbered_adapter failed\n");
+		goto fail4;
+	}
+	dev_info(&pdev->dev, "Coldfire I2C bus driver\n");
+
+	return 0;
+
+fail4:
+	clk_disable(mcfi2c->clk);
+	clk_put(mcfi2c->clk);
+fail3:
+	free_irq(mcfi2c->irq, mcfi2c);
+fail2:
+	iounmap(mcfi2c->iobase);
+fail1:
+	release_mem_region(res->start, resource_size(res));
+fail0:
+	kfree(mcfi2c);
+
+	return status;
+}
+
+static int __devexit mcfi2c_remove(struct platform_device *pdev)
+{
+	struct mcfi2c *mcfi2c = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	/* disable the hardware */
+	mcfi2c_wr_cr(mcfi2c, 0);
+
+	platform_set_drvdata(pdev, NULL);
+	i2c_del_adapter(&mcfi2c->adapter);
+	clk_disable(mcfi2c->clk);
+	clk_put(mcfi2c->clk);
+	free_irq(mcfi2c->irq, mcfi2c);
+	iounmap(mcfi2c->iobase);
+	release_mem_region(res->start, resource_size(res));
+	kfree(mcfi2c);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int mcfi2c_suspend(struct device *dev)
+{
+	struct mcfi2c *mcfi2c = platform_get_drvdata(to_platform_device(dev));
+
+	mcfi2c_wr_cr(mcfi2c, 0);
+	clk_disable(mcfi2c->clk);
+
+	return 0;
+}
+
+static int mcfi2c_resume(struct device *dev)
+{
+	struct mcfi2c *mcfi2c = platform_get_drvdata(to_platform_device(dev));
+
+	clk_enable(mcfi2c->clk);
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
+
+	return 0;
+}
+
+static struct dev_pm_ops mcfi2c_dev_pm_ops = {
+	.suspend	= mcfi2c_suspend,
+	.resume		= mcfi2c_resume,
+};
+
+#define	MCFI2C_DEV_PM_OPS (&mcfi2c_dev_pm_ops)
+#else
+#define	MCFI2C_DEV_PM_OPS NULL
+#endif
+
+static struct platform_driver mcfi2c_driver = {
+	.driver.name	= DRIVER_NAME,
+	.driver.owner	= THIS_MODULE,
+	.driver.pm	= MCFI2C_DEV_PM_OPS,
+	.remove		= __devexit_p(mcfi2c_remove),
+};
+
+static int __init mcfi2c_init(void)
+{
+	return platform_driver_probe(&mcfi2c_driver, mcfi2c_probe);
+}
+module_init(mcfi2c_init);
+
+static void __exit mcfi2c_exit(void)
+{
+	platform_driver_unregister(&mcfi2c_driver);
+}
+module_exit(mcfi2c_exit);
+
+MODULE_AUTHOR("Steven King <sfking@fdwdc.com>");
+MODULE_DESCRIPTION("I2C-Bus support for Freescale Coldfire processors");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH] m68knommu: driver for Freescale Coldfire I2C controller.
  2010-01-11  0:00 [PATCH] m68knommu: driver for Freescale Coldfire I2C controller Steven King
@ 2010-01-11  8:37 ` Jean Delvare
       [not found]   ` <20100111093729.63455a90-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
  0 siblings, 1 reply; 7+ messages in thread
From: Jean Delvare @ 2010-01-11  8:37 UTC (permalink / raw)
  To: Steven King; +Cc: gerg, ben-linux, linux-i2c, linux-kernel, uclinux-dev

On Sun, 10 Jan 2010 16:00:41 -0800, Steven King wrote:
> Add support for the I2C controller used on Freescale/Motorola Coldfire MCUs. 
> 
> Signed-off-by: Steven King <sfking@fdwdc.com>
> 
>  drivers/i2c/busses/Kconfig   |   11 +
>  drivers/i2c/busses/Makefile  |    1 +
>  drivers/i2c/busses/i2c-mcf.c |  463 ++++++++++++++++++++++++++++++++++++++++++

Please come up with a longer name. i2c-coldfire, for example.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2] m68knommu: driver for Freescale Coldfire I2C controller.
       [not found]   ` <20100111093729.63455a90-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
@ 2010-01-11 18:24     ` Steven King
       [not found]       ` <201001111024.05506.sfking-xS0NTnu2YfYAvxtiuMwx3w@public.gmane.org>
  0 siblings, 1 reply; 7+ messages in thread
From: Steven King @ 2010-01-11 18:24 UTC (permalink / raw)
  To: Jean Delvare
  Cc: gerg-XXXsiaCtIV5Wk0Htik3J/w, ben-linux-elnMNo+KYs3YtjvyW6yDsg,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	uclinux-dev-JBU5SbJe1FlAfugRpC6u6w

Changes for this version:
	rename drivers/i2c/busses/i2c-mcf.c to drivers/i2c/busses/i2c-coldfire.c
	use I2C_COLDFIRE in drivers/i2c/busses/{Kconfig,Makefile}

------

Add support for the I2C controller used on Freescale/Motorola Coldfire MCUs.

Signed-off-by: Steven King <sfking-xS0NTnu2YfYAvxtiuMwx3w@public.gmane.org>

 drivers/i2c/busses/Kconfig        |   11 +
 drivers/i2c/busses/Makefile       |    1 +
 drivers/i2c/busses/i2c-coldfire.c |  463 +++++++++++++++++++++++++++++++++++++
 3 files changed, 475 insertions(+), 0 deletions(-)

diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 5f318ce..83c2904 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -320,6 +320,17 @@ config I2C_BLACKFIN_TWI_CLK_KHZ
 	help
 	  The unit of the TWI clock is kHz.
 
+config I2C_COLDFIRE
+	tristate "Freescale Coldfire I2C driver"
+	depends on (M5206 || M5206e || M520x || M523x || M5249 || M527x || M528x || M5307 || M532x || M5407)
+	help
+	  This driver supports the I2C interface available on some Freescale
+	  Coldfire processors (M520x, M523x, M5249, M5271, M5275, M528x,
+	  M5307, M532x, M5407).
+
+	  This driver can be built as a module.  If so, the module
+	  will be called i2c-coldfire.
+
 config I2C_CPM
 	tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)"
 	depends on (CPM1 || CPM2) && OF_I2C
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 302c551..0185333 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_I2C_POWERMAC)	+= i2c-powermac.o
 obj-$(CONFIG_I2C_AT91)		+= i2c-at91.o
 obj-$(CONFIG_I2C_AU1550)	+= i2c-au1550.o
 obj-$(CONFIG_I2C_BLACKFIN_TWI)	+= i2c-bfin-twi.o
+obj-$(CONFIG_I2C_COLDFIRE)	+= i2c-coldfire.o
 obj-$(CONFIG_I2C_CPM)		+= i2c-cpm.o
 obj-$(CONFIG_I2C_DAVINCI)	+= i2c-davinci.o
 obj-$(CONFIG_I2C_DESIGNWARE)	+= i2c-designware.o
diff --git a/drivers/i2c/busses/i2c-coldfire.c b/drivers/i2c/busses/i2c-coldfire.c
new file mode 100644
index 0000000..27f0d0b
--- /dev/null
+++ b/drivers/i2c/busses/i2c-coldfire.c
@@ -0,0 +1,463 @@
+/* Freescale/Motorola Coldfire I2C driver.
+ *
+ * Copyright 2010 Steven King <sfking-xS0NTnu2YfYAvxtiuMwx3w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+
+#include <asm/mcfi2c.h>
+
+#define	DRIVER_NAME "mcfi2c"
+
+#define	MCFI2C_ADR			0x00
+#define	MCFI2C_FDR			0x04
+#define	MCFI2C_CR			0x08
+#define		MCFI2C_CR_IEN		0x80
+#define		MCFI2C_CR_IIEN		0x40
+#define		MCFI2C_CR_MSTA		0x20
+#define		MCFI2C_CR_MTX		0x10
+#define		MCFI2C_CR_TXAK		0x08
+#define		MCFI2C_CR_RSTA		0x04
+#define	MCFI2C_DR			0x10
+#define	MCFI2C_SR			0x0C
+#define		MCFI2C_SR_ICF		0x80
+#define		MCFI2C_SR_IAAS		0x40
+#define		MCFI2C_SR_IBB		0x20
+#define		MCFI2C_SR_IAL		0x10
+#define		MCFI2C_SR_SRW		0x04
+#define		MCFI2C_SR_IIF		0x02
+#define		MCFI2C_SR_RXAK		0x01
+
+#define	DEFAULT_I2C_BUS_SPEED		100000
+
+struct mcfi2c {
+	struct i2c_adapter	adapter;
+	void __iomem		*iobase;
+	int			irq;
+	struct clk		*clk;
+	struct completion	completion;
+};
+
+static u8 mcfi2c_rd_cr(struct mcfi2c *mcfi2c)
+{
+	return readb(mcfi2c->iobase + MCFI2C_CR);
+}
+
+static void mcfi2c_wr_cr(struct mcfi2c *mcfi2c, u8 val)
+{
+	writeb(val, mcfi2c->iobase + MCFI2C_CR);
+}
+
+static u8 mcfi2c_rd_sr(struct mcfi2c *mcfi2c)
+{
+	return readb(mcfi2c->iobase + MCFI2C_SR);
+}
+
+static void mcfi2c_wr_sr(struct mcfi2c *mcfi2c, u8 val)
+{
+	writeb(val, mcfi2c->iobase + MCFI2C_SR);
+}
+
+static u8 mcfi2c_rd_dr(struct mcfi2c *mcfi2c)
+{
+	return readb(mcfi2c->iobase + MCFI2C_DR);
+}
+
+static void mcfi2c_wr_dr(struct mcfi2c *mcfi2c, u8 val)
+{
+	writeb(val, mcfi2c->iobase + MCFI2C_DR);
+}
+
+static void mcfi2c_start(struct mcfi2c *mcfi2c)
+{
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
+		     MCFI2C_CR_MTX);
+}
+
+static void mcfi2c_repeat_start(struct mcfi2c *mcfi2c)
+{
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
+		     MCFI2C_CR_MTX | MCFI2C_CR_RSTA);
+}
+
+static void mcfi2c_stop(struct mcfi2c *mcfi2c)
+{
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
+}
+
+static void mcfi2c_tx_ack(struct mcfi2c *mcfi2c)
+{
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA);
+}
+
+static void mcfi2c_tx_nak(struct mcfi2c *mcfi2c)
+{
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
+		     MCFI2C_CR_TXAK);
+}
+
+static irqreturn_t mcfi2c_irq_handler(int this_irq, void *dev_id)
+{
+	struct mcfi2c *mcfi2c = dev_id;
+
+	/* clear interrupt */
+	mcfi2c_wr_sr(mcfi2c, 0);
+	complete(&mcfi2c->completion);
+
+	return IRQ_HANDLED;
+}
+
+static void mcfi2c_reset(struct mcfi2c *mcfi2c)
+{
+	mcfi2c_wr_cr(mcfi2c, 0);
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_MSTA);
+	mcfi2c_rd_dr(mcfi2c);
+	mcfi2c_wr_sr(mcfi2c, 0);
+	mcfi2c_wr_cr(mcfi2c, 0);
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
+}
+
+static void mcfi2c_wait_for_bus_idle(struct mcfi2c *mcfi2c)
+{
+	if (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB) {
+		unsigned long timeout = jiffies + HZ / 2;
+		do {
+			cond_resched();
+			if (time_after(jiffies, timeout)) {
+				mcfi2c_reset(mcfi2c);
+				break;
+			}
+		} while (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB);
+	}
+}
+
+static int mcfi2c_wait_for_bus_busy(struct mcfi2c *mcfi2c)
+{
+	u8 sr;
+	while (!((sr = mcfi2c_rd_sr(mcfi2c)) & MCFI2C_SR_IBB))
+		if (sr & MCFI2C_SR_IAL) {
+			mcfi2c_reset(mcfi2c);
+			return -EIO;
+		}
+	return 0;
+}
+
+static int mcfi2c_xmit(struct mcfi2c *mcfi2c, u16 addr, u16 flags, u8 *buf,
+		       u16 len, int timeout, int more)
+{
+	if (!(mcfi2c_rd_cr(mcfi2c) & MCFI2C_CR_MSTA)) {
+		mcfi2c_wait_for_bus_idle(mcfi2c);
+
+		INIT_COMPLETION(mcfi2c->completion);
+		mcfi2c_start(mcfi2c);
+
+		if (mcfi2c_wait_for_bus_busy(mcfi2c))
+			return -EIO;
+	}
+
+	mcfi2c_wr_dr(mcfi2c, (addr << 1) | (flags & I2C_M_RD));
+	while (wait_for_completion_timeout(&mcfi2c->completion, timeout)) {
+		u8 sr = mcfi2c_rd_sr(mcfi2c);
+		if (sr & MCFI2C_SR_IAL) {
+			mcfi2c_wr_sr(mcfi2c, ~MCFI2C_SR_IAL);
+			return -EIO;
+		} else if (mcfi2c_rd_cr(mcfi2c) & MCFI2C_CR_MTX) {
+			if (sr & MCFI2C_SR_RXAK) {
+				mcfi2c_stop(mcfi2c);
+				return -EIO;
+			} else if (flags & I2C_M_RD) {
+				if (len > 1)
+					mcfi2c_tx_ack(mcfi2c);
+				else
+					mcfi2c_tx_nak(mcfi2c);
+				/* dummy read */
+				mcfi2c_rd_dr(mcfi2c);
+			} else if (len--) {
+				mcfi2c_wr_dr(mcfi2c, *buf++);
+			} else {
+				if (more)
+					mcfi2c_repeat_start(mcfi2c);
+				else
+					mcfi2c_stop(mcfi2c);
+				return 0;
+			}
+		} else if (--len) {
+			if (!(len > 1))
+				mcfi2c_tx_nak(mcfi2c);
+			*buf++ = mcfi2c_rd_dr(mcfi2c);
+		} else {
+			if (more)
+				mcfi2c_repeat_start(mcfi2c);
+			else
+				mcfi2c_stop(mcfi2c);
+			*buf++ = mcfi2c_rd_dr(mcfi2c);
+			return 0;
+		}
+	}
+	mcfi2c_stop(mcfi2c);
+
+	return -ETIMEDOUT;
+}
+
+static int mcfi2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
+		int num)
+{
+	struct mcfi2c *mcfi2c = i2c_get_adapdata(adapter);
+	int cnt = 0;
+	int status;
+	int retries;
+
+	while (num--) {
+		retries = adapter->retries;
+		if (msgs->flags & ~I2C_M_RD)
+			return -EINVAL;
+		do {
+			status = mcfi2c_xmit(mcfi2c, msgs->addr, msgs->flags,
+					     msgs->buf, msgs->len,
+					     adapter->timeout, num);
+		} while (status && retries--);
+		if (status)
+			return status;
+		++cnt;
+		++msgs;
+	}
+
+	return cnt;
+}
+
+static u32 mcfi2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm mcfi2c_algo = {
+	.master_xfer	= mcfi2c_xfer,
+	.functionality	= mcfi2c_func,
+};
+
+static const u16 mcfi2c_fdr[] = {
+	  28,   30,   34,   40,   44,   48,   56,   68,
+	  80,   88,  104,  128,  144,  160,  192,  240,
+	 288,  320,  384,  480,  576,  640,  768,  960,
+	1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840,
+	  20,   22,   24,   26,   28,   32,   36,   40,
+	  48,   56,   64,   72,   80,   96,  112,  128,
+	 160,  192,  224,  256,  320,  384,  448,  512,
+	 640,  768,  896, 1024, 1280, 1536, 1792, 2048
+};
+
+static u8 __devinit mcfi2c_calc_fdr(struct mcfi2c *mcfi2c,
+				    struct mcfi2c_platform_data *pdata)
+{
+	u32 bitrate = (pdata && pdata->bitrate) ?
+			pdata->bitrate : DEFAULT_I2C_BUS_SPEED;
+	int div = clk_get_rate(mcfi2c->clk)/bitrate;
+	int r = 0, i = 0;
+
+	do
+		if (abs(mcfi2c_fdr[i] - div) < abs(mcfi2c_fdr[r] - div))
+			r = i;
+	while (++i < ARRAY_SIZE(mcfi2c_fdr));
+
+	return r;
+}
+
+static int __devinit mcfi2c_probe(struct platform_device *pdev)
+{
+	struct mcfi2c *mcfi2c;
+	struct resource *res;
+	int status;
+
+	mcfi2c = kzalloc(sizeof(*mcfi2c), GFP_KERNEL);
+	if (!mcfi2c) {
+		dev_dbg(&pdev->dev, "kzalloc failed\n");
+
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_dbg(&pdev->dev, "platform_get_resource failed\n");
+		status = -ENXIO;
+		goto fail0;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+		dev_dbg(&pdev->dev, "request_mem_region failed\n");
+		status = -EBUSY;
+		goto fail0;
+	}
+
+	mcfi2c->iobase = ioremap(res->start, resource_size(res));
+	if (!mcfi2c->iobase) {
+		dev_dbg(&pdev->dev, "ioremap failed\n");
+		status = -ENOMEM;
+		goto fail1;
+	}
+
+	mcfi2c->irq = platform_get_irq(pdev, 0);
+	if (mcfi2c->irq < 0) {
+		dev_dbg(&pdev->dev, "platform_get_irq failed\n");
+		status = -ENXIO;
+		goto fail2;
+	}
+	status = request_irq(mcfi2c->irq, mcfi2c_irq_handler, IRQF_DISABLED,
+			     pdev->name, mcfi2c);
+	if (status) {
+		dev_dbg(&pdev->dev, "request_irq failed\n");
+		goto fail2;
+	}
+
+	mcfi2c->clk = clk_get(&pdev->dev, "i2c_clk");
+	if (IS_ERR(mcfi2c->clk)) {
+		dev_dbg(&pdev->dev, "clk_get failed\n");
+		status = PTR_ERR(mcfi2c->clk);
+		goto fail3;
+	}
+	clk_enable(mcfi2c->clk);
+
+	platform_set_drvdata(pdev, mcfi2c);
+
+	init_completion(&mcfi2c->completion);
+
+	writeb(mcfi2c_calc_fdr(mcfi2c, pdev->dev.platform_data),
+	       mcfi2c->iobase + MCFI2C_FDR);
+
+	writeb(0x00, mcfi2c->iobase + MCFI2C_ADR);
+
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
+
+	/* if the bus busy (IBB) is set, reset the controller */
+	if (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB)
+		mcfi2c_reset(mcfi2c);
+
+	mcfi2c->adapter.algo		= &mcfi2c_algo;
+	mcfi2c->adapter.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD;
+	mcfi2c->adapter.dev.parent	= &pdev->dev;
+	mcfi2c->adapter.nr		= pdev->id;
+	mcfi2c->adapter.retries		= 2;
+	snprintf(mcfi2c->adapter.name, sizeof(mcfi2c->adapter.name),
+			DRIVER_NAME ".%d", pdev->id);
+
+	i2c_set_adapdata(&mcfi2c->adapter, mcfi2c);
+
+	status = i2c_add_numbered_adapter(&mcfi2c->adapter);
+	if (status < 0) {
+		dev_dbg(&pdev->dev, "i2c_add_numbered_adapter failed\n");
+		goto fail4;
+	}
+	dev_info(&pdev->dev, "Coldfire I2C bus driver\n");
+
+	return 0;
+
+fail4:
+	clk_disable(mcfi2c->clk);
+	clk_put(mcfi2c->clk);
+fail3:
+	free_irq(mcfi2c->irq, mcfi2c);
+fail2:
+	iounmap(mcfi2c->iobase);
+fail1:
+	release_mem_region(res->start, resource_size(res));
+fail0:
+	kfree(mcfi2c);
+
+	return status;
+}
+
+static int __devexit mcfi2c_remove(struct platform_device *pdev)
+{
+	struct mcfi2c *mcfi2c = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	/* disable the hardware */
+	mcfi2c_wr_cr(mcfi2c, 0);
+
+	platform_set_drvdata(pdev, NULL);
+	i2c_del_adapter(&mcfi2c->adapter);
+	clk_disable(mcfi2c->clk);
+	clk_put(mcfi2c->clk);
+	free_irq(mcfi2c->irq, mcfi2c);
+	iounmap(mcfi2c->iobase);
+	release_mem_region(res->start, resource_size(res));
+	kfree(mcfi2c);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int mcfi2c_suspend(struct device *dev)
+{
+	struct mcfi2c *mcfi2c = platform_get_drvdata(to_platform_device(dev));
+
+	mcfi2c_wr_cr(mcfi2c, 0);
+	clk_disable(mcfi2c->clk);
+
+	return 0;
+}
+
+static int mcfi2c_resume(struct device *dev)
+{
+	struct mcfi2c *mcfi2c = platform_get_drvdata(to_platform_device(dev));
+
+	clk_enable(mcfi2c->clk);
+	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
+
+	return 0;
+}
+
+static struct dev_pm_ops mcfi2c_dev_pm_ops = {
+	.suspend	= mcfi2c_suspend,
+	.resume		= mcfi2c_resume,
+};
+
+#define	MCFI2C_DEV_PM_OPS (&mcfi2c_dev_pm_ops)
+#else
+#define	MCFI2C_DEV_PM_OPS NULL
+#endif
+
+static struct platform_driver mcfi2c_driver = {
+	.driver.name	= DRIVER_NAME,
+	.driver.owner	= THIS_MODULE,
+	.driver.pm	= MCFI2C_DEV_PM_OPS,
+	.remove		= __devexit_p(mcfi2c_remove),
+};
+
+static int __init mcfi2c_init(void)
+{
+	return platform_driver_probe(&mcfi2c_driver, mcfi2c_probe);
+}
+module_init(mcfi2c_init);
+
+static void __exit mcfi2c_exit(void)
+{
+	platform_driver_unregister(&mcfi2c_driver);
+}
+module_exit(mcfi2c_exit);
+
+MODULE_AUTHOR("Steven King <sfking-xS0NTnu2YfYAvxtiuMwx3w@public.gmane.org>");
+MODULE_DESCRIPTION("I2C-Bus support for Freescale Coldfire processors");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] m68knommu: driver for Freescale Coldfire I2C controller.
       [not found]       ` <201001111024.05506.sfking-xS0NTnu2YfYAvxtiuMwx3w@public.gmane.org>
@ 2010-01-24 15:15         ` Ben Dooks
  2010-01-25 19:56           ` Steven King
  0 siblings, 1 reply; 7+ messages in thread
From: Ben Dooks @ 2010-01-24 15:15 UTC (permalink / raw)
  To: Steven King
  Cc: Jean Delvare, gerg-XXXsiaCtIV5Wk0Htik3J/w,
	ben-linux-elnMNo+KYs3YtjvyW6yDsg,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	uclinux-dev-JBU5SbJe1FlAfugRpC6u6w

On Mon, Jan 11, 2010 at 10:24:05AM -0800, Steven King wrote:
> Changes for this version:
> 	rename drivers/i2c/busses/i2c-mcf.c to drivers/i2c/busses/i2c-coldfire.c
> 	use I2C_COLDFIRE in drivers/i2c/busses/{Kconfig,Makefile}
> 
> ------
> 
> Add support for the I2C controller used on Freescale/Motorola Coldfire MCUs.
> 
> Signed-off-by: Steven King <sfking-xS0NTnu2YfYAvxtiuMwx3w@public.gmane.org>

The commit messsage should go first, the changelog and other stuff
that won't go in should go beflore the --- line.
 
>  drivers/i2c/busses/Kconfig        |   11 +
>  drivers/i2c/busses/Makefile       |    1 +
>  drivers/i2c/busses/i2c-coldfire.c |  463 +++++++++++++++++++++++++++++++++++++
>  3 files changed, 475 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 5f318ce..83c2904 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -320,6 +320,17 @@ config I2C_BLACKFIN_TWI_CLK_KHZ
>  	help
>  	  The unit of the TWI clock is kHz.
>  
> +config I2C_COLDFIRE
> +	tristate "Freescale Coldfire I2C driver"
> +	depends on (M5206 || M5206e || M520x || M523x || M5249 || M527x || M528x || M5307 || M532x || M5407)
> +	help
> +	  This driver supports the I2C interface available on some Freescale
> +	  Coldfire processors (M520x, M523x, M5249, M5271, M5275, M528x,
> +	  M5307, M532x, M5407).
> +
> +	  This driver can be built as a module.  If so, the module
> +	  will be called i2c-coldfire.
> +
>  config I2C_CPM
>  	tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)"
>  	depends on (CPM1 || CPM2) && OF_I2C
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index 302c551..0185333 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -31,6 +31,7 @@ obj-$(CONFIG_I2C_POWERMAC)	+= i2c-powermac.o
>  obj-$(CONFIG_I2C_AT91)		+= i2c-at91.o
>  obj-$(CONFIG_I2C_AU1550)	+= i2c-au1550.o
>  obj-$(CONFIG_I2C_BLACKFIN_TWI)	+= i2c-bfin-twi.o
> +obj-$(CONFIG_I2C_COLDFIRE)	+= i2c-coldfire.o
>  obj-$(CONFIG_I2C_CPM)		+= i2c-cpm.o
>  obj-$(CONFIG_I2C_DAVINCI)	+= i2c-davinci.o
>  obj-$(CONFIG_I2C_DESIGNWARE)	+= i2c-designware.o
> diff --git a/drivers/i2c/busses/i2c-coldfire.c b/drivers/i2c/busses/i2c-coldfire.c
> new file mode 100644
> index 0000000..27f0d0b
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-coldfire.c
> @@ -0,0 +1,463 @@
> +/* Freescale/Motorola Coldfire I2C driver.
> + *
> + * Copyright 2010 Steven King <sfking-xS0NTnu2YfYAvxtiuMwx3w@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/interrupt.h>
> +#include <linux/errno.h>
> +#include <linux/platform_device.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +
> +#include <asm/mcfi2c.h>
> +
> +#define	DRIVER_NAME "mcfi2c"
> +
> +#define	MCFI2C_ADR			0x00
> +#define	MCFI2C_FDR			0x04
> +#define	MCFI2C_CR			0x08
> +#define		MCFI2C_CR_IEN		0x80
> +#define		MCFI2C_CR_IIEN		0x40
> +#define		MCFI2C_CR_MSTA		0x20
> +#define		MCFI2C_CR_MTX		0x10
> +#define		MCFI2C_CR_TXAK		0x08
> +#define		MCFI2C_CR_RSTA		0x04
> +#define	MCFI2C_DR			0x10
> +#define	MCFI2C_SR			0x0C
> +#define		MCFI2C_SR_ICF		0x80
> +#define		MCFI2C_SR_IAAS		0x40
> +#define		MCFI2C_SR_IBB		0x20
> +#define		MCFI2C_SR_IAL		0x10
> +#define		MCFI2C_SR_SRW		0x04
> +#define		MCFI2C_SR_IIF		0x02
> +#define		MCFI2C_SR_RXAK		0x01
> +
> +#define	DEFAULT_I2C_BUS_SPEED		100000
> +
> +struct mcfi2c {
> +	struct i2c_adapter	adapter;
> +	void __iomem		*iobase;
> +	int			irq;
> +	struct clk		*clk;
> +	struct completion	completion;
> +};
> +
> +static u8 mcfi2c_rd_cr(struct mcfi2c *mcfi2c)
> +{
> +	return readb(mcfi2c->iobase + MCFI2C_CR);
> +}
> +
> +static void mcfi2c_wr_cr(struct mcfi2c *mcfi2c, u8 val)
> +{
> +	writeb(val, mcfi2c->iobase + MCFI2C_CR);
> +}
> +
> +static u8 mcfi2c_rd_sr(struct mcfi2c *mcfi2c)
> +{
> +	return readb(mcfi2c->iobase + MCFI2C_SR);
> +}
> +
> +static void mcfi2c_wr_sr(struct mcfi2c *mcfi2c, u8 val)
> +{
> +	writeb(val, mcfi2c->iobase + MCFI2C_SR);
> +}
> +
> +static u8 mcfi2c_rd_dr(struct mcfi2c *mcfi2c)
> +{
> +	return readb(mcfi2c->iobase + MCFI2C_DR);
> +}
> +
> +static void mcfi2c_wr_dr(struct mcfi2c *mcfi2c, u8 val)
> +{
> +	writeb(val, mcfi2c->iobase + MCFI2C_DR);
> +}

not entirely sure why you bother wrapping these accesses, but I'm not
going to block this driver just becuase I don't like it.

> +static void mcfi2c_start(struct mcfi2c *mcfi2c)
> +{
> +	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
> +		     MCFI2C_CR_MTX);
> +}
> +
> +static void mcfi2c_repeat_start(struct mcfi2c *mcfi2c)
> +{
> +	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
> +		     MCFI2C_CR_MTX | MCFI2C_CR_RSTA);
> +}
> +
> +static void mcfi2c_stop(struct mcfi2c *mcfi2c)
> +{
> +	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
> +}
> +
> +static void mcfi2c_tx_ack(struct mcfi2c *mcfi2c)
> +{
> +	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA);
> +}
> +
> +static void mcfi2c_tx_nak(struct mcfi2c *mcfi2c)
> +{
> +	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_IIEN | MCFI2C_CR_MSTA |
> +		     MCFI2C_CR_TXAK);
> +}
> +
> +static irqreturn_t mcfi2c_irq_handler(int this_irq, void *dev_id)
> +{
> +	struct mcfi2c *mcfi2c = dev_id;
> +
> +	/* clear interrupt */
> +	mcfi2c_wr_sr(mcfi2c, 0);
> +	complete(&mcfi2c->completion);
> +
> +	return IRQ_HANDLED;
> +}

I'm interested in why you don't just handle the interrupt here and
wake the thread once all the data is handled?

> +static void mcfi2c_reset(struct mcfi2c *mcfi2c)
> +{
> +	mcfi2c_wr_cr(mcfi2c, 0);
> +	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN | MCFI2C_CR_MSTA);
> +	mcfi2c_rd_dr(mcfi2c);
> +	mcfi2c_wr_sr(mcfi2c, 0);
> +	mcfi2c_wr_cr(mcfi2c, 0);
> +	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
> +}
> +
> +static void mcfi2c_wait_for_bus_idle(struct mcfi2c *mcfi2c)
> +{
> +	if (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB) {
> +		unsigned long timeout = jiffies + HZ / 2;
> +		do {
> +			cond_resched();
> +			if (time_after(jiffies, timeout)) {
> +				mcfi2c_reset(mcfi2c);
> +				break;
> +			}
> +		} while (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB);
> +	}
> +}
> +
> +static int mcfi2c_wait_for_bus_busy(struct mcfi2c *mcfi2c)
> +{
> +	u8 sr;
> +	while (!((sr = mcfi2c_rd_sr(mcfi2c)) & MCFI2C_SR_IBB))
> +		if (sr & MCFI2C_SR_IAL) {
> +			mcfi2c_reset(mcfi2c);
> +			return -EIO;
> +		}
> +	return 0;
> +}
> +
> +static int mcfi2c_xmit(struct mcfi2c *mcfi2c, u16 addr, u16 flags, u8 *buf,
> +		       u16 len, int timeout, int more)
> +{
> +	if (!(mcfi2c_rd_cr(mcfi2c) & MCFI2C_CR_MSTA)) {
> +		mcfi2c_wait_for_bus_idle(mcfi2c);
> +
> +		INIT_COMPLETION(mcfi2c->completion);
> +		mcfi2c_start(mcfi2c);
> +
> +		if (mcfi2c_wait_for_bus_busy(mcfi2c))
> +			return -EIO;
> +	}
> +
> +	mcfi2c_wr_dr(mcfi2c, (addr << 1) | (flags & I2C_M_RD));
> +	while (wait_for_completion_timeout(&mcfi2c->completion, timeout)) {
> +		u8 sr = mcfi2c_rd_sr(mcfi2c);
> +		if (sr & MCFI2C_SR_IAL) {
> +			mcfi2c_wr_sr(mcfi2c, ~MCFI2C_SR_IAL);
> +			return -EIO;
> +		} else if (mcfi2c_rd_cr(mcfi2c) & MCFI2C_CR_MTX) {
> +			if (sr & MCFI2C_SR_RXAK) {
> +				mcfi2c_stop(mcfi2c);
> +				return -EIO;
> +			} else if (flags & I2C_M_RD) {
> +				if (len > 1)
> +					mcfi2c_tx_ack(mcfi2c);
> +				else
> +					mcfi2c_tx_nak(mcfi2c);
> +				/* dummy read */
> +				mcfi2c_rd_dr(mcfi2c);
> +			} else if (len--) {
> +				mcfi2c_wr_dr(mcfi2c, *buf++);
> +			} else {
> +				if (more)
> +					mcfi2c_repeat_start(mcfi2c);
> +				else
> +					mcfi2c_stop(mcfi2c);
> +				return 0;
> +			}
> +		} else if (--len) {
> +			if (!(len > 1))
> +				mcfi2c_tx_nak(mcfi2c);
> +			*buf++ = mcfi2c_rd_dr(mcfi2c);
> +		} else {
> +			if (more)
> +				mcfi2c_repeat_start(mcfi2c);
> +			else
> +				mcfi2c_stop(mcfi2c);
> +			*buf++ = mcfi2c_rd_dr(mcfi2c);
> +			return 0;
> +		}
> +	}
> +	mcfi2c_stop(mcfi2c);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mcfi2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
> +		int num)
> +{
> +	struct mcfi2c *mcfi2c = i2c_get_adapdata(adapter);
> +	int cnt = 0;
> +	int status;
> +	int retries;
> +
> +	while (num--) {
> +		retries = adapter->retries;
> +		if (msgs->flags & ~I2C_M_RD)
> +			return -EINVAL;
> +		do {
> +			status = mcfi2c_xmit(mcfi2c, msgs->addr, msgs->flags,
> +					     msgs->buf, msgs->len,
> +					     adapter->timeout, num);
> +		} while (status && retries--);
> +		if (status)
> +			return status;
> +		++cnt;
> +		++msgs;
> +	}
> +
> +	return cnt;
> +}
> +
> +static u32 mcfi2c_func(struct i2c_adapter *adapter)
> +{
> +	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm mcfi2c_algo = {
> +	.master_xfer	= mcfi2c_xfer,
> +	.functionality	= mcfi2c_func,
> +};
> +
> +static const u16 mcfi2c_fdr[] = {
> +	  28,   30,   34,   40,   44,   48,   56,   68,
> +	  80,   88,  104,  128,  144,  160,  192,  240,
> +	 288,  320,  384,  480,  576,  640,  768,  960,
> +	1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840,
> +	  20,   22,   24,   26,   28,   32,   36,   40,
> +	  48,   56,   64,   72,   80,   96,  112,  128,
> +	 160,  192,  224,  256,  320,  384,  448,  512,
> +	 640,  768,  896, 1024, 1280, 1536, 1792, 2048
> +};
> +
> +static u8 __devinit mcfi2c_calc_fdr(struct mcfi2c *mcfi2c,
> +				    struct mcfi2c_platform_data *pdata)
> +{
> +	u32 bitrate = (pdata && pdata->bitrate) ?
> +			pdata->bitrate : DEFAULT_I2C_BUS_SPEED;
> +	int div = clk_get_rate(mcfi2c->clk)/bitrate;
> +	int r = 0, i = 0;
> +
> +	do
> +		if (abs(mcfi2c_fdr[i] - div) < abs(mcfi2c_fdr[r] - div))
> +			r = i;
> +	while (++i < ARRAY_SIZE(mcfi2c_fdr));
> +
> +	return r;
> +}
> +
> +static int __devinit mcfi2c_probe(struct platform_device *pdev)
> +{
> +	struct mcfi2c *mcfi2c;
> +	struct resource *res;
> +	int status;
> +
> +	mcfi2c = kzalloc(sizeof(*mcfi2c), GFP_KERNEL);
> +	if (!mcfi2c) {
> +		dev_dbg(&pdev->dev, "kzalloc failed\n");
> +
> +		return -ENOMEM;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_dbg(&pdev->dev, "platform_get_resource failed\n");
> +		status = -ENXIO;
> +		goto fail0;
> +	}
> +
> +	if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
> +		dev_dbg(&pdev->dev, "request_mem_region failed\n");
> +		status = -EBUSY;
> +		goto fail0;
> +	}
> +
> +	mcfi2c->iobase = ioremap(res->start, resource_size(res));
> +	if (!mcfi2c->iobase) {
> +		dev_dbg(&pdev->dev, "ioremap failed\n");
> +		status = -ENOMEM;
> +		goto fail1;
> +	}
> +
> +	mcfi2c->irq = platform_get_irq(pdev, 0);
> +	if (mcfi2c->irq < 0) {
> +		dev_dbg(&pdev->dev, "platform_get_irq failed\n");
> +		status = -ENXIO;
> +		goto fail2;
> +	}
> +	status = request_irq(mcfi2c->irq, mcfi2c_irq_handler, IRQF_DISABLED,
> +			     pdev->name, mcfi2c);

do you really need IRQF_DISABLED here? your irq handler hardly does
anything.

> +	if (status) {
> +		dev_dbg(&pdev->dev, "request_irq failed\n");
> +		goto fail2;
> +	}
> +
> +	mcfi2c->clk = clk_get(&pdev->dev, "i2c_clk");

hmm, think the default device clock should be findable by clk_get(dev,
NULL).

> +	if (IS_ERR(mcfi2c->clk)) {
> +		dev_dbg(&pdev->dev, "clk_get failed\n");
> +		status = PTR_ERR(mcfi2c->clk);
> +		goto fail3;
> +	}
> +	clk_enable(mcfi2c->clk);
> +
> +	platform_set_drvdata(pdev, mcfi2c);
> +
> +	init_completion(&mcfi2c->completion);
> +
> +	writeb(mcfi2c_calc_fdr(mcfi2c, pdev->dev.platform_data),
> +	       mcfi2c->iobase + MCFI2C_FDR);
> +
> +	writeb(0x00, mcfi2c->iobase + MCFI2C_ADR);
> +
> +	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
> +
> +	/* if the bus busy (IBB) is set, reset the controller */
> +	if (mcfi2c_rd_sr(mcfi2c) & MCFI2C_SR_IBB)
> +		mcfi2c_reset(mcfi2c);
> +
> +	mcfi2c->adapter.algo		= &mcfi2c_algo;
> +	mcfi2c->adapter.class		= I2C_CLASS_HWMON | I2C_CLASS_SPD;
> +	mcfi2c->adapter.dev.parent	= &pdev->dev;
> +	mcfi2c->adapter.nr		= pdev->id;
> +	mcfi2c->adapter.retries		= 2;
> +	snprintf(mcfi2c->adapter.name, sizeof(mcfi2c->adapter.name),
> +			DRIVER_NAME ".%d", pdev->id);
> +
> +	i2c_set_adapdata(&mcfi2c->adapter, mcfi2c);
> +
> +	status = i2c_add_numbered_adapter(&mcfi2c->adapter);
> +	if (status < 0) {
> +		dev_dbg(&pdev->dev, "i2c_add_numbered_adapter failed\n");
> +		goto fail4;
> +	}
> +	dev_info(&pdev->dev, "Coldfire I2C bus driver\n");
> +
> +	return 0;
> +
> +fail4:
> +	clk_disable(mcfi2c->clk);
> +	clk_put(mcfi2c->clk);
> +fail3:
> +	free_irq(mcfi2c->irq, mcfi2c);
> +fail2:
> +	iounmap(mcfi2c->iobase);
> +fail1:
> +	release_mem_region(res->start, resource_size(res));
> +fail0:
> +	kfree(mcfi2c);
> +
> +	return status;
> +}
> +
> +static int __devexit mcfi2c_remove(struct platform_device *pdev)
> +{
> +	struct mcfi2c *mcfi2c = platform_get_drvdata(pdev);
> +	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> +	/* disable the hardware */
> +	mcfi2c_wr_cr(mcfi2c, 0);
> +
> +	platform_set_drvdata(pdev, NULL);
> +	i2c_del_adapter(&mcfi2c->adapter);
> +	clk_disable(mcfi2c->clk);
> +	clk_put(mcfi2c->clk);
> +	free_irq(mcfi2c->irq, mcfi2c);
> +	iounmap(mcfi2c->iobase);
> +	release_mem_region(res->start, resource_size(res));
> +	kfree(mcfi2c);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int mcfi2c_suspend(struct device *dev)
> +{
> +	struct mcfi2c *mcfi2c = platform_get_drvdata(to_platform_device(dev));
> +
> +	mcfi2c_wr_cr(mcfi2c, 0);
> +	clk_disable(mcfi2c->clk);
> +
> +	return 0;
> +}
> +
> +static int mcfi2c_resume(struct device *dev)
> +{
> +	struct mcfi2c *mcfi2c = platform_get_drvdata(to_platform_device(dev));
> +
> +	clk_enable(mcfi2c->clk);
> +	mcfi2c_wr_cr(mcfi2c, MCFI2C_CR_IEN);
> +
> +	return 0;
> +}
> +
> +static struct dev_pm_ops mcfi2c_dev_pm_ops = {
> +	.suspend	= mcfi2c_suspend,
> +	.resume		= mcfi2c_resume,
> +};
> +
> +#define	MCFI2C_DEV_PM_OPS (&mcfi2c_dev_pm_ops)
> +#else
> +#define	MCFI2C_DEV_PM_OPS NULL
> +#endif
> +
> +static struct platform_driver mcfi2c_driver = {
> +	.driver.name	= DRIVER_NAME,
> +	.driver.owner	= THIS_MODULE,
> +	.driver.pm	= MCFI2C_DEV_PM_OPS,
> +	.remove		= __devexit_p(mcfi2c_remove),
> +};
> +
> +static int __init mcfi2c_init(void)
> +{
> +	return platform_driver_probe(&mcfi2c_driver, mcfi2c_probe);
> +}
> +module_init(mcfi2c_init);
> +
> +static void __exit mcfi2c_exit(void)
> +{
> +	platform_driver_unregister(&mcfi2c_driver);
> +}
> +module_exit(mcfi2c_exit);
> +
> +MODULE_AUTHOR("Steven King <sfking-xS0NTnu2YfYAvxtiuMwx3w@public.gmane.org>");
> +MODULE_DESCRIPTION("I2C-Bus support for Freescale Coldfire processors");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:" DRIVER_NAME);


-- 
Ben (ben-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org, http://www.fluff.org/)

  'a smiley only costs 4 bytes'

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] m68knommu: driver for Freescale Coldfire I2C controller.
  2010-01-24 15:15         ` Ben Dooks
@ 2010-01-25 19:56           ` Steven King
  2010-03-12 11:04             ` Philippe De Muyter
  0 siblings, 1 reply; 7+ messages in thread
From: Steven King @ 2010-01-25 19:56 UTC (permalink / raw)
  To: Ben Dooks; +Cc: Jean Delvare, uclinux-dev, linux-i2c, linux-kernel

On Sunday 24 January 2010 07:15:19 you wrote:
> On Mon, Jan 11, 2010 at 10:24:05AM -0800, Steven King wrote:
> > Changes for this version:
> > 	rename drivers/i2c/busses/i2c-mcf.c to drivers/i2c/busses/i2c-coldfire.c
> > 	use I2C_COLDFIRE in drivers/i2c/busses/{Kconfig,Makefile}
> >
> > ------
> >
> > Add support for the I2C controller used on Freescale/Motorola Coldfire
> > MCUs.
> >
> > Signed-off-by: Steven King <sfking@fdwdc.com>
>
> The commit messsage should go first, the changelog and other stuff
> that won't go in should go beflore the --- line.

My bad, I think I was paying more attention to making sure this mailer didn't 
line wrap on me.  I can repost if you want.

> > +static irqreturn_t mcfi2c_irq_handler(int this_irq, void *dev_id)
> > +{
> > +	struct mcfi2c *mcfi2c = dev_id;
> > +
> > +	/* clear interrupt */
> > +	mcfi2c_wr_sr(mcfi2c, 0);
> > +	complete(&mcfi2c->completion);
> > +
> > +	return IRQ_HANDLED;
> > +}
>
> I'm interested in why you don't just handle the interrupt here and
> wake the thread once all the data is handled?

No particular reason; when I started working on this I looked at how some of 
the other drivers in drivers/i2c/busses were implemented, found one whose 
workings I could understand without knowing anything about that particular 
SoC (which one I don't remember) and used it as a model to adapt the i2c code 
I use with other Freescale/Motorola MCUs that don't run linux (there is very 
little difference in the i2c controller on an 8-bit hc08, 16 bit hc11/12, 
32bit Coldfire v1 and the Coldfire v2[+] that are currently supported).  So 
other than having easy to understand (for me atleast) code that shared a lot 
of similarity with the code I use for the some of the other  systems I code 
for, no real reason.  That and I find statefull irq handlers disconcerting.

> > +	status = request_irq(mcfi2c->irq, mcfi2c_irq_handler, IRQF_DISABLED,
> > +			     pdev->name, mcfi2c);
>
> do you really need IRQF_DISABLED here? your irq handler hardly does
> anything.

Yes, without it, I was getting a spurious interrupt (been there, done that).

> > +	if (status) {
> > +		dev_dbg(&pdev->dev, "request_irq failed\n");
> > +		goto fail2;
> > +	}
> > +
> > +	mcfi2c->clk = clk_get(&pdev->dev, "i2c_clk");
>
> hmm, think the default device clock should be findable by clk_get(dev,
> NULL).

Interesting.   Again, I had looked at the existing code in drivers/i2c/busses 
to see what the other drivers were doing and most were passing an id string.

Its a trivial change so its no big deal, but I'm curious, absent anything in 
Documentation, if the bulk of the existing code isn't documentation for the 
correct use of an api, then what is?

-- 
Steven King -- sfking at fdwdc dot com
_______________________________________________
uClinux-dev mailing list
uClinux-dev@uclinux.org
http://mailman.uclinux.org/mailman/listinfo/uclinux-dev
This message was resent by uclinux-dev@uclinux.org
To unsubscribe see:
http://mailman.uclinux.org/mailman/options/uclinux-dev

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Re: [PATCH v2] m68knommu: driver for Freescale Coldfire I2C controller.
  2010-01-25 19:56           ` Steven King
@ 2010-03-12 11:04             ` Philippe De Muyter
  2010-03-12 15:42               ` Lennart Sorensen
  0 siblings, 1 reply; 7+ messages in thread
From: Philippe De Muyter @ 2010-03-12 11:04 UTC (permalink / raw)
  To: uClinux development list; +Cc: Jean Delvare, linux-i2c, Ben Dooks, linux-kernel

Hello all,

On Mon, Jan 25, 2010 at 11:56:30AM -0800, Steven King wrote:
> > >
> > > Add support for the I2C controller used on Freescale/Motorola Coldfire
> > > MCUs.
> > >
> > > Signed-off-by: Steven King <sfking@fdwdc.com>

What's the status of this ?

I need to use i2c for a coldfire uclinux project (with a mcf5484) and I
now have 3 different coldfire i2c drivers, none of which is in mainline.

i2c-mcf.c (from uClinux-dist-20090618, but not in http://git.kernel.org/?p=linux/kernel/git/gerg/m68knommu.git;a=summary)
i2c-mcf548x.c (from ltib-m5475evb-20080808, found as "Linux BSP for MCF5484LITE, MCF5475/85EVB" at http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MCF548X&fpsp=1&tab=Design_Tools_Tab)
i2c-coldfire.c (from http://lkml.org/lkml/2010/1/11/165)

I like to work with mainline sources to be be able to contribute to and benefit
from collective work.

Which one has the best chances to be put in mainline ?

Best regards

Philippe
_______________________________________________
uClinux-dev mailing list
uClinux-dev@uclinux.org
http://mailman.uclinux.org/mailman/listinfo/uclinux-dev
This message was resent by uclinux-dev@uclinux.org
To unsubscribe see:
http://mailman.uclinux.org/mailman/options/uclinux-dev

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Re: [PATCH v2] m68knommu: driver for Freescale Coldfire I2C controller.
  2010-03-12 11:04             ` Philippe De Muyter
@ 2010-03-12 15:42               ` Lennart Sorensen
  0 siblings, 0 replies; 7+ messages in thread
From: Lennart Sorensen @ 2010-03-12 15:42 UTC (permalink / raw)
  To: uClinux development list; +Cc: Jean Delvare, linux-i2c, Ben Dooks, linux-kernel

On Fri, Mar 12, 2010 at 12:04:53PM +0100, Philippe De Muyter wrote:
> Hello all,
> 
> On Mon, Jan 25, 2010 at 11:56:30AM -0800, Steven King wrote:
> > > >
> > > > Add support for the I2C controller used on Freescale/Motorola Coldfire
> > > > MCUs.
> > > >
> > > > Signed-off-by: Steven King <sfking@fdwdc.com>
> 
> What's the status of this ?
> 
> I need to use i2c for a coldfire uclinux project (with a mcf5484) and I
> now have 3 different coldfire i2c drivers, none of which is in mainline.
> 
> i2c-mcf.c (from uClinux-dist-20090618, but not in http://git.kernel.org/?p=linux/kernel/git/gerg/m68knommu.git;a=summary)

It seems I use that one based on the filename.

> i2c-mcf548x.c (from ltib-m5475evb-20080808, found as "Linux BSP for MCF5484LITE, MCF5475/85EVB" at http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MCF548X&fpsp=1&tab=Design_Tools_Tab)
> i2c-coldfire.c (from http://lkml.org/lkml/2010/1/11/165)
> 
> I like to work with mainline sources to be be able to contribute to and benefit
> from collective work.
> 
> Which one has the best chances to be put in mainline ?

No idea.

I am using this one on a 5271:

http://www.bitshrine.org/gpp/0006-I2C-device-driver.patch
http://www.bitshrine.org/gpp/0043-I2C-bug-fix.patch

No problems so far.

This is the patch that I use on top of
git://git.kernel.org/pub/scm/linux/kernel/git/geert/linux-m68k.git

diff -urN build-2.6.29-1-m68k-uclinux-target1-nfs/arch/m68k/include/asm/m527xsim.h build-2.6.29-1-m68k-uclinux-target1/arch/m68k/include/asm/m527xsim.h
--- build-2.6.29-1-m68k-uclinux-target1-nfs/arch/m68k/include/asm/m527xsim.h	2010-02-12 11:49:19.000000000 -0500
+++ build-2.6.29-1-m68k-uclinux-target1/arch/m68k/include/asm/m527xsim.h	2010-02-12 12:10:59.000000000 -0500
@@ -248,5 +248,44 @@
 #define	MCF_RCR_SWRESET		0x80		/* Software reset bit */
 #define	MCF_RCR_FRCSTOUT	0x40		/* Force external reset */
 
+/*********************************************************************
+*
+* I2C Module (I2C) (Stolen from m5301xsim.h)
+*
+*********************************************************************/
+/* Register read/write macros */
+#ifdef CONFIG_M5271
+#define MCF_I2C_I2ADR           (volatile unsigned char *)(MCF_MBAR + 0x300)
+#define MCF_I2C_I2FDR           (volatile unsigned char *)(MCF_MBAR + 0x304)
+#define MCF_I2C_I2CR            (volatile unsigned char *)(MCF_MBAR + 0x308)
+#define MCF_I2C_I2SR            (volatile unsigned char *)(MCF_MBAR + 0x30C)
+#define MCF_I2C_I2DR            (volatile unsigned char *)(MCF_MBAR + 0x310)
+
+/* Bit definitions and macros for I2AR */
+#define MCF_I2C_I2AR_ADR(x)     (((x)&0x7F)<<1)
+
+/* Bit definitions and macros for I2FDR */
+#define MCF_I2C_I2FDR_IC(x)     (((x)&0x3F))
+
+/* Bit definitions and macros for I2CR */
+#define MCF_I2C_I2CR_RSTA       (0x04)
+#define MCF_I2C_I2CR_TXAK       (0x08)
+#define MCF_I2C_I2CR_MTX        (0x10)
+#define MCF_I2C_I2CR_MSTA       (0x20)
+#define MCF_I2C_I2CR_IIEN       (0x40)
+#define MCF_I2C_I2CR_IEN        (0x80)
+
+/* Bit definitions and macros for I2SR */
+#define MCF_I2C_I2SR_RXAK       (0x01)
+#define MCF_I2C_I2SR_IIF        (0x02)
+#define MCF_I2C_I2SR_SRW        (0x04)
+#define MCF_I2C_I2SR_IAL        (0x10)
+#define MCF_I2C_I2SR_IBB        (0x20)
+#define MCF_I2C_I2SR_IAAS       (0x40)
+#define MCF_I2C_I2SR_ICF        (0x80)
+
+/* Bit definitions and macros for I2DR */
+#define MCF_I2C_I2DR_DATA(x)    (x)
+#endif /* I2C Module for CONFIG_M5271 */
 /****************************************************************************/
 #endif	/* m527xsim_h */
diff -urN build-2.6.29-1-m68k-uclinux-target1-nfs/drivers/i2c/busses/Kconfig build-2.6.29-1-m68k-uclinux-target1/drivers/i2c/busses/Kconfig
--- build-2.6.29-1-m68k-uclinux-target1-nfs/drivers/i2c/busses/Kconfig	2010-02-12 11:49:21.000000000 -0500
+++ build-2.6.29-1-m68k-uclinux-target1/drivers/i2c/busses/Kconfig	2010-02-12 12:10:59.000000000 -0500
@@ -417,6 +417,16 @@
 	  This driver is deprecated and will be dropped soon. Use i2c-gpio
 	  instead.
 
+config I2C_MCF
+	tristate "MCF ColdFire"
+	depends on I2C && EXPERIMENTAL
+	help
+	  If you say yes to this option, support will be included for the
+	  I2C on most ColdFire CPUs
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called i2c-mcf.
+
 config I2C_MPC
 	tristate "MPC107/824x/85xx/52xx/86xx"
 	depends on PPC32
diff -urN build-2.6.29-1-m68k-uclinux-target1-nfs/drivers/i2c/busses/Makefile build-2.6.29-1-m68k-uclinux-target1/drivers/i2c/busses/Makefile
--- build-2.6.29-1-m68k-uclinux-target1-nfs/drivers/i2c/busses/Makefile	2010-02-12 11:49:21.000000000 -0500
+++ build-2.6.29-1-m68k-uclinux-target1/drivers/i2c/busses/Makefile	2010-02-12 12:10:59.000000000 -0500
@@ -71,6 +71,7 @@
 obj-$(CONFIG_I2C_STUB)		+= i2c-stub.o
 obj-$(CONFIG_SCx200_ACB)	+= scx200_acb.o
 obj-$(CONFIG_SCx200_I2C)	+= scx200_i2c.o
+obj-$(CONFIG_I2C_MCF)		+= i2c-mcf.o
 
 ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
 EXTRA_CFLAGS += -DDEBUG
diff -urN build-2.6.29-1-m68k-uclinux-target1-nfs/drivers/i2c/busses/i2c-mcf.c build-2.6.29-1-m68k-uclinux-target1/drivers/i2c/busses/i2c-mcf.c
--- build-2.6.29-1-m68k-uclinux-target1-nfs/drivers/i2c/busses/i2c-mcf.c	1969-12-31 19:00:00.000000000 -0500
+++ build-2.6.29-1-m68k-uclinux-target1/drivers/i2c/busses/i2c-mcf.c	2010-02-12 12:10:59.000000000 -0500
@@ -0,0 +1,601 @@
+/*
+    i2c-mcf.c - Part of lm_sensors, Linux kernel modules for hardware monitoring
+
+    Copyright (c) 2005, Derek CL Cheung <derek.cheung@sympatico.ca>
+					<http://www3.sympatico.ca/derek.cheung>
+
+    Copyright (c) 2006, emlix Sebastian Hess <sh@emlix.com>
+
+    Yaroslav Vinogradov <yaroslav.vinogradov@freescale.com>
+    Copyright Freescale Semiconductor, Inc 2006-2007
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+	Changes:
+	v0.1	26 March 2005
+	Initial Release - developed on uClinux with 2.6.9 kernel
+
+	v0.2	29 May 2006
+	Modified to be more generic and added support for
+	i2c_master_xfer
+
+	v0.3	12 Sep 2007
+	Added support for more Coldfire systems, removed c++ comments
+
+    This I2C adaptor supports the ColdFire 5282 CPU I2C module. Since most Coldfire
+    CPUs' I2C module use the same register set (e.g., MCF5249), the code is very
+    portable and re-usable to other Coldfire CPUs.
+
+    The transmission frequency is set at about 100KHz for the 5282Lite CPU board with
+    8MHz crystal. If the CPU board uses different system clock frequency, you should
+    change the following line:
+                static int __init i2c_coldfire_init(void)
+                {
+                                .........
+                        // Set transmission frequency 0x15 = ~100kHz
+                        *MCF_I2C_I2FDR = 0x15;
+                                ........
+                }
+
+    Remember to perform a dummy read to set the ColdFire CPU's I2C module for read before
+    reading the actual byte from a device
+
+    The I2C_SM_BUS_BLOCK_DATA function are not yet ready but most lm_senors do not care
+
+*/
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+#include <asm/types.h>
+#include <linux/platform_device.h>
+#include "i2c-mcf.h"
+
+static struct i2c_algorithm coldfire_algorithm = {
+	.smbus_xfer = coldfire_i2c_access,
+	.master_xfer = coldfire_i2c_master,
+	.functionality = coldfire_func,
+};
+
+static struct i2c_adapter coldfire_adapter = {
+	.owner = THIS_MODULE,
+	.class = I2C_CLASS_HWMON,
+	.algo = &coldfire_algorithm,
+	.name = "ColdFire I2C adapter",
+};
+
+__u16 lastaddr;
+__u16 lastop;
+
+static inline int coldfire_do_first_start(__u16 addr, __u16 flags)
+{
+	int err;
+	/*
+	 * Generate a stop and put the I2C module into slave mode
+	 */
+	*MCF_I2C_I2CR &= ~MCF_I2C_I2CR_MSTA;
+
+	/*
+	 * Generate a new Start signal
+	 */
+	err =
+	    coldfire_i2c_start(flags & I2C_M_RD ? I2C_SMBUS_READ :
+			       I2C_SMBUS_WRITE, addr, FIRST_START);
+	if (err)
+		return err;
+
+	lastaddr = addr;
+	lastop = flags & I2C_M_RD;	/* Ensure everything for new start */
+	return 0;
+}
+
+/*
+ *  read one byte data from the I2C bus
+ */
+static int coldfire_read_data(u8 * const rxData,
+			      const enum I2C_ACK_TYPE ackType)
+{
+
+	int timeout;
+
+	*MCF_I2C_I2CR &= ~MCF_I2C_I2CR_MTX;	/* master receive mode */
+
+	if (ackType == NACK)
+		*MCF_I2C_I2CR |= MCF_I2C_I2CR_TXAK;	/* generate NA */
+	else
+		*MCF_I2C_I2CR &= ~MCF_I2C_I2CR_TXAK;	/* generate ACK */
+
+	/* read data from the I2C bus */
+	*rxData = *MCF_I2C_I2DR;
+
+	/* printk(">>> %s I2DR data is %.2x \n", __FUNCTION__, *rxData); */
+
+	/* wait for data transfer to complete */
+	timeout = 500;
+	while (timeout-- && !(*MCF_I2C_I2SR & MCF_I2C_I2SR_IIF))
+		udelay(1);
+	if (timeout <= 0)
+		printk("%s - I2C IIF never set. Timeout is %d \n", __FUNCTION__,
+		       timeout);
+
+	/* reset the interrupt bit */
+	*MCF_I2C_I2SR &= ~MCF_I2C_I2SR_IIF;
+
+	if (timeout <= 0)
+		return -1;
+	else
+		return 0;
+
+};
+
+/*
+ *  write one byte data onto the I2C bus
+ */
+static int coldfire_write_data(const u8 txData)
+{
+
+	int timeout;
+
+	timeout = 500;
+
+	*MCF_I2C_I2CR |= MCF_I2C_I2CR_MTX;	/* I2C module into TX mode */
+	*MCF_I2C_I2DR = txData;	/* send the data */
+
+	/* wait for data transfer to complete */
+	/* rely on the interrupt handling bit */
+	timeout = 500;
+	while (timeout-- && !(*MCF_I2C_I2SR & MCF_I2C_I2SR_IIF))
+		udelay(1);
+	if (timeout <= 0)
+		printk("%s - I2C IIF never set. Timeout is %d \n", __FUNCTION__,
+		       timeout);
+
+	/* reset the interrupt bit */
+	*MCF_I2C_I2SR &= ~MCF_I2C_I2SR_IIF;
+#if defined(CONFIG_M532x) || defined(CONFIG_M537x)
+	if (timeout <= 0 || (*MCF_I2C_I2SR & MCF_I2C_I2SR_RXAK))
+#else
+	if (timeout <= 0)
+#endif
+		return -1;
+	else
+		return 0;
+
+};
+
+/*
+ *  Generate I2C start or repeat start signal
+ *  Combine the 7 bit target_address and the R/W bit and put it onto the I2C bus
+ */
+static int coldfire_i2c_start(const char read_write, const u16 target_address,
+			      const enum I2C_START_TYPE start_type)
+{
+
+	int timeout;
+
+	/* printk(">>> %s START TYPE %s \n", __FUNCTION__, start_type == FIRST_START ? "FIRST_START" : "REPEAT_START"); */
+
+	*MCF_I2C_I2CR |= MCF_I2C_I2CR_IEN;
+
+	if (start_type == FIRST_START) {
+		/* Make sure the I2C bus is idle */
+		timeout = 500;	/* 500us timeout */
+		while (timeout-- && (*MCF_I2C_I2SR & MCF_I2C_I2SR_IBB))
+			udelay(1);
+		if (timeout <= 0) {
+			printk
+			    ("%s - I2C bus always busy in the past 500us timeout is %d \n",
+			     __FUNCTION__, timeout);
+			goto check_rc;
+		}
+		/* generate a START and put the I2C module into MASTER TX mode */
+		*MCF_I2C_I2CR |= (MCF_I2C_I2CR_MSTA | MCF_I2C_I2CR_MTX);
+
+		/* wait for bus busy to be set */
+		timeout = 500;
+		while (timeout-- && !(*MCF_I2C_I2SR & MCF_I2C_I2SR_IBB))
+			udelay(1);
+		if (timeout <= 0) {
+			printk
+			    ("%s - I2C bus is never busy after START. Timeout is %d \n",
+			     __FUNCTION__, timeout);
+			goto check_rc;
+		}
+
+	} else {
+		/* this is repeat START */
+		udelay(500);	/* need some delay before repeat start */
+		*MCF_I2C_I2CR |= (MCF_I2C_I2CR_MSTA | MCF_I2C_I2CR_RSTA);
+	}
+
+	/* combine the R/W bit and the 7 bit target address and put it onto the I2C bus */
+	*MCF_I2C_I2DR =
+	    ((target_address & 0x7F) << 1) | (read_write ==
+					      I2C_SMBUS_WRITE ? 0x00 : 0x01);
+
+	/* wait for bus transfer to complete */
+	/* when one byte transfer is completed, IIF set at the faling edge of the 9th clock */
+	timeout = 500;
+	while (timeout-- && !(*MCF_I2C_I2SR & MCF_I2C_I2SR_IIF))
+		udelay(1);
+	if (timeout <= 0)
+		printk("%s - I2C IIF never set. Timeout is %d \n", __FUNCTION__,
+		       timeout);
+
+      check_rc:
+	/* reset the interrupt bit */
+	*MCF_I2C_I2SR &= ~MCF_I2C_I2SR_IIF;
+
+#if defined(CONFIG_M532x) || defined(CONFIG_M537x)
+	if (timeout <= 0 || (*MCF_I2C_I2SR & MCF_I2C_I2SR_RXAK))
+#else
+	if (timeout <= 0)
+#endif
+		return -1;
+	else
+		return 0;
+};
+
+/*
+ *  5282 SMBUS supporting functions
+ */
+
+static s32 coldfire_i2c_access(struct i2c_adapter *adap, u16 addr,
+			       unsigned short flags, char read_write,
+			       u8 command, int size, union i2c_smbus_data *data)
+{
+	int i, len, rc = 0;
+	u8 rxData, tempRxData[2];
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+		rc = coldfire_i2c_start(read_write, addr, FIRST_START);	/* generate START */
+		break;
+	case I2C_SMBUS_BYTE:
+		rc = coldfire_i2c_start(read_write, addr, FIRST_START);
+		*MCF_I2C_I2CR |= MCF_I2C_I2CR_TXAK;	/* generate NA */
+		if (read_write == I2C_SMBUS_WRITE)
+			rc += coldfire_write_data(command);
+		else {
+			coldfire_read_data(&rxData, NACK);	/* dummy read */
+			rc += coldfire_read_data(&rxData, NACK);
+			data->byte = rxData;
+		}
+		*MCF_I2C_I2CR &= ~MCF_I2C_I2CR_TXAK;	/* reset ACK bit */
+		break;
+	case I2C_SMBUS_BYTE_DATA:
+		rc = coldfire_i2c_start(I2C_SMBUS_WRITE, addr, FIRST_START);
+		rc += coldfire_write_data(command);
+		if (read_write == I2C_SMBUS_WRITE)
+			rc += coldfire_write_data(data->byte);
+		else {
+			/* This is SMBus READ Byte Data Request. Perform REPEAT START */
+			rc +=
+			    coldfire_i2c_start(I2C_SMBUS_READ, addr,
+					       REPEAT_START);
+			coldfire_read_data(&rxData, ACK);	/* dummy read */
+			/* Disable Acknowledge, generate STOP after next byte transfer */
+			rc += coldfire_read_data(&rxData, NACK);
+			data->byte = rxData;
+		}
+		*MCF_I2C_I2CR &= ~MCF_I2C_I2CR_TXAK;	/* reset to normal ACk */
+		break;
+	case I2C_SMBUS_PROC_CALL:
+	case I2C_SMBUS_WORD_DATA:
+		dev_info(&adap->dev, "size = I2C_SMBUS_WORD_DATA \n");
+		rc = coldfire_i2c_start(I2C_SMBUS_WRITE, addr, FIRST_START);
+		rc += coldfire_write_data(command);
+		if (read_write == I2C_SMBUS_WRITE) {
+			rc += coldfire_write_data(data->word & 0x00FF);
+			rc += coldfire_write_data((data->word & 0x00FF) >> 8);
+		} else {
+			/* This is SMBUS READ WORD request. Peform REPEAT START */
+			rc +=
+			    coldfire_i2c_start(I2C_SMBUS_READ, addr,
+					       REPEAT_START);
+			coldfire_read_data(&rxData, ACK);	/* dummy read */
+			/* Disable Acknowledge, generate STOP after next byte transfer */
+			/* read the MS byte from the device */
+			rc += coldfire_read_data(&rxData, NACK);
+			tempRxData[1] = rxData;
+			/* read the LS byte from the device */
+			rc += coldfire_read_data(&rxData, NACK);
+			tempRxData[0] = rxData;
+			/* the host driver expect little endian convention. Swap the byte */
+			data->word = (tempRxData[0] << 8) | tempRxData[1];
+		}
+		*MCF_I2C_I2CR &= ~MCF_I2C_I2CR_TXAK;
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+		/* this is not ready yet!!! */
+		break;
+	case I2C_SMBUS_BLOCK_PROC_CALL:
+		{
+			rc = coldfire_i2c_start(I2C_SMBUS_WRITE, addr,
+						FIRST_START);
+			rc += coldfire_write_data(command);
+			if (read_write == I2C_SMBUS_READ) {
+				/* This is SMBUS READ BLOCK request. Peform REPEAT START */
+				rc +=
+				    coldfire_i2c_start(I2C_SMBUS_READ, addr,
+						       REPEAT_START);
+				coldfire_read_data(&rxData, ACK);	/* dummy read */
+				len = data->block[0];
+				if (len < 0)
+					len = 0;
+				if (len > 32)
+					len = 32;
+				for (i = 1; i < len; i++) {
+					/* read byte from the device */
+					rc +=
+					    coldfire_read_data(&data->block[i],
+							       ACK);
+				}
+				/* read last byte from the device */
+				rc += coldfire_read_data(&data->block[i], NACK);
+			}
+			*MCF_I2C_I2CR &= ~MCF_I2C_I2CR_TXAK;
+			break;
+		}
+	default:
+		printk("Unsupported I2C size \n");
+		rc = -1;
+		break;
+	};
+
+	/* Generate a STOP and put I2C module into slave mode */
+	*MCF_I2C_I2CR &= ~MCF_I2C_I2CR_MSTA;
+
+	/* restore interrupt */
+	*MCF_I2C_I2CR |= MCF_I2C_I2CR_IIEN;
+
+	if (rc < 0)
+		return -1;
+	else
+		return 0;
+};
+
+/*
+ *  List the SMBUS functions supported by this I2C adaptor
+ *  Also tell the I2C Subsystem that we are able of master_xfer()
+ */
+static u32 coldfire_func(struct i2c_adapter *adapter)
+{
+	return (I2C_FUNC_SMBUS_QUICK |
+		I2C_FUNC_SMBUS_BYTE |
+		I2C_FUNC_SMBUS_PROC_CALL |
+		I2C_FUNC_SMBUS_BYTE_DATA |
+		I2C_FUNC_SMBUS_WORD_DATA |
+		I2C_FUNC_I2C | I2C_FUNC_SMBUS_BLOCK_DATA);
+};
+
+static int coldfire_i2c_master(struct i2c_adapter *adap, struct i2c_msg *msgs,
+			       int num)
+{
+	u8 dummyRead;
+	struct i2c_msg *p;
+	int i, err = 0;
+	int ic = 0;
+
+	lastaddr = 0;
+	lastop = 8;
+
+	/* disable the IRQ, we are doing polling */
+	*MCF_I2C_I2CR &= ~MCF_I2C_I2CR_IIEN;
+
+	dev_dbg(&adap->dev, "Num of actions: %d\n", num);
+
+	for (i = 0; !err && i < num; i++) {
+		p = &msgs[i];
+
+		if (!p->len) {
+			dev_dbg(&adap->dev, "p->len == 0!\n");
+			continue;
+		}
+		/*
+		 * Generate a new Start, if the target address differs from the last target,
+		 * generate a stop in this case first
+		 */
+		if (p->addr != lastaddr) {
+			err = coldfire_do_first_start(p->addr, p->flags);
+			if (err) {
+				dev_dbg(&adap->dev, "First Init failed!\n");
+				break;
+			}
+		}
+
+		else if ((p->flags & I2C_M_RD) != lastop) {
+			/*
+			 * If the Operational Mode changed, we need to do this here ...
+			 */
+			dev_dbg(&adap->dev,
+				"%s(): Direction changed, was: %d; is now: %d\n",
+				__FUNCTION__, lastop, p->flags & I2C_M_RD);
+
+			/* Last op was an read, now it's write: complete stop and reinit */
+			if (lastop & I2C_M_RD) {
+				dev_dbg(&adap->dev,
+					"%s(): The device is in read state, we must reset!\n",
+					__FUNCTION__);
+				if ((err =
+				     coldfire_do_first_start(p->addr,
+							     p->flags)))
+					break;
+			} else {
+				dev_dbg(&adap->dev,
+					"%s(): We switchted to read mode\n",
+					__FUNCTION__);
+				if ((err =
+				     coldfire_i2c_start((p->
+							 flags & I2C_M_RD) ?
+							I2C_SMBUS_READ :
+							I2C_SMBUS_WRITE,
+							p->addr, REPEAT_START)))
+					break;
+			}
+
+			lastop = p->flags & I2C_M_RD;	/* Save the last op */
+		}
+
+		if (p->flags & I2C_M_RD) {
+			/*
+			 * When ever we get here, a new session was activated, so
+			 * read a dummy byte
+			 */
+			coldfire_read_data(&dummyRead, ACK);
+			/*
+			 * read p->len -1 bytes with ACK to the slave,
+			 * read the last byte without the ACK, to inform him about the
+			 * stop afterwards
+			 */
+			ic = 0;
+			while (!err && (ic < p->len - 1)) {
+				err = coldfire_read_data(p->buf + ic, ACK);
+				ic++;
+			}
+			if (!err)
+				err = coldfire_read_data(p->buf + ic, NACK);
+			dev_dbg(&coldfire_adapter.dev, "read: %2x\n",
+				p->buf[ic]);
+		} else {
+			if (p->len == 2)
+				dev_dbg(&coldfire_adapter.dev,
+					"writing: 0x %2x %2x\n", p->buf[0],
+					p->buf[1]);
+
+			/*
+			 * Write data to the slave
+			 */
+			for (ic = 0; !err && ic < p->len; ic++) {
+				err = coldfire_write_data(p->buf[ic]);
+				if (err) {
+					dev_dbg(&coldfire_adapter.dev,
+						"Failed to write data\n");
+				}
+			}
+		}
+	}
+
+	/*
+	 * Put the device into slave mode to enable the STOP Generation (the RTC needs this)
+	 */
+	*MCF_I2C_I2CR &= ~MCF_I2C_I2CR_MSTA;
+
+	*MCF_I2C_I2CR &= ~MCF_I2C_I2CR_TXAK;	/* reset the ACK bit */
+
+	/* restore interrupt */
+	*MCF_I2C_I2CR |= MCF_I2C_I2CR_IIEN;
+
+	/* Return the number of messages processed, or the error code. */
+	if (err == 0)
+		err = num;
+	return err;
+}
+
+/*
+ *  Initalize the 5282 I2C module
+ *  Disable the 5282 I2C interrupt capability. Just use callback
+ */
+
+static int __init i2c_coldfire_init(void)
+{
+	int retval;
+	u8 dummyRead;
+
+#if defined(CONFIG_M532x) || defined(CONFIG_M523x) || defined(CONFIG_M537x)
+	/*
+	 * Initialize the GPIOs for I2C
+	 */
+	MCF_GPIO_PAR_FECI2C |= (0 | MCF_GPIO_PAR_FECI2C_PAR_SDA(3)
+				| MCF_GPIO_PAR_FECI2C_PAR_SCL(3));
+#elif defined(CONFIG_M5253)
+	{
+		volatile u32 *reg;
+		/* GPIO Bit 41 = SCL0, Bit 42 = SDA0 */
+		reg = (volatile u32 *)(MCF_MBAR2 + MCFSIM2_GPIO1FUNC);
+		*reg &= 0xFFFFF9FF;
+	}
+#elif defined(CONFIG_M5227x)
+	/* Initialize the GPIOs for I2C */
+	MCF_GPIO_PAR_I2C |= (0
+			     | MCF_GPIO_PAR_I2C_PAR_SDA_SDA
+			     | MCF_GPIO_PAR_I2C_PAR_SCL_SCL);
+#elif defined(CONFIG_M5301x)
+	MCF_GPIO_PAR_FECI2C |= (0
+				| MCF_GPIO_PAR_FECI2C_PAR_SDA_SDA
+				| MCF_GPIO_PAR_FECI2C_PAR_SCL_SCL);
+#else
+	/* Initialize PASP0 and PASP1 to I2C functions, 5282 user guide 26-19 */
+	/* Port AS Pin Assignment Register (PASPAR)             */
+	/*              PASPA1 = 11 = AS1 pin is I2C SDA        */
+	/*              PASPA0 = 11 = AS0 pin is I2C SCL        */
+	*MCF5282_GPIO_PASPAR |= 0x000F;	/* u16 declaration */
+#endif
+
+	/* Set transmission frequency 0x15 = ~100kHz */
+	*MCF_I2C_I2FDR = 0x15;
+
+	/* set the 5282 I2C slave address though we never use it */
+	*MCF_I2C_I2ADR = 0x6A;
+
+	/* Enable I2C module and if IBB is set, do the special initialzation */
+	/* procedures as are documented at the 5282 User Guide page 24-11 */
+	*MCF_I2C_I2CR |= MCF_I2C_I2CR_IEN;
+	if ((*MCF_I2C_I2SR & MCF_I2C_I2SR_IBB) == 1) {
+		printk("%s - do special 5282 I2C init procedures \n",
+		       __FUNCTION__);
+		*MCF_I2C_I2CR = 0x00;
+		*MCF_I2C_I2CR = 0xA0;
+		dummyRead = *MCF_I2C_I2DR;
+		*MCF_I2C_I2SR = 0x00;
+		*MCF_I2C_I2CR = 0x00;
+	}
+
+	/* default I2C mode is - slave and receive */
+	*MCF_I2C_I2CR &= ~(MCF_I2C_I2CR_MSTA | MCF_I2C_I2CR_MTX);
+
+	coldfire_adapter.dev.parent = &platform_bus;
+	retval = i2c_add_adapter(&coldfire_adapter);
+
+	if (retval < 0)
+		printk("%s - return code is: %d \n", __FUNCTION__, retval);
+
+	return retval;
+};
+
+/*
+ *  I2C module exit function
+ */
+
+static void __exit i2c_coldfire_exit(void)
+{
+	/* disable I2C and Interrupt */
+	*MCF_I2C_I2CR &= ~(MCF_I2C_I2CR_IEN | MCF_I2C_I2CR_IIEN);
+	i2c_del_adapter(&coldfire_adapter);
+
+};
+
+MODULE_AUTHOR("Derek CL Cheung <derek.cheung@sympatico.ca>");
+MODULE_DESCRIPTION("MCF5282 I2C adaptor");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_coldfire_init);
+module_exit(i2c_coldfire_exit);
diff -urN build-2.6.29-1-m68k-uclinux-target1-nfs/drivers/i2c/busses/i2c-mcf.h build-2.6.29-1-m68k-uclinux-target1/drivers/i2c/busses/i2c-mcf.h
--- build-2.6.29-1-m68k-uclinux-target1-nfs/drivers/i2c/busses/i2c-mcf.h	1969-12-31 19:00:00.000000000 -0500
+++ build-2.6.29-1-m68k-uclinux-target1/drivers/i2c/busses/i2c-mcf.h	2010-02-12 12:10:59.000000000 -0500
@@ -0,0 +1,52 @@
+/*
+    i2c-mcf5282.h - header file for i2c-mcf5282.c
+
+    Copyright (c) 2005, Derek CL Cheung <derek.cheung@sympatico.ca>
+                                        <http://www3.sympatico.ca/derek.cheung>
+
+    Copyright (c) 2006, emlix and Freescale
+			Sebastian Hess <sh@emlix.com>
+			Yaroslav Vinogradov <yaroslav.vinogradov@freescale.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    Changes:
+    v0.1	26 March 2005
+        	Initial Release - developed on uClinux with 2.6.9 kernel
+    v0.2	29 May 2006
+		Modified to be more generic and added support for
+		i2c_master_xfer
+*/
+
+
+#ifndef __I2C_MCF5282_H__
+#define __I2C_MCF5282_H__
+
+enum I2C_START_TYPE { FIRST_START, REPEAT_START };
+enum I2C_ACK_TYPE { ACK, NACK};
+
+/* Function prototypes */
+static u32 coldfire_func(struct i2c_adapter *adapter);
+static s32 coldfire_i2c_access(struct i2c_adapter *adap, u16 address,
+                              unsigned short flags, char read_write,
+                              u8 command, int size, union i2c_smbus_data *data);
+static int coldfire_write_data(const u8 data);
+static int coldfire_i2c_start(const char read_write, const u16 target_address, const enum I2C_START_TYPE i2c_start);
+static int coldfire_read_data(u8 * const rxData, const enum I2C_ACK_TYPE ackType);
+static int coldfire_i2c_master(struct i2c_adapter *adap,struct i2c_msg *msgs, int num);
+void dumpReg(char *, u16 addr, u8 data);
+
+/********************************************************************/
+#endif /*  __I2C_MCF5282_H__ */

I think that __I2C_MCF5282_H__ should be __I2C_MCF_H__ instead, but well
what can you do.

-- 
Len Sorensen
_______________________________________________
uClinux-dev mailing list
uClinux-dev@uclinux.org
http://mailman.uclinux.org/mailman/listinfo/uclinux-dev
This message was resent by uclinux-dev@uclinux.org
To unsubscribe see:
http://mailman.uclinux.org/mailman/options/uclinux-dev

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2010-03-12 15:42 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-01-11  0:00 [PATCH] m68knommu: driver for Freescale Coldfire I2C controller Steven King
2010-01-11  8:37 ` Jean Delvare
     [not found]   ` <20100111093729.63455a90-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2010-01-11 18:24     ` [PATCH v2] " Steven King
     [not found]       ` <201001111024.05506.sfking-xS0NTnu2YfYAvxtiuMwx3w@public.gmane.org>
2010-01-24 15:15         ` Ben Dooks
2010-01-25 19:56           ` Steven King
2010-03-12 11:04             ` Philippe De Muyter
2010-03-12 15:42               ` Lennart Sorensen

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).