[U-Boot-Users] [Patch 8/9] U-boot-V2:I2C:Busses: Add I2c OMAP Adapter
Menon, Nishanth
x0nishan at ti.com
Wed Jun 18 14:36:05 CEST 2008
Introduce the i2c adapter for OMAP i2c controller. This is mostly a port from
2.6.26 rc5 kernel at:
http://git.kernel.org/git/?p=linux/kernel/git/tmlind/linux-omap-2.6.git;a=summary
Signed-off-by: Nishanth Menon <x0nishan at ti.com>
---
drivers/i2c/busses/Kconfig | 7
drivers/i2c/busses/i2c-omap.c | 692 ++++++++++++++++++++++++++++++++++++++++
include/asm-arm/arch-omap/i2c.h | 158 +++++++++
3 files changed, 857 insertions(+)
Index: u-boot-v2.git/drivers/i2c/busses/i2c-omap.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ u-boot-v2.git/drivers/i2c/busses/i2c-omap.c 2008-06-17 17:01:06.000000000 -0500
@@ -0,0 +1,692 @@
+/**
+ * @file
+ * @brief OMAP I2C Adapter driver
+ *
+ * FileName: drivers/i2c/busses/i2c-omap.c
+ *
+ */
+/*
+ * (C) Copyright 2008
+ * Texas Instruments, <www.ti.com>
+ * Nishanth Menon <x0nishan at ti.com>
+ *
+ * based on Linux kernel omap i2c adapter driver
+ *
+ * Copyright (C) 2003 MontaVista Software, Inc.
+ * Copyright (C) 2005 Nokia Corporation
+ * Copyright (C) 2004 - 2007 Texas Instruments.
+ *
+ * Originally written by MontaVista Software, Inc.
+ * Additional contributions by:
+ * Tony Lindgren <tony at atomide.com>
+ * Imre Deak <imre.deak at nokia.com>
+ * Juha Yrjola <juha.yrjola at nokia.com>
+ * Syed Khasim <x0khasim at ti.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.
+ */
+
+#include <common.h>
+#include <config.h>
+#include <errno.h>
+#include <init.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <clock.h>
+#include <linux/i2c.h>
+#include <asm/io.h>
+#include <asm/arch/silicon.h>
+#include <asm/arch/i2c.h>
+
+/*---------------------- Debug Messages -------------------------------------*/
+#ifdef CONFIG_I2C_DEBUG_BUS
+#define DBG_MODULE_NAME "omap-i2c"
+#define dev_dbg(ARGS...) fprintf(stdout, ARGS);
+#define dev_warn(ARGS...) fprintf(stderr, ARGS);
+#define dbg_entry(FORMAT, ARGS...) fprintf(stdout,\
+ DBG_MODULE_NAME"%s:%d:Entry:"FORMAT"\n",\
+ __func__, __LINE__, ARGS)
+#define dbg_exit(FORMAT, ARGS...) fprintf(stdout,\
+ DBG_MODULE_NAME"%s:%d:Exit:"FORMAT"\n",\
+ __func__, __LINE__, ARGS)
+#else
+#define dev_dbg(ARGS...)
+#define dev_warn(ARGS...)
+#define dbg_entry(FORMAT, ARGS...)
+#define dbg_exit(FORMAT, ARGS...)
+#endif
+#define dev_err(ARGS...) fprintf(stderr, ARGS);
+
+/*---------------------- Defines --------------------------------------------*/
+/* Hack to enable zero length transfers and smbus quick until clean fix
+ is available */
+#define OMAP_HACK
+
+/*---------------------- Private Data Structures ----------------------------*/
+struct omap_i2c_dev {
+ unsigned long base; /* virtual */
+ u32 speed; /* Speed of bus in Khz */
+ u32 fclk; /* Functional clock speed */
+ u16 cmd_err;
+ u8 *buf;
+ size_t buf_len;
+ struct i2c_adapter adapter;
+ u8 fifo_size; /* use as flag and value
+ * fifo_size==0 implies no fifo
+ * if set, should be trsh+1
+ */
+ unsigned rev1:1;
+ unsigned b_hw:1; /* bad h/w fixes */
+};
+
+/*---------------------- Implementation -------------------------------------*/
+
+static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev,
+ int reg, u16 val)
+{
+ dbg_entry("i2c_dev=%x reg=%x val=%x", (u32) i2c_dev, reg, val);
+ __raw_writew(val, i2c_dev->base + reg);
+}
+
+static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg)
+{
+ dbg_entry("i2c_dev=%x reg=%x", (u32) i2c_dev, reg);
+ return __raw_readw(i2c_dev->base + reg);
+}
+
+static int omap_i2c_init(struct omap_i2c_dev *dev)
+{
+ u16 psc = 0, scll = 0, sclh = 0;
+ u16 fsscll = 0, fssclh = 0, hsscll = 0, hssclh = 0;
+ unsigned long fclk_rate = 12000000;
+ unsigned long internal_clk = 0;
+
+ dbg_entry("dev=%x", (u32) dev);
+ if (!dev->rev1) {
+ /* For some reason we need to set the EN bit before the
+ * reset done bit gets set. */
+ uint64_t timeout = OMAP_I2C_TIMEOUT;
+ uint64_t start;
+
+ start = get_time_ns();
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, OMAP_I2C_SYSC_SRST);
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
+ while (!(omap_i2c_read_reg(dev, OMAP_I2C_SYSS_REG) &
+ OMAP_I2C_SYSS_RDONE)) {
+ if (is_timeout(start, timeout)) {
+ dev_err("timeout waiting for bus ready\n");
+ return -ETIMEDOUT;
+ }
+ }
+ }
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
+
+ if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+
+ /* HSI2C controller internal clk rate should be 19.2 Mhz */
+ if (dev->speed > 400)
+ internal_clk = 19200;
+ else if (dev->speed > 100)
+ internal_clk = 9600;
+ else
+ internal_clk = 4000;
+ fclk_rate = dev->fclk / 1000;
+
+ /* Compute prescaler divisor */
+ psc = fclk_rate / internal_clk;
+ psc = psc - 1;
+
+ /* If configured for High Speed */
+ if (dev->speed > 400) {
+ /* For first phase of HS mode */
+ fsscll = internal_clk / (400 * 2) - 7;
+ fssclh = internal_clk / (400 * 2) - 5;
+
+ /* For second phase of HS mode */
+ hsscll = fclk_rate / (dev->speed * 2) - 7;
+ hssclh = fclk_rate / (dev->speed * 2) - 5;
+ } else {
+ /* To handle F/S modes */
+ fsscll = internal_clk / (dev->speed * 2) - 7;
+ fssclh = internal_clk / (dev->speed * 2) - 5;
+ }
+ scll = (hsscll << OMAP_I2C_SCLL_HSSCLL) | fsscll;
+ sclh = (hssclh << OMAP_I2C_SCLH_HSSCLH) | fssclh;
+ dev_dbg("base=%x,fclk=%d fclk_rate =%d, iclk=%d dev->speed=%d\n"
+ "psc=%x fsscll=%x fssclh=%x hsscll=%x hssclh=%x\n"
+ "scll=%x sclh=%x\n",
+ dev->base, dev->fclk, fclk_rate, internal_clk,
+ dev->speed, psc, fsscll, fssclh, hsscll, hssclh,
+ scll, sclh);
+ } else {
+ /* Program desired operating rate */
+ fclk_rate /= (psc + 1) * 1000;
+ if (psc > 2)
+ psc = 2;
+ scll = fclk_rate / (dev->speed * 2) - 7 + psc;
+ sclh = fclk_rate / (dev->speed * 2) - 7 + psc;
+ }
+
+ /* Setup clock prescaler to obtain approx 12MHz I2C module clock: */
+ omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, psc);
+
+ /* SCL low and high time values */
+ omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, scll);
+ omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, sclh);
+
+ if (dev->fifo_size)
+ /* Note: setup required fifo size - 1 */
+ omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG,
+ (dev->fifo_size - 1) << 8 | /* RTRSH */
+ OMAP_I2C_BUF_RXFIF_CLR |
+ (dev->fifo_size - 1) | /* XTRSH */
+ OMAP_I2C_BUF_TXFIF_CLR);
+
+ /* DO NOT Enable interrupts -we work in polling mode */
+ omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, 0);
+
+ /* Take the I2C module out of reset: */
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
+
+ return 0;
+}
+
+/*
+ * Waiting on Bus Busy
+ */
+static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev)
+{
+ uint64_t start, timeout;
+
+ dbg_entry("dev=%x", (u32) dev);
+
+ timeout = OMAP_I2C_TIMEOUT;
+ start = get_time_ns();
+
+ while (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) {
+ if (is_timeout(start, timeout)) {
+ dev_err("timeout waiting for bus ready\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int omap_i2c_rev1_wait_event(struct omap_i2c_dev *dev, uint64_t timeout)
+{
+ u16 iv, w;
+ int stat = 0;
+ uint64_t start;
+
+ start = get_time_ns();
+ dbg_entry("dev=%x", (u32) dev);
+ /* we dont have anything better to do..
+ * So poll the status reg
+ */
+ while (!stat) {
+ if (is_timeout(start, timeout)) {
+ dev_err("waitevent Timeout!");
+ stat = -ETIMEDOUT;
+ continue;
+ }
+ iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG);
+ switch (iv) {
+ case 0x01: /* Arbitration lost */
+ dev_err("Arbitration lost\n");
+ dev->cmd_err = OMAP_I2C_STAT_AL;
+ return 0;
+ break;
+ case 0x02: /* No acknowledgement */
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG,
+ OMAP_I2C_CON_STP);
+ dev->cmd_err = OMAP_I2C_STAT_NACK;
+ return 0;
+ break;
+ case 0x03: /* Register access ready */
+ dev->cmd_err = 0;
+ return 0;
+ case 0x04: /* Receive data ready */
+ if (dev->buf_len) {
+ w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
+ *dev->buf++ = w;
+ dev->buf_len--;
+ if (dev->buf_len) {
+ *dev->buf++ = w >> 8;
+ dev->buf_len--;
+ }
+ } else {
+ dev_err("RRDY IRQ while no data requested\n");
+ stat = -2;
+ }
+ break;
+ case 0x05: /* Transmit data ready */
+ if (dev->buf_len) {
+ w = *dev->buf++;
+ dev->buf_len--;
+ if (dev->buf_len) {
+ w |= *dev->buf++ << 8;
+ dev->buf_len--;
+ }
+ omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
+ } else {
+ dev_err("XRDY IRQ while no data to send\n");
+ stat = -1;
+ }
+ case 0x00: /* None */
+ /* default.. continue to wait */
+ break;
+ }
+ }
+
+ return stat;
+}
+
+static int omap_i2c_wait_event(struct omap_i2c_dev *dev, uint64_t timeout)
+{
+ u16 bits;
+ u16 stat, w;
+ int err;
+ uint64_t start;
+ dev_dbg("dev=%x timeout=%x", (u32) dev, timeout);
+
+ bits = OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ARDY |
+ OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR |
+ OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR;
+
+ start = get_time_ns();
+ /* We will exit out of this loop eventually.
+ * Trusting the hardware?
+ */
+ while (1) {
+ stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG)) & bits;
+
+ /* No Stat update? re-read */
+ if (!stat) {
+ if (is_timeout(start, timeout)) {
+ dev_err("waitevent Timeout!\n");
+ return -ETIMEDOUT;
+ }
+ continue;
+ }
+
+ /* Ack other signals, but NOT RDY signals -> we need to do
+ * that after transfer of data
+ */
+ omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat &
+ ~(OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR |
+ OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
+
+ err = 0;
+ if (stat & OMAP_I2C_STAT_NACK) {
+ dev_dbg("Nacked!\n", stat);
+ err |= OMAP_I2C_STAT_NACK;
+ }
+ if (stat & OMAP_I2C_STAT_AL) {
+ dev_err("Arbitration lost\n");
+ err |= OMAP_I2C_STAT_AL;
+ }
+ if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
+ OMAP_I2C_STAT_AL)) {
+ dev->cmd_err = err;
+ return 0;
+ }
+
+ if (stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)) {
+ u8 num_bytes = 1;
+ if (dev->fifo_size) {
+ num_bytes =
+ (stat & OMAP_I2C_STAT_RRDY) ? dev->
+ fifo_size : omap_i2c_read_reg(dev,
+ OMAP_I2C_BUFSTAT_REG);
+ }
+ while (num_bytes) {
+ num_bytes--;
+ w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
+ if (dev->buf_len) {
+ *dev->buf++ = w;
+ dev->buf_len--;
+ /* Data reg from 2430 is 8 bit wide */
+ if (!cpu_is_omap2430() &&
+ !cpu_is_omap34xx()) {
+ if (dev->buf_len) {
+ *dev->buf++ = w >> 8;
+ dev->buf_len--;
+ }
+ }
+ } else {
+ if (stat & OMAP_I2C_STAT_RRDY)
+ dev_err
+ ("RRDY IRQ while no data "
+ "requested\n");
+ if (stat & OMAP_I2C_STAT_RDR)
+ dev_err("RDR IRQ while no data "
+ "requested\n");
+ return -2;
+ }
+ }
+ omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat &
+ (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR));
+ }
+ if (stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)) {
+ u8 num_bytes = 1;
+ if (dev->fifo_size) {
+ num_bytes =
+ (stat & OMAP_I2C_STAT_XRDY) ? dev->
+ fifo_size : omap_i2c_read_reg(dev,
+ OMAP_I2C_BUFSTAT_REG);
+ }
+ while (num_bytes) {
+ num_bytes--;
+ w = 0;
+ if (dev->buf_len) {
+ w = *dev->buf++;
+ dev->buf_len--;
+ /* Data reg from 2430 is 8 bit wide */
+ if (!cpu_is_omap2430() &&
+ !cpu_is_omap34xx()) {
+ if (dev->buf_len) {
+ w |= *dev->buf++ << 8;
+ dev->buf_len--;
+ }
+ }
+ } else {
+ if (stat & OMAP_I2C_STAT_XRDY)
+ dev_err("XRDY IRQ while no "
+ "data to send\n");
+ if (stat & OMAP_I2C_STAT_XDR)
+ dev_err("XDR IRQ while no "
+ "data to send\n");
+ return -2;
+ }
+ omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
+ }
+ omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat &
+ (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
+ }
+ if (stat & OMAP_I2C_STAT_ROVR) {
+ dev_err("Receive overrun\n");
+ dev->cmd_err |= OMAP_I2C_STAT_ROVR;
+ }
+ if (stat & OMAP_I2C_STAT_XUDF) {
+ dev_err("Transmit overflow\n");
+ dev->cmd_err |= OMAP_I2C_STAT_XUDF;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Low level master read/write transaction.
+ */
+static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
+ struct i2c_msg *msg, int stop)
+{
+ struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+#ifdef OMAP_HACK
+ u8 zero_byte = 0;
+#endif
+ int r;
+ u16 w;
+
+ dbg_entry("adap=%x msg=%s stop=%x", (u32) adap, (u32) msg, stop);
+ dev_dbg("addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
+ msg->addr, msg->len, msg->flags, stop);
+
+#ifndef OMAP_HACK
+ if (msg->len == 0)
+ return -EINVAL;
+
+ omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr);
+
+ /* REVISIT: Could the STB bit of I2C_CON be used with probing? */
+ dev->buf = msg->buf;
+ dev->buf_len = msg->len;
+
+#else
+
+ omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr);
+ /* REVISIT: Remove this hack when we can get I2C chips from board-*.c
+ * files
+ * Sigh, seems we can't do zero length transactions. Thus, we
+ * can't probe for devices w/o actually sending/receiving at least
+ * a single byte. So we'll set count to 1 for the zero length
+ * transaction case and hope we don't cause grief for some
+ * arbitrary device due to random byte write/read during
+ * probes.
+ */
+ if (msg->len == 0) {
+ dev->buf = &zero_byte;
+ dev->buf_len = 1;
+ } else {
+ dev->buf = msg->buf;
+ dev->buf_len = msg->len;
+ }
+#endif
+
+ omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len);
+
+ /* Clear the FIFO Buffers */
+ w = omap_i2c_read_reg(dev, OMAP_I2C_BUF_REG);
+ w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR;
+ omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, w);
+
+ dev->cmd_err = 0;
+
+ w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT;
+
+ /* High speed configuration */
+ if (dev->speed > 400)
+ w |= OMAP_I2C_CON_OPMODE_HS;
+
+ if (msg->flags & I2C_M_TEN)
+ w |= OMAP_I2C_CON_XA;
+ if (!(msg->flags & I2C_M_RD))
+ w |= OMAP_I2C_CON_TRX;
+
+ if (!dev->b_hw && stop)
+ w |= OMAP_I2C_CON_STP;
+
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
+
+ if (dev->b_hw && stop) {
+ /* H/w behavior: dont write stt and stp together.. */
+ while (omap_i2c_read_reg(dev, OMAP_I2C_CON_REG) &
+ OMAP_I2C_CON_STT) {
+ /* Dont do anything - this will come in a
+ * couple of loops at max */
+ }
+ w |= OMAP_I2C_CON_STP;
+ w &= ~OMAP_I2C_CON_STT;
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
+ }
+ /* We dont get data in one millisec.. some thing is definitely
+ * and badly wrong!! */
+ if ((cpu_is_omap15xx()) && dev->rev1)
+ r = omap_i2c_rev1_wait_event(dev, MSECOND);
+ else
+ r = omap_i2c_wait_event(dev, MSECOND);
+ dev->buf_len = 0;
+ if (r < 0) {
+ dev_err("controller error %d\n", r);
+ omap_i2c_init(dev);
+ return r;
+ }
+
+ if (!dev->cmd_err)
+ return 0;
+
+ /* We have an error */
+ if (dev->cmd_err & (OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ROVR |
+ OMAP_I2C_STAT_XUDF)) {
+ /* Reprogram the controller ->NOTE 2 as in flow chart of TRM */
+ omap_i2c_init(dev);
+ return -EIO;
+ }
+
+ if (dev->cmd_err & OMAP_I2C_STAT_NACK) {
+ /* Reprogram the controller ->NOTE 2 as in flow chart of TRM */
+ omap_i2c_init(dev);
+ if (msg->flags & I2C_M_IGNORE_NAK)
+ return 0;
+ return -EREMOTEIO;
+ }
+ return -EIO;
+}
+
+/*
+ * Prepare controller for a transaction and call omap_i2c_xfer_msg
+ * to do the work during transfer processing
+ */
+static int
+omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+ int i;
+ int r;
+
+ dbg_entry("adap=%x msgs=%x num=%x", (u32) adap, (u32) msgs, num);
+ r = omap_i2c_wait_for_bb(dev);
+ if (r < 0) {
+ dev_err("timedout waiting for bus to free\n");
+ goto out;
+ }
+
+ for (i = 0; i < num; i++) {
+ r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
+ if (r != 0)
+ break;
+ }
+
+ if (r == 0)
+ r = num;
+out:
+ return r;
+}
+
+static u32 omap_i2c_func(struct i2c_adapter *adap)
+{
+ dbg_entry("adap=%x", (u32) adap);
+#ifndef OMAP_HACK
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+#else
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+#endif
+}
+
+static const struct i2c_algorithm omap_i2c_algo = {
+ .master_xfer = omap_i2c_xfer,
+ .functionality = omap_i2c_func,
+};
+
+static int omap_register_i2c_bus(int bus_id, unsigned long base,
+ u32 clkrate, u32 func_clk,
+ struct i2c_board_info const *info, unsigned int len)
+{
+ struct omap_i2c_dev *dev;
+ struct i2c_adapter *adap;
+ int r = 0;
+ dbg_entry("bus_id=%x base=%x clkrate=%x func_clk=%x info=%x len=%x",
+ bus_id, base, clkrate, func_clk, (u32) info, len);
+ if (info) {
+ r = i2c_register_board_info(bus_id, info, len);
+ if (r) {
+ dev_err("unable to register board info %d", r);
+ return r;
+ }
+ }
+ dev = calloc(sizeof(struct omap_i2c_dev), 1);
+ if (!dev) {
+ dev_err("malloc failed for omap_i2c_dev");
+ return -ENOMEM;
+ }
+
+ if (!clkrate)
+ clkrate = 100;
+
+ dev->speed = clkrate;
+ dev->fclk = func_clk;
+ dev->base = base;
+
+ if (cpu_is_omap15xx())
+ dev->rev1 = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) < 0x20;
+
+ if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+ /* Set up the fifo size - Get total size */
+ dev->fifo_size = 0x8 <<
+ ((omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG) >> 14) &
+ 0x3);
+ /*
+ * Set up notification threshold as half the total available
+ * size. This is to ensure that we can handle the status on
+ * int call back latencies
+ */
+ dev->fifo_size = (dev->fifo_size / 2);
+ dev->b_hw = 1; /* Enable hardware fixes */
+ }
+
+ /* reset ASAP, clearing any IRQs */
+ omap_i2c_init(dev);
+
+ r = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff;
+ dev_dbg("bus %d rev%d.%d at %d kHz\n",
+ bus_id, r >> 4, r & 0xf, dev->speed);
+
+ adap = &dev->adapter;
+ i2c_set_adapdata(adap, dev);
+ strncpy(adap->name, "OMAP I2C adapter", sizeof(adap->name));
+ adap->algo = &omap_i2c_algo;
+ adap->dev.parent = NULL;
+
+ /* i2c device drivers may be active on return from add_adapter() */
+ adap->nr = bus_id;
+ r = i2c_add_numbered_adapter(adap);
+ if (r) {
+ dev_err("failure adding adapter %d\n", r);
+ free(dev);
+ }
+ return r;
+}
+
+static int omap_i2c_probe(struct device_d *dev)
+{
+ struct i2c_omap_platform_data *i2cp =
+ (struct i2c_omap_platform_data *) (dev->platform_data);
+ if (i2cp == NULL) {
+ dev_err("I2c_OMAP: need platform data\n");
+ return -EINVAL;
+ }
+ return omap_register_i2c_bus(i2cp->bus_id,
+ dev->map_base,
+ i2cp->i2c_clock_speed,
+ i2cp->func_clk,
+ i2cp->info,
+ i2cp->len);
+}
+
+static struct driver_d omapi2c_drv = {
+ .name = OMAP_I2C_ADAPTER_NAME,
+ .probe = omap_i2c_probe,
+ .type = DEVICE_TYPE_I2C,
+};
+
+static int omap_i2cdrv_init(void)
+{
+ int ret;
+ ret = register_driver(&omapi2c_drv);
+ return ret;
+}
+device_initcall(omap_i2cdrv_init);
Index: u-boot-v2.git/drivers/i2c/busses/Kconfig
===================================================================
--- u-boot-v2.git.orig/drivers/i2c/busses/Kconfig 2008-06-17 17:01:06.000000000 -0500
+++ u-boot-v2.git/drivers/i2c/busses/Kconfig 2008-06-17 17:01:06.000000000 -0500
@@ -4,4 +4,11 @@
menu "I2C Hardware Bus support"
+config I2C_OMAP
+ bool "OMAP I2C adapter"
+ depends on ARCH_OMAP
+ help
+ If you say yes to this option, support will be included for the
+ I2C interface on the Texas Instruments OMAP family of processors.
+ For details see http://www.ti.com/omap.
endmenu
Index: u-boot-v2.git/include/asm-arm/arch-omap/i2c.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ u-boot-v2.git/include/asm-arm/arch-omap/i2c.h 2008-06-17 17:01:06.000000000 -0500
@@ -0,0 +1,158 @@
+/**
+ * @file
+ * @brief definitions for OMAP I2C Adapter driver
+ *
+ * FileName: include/asm-arm/arch-omap/i2c.h
+ *
+ */
+/*
+ * (C) Copyright 2008
+ * Texas Instruments, <www.ti.com>
+ * Nishanth Menon <x0nishan at ti.com>
+ *
+ * based on Linux kernel omap i2c adapter driver.
+ *
+ * Copyright (C) 2003 MontaVista Software, Inc.
+ * Copyright (C) 2005 Nokia Corporation
+ * Copyright (C) 2004 - 2007 Texas Instruments.
+ *
+ * Originally written by MontaVista Software, Inc.
+ * Additional contributions by:
+ * Tony Lindgren <tony at atomide.com>
+ * Imre Deak <imre.deak at nokia.com>
+ * Juha Yrjola <juha.yrjola at nokia.com>
+ * Syed Khasim <x0khasim at ti.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.
+ */
+
+#ifndef __ASM_ARCH_OMAP_I2C_H
+#define __ASM_ARCH_OMAP_I2C_H
+
+/*********** Stuff Interesting to board files ***********/
+
+#define OMAP_I2C_FCLK_96MHZ (96 * 1000 * 1000)
+#define OMAP_I2C_FCLK_48MHZ (48 * 1000 * 1000)
+
+/* Some Common i2c speeds */
+#define OMAP_I2C_CLK_2P6M (2600)
+#define OMAP_I2C_CLK_400K (400)
+#define OMAP_I2C_CLK_100K (100)
+
+/** I2c platform data that is registered by board files */
+struct i2c_omap_platform_data {
+ /** Give the bus index here */
+ int bus_id;
+ /** required i2c clock speed in khz */
+ u32 i2c_clock_speed;
+ /** Func clk speed of i2c -based on clock arch */
+ u32 func_clk;
+ /** If you use i2c_board_info else NULL */
+ struct i2c_board_info const *info;
+ /** num items of i2c_board_info else 0 */
+ unsigned int len;
+};
+
+/** Use this when you define an adapter instance in board file */
+#define OMAP_I2C_ADAPTER_NAME "omap-i2c"
+
+/*********** Stuff Interesting to OMAP Adapter ***********/
+
+/* timeout waiting for the controller to respond */
+#define OMAP_I2C_TIMEOUT MSECOND
+
+/* I2C Register defines */
+#define OMAP_I2C_REV_REG 0x00
+#define OMAP_I2C_IE_REG 0x04
+#define OMAP_I2C_STAT_REG 0x08
+#define OMAP_I2C_IV_REG 0x0c
+#define OMAP_I2C_SYSS_REG 0x10
+#define OMAP_I2C_BUF_REG 0x14
+#define OMAP_I2C_CNT_REG 0x18
+#define OMAP_I2C_DATA_REG 0x1c
+#define OMAP_I2C_SYSC_REG 0x20
+#define OMAP_I2C_CON_REG 0x24
+#define OMAP_I2C_OA_REG 0x28
+#define OMAP_I2C_SA_REG 0x2c
+#define OMAP_I2C_PSC_REG 0x30
+#define OMAP_I2C_SCLL_REG 0x34
+#define OMAP_I2C_SCLH_REG 0x38
+#define OMAP_I2C_SYSTEST_REG 0x3c
+#define OMAP_I2C_BUFSTAT_REG 0x40
+
+/* I2C Interrupt Enable Register (OMAP_I2C_IE): */
+#define OMAP_I2C_IE_XDR (1 << 14) /* TX Buffer drn int enable */
+#define OMAP_I2C_IE_RDR (1 << 13) /* RX Buffer drn int enable */
+#define OMAP_I2C_IE_XRDY (1 << 4) /* TX data ready int enable */
+#define OMAP_I2C_IE_RRDY (1 << 3) /* RX data ready int enable */
+#define OMAP_I2C_IE_ARDY (1 << 2) /* Access ready int enable */
+#define OMAP_I2C_IE_NACK (1 << 1) /* No ack interrupt enable */
+#define OMAP_I2C_IE_AL (1 << 0) /* Arbitration lost int ena */
+
+/* I2C Status Register (OMAP_I2C_STAT): */
+#define OMAP_I2C_STAT_XDR (1 << 14) /* TX Buffer draining */
+#define OMAP_I2C_STAT_RDR (1 << 13) /* RX Buffer draining */
+#define OMAP_I2C_STAT_BB (1 << 12) /* Bus busy */
+#define OMAP_I2C_STAT_ROVR (1 << 11) /* Receive overrun */
+#define OMAP_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */
+#define OMAP_I2C_STAT_AAS (1 << 9) /* Address as slave */
+#define OMAP_I2C_STAT_AD0 (1 << 8) /* Address zero */
+#define OMAP_I2C_STAT_XRDY (1 << 4) /* Transmit data ready */
+#define OMAP_I2C_STAT_RRDY (1 << 3) /* Receive data ready */
+#define OMAP_I2C_STAT_ARDY (1 << 2) /* Register access ready */
+#define OMAP_I2C_STAT_NACK (1 << 1) /* No ack interrupt enable */
+#define OMAP_I2C_STAT_AL (1 << 0) /* Arbitration lost int ena */
+
+/* I2C Buffer Configuration Register (OMAP_I2C_BUF): */
+#define OMAP_I2C_BUF_RDMA_EN (1 << 15) /* RX DMA channel enable */
+#define OMAP_I2C_BUF_RXFIF_CLR (1 << 14) /* RX FIFO Clear */
+#define OMAP_I2C_BUF_XDMA_EN (1 << 7) /* TX DMA channel enable */
+#define OMAP_I2C_BUF_TXFIF_CLR (1 << 6) /* TX FIFO Clear */
+
+/* I2C Configuration Register (OMAP_I2C_CON): */
+#define OMAP_I2C_CON_EN (1 << 15) /* I2C module enable */
+#define OMAP_I2C_CON_BE (1 << 14) /* Big endian mode */
+#define OMAP_I2C_CON_OPMODE_HS (1 << 12) /* High Speed support */
+#define OMAP_I2C_CON_STB (1 << 11) /* Start byte mode (master) */
+#define OMAP_I2C_CON_MST (1 << 10) /* Master/slave mode */
+#define OMAP_I2C_CON_TRX (1 << 9) /* TX/RX mode (master only) */
+#define OMAP_I2C_CON_XA (1 << 8) /* Expand address */
+#define OMAP_I2C_CON_RM (1 << 2) /* Repeat mode (master only) */
+#define OMAP_I2C_CON_STP (1 << 1) /* Stop cond (master only) */
+#define OMAP_I2C_CON_STT (1 << 0) /* Start condition (master) */
+
+/* I2C SCL time value when Master */
+#define OMAP_I2C_SCLL_HSSCLL 8
+#define OMAP_I2C_SCLH_HSSCLH 8
+
+/* I2C System Test Register (OMAP_I2C_SYSTEST): */
+#ifdef DEBUG
+#define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */
+#define OMAP_I2C_SYSTEST_FREE (1 << 14) /* Free running mode */
+#define OMAP_I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */
+#define OMAP_I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */
+#define OMAP_I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense in */
+#define OMAP_I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive out */
+#define OMAP_I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense in */
+#define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */
+#endif
+
+/* I2C System Status register (OMAP_I2C_SYSS): */
+#define OMAP_I2C_SYSS_RDONE (1 << 0) /* Reset Done */
+
+/* I2C System Configuration Register (OMAP_I2C_SYSC): */
+#define OMAP_I2C_SYSC_SRST (1 << 1) /* Soft Reset */
+
+#endif /* __ASM_ARCH_OMAP_I2C_H */
More information about the U-Boot
mailing list