mirror of
git://projects.qi-hardware.com/openwrt-xburst.git
synced 2024-11-15 00:08:26 +02: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__ */
|