mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 14:34:27 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			700 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			700 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From e1618e943f60a4f8f778c4f7c389e2fa13eb0a83 Mon Sep 17 00:00:00 2001
 | 
						|
From: Kurt Mahan <kmahan@freescale.com>
 | 
						|
Date: Thu, 26 Jun 2008 16:29:56 -0600
 | 
						|
Subject: [PATCH] Add I2C support for the M5445x platforms.
 | 
						|
 | 
						|
LTIBName: m5445x-i2c
 | 
						|
Signed-off-by: Kurt Mahan <kmahan@freescale.com>
 | 
						|
---
 | 
						|
 drivers/i2c/busses/Kconfig   |   10 +
 | 
						|
 drivers/i2c/busses/Makefile  |    1 +
 | 
						|
 drivers/i2c/busses/i2c-mcf.c |  573 ++++++++++++++++++++++++++++++++++++++++++
 | 
						|
 drivers/i2c/busses/i2c-mcf.h |   75 ++++++
 | 
						|
 4 files changed, 659 insertions(+), 0 deletions(-)
 | 
						|
 create mode 100644 drivers/i2c/busses/i2c-mcf.c
 | 
						|
 create mode 100644 drivers/i2c/busses/i2c-mcf.h
 | 
						|
 | 
						|
--- a/drivers/i2c/busses/Kconfig
 | 
						|
+++ b/drivers/i2c/busses/Kconfig
 | 
						|
@@ -302,6 +302,16 @@ config I2C_POWERMAC
 | 
						|
 	  This support is also available as a module.  If so, the module
 | 
						|
 	  will be called i2c-powermac.
 | 
						|
 
 | 
						|
+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
 | 
						|
--- a/drivers/i2c/busses/Makefile
 | 
						|
+++ b/drivers/i2c/busses/Makefile
 | 
						|
@@ -53,6 +53,7 @@ obj-$(CONFIG_I2C_VOODOO3)	+= i2c-voodoo3
 | 
						|
 obj-$(CONFIG_SCx200_ACB)	+= scx200_acb.o
 | 
						|
 obj-$(CONFIG_SCx200_I2C)	+= scx200_i2c.o
 | 
						|
 obj-$(CONFIG_I2C_MCF548x)       += i2c-mcf548x.o
 | 
						|
+obj-$(CONFIG_I2C_MCF)		+= i2c-mcf.o
 | 
						|
 
 | 
						|
 ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
 | 
						|
 EXTRA_CFLAGS += -DDEBUG
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/i2c/busses/i2c-mcf.c
 | 
						|
@@ -0,0 +1,573 @@
 | 
						|
+/*
 | 
						|
+    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-2007, emlix
 | 
						|
+			Sebastian Hess <sh@emlix.com>
 | 
						|
+
 | 
						|
+    Copyright (c) 2006-2007 Freescale Semiconductor, Inc
 | 
						|
+			Yaroslav Vinogradov <yaroslav.vinogradov@freescale.com>
 | 
						|
+			Matt Waddel <Matt.Waddel@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
 | 
						|
+
 | 
						|
+    This I2C adaptor supports the ColdFire 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 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 = {
 | 
						|
+	/*.name		= "ColdFire I2C algorithm",
 | 
						|
+	.id		= I2C_ALGO_SMBUS,*/
 | 
						|
+	.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 (timeout <= 0 )
 | 
						|
+		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 (timeout <= 0)
 | 
						|
+		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 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:
 | 
						|
+			/* Not done yet */
 | 
						|
+			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_M5445X)
 | 
						|
+	/*
 | 
						|
+	 * 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;
 | 
						|
+	}
 | 
						|
+#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	*/
 | 
						|
+	*MCF_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);
 | 
						|
--- /dev/null
 | 
						|
+++ b/drivers/i2c/busses/i2c-mcf.h
 | 
						|
@@ -0,0 +1,75 @@
 | 
						|
+/*
 | 
						|
+    i2c-mcf.h - header file for i2c-mcf.c
 | 
						|
+
 | 
						|
+    Copyright (c) 2005, Derek CL Cheung <derek.cheung@sympatico.ca>
 | 
						|
+                                        <http://www3.sympatico.ca/derek.cheung>
 | 
						|
+
 | 
						|
+    Copyright (c) 2006-2007, emlix
 | 
						|
+			Sebastian Hess <sh@emlix.com>
 | 
						|
+
 | 
						|
+    Copyright (c) 2006-2007 Freescale Semiconductor, Inc
 | 
						|
+			Yaroslav Vinogradov <yaroslav.vinogradov@freescale.com>
 | 
						|
+			Matt Waddel <Matt.Waddel@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_MCF_H__
 | 
						|
+#define __I2C_MCF_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);
 | 
						|
+
 | 
						|
+#define MCF_I2C_I2ADR_ADDR(x)	(((x)&0x7F)<<0x01)
 | 
						|
+#define MCF_I2C_I2FDR_IC(x)	(((x)&0x3F))
 | 
						|
+
 | 
						|
+/* I2C Control Register */
 | 
						|
+#define MCF_I2C_I2CR_IEN	(0x80)	/* I2C enable */
 | 
						|
+#define MCF_I2C_I2CR_IIEN	(0x40)	/* interrupt enable */
 | 
						|
+#define MCF_I2C_I2CR_MSTA	(0x20)	/* master/slave mode */
 | 
						|
+#define MCF_I2C_I2CR_MTX	(0x10)	/* transmit/receive mode */
 | 
						|
+#define MCF_I2C_I2CR_TXAK	(0x08)	/* transmit acknowledge enable */
 | 
						|
+#define MCF_I2C_I2CR_RSTA	(0x04)	/* repeat start */
 | 
						|
+
 | 
						|
+/* I2C Status Register */
 | 
						|
+#define MCF_I2C_I2SR_ICF	(0x80)	/* data transfer bit */
 | 
						|
+#define MCF_I2C_I2SR_IAAS	(0x40)	/* I2C addressed as a slave */
 | 
						|
+#define MCF_I2C_I2SR_IBB	(0x20)	/* I2C bus busy */
 | 
						|
+#define MCF_I2C_I2SR_IAL	(0x10)	/* aribitration lost */
 | 
						|
+#define MCF_I2C_I2SR_SRW	(0x04)	/* slave read/write */
 | 
						|
+#define MCF_I2C_I2SR_IIF	(0x02)	/* I2C interrupt */
 | 
						|
+#define MCF_I2C_I2SR_RXAK	(0x01)	/* received acknowledge */
 | 
						|
+
 | 
						|
+/********************************************************************/
 | 
						|
+#endif /*  __I2C_MCF_H__ */
 |