summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Kleine-Budde <mkl@pengutronix.de>2009-11-26 10:16:25 +0100
committerMarc Kleine-Budde <mkl@pengutronix.de>2009-11-26 10:16:25 +0100
commit337b1456480b7497d0a68084d2f5806fe4f80f1c (patch)
treea8594b4762835e1df0a7297107c5412992354d5b
parent9c0f28528425434728a0e24e093f12e0863add8e (diff)
parentfd5cd7115c63db40b26a3a05e18c7f4ddfdfa391 (diff)
downloadbarebox-337b1456480b7497d0a68084d2f5806fe4f80f1c.tar.gz
barebox-337b1456480b7497d0a68084d2f5806fe4f80f1c.tar.xz
Merge branch 'dev/i2c' into dev/mx35-3-stack-base
-rw-r--r--drivers/Kconfig1
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/i2c/Kconfig16
-rw-r--r--drivers/i2c/Makefile6
-rw-r--r--drivers/i2c/i2c-imx.c501
-rw-r--r--drivers/i2c/i2c.c353
-rw-r--r--drivers/i2c/mc13892.c111
-rw-r--r--drivers/i2c/mc9sdz60.c109
-rw-r--r--include/i2c/i2c.h124
-rw-r--r--include/i2c/mc13892.h7
-rw-r--r--include/i2c/mc9sdz60.h7
11 files changed, 1236 insertions, 0 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index eb3a587ec9..8bab7ac0bb 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -3,6 +3,7 @@ menu "Drivers "
source "drivers/serial/Kconfig"
source "drivers/net/Kconfig"
source "drivers/spi/Kconfig"
+source "drivers/i2c/Kconfig"
source "drivers/nor/Kconfig"
source "drivers/nand/Kconfig"
source "drivers/usb/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 518060a941..5dc7756aa1 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -4,4 +4,5 @@ obj-y += nand/
obj-y += nor/
obj-y += usb/
obj-$(CONFIG_SPI) += spi/
+obj-$(CONFIG_I2C) += i2c/
obj-$(CONFIG_VIDEO) += video/
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
new file mode 100644
index 0000000000..46723ed84a
--- /dev/null
+++ b/drivers/i2c/Kconfig
@@ -0,0 +1,16 @@
+menuconfig I2C
+ bool "I2C drivers "
+
+if I2C
+
+config DRIVER_I2C_IMX
+ bool "i.MX I2C Master driver"
+ depends on ARCH_IMX
+
+config DRIVER_I2C_MC13892
+ bool "MC13892 a.k.a. PMIC driver"
+
+config DRIVER_I2C_MC9SDZ60
+ bool "MC9SDZ60 driver"
+
+endif
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
new file mode 100644
index 0000000000..5dd642fda3
--- /dev/null
+++ b/drivers/i2c/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_I2C) += i2c.o
+
+obj-$(CONFIG_DRIVER_I2C_IMX) += i2c-imx.o
+
+obj-$(CONFIG_DRIVER_I2C_MC13892) += mc13892.o
+obj-$(CONFIG_DRIVER_I2C_MC9SDZ60) += mc9sdz60.o
diff --git a/drivers/i2c/i2c-imx.c b/drivers/i2c/i2c-imx.c
new file mode 100644
index 0000000000..33d9fe9e74
--- /dev/null
+++ b/drivers/i2c/i2c-imx.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2002 Motorola GSG-China
+ * 2009 Marc Kleine-Budde, Pengutronix
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * Author:
+ * Darius Augulis, Teltonika Inc.
+ *
+ * Desc.:
+ * Implementation of I2C Adapter/Algorithm Driver
+ * for I2C Bus integrated in Freescale i.MX/MXC processors
+ *
+ * Derived from Motorola GSG China I2C example driver
+ *
+ * Copyright (C) 2005 Torsten Koschorrek <koschorrek at synertronixx.de
+ * Copyright (C) 2005 Matthias Blaschke <blaschke at synertronixx.de
+ * Copyright (C) 2007 RightHand Technologies, Inc.
+ * Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt>
+ *
+ */
+
+#include <clock.h>
+#include <common.h>
+#include <driver.h>
+#include <gpio.h>
+#include <init.h>
+#include <malloc.h>
+#include <types.h>
+#include <xfuncs.h>
+
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <i2c/i2c.h>
+#include <mach/generic.h>
+#include <mach/clock.h>
+
+/* This will be the driver name */
+#define DRIVER_NAME "i2c-imx"
+
+/* Default value */
+#define IMX_I2C_BIT_RATE 100000 /* 100kHz */
+
+/* IMX I2C registers */
+#define IMX_I2C_IADR 0x00 /* i2c slave address */
+#define IMX_I2C_IFDR 0x04 /* i2c frequency divider */
+#define IMX_I2C_I2CR 0x08 /* i2c control */
+#define IMX_I2C_I2SR 0x0C /* i2c status */
+#define IMX_I2C_I2DR 0x10 /* i2c transfer data */
+
+/* Bits of IMX I2C registers */
+#define I2SR_RXAK 0x01
+#define I2SR_IIF 0x02
+#define I2SR_SRW 0x04
+#define I2SR_IAL 0x10
+#define I2SR_IBB 0x20
+#define I2SR_IAAS 0x40
+#define I2SR_ICF 0x80
+#define I2CR_RSTA 0x04
+#define I2CR_TXAK 0x08
+#define I2CR_MTX 0x10
+#define I2CR_MSTA 0x20
+#define I2CR_IIEN 0x40
+#define I2CR_IEN 0x80
+
+/*
+ * sorted list of clock divider, register value pairs
+ * taken from table 26-5, p.26-9, Freescale i.MX
+ * Integrated Portable System Processor Reference Manual
+ * Document Number: MC9328MXLRM, Rev. 5.1, 06/2007
+ *
+ * Duplicated divider values removed from list
+ */
+static u16 i2c_clk_div[50][2] = {
+ { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 },
+ { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 },
+ { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 },
+ { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2A }, { 72, 0x2B },
+ { 80, 0x2C }, { 88, 0x09 }, { 96, 0x2D }, { 104, 0x0A },
+ { 112, 0x2E }, { 128, 0x2F }, { 144, 0x0C }, { 160, 0x30 },
+ { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0F }, { 256, 0x33 },
+ { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 },
+ { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 },
+ { 768, 0x39 }, { 896, 0x3A }, { 960, 0x17 }, { 1024, 0x3B },
+ { 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E },
+ { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D },
+ { 3072, 0x1E }, { 3840, 0x1F }
+};
+
+struct imx_i2c_struct {
+ struct i2c_adapter adapter;
+ unsigned int disable_delay;
+ int stopped;
+ unsigned int ifdr; /* IMX_I2C_IFDR */
+};
+#define to_imx_i2c_struct(a) container_of(a, struct imx_i2c_struct, adapter)
+
+#ifdef CONFIG_I2C_DEBUG
+static void i2c_imx_dump_reg(struct i2c_adapter *adapter)
+{
+ unsigned long base = adapter->dev->map_base;
+ u32 reg_cr, reg_sr;
+
+ reg_cr = readb(base + IMX_I2C_I2CR);
+ reg_sr = readb(base + IMX_I2C_I2SR);
+
+ dev_dbg(adapter->dev, "CONTROL:\t"
+ "IEN =%d, IIEN=%d, MSTA=%d, MTX =%d, TXAK=%d, RSTA=%d\n",
+ (reg_cr & I2CR_IEN ? 1 : 0), (reg_cr & I2CR_IIEN ? 1 : 0),
+ (reg_cr & I2CR_MSTA ? 1 : 0), (reg_cr & I2CR_MTX ? 1 : 0),
+ (reg_cr & I2CR_TXAK ? 1 : 0), (reg_cr & I2CR_RSTA ? 1 : 0));
+
+ dev_dbg(adapter->dev, "STATUS:\t"
+ "ICF =%d, IAAS=%d, IB =%d, IAL =%d, SRW =%d, IIF =%d, RXAK=%d\n",
+ (reg_sr & I2SR_ICF ? 1 : 0), (reg_sr & I2SR_IAAS ? 1 : 0),
+ (reg_sr & I2SR_IBB ? 1 : 0), (reg_sr & I2SR_IAL ? 1 : 0),
+ (reg_sr & I2SR_SRW ? 1 : 0), (reg_sr & I2SR_IIF ? 1 : 0),
+ (reg_sr & I2SR_RXAK ? 1 : 0));
+}
+#else
+static inline void i2c_imx_dump_reg(struct i2c_adapter *adapter)
+{
+ return;
+}
+#endif
+
+static int i2c_imx_bus_busy(struct i2c_adapter *adapter, int for_busy)
+{
+ unsigned long base = adapter->dev->map_base;
+ uint64_t start;
+ unsigned int temp;
+
+ start = get_time_ns();
+ while (1) {
+ temp = readb(base + IMX_I2C_I2SR);
+ if (for_busy && (temp & I2SR_IBB))
+ break;
+ if (!for_busy && !(temp & I2SR_IBB))
+ break;
+ if (is_timeout(start, MSECOND)) {
+ dev_warn(adapter->dev, "<%s> I2C bus is busy\n", __func__);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int i2c_imx_trx_complete(struct i2c_adapter *adapter)
+{
+ unsigned long base = adapter->dev->map_base;
+ uint64_t start;
+
+ start = get_time_ns();
+ while (1) {
+ unsigned int reg = readb(base + IMX_I2C_I2SR);
+ if (reg & I2SR_ICF)
+ break;
+
+ if (is_timeout(start, 100 * MSECOND)) {
+ dev_warn(adapter->dev, "<%s> TXR timeout\n", __func__);
+ return -EIO;
+ }
+
+ writeb(0x0, base + IMX_I2C_I2SR);
+ }
+
+ return 0;
+}
+
+static int i2c_imx_wait_iif(struct i2c_adapter *adapter)
+{
+ unsigned long base = adapter->dev->map_base;
+ uint64_t start;
+
+ start = get_time_ns();
+ while (1) {
+ unsigned int reg = readb(base + IMX_I2C_I2SR);
+ if (reg & I2SR_IIF)
+ break;
+
+ if (is_timeout(start, 100 * MSECOND)) {
+ dev_warn(adapter->dev, "<%s> IIF timeout\n", __func__);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int i2c_imx_acked(struct i2c_adapter *adapter)
+{
+ unsigned long base = adapter->dev->map_base;
+
+ if (readb(base + IMX_I2C_I2SR) & I2SR_RXAK) {
+ dev_notice(adapter->dev, "<%s> No ACK\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int i2c_imx_start(struct i2c_adapter *adapter)
+{
+ struct imx_i2c_struct *i2c_imx = to_imx_i2c_struct(adapter);
+ unsigned long base = adapter->dev->map_base;
+ unsigned int temp = 0;
+ int result;
+
+ writeb(i2c_imx->ifdr, base + IMX_I2C_IFDR);
+ /* Enable I2C controller */
+ writeb(0, base + IMX_I2C_I2SR);
+ writeb(I2CR_IEN, base + IMX_I2C_I2CR);
+
+ /* Wait controller to be stable */
+ udelay(100);
+
+ /* Start I2C transaction */
+ temp = readb(base + IMX_I2C_I2CR);
+ temp |= I2CR_MSTA;
+ writeb(temp, base + IMX_I2C_I2CR);
+
+ result = i2c_imx_bus_busy(adapter, 1);
+ if (result)
+ return result;
+
+ i2c_imx->stopped = 0;
+
+ temp |= I2CR_MTX | I2CR_TXAK;
+ writeb(temp, base + IMX_I2C_I2CR);
+
+ return result;
+}
+
+static void i2c_imx_stop(struct i2c_adapter *adapter)
+{
+ struct imx_i2c_struct *i2c_imx = to_imx_i2c_struct(adapter);
+ unsigned long base = adapter->dev->map_base;
+ unsigned int temp = 0;
+
+ if (!i2c_imx->stopped) {
+ /* Stop I2C transaction */
+ temp = readb(base + IMX_I2C_I2CR);
+ temp &= ~(I2CR_MSTA | I2CR_MTX);
+ writeb(temp, base + IMX_I2C_I2CR);
+ i2c_imx->stopped = 1;
+ }
+ if (cpu_is_mx1()) {
+ /*
+ * This delay caused by an i.MXL hardware bug.
+ * If no (or too short) delay, no "STOP" bit will be generated.
+ */
+ udelay(i2c_imx->disable_delay);
+ }
+
+ if (!i2c_imx->stopped)
+ i2c_imx_bus_busy(adapter, 0);
+
+ /* Disable I2C controller */
+ writeb(0, base + IMX_I2C_I2CR);
+}
+
+static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
+ unsigned int rate)
+{
+ unsigned int i2c_clk_rate;
+ unsigned int div;
+ int i;
+
+ /* Divider value calculation */
+ i2c_clk_rate = imx_get_i2cclk();
+ div = (i2c_clk_rate + rate - 1) / rate;
+ if (div < i2c_clk_div[0][0])
+ i = 0;
+ else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0])
+ i = ARRAY_SIZE(i2c_clk_div) - 1;
+ else
+ for (i = 0; i2c_clk_div[i][0] < div; i++)
+ ;
+
+ /* Store divider value */
+ i2c_imx->ifdr = i2c_clk_div[i][1];
+
+ /*
+ * There dummy delay is calculated.
+ * It should be about one I2C clock period long.
+ * This delay is used in I2C bus disable function
+ * to fix chip hardware bug.
+ */
+ i2c_imx->disable_delay =
+ (500000U * i2c_clk_div[i][0] + (i2c_clk_rate / 2) - 1) /
+ (i2c_clk_rate / 2);
+
+ dev_dbg(i2c_imx->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n",
+ __func__, i2c_clk_rate, div);
+ dev_dbg(i2c_imx->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n",
+ __func__, i2c_clk_div[i][1], i2c_clk_div[i][0]);
+}
+
+static int i2c_imx_write(struct i2c_adapter *adapter, struct i2c_msg *msgs)
+{
+ unsigned long base = adapter->dev->map_base;
+ int i, result;
+
+ dev_dbg(adapter->dev,
+ "<%s> write slave address: addr=0x%02x\n",
+ __func__, msgs->addr << 1);
+
+ /* write slave address */
+ writeb(msgs->addr << 1, base + IMX_I2C_I2DR);
+
+ result = i2c_imx_trx_complete(adapter);
+ if (result)
+ return result;
+ result = i2c_imx_acked(adapter);
+ if (result)
+ return result;
+
+ /* write data */
+ for (i = 0; i < msgs->len; i++) {
+ dev_dbg(adapter->dev,
+ "<%s> write byte: B%d=0x%02X\n",
+ __func__, i, msgs->buf[i]);
+ writeb(msgs->buf[i], base + IMX_I2C_I2DR);
+
+ result = i2c_imx_trx_complete(adapter);
+ if (result)
+ return result;
+ result = i2c_imx_acked(adapter);
+ if (result)
+ return result;
+ }
+ return 0;
+}
+
+static int i2c_imx_read(struct i2c_adapter *adapter, struct i2c_msg *msgs)
+{
+ struct imx_i2c_struct *i2c_imx = to_imx_i2c_struct(adapter);
+ unsigned long base = adapter->dev->map_base;
+ int i, result;
+ unsigned int temp;
+
+ dev_dbg(adapter->dev,
+ "<%s> write slave address: addr=0x%02x\n",
+ __func__, (msgs->addr << 1) | 0x01);
+
+ /* write slave address */
+ writeb((msgs->addr << 1) | 0x01, base + IMX_I2C_I2DR);
+
+ result = i2c_imx_trx_complete(adapter);
+ if (result)
+ return result;
+ result = i2c_imx_acked(adapter);
+ if (result)
+ return result;
+
+ result = i2c_imx_wait_iif(adapter);
+ if (result)
+ return result;
+
+ /* setup bus to read data */
+ temp = readb(base + IMX_I2C_I2CR);
+ temp &= ~I2CR_MTX;
+ if (msgs->len - 1)
+ temp &= ~I2CR_TXAK;
+ writeb(temp, base + IMX_I2C_I2CR);
+
+ readb(base + IMX_I2C_I2DR); /* dummy read */
+
+ /* read data */
+ for (i = 0; i < msgs->len; i++) {
+ result = i2c_imx_trx_complete(adapter);
+ if (result)
+ return result;
+
+ if (i == (msgs->len - 1)) {
+ /*
+ * It must generate STOP before read I2DR to prevent
+ * controller from generating another clock cycle
+ */
+ temp = readb(base + IMX_I2C_I2CR);
+ temp &= ~(I2CR_MSTA | I2CR_MTX);
+ writeb(temp, base + IMX_I2C_I2CR);
+ i2c_imx_bus_busy(adapter, 0);
+ i2c_imx->stopped = 1;
+ } else if (i == (msgs->len - 2)) {
+ temp = readb(base + IMX_I2C_I2CR);
+ temp |= I2CR_TXAK;
+ writeb(temp, base + IMX_I2C_I2CR);
+ }
+ msgs->buf[i] = readb(base + IMX_I2C_I2DR);
+
+ dev_dbg(adapter->dev, "<%s> read byte: B%d=0x%02X\n",
+ __func__, i, msgs->buf[i]);
+ }
+ return 0;
+}
+
+static int i2c_imx_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ unsigned long base = adapter->dev->map_base;
+ unsigned int i, temp;
+ int result;
+
+ /* Start I2C transfer */
+ result = i2c_imx_start(adapter);
+ if (result)
+ goto fail0;
+
+ /* read/write data */
+ for (i = 0; i < num; i++) {
+ if (i) {
+ temp = readb(base + IMX_I2C_I2CR);
+ temp |= I2CR_RSTA;
+ writeb(temp, base + IMX_I2C_I2CR);
+
+ result = i2c_imx_bus_busy(adapter, 1);
+ if (result)
+ goto fail0;
+ }
+ i2c_imx_dump_reg(adapter);
+
+ /* write/read data */
+ if (msgs[i].flags & I2C_M_RD)
+ result = i2c_imx_read(adapter, &msgs[i]);
+ else
+ result = i2c_imx_write(adapter, &msgs[i]);
+ }
+
+fail0:
+ /* Stop I2C transfer */
+ i2c_imx_stop(adapter);
+
+ return (result < 0) ? result : num;
+}
+
+static int __init i2c_imx_probe(struct device_d *pdev)
+{
+ struct imx_i2c_struct *i2c_imx;
+ struct i2c_platform_data *pdata;
+ unsigned long base = pdev->map_base;
+ int ret;
+
+ pdata = pdev->platform_data;
+
+ i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL);
+
+ /* Setup i2c_imx driver structure */
+ i2c_imx->adapter.master_xfer = i2c_imx_xfer;
+ i2c_imx->adapter.nr = pdev->id;
+ i2c_imx->adapter.dev = pdev;
+
+ /* Set up clock divider */
+ if (pdata && pdata->bitrate)
+ i2c_imx_set_clk(i2c_imx, pdata->bitrate);
+ else
+ i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE);
+
+ /* Set up chip registers to defaults */
+ writeb(0, base + IMX_I2C_I2CR);
+ writeb(0, base + IMX_I2C_I2SR);
+
+ /* Add I2C adapter */
+ ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
+ if (ret < 0) {
+ dev_err(pdev, "registration failed\n");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ kfree(i2c_imx);
+ return ret;
+}
+
+static struct driver_d i2c_imx_driver = {
+ .probe = i2c_imx_probe,
+ .name = DRIVER_NAME,
+};
+
+static int __init i2c_adap_imx_init(void)
+{
+ return register_driver(&i2c_imx_driver);
+}
+device_initcall(i2c_adap_imx_init);
diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c
new file mode 100644
index 0000000000..a00e53a388
--- /dev/null
+++ b/drivers/i2c/i2c.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2009 Marc Kleine-Budde <mkl@pengutronix.de>
+ *
+ * This file is released under the GPLv2
+ *
+ * Derived from:
+ * - i2c-core.c - a device driver for the iic-bus interface
+ * Copyright (C) 1995-99 Simon G. Vogl
+ * - at24.c - handle most I2C EEPROMs
+ * Copyright (C) 2005-2007 David Brownell
+ * Copyright (C) 2008 Wolfram Sang, Pengutronix
+ * - spi.c - u-boot-v2 SPI Framework
+ * Copyright (C) 2008 Sascha Hauer, Pengutronix
+ * - Linux SPI Framework
+ * Copyright (C) 2005 David Brownell
+ *
+ */
+
+#include <clock.h>
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <xfuncs.h>
+
+#include <i2c/i2c.h>
+
+/**
+ * I2C devices should normally not be created by I2C device drivers;
+ * that would make them board-specific. Similarly with I2C master
+ * drivers. Device registration normally goes into like
+ * arch/.../mach.../board-YYY.c with other readonly (flashable)
+ * information about mainboard devices.
+ */
+struct boardinfo {
+ struct list_head list;
+ unsigned int bus_num;
+ unsigned int n_board_info;
+ struct i2c_board_info board_info[0];
+};
+
+static LIST_HEAD(board_list);
+
+
+/**
+ * i2c_transfer - execute a single or combined I2C message
+ * @param adap Handle to I2C bus
+ * @param msgs One or more messages to execute before STOP is
+ * issued to terminate the operation; each
+ * message begins with a START.
+ *
+ * @param num Number of messages to be executed.
+ *
+ * Returns negative errno, else the number of messages executed.
+ *
+ * Note that there is no requirement that each message be sent to the
+ * same slave address, although that is the most common model.
+ */
+int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ uint64_t start;
+ int ret, try;
+
+ /*
+ * REVISIT the fault reporting model here is weak:
+ *
+ * - When we get an error after receiving N bytes from a slave,
+ * there is no way to report "N".
+ *
+ * - When we get a NAK after transmitting N bytes to a slave,
+ * there is no way to report "N" ... or to let the master
+ * continue executing the rest of this combined message, if
+ * that's the appropriate response.
+ *
+ * - When for example "num" is two and we successfully complete
+ * the first message but get an error part way through the
+ * second, it's unclear whether that should be reported as
+ * one (discarding status on the second message) or errno
+ * (discarding status on the first one).
+ */
+
+ for (ret = 0; ret < num; ret++) {
+ dev_dbg(adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
+ "len=%d\n", ret, (msgs[ret].flags & I2C_M_RD)
+ ? 'R' : 'W', msgs[ret].addr, msgs[ret].len);
+ }
+
+ /* Retry automatically on arbitration loss */
+ start = get_time_ns();
+ for (ret = 0, try = 0; try <= 2; try++) {
+ ret = adap->master_xfer(adap, msgs, num);
+ if (ret != -EAGAIN)
+ break;
+ if (is_timeout(start, SECOND >> 1))
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(i2c_transfer);
+
+/**
+ * i2c_master_send - issue a single I2C message in master transmit mode
+ *
+ * @param client Handle to slave device
+ * @param buf Data that will be written to the slave
+ * @param count How many bytes to write
+ *
+ * Returns negative errno, or else the number of bytes written.
+ */
+int i2c_master_send(struct i2c_client *client, const char *buf, int count)
+{
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msg;
+ int ret;
+
+ msg.addr = client->addr;
+ msg.len = count;
+ msg.buf = (char *)buf;
+
+ ret = i2c_transfer(adap, &msg, 1);
+
+ /*
+ * If everything went ok (i.e. 1 msg transmitted), return
+ * #bytes transmitted, else error code.
+ */
+ return (ret == 1) ? count : ret;
+}
+EXPORT_SYMBOL(i2c_master_send);
+
+/**
+ * i2c_master_recv - issue a single I2C message in master receive mode
+ *
+ * @param client Handle to slave device
+ * @param buf Where to store data read from slave
+ * @param count How many bytes to read
+ *
+ * Returns negative errno, or else the number of bytes read.
+ */
+int i2c_master_recv(struct i2c_client *client, char *buf, int count)
+{
+ struct i2c_adapter *adap = client->adapter;
+ struct i2c_msg msg;
+ int ret;
+
+ msg.addr = client->addr;
+ msg.flags = I2C_M_RD;
+ msg.len = count;
+ msg.buf = buf;
+
+ ret = i2c_transfer(adap, &msg, 1);
+
+ /*
+ * If everything went ok (i.e. 1 msg transmitted), return
+ * #bytes transmitted, else error code.
+ */
+ return (ret == 1) ? count : ret;
+}
+EXPORT_SYMBOL(i2c_master_recv);
+
+int i2c_read_reg(struct i2c_client *client, u32 addr, u8 *buf, u16 count)
+{
+ u8 msgbuf[2];
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .buf = msgbuf,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ .len = count,
+ },
+ };
+ int status, i;
+
+ i = 0;
+ if (addr & I2C_ADDR_16_BIT)
+ msgbuf[i++] = addr >> 8;
+ msgbuf[i++] = addr;
+ msg->len = i;
+
+ status = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ dev_dbg(&client->dev, "%s: %zu@%d --> %d\n", __func__,
+ count, addr, status);
+
+ if (status == ARRAY_SIZE(msg))
+ return count;
+ else if (status >= 0)
+ return -EIO;
+ else
+ return status;
+}
+EXPORT_SYMBOL(i2c_read_reg);
+
+int i2c_write_reg(struct i2c_client *client, u32 addr, const u8 *buf, u16 count)
+{
+ u8 msgbuf[256]; /* FIXME */
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .buf = msgbuf,
+ .len = count,
+ }
+ };
+ int status, i;
+
+ i = 0;
+ if (addr & I2C_ADDR_16_BIT)
+ msgbuf[i++] = addr >> 8;
+ msgbuf[i++] = addr;
+ msg->len += i;
+
+ memcpy(msg->buf + i, buf, count);
+
+ status = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ dev_dbg(&client->dev, "%s: %u@%d --> %d\n", __func__,
+ count, addr, status);
+
+ if (status == ARRAY_SIZE(msg))
+ return count;
+ else if (status >= 0)
+ return -EIO;
+ else
+ return status;
+}
+EXPORT_SYMBOL(i2c_write_reg);
+
+/**
+ * i2c_new_device - instantiate one new I2C device
+ *
+ * @param adapter Controller to which device is connected
+ * @param chip Describes the I2C device
+ *
+ * On typical mainboards, this is purely internal; and it's not needed
+ * after board init creates the hard-wired devices. Some development
+ * platforms may not be able to use i2c_register_board_info though,
+ * and this is exported so that for example a USB or parport based
+ * adapter driver could add devices (which it would learn about
+ * out-of-band).
+ *
+ * Returns the new device, or NULL.
+ */
+struct i2c_client *i2c_new_device(struct i2c_adapter *adapter,
+ struct i2c_board_info *chip)
+{
+ struct i2c_client *client;
+ int status;
+
+ client = xzalloc(sizeof *client);
+ strcpy(client->dev.name, chip->type);
+ client->dev.type_data = client;
+ client->adapter = adapter;
+ client->addr = chip->addr;
+
+ status = register_device(&client->dev);
+
+#if 0
+ /* drivers may modify this initial i/o setup */
+ status = master->setup(client);
+ if (status < 0) {
+ printf("can't setup %s, status %d\n",
+ client->dev.name, status);
+ goto fail;
+ }
+#endif
+
+ return client;
+
+#if 0
+ fail:
+ free(proxy);
+ return NULL;
+#endif
+}
+EXPORT_SYMBOL(i2c_new_device);
+
+/**
+ * i2c_register_board_info - register I2C devices for a given board
+ *
+ * @param info array of chip descriptors
+ * @param n how many descriptors are provided
+ *
+ * Board-specific early init code calls this (probably during
+ * arch_initcall) with segments of the I2C device table.
+ *
+ * Other code can also call this, e.g. a particular add-on board might
+ * provide I2C devices through its expansion connector, so code
+ * initializing that board would naturally declare its I2C devices.
+ *
+ */
+int i2c_register_board_info(int bus_num, struct i2c_board_info const *info, unsigned n)
+{
+ struct boardinfo *bi;
+
+ bi = xmalloc(sizeof(*bi) + n * sizeof(*info));
+
+ bi->n_board_info = n;
+ bi->bus_num = bus_num;
+ memcpy(bi->board_info, info, n * sizeof(*info));
+
+ list_add_tail(&bi->list, &board_list);
+
+ return 0;
+}
+
+static void scan_boardinfo(struct i2c_adapter *adapter)
+{
+ struct boardinfo *bi;
+
+ list_for_each_entry(bi, &board_list, list) {
+ struct i2c_board_info *chip = bi->board_info;
+ unsigned n;
+
+ if (bi->bus_num != adapter->nr)
+ continue;
+
+ for (n = bi->n_board_info; n > 0; n--, chip++) {
+ debug("%s: bus_num: %d, chip->addr 0x%02x\n", __func__, bi->bus_num, chip->addr);
+ /*
+ * NOTE: this relies on i2c_new_device to
+ * issue diagnostics when given bogus inputs
+ */
+ (void) i2c_new_device(adapter, chip);
+ }
+ }
+}
+
+/**
+ * i2c_register_master - register I2C master controller
+ *
+ * @param master initialized master, originally from i2c_alloc_master()
+ *
+ * I2C master controllers connect to their drivers using some non-I2C
+ * bus, such as the platform bus. The final stage of probe() in that
+ * code includes calling i2c_register_master() to hook up to this I2C
+ * bus glue.
+ *
+ * I2C controllers use board specific (often SOC specific) bus
+ * numbers, and board-specific addressing for I2C devices combines
+ * those numbers with chip select numbers. Since I2C does not directly
+ * support dynamic device identification, boards need configuration
+ * tables telling which chip is at which address.
+ *
+ */
+int i2c_add_numbered_adapter(struct i2c_adapter *adapter)
+{
+ /* populate children from any i2c device tables */
+ scan_boardinfo(adapter);
+
+ return 0;
+}
+EXPORT_SYMBOL(i2c_register_master);
diff --git a/drivers/i2c/mc13892.c b/drivers/i2c/mc13892.c
new file mode 100644
index 0000000000..54661d4f5e
--- /dev/null
+++ b/drivers/i2c/mc13892.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2007 Sascha Hauer, Pengutronix
+ * 2009 Marc Kleine-Budde <mkl@pengutronix.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <xfuncs.h>
+#include <errno.h>
+
+#include <i2c/i2c.h>
+
+#include <asm/byteorder.h>
+
+#define DRIVERNAME "mc13892"
+
+struct mc_priv {
+ struct cdev cdev;
+ struct i2c_client *client;
+};
+
+#define to_mc_priv(a) container_of(a, struct mc_priv, cdev)
+
+static struct mc_priv *mc_dev;
+
+struct i2c_client *mc13892_get_client(void)
+{
+ if (!mc_dev)
+ return NULL;
+
+ return mc_dev->client;
+}
+
+static u32 mc_read_reg(struct mc_priv *mc, int reg)
+{
+ u32 buf;
+
+ i2c_read_reg(mc->client, reg, (u8 *)&buf, sizeof(buf));
+
+ return buf;
+}
+
+static ssize_t mc_read(struct cdev *cdev, void *_buf, size_t count, ulong offset, ulong flags)
+{
+ struct mc_priv *priv = to_mc_priv(cdev);
+ int i = count >> 2;
+ u32 *buf = _buf;
+
+ offset >>= 2;
+
+ while (i) {
+ *buf = mc_read_reg(priv, offset);
+ buf++;
+ i--;
+ offset++;
+ }
+
+ return count;
+}
+
+static struct file_operations mc_fops = {
+ .lseek = dev_lseek_default,
+ .read = mc_read,
+};
+
+static int mc_probe(struct device_d *dev)
+{
+ if (mc_dev)
+ return -EBUSY;
+
+ mc_dev = xzalloc(sizeof(struct mc_priv));
+ mc_dev->cdev.name = DRIVERNAME;
+ mc_dev->client = to_i2c_client(dev);
+ mc_dev->cdev.size = 256;
+ mc_dev->cdev.dev = dev;
+ mc_dev->cdev.ops = &mc_fops;
+
+ devfs_create(&mc_dev->cdev);
+
+ return 0;
+}
+
+static struct driver_d mc_driver = {
+ .name = DRIVERNAME,
+ .probe = mc_probe,
+};
+
+static int mc_init(void)
+{
+ register_driver(&mc_driver);
+ return 0;
+}
+
+device_initcall(mc_init);
diff --git a/drivers/i2c/mc9sdz60.c b/drivers/i2c/mc9sdz60.c
new file mode 100644
index 0000000000..4b1068d17e
--- /dev/null
+++ b/drivers/i2c/mc9sdz60.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2007 Sascha Hauer, Pengutronix
+ * 2009 Marc Kleine-Budde <mkl@pengutronix.de>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <xfuncs.h>
+#include <errno.h>
+
+#include <i2c/i2c.h>
+
+#include <asm/byteorder.h>
+
+#define DRIVERNAME "mc9sdz60"
+
+struct mc_priv {
+ struct cdev cdev;
+ struct i2c_client *client;
+};
+
+#define to_mc_priv(a) container_of(a, struct mc_priv, cdev)
+
+static struct mc_priv *mc_dev;
+
+struct i2c_client *mc9sdz60_get_client(void)
+{
+ if (!mc_dev)
+ return NULL;
+
+ return mc_dev->client;
+}
+
+static u32 mc_read_reg(struct mc_priv *mc, int reg)
+{
+ u8 buf;
+
+ i2c_read_reg(mc->client, reg, &buf, sizeof(buf));
+
+ return buf;
+}
+
+static ssize_t mc_read(struct cdev *cdev, void *_buf, size_t count, ulong offset, ulong flags)
+{
+ struct mc_priv *priv = to_mc_priv(cdev);
+ int i = count;
+ u8 *buf = _buf;
+
+ while (i) {
+ *buf = mc_read_reg(priv, offset);
+ buf++;
+ i--;
+ offset++;
+ }
+
+ return count;
+}
+
+static struct file_operations mc_fops = {
+ .lseek = dev_lseek_default,
+ .read = mc_read,
+};
+
+static int mc_probe(struct device_d *dev)
+{
+ if (mc_dev)
+ return -EBUSY;
+
+ mc_dev = xzalloc(sizeof(struct mc_priv));
+ mc_dev->cdev.name = DRIVERNAME;
+ mc_dev->client = to_i2c_client(dev);
+ mc_dev->cdev.size = 256;
+ mc_dev->cdev.dev = dev;
+ mc_dev->cdev.ops = &mc_fops;
+
+ devfs_create(&mc_dev->cdev);
+
+ return 0;
+}
+
+static struct driver_d mc_driver = {
+ .name = DRIVERNAME,
+ .probe = mc_probe,
+};
+
+static int mc_init(void)
+{
+ register_driver(&mc_driver);
+ return 0;
+}
+
+device_initcall(mc_init);
diff --git a/include/i2c/i2c.h b/include/i2c/i2c.h
new file mode 100644
index 0000000000..670898ba4a
--- /dev/null
+++ b/include/i2c/i2c.h
@@ -0,0 +1,124 @@
+/*
+ * i2c.h - definitions for the u-boot-v2 i2c framework
+ *
+ * Copyricht (C) 2009 by Marc Kleine-Budde <mkl@pengutronix.de>
+ *
+ * This file is released under the GPLv2
+ *
+ * Derived from:
+ * - i2c.h - i.MX I2C driver header file
+ * Copyright (c) 2008, Darius Augulis <augulis.darius@gmail.com>
+ * - i2c.h - definitions for the i2c-bus interface
+ * Copyright (C) 1995-2000 Simon G. Vogl
+ *
+ */
+
+#ifndef I2C_I2C_H
+#define I2C_I2C_H
+
+/*
+ * struct i2c_platform_data - structure of platform data for MXC I2C driver
+ * @param bitrate Bus speed measured in Hz
+ *
+ */
+struct i2c_platform_data {
+ int bitrate;
+};
+
+#define I2C_NAME_SIZE 20
+
+#define I2C_M_RD 0x0001 /* read data, from slave to master */
+
+/**
+ * struct i2c_msg - an I2C transaction segment beginning with START
+ *
+ * An i2c_msg is the low level representation of one segment of an I2C
+ * transaction. It is visible to drivers in the @i2c_transfer()
+ * procedure and to I2C adapter drivers through the
+ * @i2c_adapter.@master_xfer() method.
+ *
+ * All I2C adapters implement the standard rules for I2C transactions.
+ * Each transaction begins with a START. That is followed by the
+ * slave address, and a bit encoding read versus write. Then follow
+ * all the data bytes, The transfer terminates with a NAK, or when all
+ * those bytes have been transferred and ACKed. If this is the last
+ * message in a group, it is followed by a STOP. Otherwise it is
+ * followed by the next @i2c_msg transaction segment, beginning with a
+ * (repeated) START.
+ *
+ */
+struct i2c_msg {
+ __u8 *buf; /**< The buffer into which data is read, or from which it's written. */
+ __u16 addr; /**< Slave address, seven bits */
+ __u16 flags; /**< I2C_M_RD is handled by all adapters */
+ __u16 len; /**< Number of data bytes in @buf being read from or written to the I2C slave address. */
+};
+
+
+/**
+ * i2c_adapter is the structure used to identify a physical i2c bus
+ * along with the access algorithms necessary to access it.
+ *
+ */
+struct i2c_adapter {
+ struct device_d *dev; /* ptr to device */
+ int nr; /* bus number */
+ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
+};
+
+
+struct i2c_client {
+ struct device_d dev;
+ struct i2c_adapter *adapter;
+ unsigned short addr;
+};
+
+#define to_i2c_client(a) container_of(a, struct i2c_client, dev)
+
+
+/**
+ * struct i2c_board_info - template for device creation
+ *
+ * I2C doesn't actually support hardware probing, Drivers commonly
+ * need more information than that, such as chip type, configuration,
+ * and so on.
+ *
+ * i2c_board_info is used to build tables of information listing I2C
+ * devices that are present. This information is used to grow the
+ * driver model tree. For mainboards this is done statically using
+ * i2c_register_board_info(); bus numbers identify adapters that
+ * aren't yet available. For add-on boards, i2c_new_device() does this
+ * dynamically with the adapter already known.
+ */
+struct i2c_board_info {
+ char type[I2C_NAME_SIZE]; /**< name of device */
+ unsigned short addr; /**< stored in i2c_client.addr */
+};
+
+/**
+ * I2C_BOARD_INFO - macro used to list an i2c device and its address
+ * @dev_type: identifies the device type
+ * @dev_addr: the device's address on the bus.
+ *
+ * This macro initializes essential fields of a struct i2c_board_info,
+ * declaring what has been provided on a particular board. Optional
+ * fields (such as associated irq, or device-specific platform_data)
+ * are provided using conventional syntax.
+ */
+#define I2C_BOARD_INFO(dev_type, dev_addr) \
+ .type = dev_type, .addr = (dev_addr)
+
+extern int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned n);
+extern int i2c_add_numbered_adapter(struct i2c_adapter *adapter);
+
+extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
+extern int i2c_master_send(struct i2c_client *client, const char *buf, int count);
+extern int i2c_master_recv(struct i2c_client *client, char *buf, int count);
+
+
+#define I2C_ADDR_16_BIT (1 << 31)
+
+extern int i2c_read_reg(struct i2c_client *client, u32 addr, u8 *buf, u16 count);
+extern int i2c_write_reg(struct i2c_client *client, u32 addr, const u8 *buf, u16 count);
+
+#endif /* I2C_I2C_H */
diff --git a/include/i2c/mc13892.h b/include/i2c/mc13892.h
new file mode 100644
index 0000000000..2f44f6c853
--- /dev/null
+++ b/include/i2c/mc13892.h
@@ -0,0 +1,7 @@
+#ifndef __ASM_ARCH_MC13892_H
+#define __ASM_ARCH_MC13892_H
+
+extern struct i2c_client *mc13892_get_client(void);
+
+#endif /* __ASM_ARCH_MC13892_H */
+
diff --git a/include/i2c/mc9sdz60.h b/include/i2c/mc9sdz60.h
new file mode 100644
index 0000000000..04cfca04ab
--- /dev/null
+++ b/include/i2c/mc9sdz60.h
@@ -0,0 +1,7 @@
+#ifndef __ASM_ARCH_MC9SDZ60_H
+#define __ASM_ARCH_MC9SDZ60_H
+
+extern struct i2c_client *mc9sdz60_get_client(void);
+
+#endif /* __ASM_ARCH_MC9SDZ60_H */
+