[U-Boot] [PATCH 4/7] tegra: Add I2C driver

Marek Vasut marek.vasut at gmail.com
Sun Jan 8 18:06:39 CET 2012


> Hi Marek,
> 
> On Mon, Dec 26, 2011 at 11:15 AM, Marek Vasut <marek.vasut at gmail.com> wrote:
> >> From: Yen Lin <yelin at nvidia.com>
> >> 
> >> Add basic i2c driver for Tegra2 with 8- and 16-bit address support.
> >> The driver supports building both with and without CONFIG_OF_CONTROL.
> >> 
> >> Without CONFIG_OF_CONTROL a number of CONFIG options must be supplied
> >> in the board config header file:
> >> 
> >> I2CSPEED_KHZ - speed to run I2C bus at (typically 100000)
> >> CONFIG_I2Cx_PIN_MUX - pin mux setting for each port (P, 1, 2, 3)
> >>       (typically this will be 0 to bring the port out the common
> >>               pins)
> >> 
> >> Signed-off-by: Simon Glass <sjg at chromium.org>
> >> ---
> >>  arch/arm/include/asm/arch-tegra2/tegra2.h     |    4 +
> >>  arch/arm/include/asm/arch-tegra2/tegra2_i2c.h |  167 ++++++++
> >>  drivers/i2c/Makefile                          |    1 +
> >>  drivers/i2c/tegra2_i2c.c                      |  533
> >> +++++++++++++++++++++++++ include/fdtdec.h                            
> >>  | 1 +
> >>  lib/fdtdec.c                                  |    1 +
> >>  6 files changed, 707 insertions(+), 0 deletions(-)
> >>  create mode 100644 arch/arm/include/asm/arch-tegra2/tegra2_i2c.h
> >>  create mode 100644 drivers/i2c/tegra2_i2c.c
> >> 
> >> diff --git a/arch/arm/include/asm/arch-tegra2/tegra2.h
> >> b/arch/arm/include/asm/arch-tegra2/tegra2.h index ca1881e..d1a70da
> >> 100644 --- a/arch/arm/include/asm/arch-tegra2/tegra2.h
> >> +++ b/arch/arm/include/asm/arch-tegra2/tegra2.h
> >> @@ -40,6 +40,10 @@
> >>  #define NV_PA_APB_UARTE_BASE (NV_PA_APB_MISC_BASE + 0x6400)
> >>  #define TEGRA2_SPI_BASE              (NV_PA_APB_MISC_BASE + 0xC380)
> >>  #define TEGRA2_PMC_BASE              (NV_PA_APB_MISC_BASE + 0xE400)
> >> +#define TEGRA2_I2C1_BASE     (NV_PA_APB_MISC_BASE + 0xC000)
> >> +#define TEGRA2_I2C2_BASE     (NV_PA_APB_MISC_BASE + 0xC400)
> >> +#define TEGRA2_I2C3_BASE     (NV_PA_APB_MISC_BASE + 0xC500)
> >> +#define TEGRA2_DVC_BASE              (NV_PA_APB_MISC_BASE + 0xD000)
> >>  #define NV_PA_CSITE_BASE     0x70040000
> >>  #define TEGRA_USB1_BASE              0xC5000000
> >>  #define TEGRA_USB3_BASE              0xC5008000
> >> diff --git a/arch/arm/include/asm/arch-tegra2/tegra2_i2c.h
> >> b/arch/arm/include/asm/arch-tegra2/tegra2_i2c.h new file mode 100644
> >> index 0000000..4920d47
> >> --- /dev/null
> >> +++ b/arch/arm/include/asm/arch-tegra2/tegra2_i2c.h
> >> @@ -0,0 +1,167 @@
> >> +/*
> >> + * NVIDIA Tegra2 I2C controller
> >> + *
> >> + * Copyright 2010-2011 NVIDIA Corporation
> >> + *
> >> + * This software may be used and distributed according to the
> >> + * terms of the GNU Public License, Version 2, incorporated
> >> + * herein by reference.
> >> + *
> >> + * This program is free software; you can redistribute it and/or
> >> + * modify it under the terms of the GNU General Public License
> >> + * Version 2 as published by the Free Software Foundation.
> >> + *
> >> + * 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
> >> + */
> >> +
> >> +#ifndef _TEGRA2_I2C_H_
> >> +#define _TEGRA2_I2C_H_
> >> +
> >> +#include <asm/types.h>
> >> +
> >> +/* Convert the number of bytes to word. */
> >> +#define BYTES_TO_WORDS(size)         (((size) + 3) >> 2)
> > 
> > Isn't this stuff in include/common.h ? div_round_up() or something ?
> > 
> >> +
> >> +/* Convert i2c slave address to be put on bus  */
> >> +#define I2C_ADDR_ON_BUS(chip)                (chip << 1)
> >> +
> >> +#ifndef CONFIG_OF_CONTROL
> >> +enum {
> >> +     I2CSPEED_KHZ = 100,             /* in KHz */
> >> +};
> >> +#endif
> >> +
> >> +enum {
> >> +     I2C_TIMEOUT_USEC = 10000,       /* Wait time for completion */
> >> +     I2C_FIFO_DEPTH = 8,             /* I2C fifo depth */
> >> +};
> >> +
> >> +enum i2c_transaction_flags {
> >> +     I2C_IS_WRITE = 0x1,             /* for I2C write operation */
> >> +     I2C_IS_10_BIT_ADDRESS = 0x2,    /* for 10-bit I2C slave address */
> >> +     I2C_USE_REPEATED_START = 0x4,   /* for repeat start */
> >> +     I2C_NO_ACK = 0x8,               /* for slave that won't generate
> >> ACK */ +     I2C_SOFTWARE_CONTROLLER = 0x10, /* for I2C transfer using
> >> GPIO */ +     I2C_NO_STOP = 0x20,
> >> +};
> >> +
> >> +/* Contians the I2C transaction details */
> >> +struct i2c_trans_info {
> >> +     /* flags to indicate the transaction details */
> >> +     enum i2c_transaction_flags flags;
> >> +     u32 address;    /* I2C slave device address */
> >> +     u32 num_bytes;  /* number of bytes to be transferred */
> >> +     /* Send/receive buffer. For I2C send operation this buffer should
> >> be
> > 
> > Wrong comment, do you run this stuff through checkpatch ?
> 
> Fixed style and adjusted comment. Yes I do, but checkpatch isn't perfect.
> 
> >> +      * filled with the data to be sent to the slave device. For I2C
> >> receive +      * operation this buffer is filled with the data received
> >> from the +      * slave device. */
> >> +     u8 *buf;
> >> +     int is_10bit_address;
> >> +};
> >> +
> >> +struct i2c_control {
> >> +     u32 tx_fifo;
> >> +     u32 rx_fifo;
> >> +     u32 packet_status;
> >> +     u32 fifo_control;
> >> +     u32 fifo_status;
> >> +     u32 int_mask;
> >> +     u32 int_status;
> >> +};
> >> +
> >> +struct dvc_ctlr {
> >> +     u32 ctrl1;                      /* 00: DVC_CTRL_REG1 */
> >> +     u32 ctrl2;                      /* 04: DVC_CTRL_REG2 */
> >> +     u32 ctrl3;                      /* 08: DVC_CTRL_REG3 */
> >> +     u32 status;                     /* 0C: DVC_STATUS_REG */
> >> +     u32 ctrl;                       /* 10: DVC_I2C_CTRL_REG */
> >> +     u32 addr_data;                  /* 14: DVC_I2C_ADDR_DATA_REG */
> >> +     u32 reserved_0[2];              /* 18: */
> >> +     u32 req;                        /* 20: DVC_REQ_REGISTER */
> >> +     u32 addr_data3;                 /* 24: DVC_I2C_ADDR_DATA_REG_3 */
> >> +     u32 reserved_1[6];              /* 28: */
> >> +     u32 cnfg;                       /* 40: DVC_I2C_CNFG */
> >> +     u32 cmd_addr0;                  /* 44: DVC_I2C_CMD_ADDR0 */
> >> +     u32 cmd_addr1;                  /* 48: DVC_I2C_CMD_ADDR1 */
> >> +     u32 cmd_data1;                  /* 4C: DVC_I2C_CMD_DATA1 */
> >> +     u32 cmd_data2;                  /* 50: DVC_I2C_CMD_DATA2 */
> >> +     u32 reserved_2[2];              /* 54: */
> >> +     u32 i2c_status;                 /* 5C: DVC_I2C_STATUS */
> >> +     struct i2c_control control;     /* 60 ~ 78 */
> >> +};
> >> +
> >> +struct i2c_ctlr {
> >> +     u32 cnfg;                       /* 00: I2C_I2C_CNFG */
> >> +     u32 cmd_addr0;                  /* 04: I2C_I2C_CMD_ADDR0 */
> >> +     u32 cmd_addr1;                  /* 08: I2C_I2C_CMD_DATA1 */
> >> +     u32 cmd_data1;                  /* 0C: I2C_I2C_CMD_DATA2 */
> >> +     u32 cmd_data2;                  /* 10: DVC_I2C_CMD_DATA2 */
> >> +     u32 reserved_0[2];              /* 14: */
> >> +     u32 status;                     /* 1C: I2C_I2C_STATUS */
> >> +     u32 sl_cnfg;                    /* 20: I2C_I2C_SL_CNFG */
> >> +     u32 sl_rcvd;                    /* 24: I2C_I2C_SL_RCVD */
> >> +     u32 sl_status;                  /* 28: I2C_I2C_SL_STATUS */
> >> +     u32 sl_addr1;                   /* 2C: I2C_I2C_SL_ADDR1 */
> >> +     u32 sl_addr2;                   /* 30: I2C_I2C_SL_ADDR2 */
> >> +     u32 reserved_1[2];              /* 34: */
> >> +     u32 sl_delay_count;             /* 3C: I2C_I2C_SL_DELAY_COUNT */
> >> +     u32 reserved_2[4];              /* 40: */
> >> +     struct i2c_control control;     /* 50 ~ 68 */
> >> +};
> >> +
> >> +/* bit fields definitions for IO Packet Header 1 format */
> >> +#define PKT_HDR1_PROTOCOL_SHIFT              4
> >> +#define PKT_HDR1_PROTOCOL_MASK               (0xf <<
> >> PKT_HDR1_PROTOCOL_SHIFT) +#define PKT_HDR1_CTLR_ID_SHIFT              
> >> 12
> >> +#define PKT_HDR1_CTLR_ID_MASK                (0xf <<
> >> PKT_HDR1_CTLR_ID_SHIFT) +#define PKT_HDR1_PKT_ID_SHIFT              
> >>  16
> >> +#define PKT_HDR1_PKT_ID_MASK         (0xff << PKT_HDR1_PKT_ID_SHIFT)
> >> +#define PROTOCOL_TYPE_I2C            1
> >> +
> >> +/* bit fields definitions for IO Packet Header 2 format */
> >> +#define PKT_HDR2_PAYLOAD_SIZE_SHIFT  0
> >> +#define PKT_HDR2_PAYLOAD_SIZE_MASK   (0xfff <<
> >> PKT_HDR2_PAYLOAD_SIZE_SHIFT) +
> >> +/* bit fields definitions for IO Packet Header 3 format */
> >> +#define PKT_HDR3_READ_MODE_SHIFT     19
> >> +#define PKT_HDR3_READ_MODE_MASK              (1 <<
> >> PKT_HDR3_READ_MODE_SHIFT) +#define PKT_HDR3_SLAVE_ADDR_SHIFT    0
> >> +#define PKT_HDR3_SLAVE_ADDR_MASK     (0x3ff <<
> >> PKT_HDR3_SLAVE_ADDR_SHIFT) +
> >> +#define DVC_CTRL_REG3_I2C_HW_SW_PROG_SHIFT   26
> >> +#define DVC_CTRL_REG3_I2C_HW_SW_PROG_MASK    \
> >> +                             (1 << DVC_CTRL_REG3_I2C_HW_SW_PROG_SHIFT)
> >> +
> >> +/* I2C_CNFG */
> >> +#define I2C_CNFG_NEW_MASTER_FSM_SHIFT        11
> >> +#define I2C_CNFG_NEW_MASTER_FSM_MASK (1 <<
> >> I2C_CNFG_NEW_MASTER_FSM_SHIFT) +#define I2C_CNFG_PACKET_MODE_SHIFT   10
> >> +#define I2C_CNFG_PACKET_MODE_MASK    (1 << I2C_CNFG_PACKET_MODE_SHIFT)
> >> +
> >> +/* I2C_SL_CNFG */
> >> +#define I2C_SL_CNFG_NEWSL_SHIFT              2
> >> +#define I2C_SL_CNFG_NEWSL_MASK               (1 <<
> >> I2C_SL_CNFG_NEWSL_SHIFT) +
> >> +/* I2C_FIFO_STATUS */
> >> +#define TX_FIFO_FULL_CNT_SHIFT               0
> >> +#define TX_FIFO_FULL_CNT_MASK                (0xf <<
> >> TX_FIFO_FULL_CNT_SHIFT) +#define TX_FIFO_EMPTY_CNT_SHIFT              4
> >> +#define TX_FIFO_EMPTY_CNT_MASK               (0xf <<
> >> TX_FIFO_EMPTY_CNT_SHIFT) +
> >> +/* I2C_INTERRUPT_STATUS */
> >> +#define I2C_INT_XFER_COMPLETE_SHIFT  7
> >> +#define I2C_INT_XFER_COMPLETE_MASK   (1 << I2C_INT_XFER_COMPLETE_SHIFT)
> >> +#define I2C_INT_NO_ACK_SHIFT         3
> >> +#define I2C_INT_NO_ACK_MASK          (1 << I2C_INT_NO_ACK_SHIFT)
> >> +#define I2C_INT_ARBITRATION_LOST_SHIFT       2
> >> +#define I2C_INT_ARBITRATION_LOST_MASK        (1 <<
> >> I2C_INT_ARBITRATION_LOST_SHIFT) +
> >> +#endif
> >> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> >> index 504db03..c123c72 100644
> >> --- a/drivers/i2c/Makefile
> >> +++ b/drivers/i2c/Makefile
> >> @@ -41,6 +41,7 @@ COBJS-$(CONFIG_DRIVER_S3C24X0_I2C) += s3c24x0_i2c.o
> >>  COBJS-$(CONFIG_S3C44B0_I2C) += s3c44b0_i2c.o
> >>  COBJS-$(CONFIG_SOFT_I2C) += soft_i2c.o
> >>  COBJS-$(CONFIG_SPEAR_I2C) += spr_i2c.o
> >> +COBJS-$(CONFIG_TEGRA2_I2C) += tegra2_i2c.o
> >>  COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
> >>  COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o
> >>  COBJS-$(CONFIG_SH_I2C) += sh_i2c.o
> >> diff --git a/drivers/i2c/tegra2_i2c.c b/drivers/i2c/tegra2_i2c.c
> >> new file mode 100644
> >> index 0000000..ae43531
> >> --- /dev/null
> >> +++ b/drivers/i2c/tegra2_i2c.c
> >> @@ -0,0 +1,533 @@
> >> +/*
> >> + * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
> >> + * Copyright (c) 2010-2011 NVIDIA Corporation
> >> + *  NVIDIA Corporation <www.nvidia.com>
> >> + *
> >> + * See file CREDITS for list of people who contributed to this
> >> + * project.
> >> + *
> >> + * 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 <asm/io.h>
> >> +#include <asm/arch/clk_rst.h>
> >> +#include <asm/arch/clock.h>
> >> +#include <asm/arch/funcmux.h>
> >> +#include <asm/arch/gpio.h>
> >> +#include <asm/arch/pinmux.h>
> >> +#include <asm/arch/tegra2_i2c.h>
> >> +#include <fdtdec.h>
> >> +
> >> +DECLARE_GLOBAL_DATA_PTR;
> >> +
> >> +static unsigned int i2c_bus_num;
> >> +
> >> +/* Information about i2c controller */
> >> +struct i2c_bus {
> >> +     int                     id;
> >> +     enum periph_id          periph_id;
> >> +     int                     speed;
> >> +     int                     pinmux_config;
> >> +     struct i2c_control      *control;
> >> +     struct i2c_ctlr         *regs;
> >> +};
> >> +
> >> +struct i2c_bus i2c_controllers[CONFIG_SYS_MAX_I2C_BUS];
> > 
> > static
> 
> done
> 
> >> +
> >> +static void set_packet_mode(struct i2c_bus *i2c_bus)
> >> +{
> >> +     u32 config;
> >> +
> >> +     config = I2C_CNFG_NEW_MASTER_FSM_MASK | I2C_CNFG_PACKET_MODE_MASK;
> >> +
> >> +     if (i2c_bus->periph_id == PERIPH_ID_DVC_I2C) {
> >> +             struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs;
> >> +
> >> +             writel(config, &dvc->cnfg);
> >> +     } else {
> >> +             writel(config, &i2c_bus->regs->cnfg);
> >> +             /*
> >> +              * program I2C_SL_CNFG.NEWSL to ENABLE. This fixes probe
> >> +              * issues, i.e., some slaves may be wrongly detected.
> >> +              */
> >> +             setbits_le32(&i2c_bus->regs->sl_cnfg,
> >> I2C_SL_CNFG_NEWSL_MASK); +     }
> >> +}
> >> +
> >> +static void i2c_reset_controller(struct i2c_bus *i2c_bus)
> >> +{
> >> +     /* Reset I2C controller. */
> >> +     reset_periph(i2c_bus->periph_id, 1);
> >> +
> >> +     /* re-program config register to packet mode */
> >> +     set_packet_mode(i2c_bus);
> >> +}
> >> +
> >> +static void i2c_init_controller(struct i2c_bus *i2c_bus)
> >> +{
> >> +     /* TODO: Fix bug which makes us need to do this */
> >> +     clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_OSC,
> >> +                            i2c_bus->speed * (8 * 2 - 1));
> >> +
> >> +     /* Reset I2C controller. */
> >> +     i2c_reset_controller(i2c_bus);
> >> +
> >> +     /* Configure I2C controller. */
> >> +     if (i2c_bus->periph_id == PERIPH_ID_DVC_I2C) {  /* only for DVC
> >> I2C */ +             struct dvc_ctlr *dvc = (struct dvc_ctlr
> >> *)i2c_bus->regs; +
> >> +             setbits_le32(&dvc->ctrl3,
> >> DVC_CTRL_REG3_I2C_HW_SW_PROG_MASK); +     }
> >> +
> >> +     funcmux_select(i2c_bus->periph_id, i2c_bus->pinmux_config);
> >> +}
> >> +
> >> +static void send_packet_headers(
> >> +     struct i2c_bus *i2c_bus,
> >> +     struct i2c_trans_info *trans,
> >> +     u32 packet_id)
> >> +{
> >> +     u32 data;
> >> +
> >> +     /* prepare header1: Header size = 0 Protocol = I2C, pktType = 0 */
> >> +     data = PROTOCOL_TYPE_I2C << PKT_HDR1_PROTOCOL_SHIFT;
> >> +     data |= packet_id << PKT_HDR1_PKT_ID_SHIFT;
> >> +     data |= i2c_bus->id << PKT_HDR1_CTLR_ID_SHIFT;
> >> +     writel(data, &i2c_bus->control->tx_fifo);
> >> +     debug("pkt header 1 sent (0x%x)\n", data);
> >> +
> >> +     /* prepare header2 */
> >> +     data = (trans->num_bytes - 1) << PKT_HDR2_PAYLOAD_SIZE_SHIFT;
> >> +     writel(data, &i2c_bus->control->tx_fifo);
> >> +     debug("pkt header 2 sent (0x%x)\n", data);
> >> +
> >> +     /* prepare IO specific header: configure the slave address */
> >> +     data = trans->address << PKT_HDR3_SLAVE_ADDR_SHIFT;
> >> +
> >> +     /* Enable Read if it is not a write transaction */
> >> +     if (!(trans->flags & I2C_IS_WRITE))
> >> +             data |= PKT_HDR3_READ_MODE_MASK;
> >> +
> >> +     /* Write I2C specific header */
> >> +     writel(data, &i2c_bus->control->tx_fifo);
> >> +     debug("pkt header 3 sent (0x%x)\n", data);
> >> +}
> >> +
> >> +static int wait_for_tx_fifo_empty(struct i2c_control *control)
> >> +{
> >> +     u32 count;
> >> +     int timeout_us = I2C_TIMEOUT_USEC;
> >> +
> >> +     while (timeout_us >= 0) {
> >> +             count = (readl(&control->fifo_status) &
> >> TX_FIFO_EMPTY_CNT_MASK) +                             >>
> >> TX_FIFO_EMPTY_CNT_SHIFT;
> >> +             if (count == I2C_FIFO_DEPTH)
> >> +                     return 1;
> >> +             udelay(10);
> >> +             timeout_us -= 10;
> >> +     };
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +static int wait_for_rx_fifo_notempty(struct i2c_control *control)
> >> +{
> >> +     u32 count;
> >> +     int timeout_us = I2C_TIMEOUT_USEC;
> >> +
> >> +     while (timeout_us >= 0) {
> >> +             count = (readl(&control->fifo_status) &
> >> TX_FIFO_FULL_CNT_MASK) +                             >>
> >> TX_FIFO_FULL_CNT_SHIFT;
> >> +             if (count)
> >> +                     return 1;
> >> +             udelay(10);
> >> +             timeout_us -= 10;
> >> +     };
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +static int wait_for_transfer_complete(struct i2c_control *control)
> >> +{
> >> +     int int_status;
> >> +     int timeout_us = I2C_TIMEOUT_USEC;
> >> +
> >> +     while (timeout_us >= 0) {
> >> +             int_status = readl(&control->int_status);
> >> +             if (int_status & I2C_INT_NO_ACK_MASK)
> >> +                     return -int_status;
> >> +             if (int_status & I2C_INT_ARBITRATION_LOST_MASK)
> >> +                     return -int_status;
> >> +             if (int_status & I2C_INT_XFER_COMPLETE_MASK)
> >> +                     return 0;
> >> +
> >> +             udelay(10);
> >> +             timeout_us -= 10;
> >> +     };
> >> +
> >> +     return -1;
> >> +}
> >> +
> >> +static int send_recv_packets(
> >> +     struct i2c_bus *i2c_bus,
> >> +     struct i2c_trans_info *trans)
> >> +{
> >> +     struct i2c_control *control = i2c_bus->control;
> >> +     u32 int_status;
> >> +     u32 words;
> >> +     u8 *dptr;
> >> +     u32 local;
> >> +     uchar last_bytes;
> >> +     int error = 0;
> >> +     int is_write = trans->flags & I2C_IS_WRITE;
> >> +
> >> +     /* clear status from previous transaction, XFER_COMPLETE, NOACK,
> >> etc. */ +     int_status = readl(&control->int_status);
> >> +     writel(int_status, &control->int_status);
> >> +
> >> +     send_packet_headers(i2c_bus, trans, 1);
> >> +
> >> +     words = BYTES_TO_WORDS(trans->num_bytes);
> >> +     last_bytes = trans->num_bytes & 3;
> >> +     dptr = trans->buf;
> >> +
> >> +     while (words) {
> >> +             if (is_write) {
> >> +                     /* deal with word alignment */
> >> +                     if ((unsigned)dptr & 3) {
> >> +                             memcpy(&local, dptr, sizeof(u32));
> >> +                             writel(local, &control->tx_fifo);
> >> +                             debug("pkt data sent (0x%x)\n", local);
> >> +                     } else {
> >> +                             writel(*(u32 *)dptr, &control->tx_fifo);
> >> +                             debug("pkt data sent (0x%x)\n", *(u32
> >> *)dptr); +                     }
> >> +                     if (!wait_for_tx_fifo_empty(control)) {
> >> +                             error = -1;
> >> +                             goto exit;
> >> +                     }
> >> +             } else {
> >> +                     if (!wait_for_rx_fifo_notempty(control)) {
> >> +                             error = -1;
> >> +                             goto exit;
> >> +                     }
> >> +                     /*
> >> +                      * for the last word, we read into our local
> >> buffer, +                      * in case that caller did not provide
> >> enough buffer. +                      */
> >> +                     local = readl(&control->rx_fifo);
> >> +                     if ((words == 1) && last_bytes)
> >> +                             memcpy(dptr, (char *)&local, last_bytes);
> >> +                     else if ((unsigned)dptr & 3)
> >> +                             memcpy(dptr, &local, sizeof(u32));
> >> +                     else
> >> +                             *(u32 *)dptr = local;
> >> +                     debug("pkt data received (0x%x)\n", local);
> >> +             }
> >> +             words--;
> >> +             dptr += sizeof(u32);
> >> +     }
> >> +
> >> +     if (wait_for_transfer_complete(control)) {
> >> +             error = -1;
> >> +             goto exit;
> >> +     }
> >> +     return 0;
> >> +exit:
> >> +     /* error, reset the controller. */
> >> +     i2c_reset_controller(i2c_bus);
> >> +
> >> +     return error;
> >> +}
> >> +
> >> +static int tegra2_i2c_write_data(u32 addr, u8 *data, u32 len)
> >> +{
> >> +     int error;
> >> +     struct i2c_trans_info trans_info;
> >> +
> >> +     trans_info.address = addr;
> >> +     trans_info.buf = data;
> >> +     trans_info.flags = I2C_IS_WRITE;
> >> +     trans_info.num_bytes = len;
> >> +     trans_info.is_10bit_address = 0;
> >> +
> >> +     error = send_recv_packets(&i2c_controllers[i2c_bus_num],
> >> &trans_info); +     if (error)
> >> +             debug("tegra2_i2c_write_data: Error (%d) !!!\n", error);
> >> +
> >> +     return error;
> >> +}
> >> +
> >> +static int tegra2_i2c_read_data(u32 addr, u8 *data, u32 len)
> >> +{
> >> +     int error;
> >> +     struct i2c_trans_info trans_info;
> >> +
> >> +     trans_info.address = addr | 1;
> >> +     trans_info.buf = data;
> >> +     trans_info.flags = 0;
> >> +     trans_info.num_bytes = len;
> >> +     trans_info.is_10bit_address = 0;
> >> +
> >> +     error = send_recv_packets(&i2c_controllers[i2c_bus_num],
> >> &trans_info); +     if (error)
> >> +             debug("tegra2_i2c_read_data: Error (%d) !!!\n", error);
> >> +
> >> +     return error;
> >> +}
> >> +
> >> +#ifndef CONFIG_OF_CONTROL
> >> +static const enum periph_id i2c_periph_ids[CONFIG_SYS_MAX_I2C_BUS] = {
> >> +     PERIPH_ID_DVC_I2C,
> >> +     PERIPH_ID_I2C1,
> >> +     PERIPH_ID_I2C2,
> >> +     PERIPH_ID_I2C3
> >> +};
> >> +
> >> +static const u32 *i2c_bus_base[CONFIG_SYS_MAX_I2C_BUS] = {
> >> +     (u32 *)TEGRA2_DVC_BASE,
> >> +     (u32 *)TEGRA2_I2C1_BASE,
> >> +     (u32 *)TEGRA2_I2C2_BASE,
> >> +     (u32 *)TEGRA2_I2C3_BASE
> >> +};
> >> +
> >> +/* pinmux_configs based on the pinmux configuration */
> >> +static const int pinmux_configs[CONFIG_SYS_MAX_I2C_BUS] = {
> >> +     CONFIG_I2CP_PIN_MUX,    /* for I2CP (DVC I2C) */
> >> +     CONFIG_I2C1_PIN_MUX,    /* for I2C1 */
> >> +     CONFIG_I2C2_PIN_MUX,    /* for I2C2 */
> >> +     CONFIG_I2C3_PIN_MUX     /* for I2C3 */
> >> +};
> >> +
> >> +static int i2c_get_config(int *index, struct i2c_bus *i2c_bus)
> >> +{
> >> +     int i = *index;
> >> +
> >> +     if (i >= CONFIG_SYS_MAX_I2C_BUS)
> >> +             return -1;
> >> +
> >> +     i2c_bus->periph_id = i2c_periph_ids[i];
> >> +     i2c_bus->pinmux_config = pinmux_configs[i];
> >> +     i2c_bus->regs = (struct i2c_ctlr *)i2c_bus_base[i];
> >> +     i2c_bus->speed = I2CSPEED_KHZ * 1000;
> >> +
> >> +     *index = i + 1;
> >> +
> >> +     return 0;
> >> +}
> >> +#else
> >> +static int i2c_get_config(int *index, struct i2c_bus *i2c_bus)
> >> +{
> >> +     const void *blob = gd->fdt_blob;
> >> +     int node;
> >> +
> >> +     node = fdtdec_next_alias(blob, "i2c", COMPAT_NVIDIA_TEGRA20_I2C,
> >> +                              index);
> >> +     if (node < 0)
> >> +             return -1;
> >> +
> >> +     i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node,
> >> "reg"); +     i2c_bus->pinmux_config = fdtdec_get_int(blob, node,
> >> "u-boot,pinmux", 0); +     i2c_bus->speed = fdtdec_get_int(blob, node,
> >> "speed", 0);
> >> +     i2c_bus->periph_id = fdtdec_get_int(blob, node,
> >> "u-boot,periph-id", -1); +
> >> +     if (i2c_bus->periph_id == -1)
> >> +             return -FDT_ERR_NOTFOUND;
> >> +
> >> +     return 0;
> >> +}
> >> +#endif
> >> +
> >> +int i2c_init_board(void)
> >> +{
> >> +     struct i2c_bus *i2c_bus;
> >> +     int index = 0;
> >> +     int i;
> >> +
> >> +     /* build the i2c_controllers[] for each controller */
> >> +     for (i = 0; i < CONFIG_SYS_MAX_I2C_BUS; ++i) {
> >> +             i2c_bus = &i2c_controllers[i];
> >> +             i2c_bus->id = i;
> >> +
> >> +             if (i2c_get_config(&index, i2c_bus)) {
> >> +                     printf("i2c_init_board: failed to find bus %d\n",
> >> i); +                     return -1;
> >> +             }
> >> +
> >> +             if (i2c_bus->periph_id == PERIPH_ID_DVC_I2C)
> >> +                     i2c_bus->control =
> >> +                             &((struct dvc_ctlr
> >> *)i2c_bus->regs)->control; +             else
> >> +                     i2c_bus->control = &i2c_bus->regs->control;
> >> +
> >> +             i2c_init_controller(i2c_bus);
> >> +     }
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +void i2c_init(int speed, int slaveaddr)
> >> +{
> >> +     debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr);
> >> +}
> >> +
> >> +/* i2c write version without the register address */
> >> +int i2c_write_data(uchar chip, uchar *buffer, int len)
> >> +{
> >> +     int rc;
> >> +
> >> +     debug("i2c_write_data: chip=0x%x, len=0x%x\n", chip, len);
> >> +     debug("write_data: ");
> >> +     /* use rc for counter */
> >> +     for (rc = 0; rc < len; ++rc)
> >> +             debug(" 0x%02x", buffer[rc]);
> >> +     debug("\n");
> >> +
> >> +     rc = tegra2_i2c_write_data(I2C_ADDR_ON_BUS(chip), buffer, len);
> >> +     if (rc)
> >> +             debug("i2c_write_data(): rc=%d\n", rc);
> >> +
> >> +     return rc;
> >> +}
> >> +
> >> +/* i2c read version without the register address */
> >> +int i2c_read_data(uchar chip, uchar *buffer, int len)
> >> +{
> >> +     int rc;
> >> +
> >> +     debug("inside i2c_read_data():\n");
> >> +     rc = tegra2_i2c_read_data(I2C_ADDR_ON_BUS(chip), buffer, len);
> >> +     if (rc) {
> >> +             debug("i2c_read_data(): rc=%d\n", rc);
> >> +             return rc;
> >> +     }
> >> +
> >> +     debug("i2c_read_data: ");
> >> +     /* reuse rc for counter*/
> >> +     for (rc = 0; rc < len; ++rc)
> >> +             debug(" 0x%02x", buffer[rc]);
> >> +     debug("\n");
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +/* Probe to see if a chip is present. */
> >> +int i2c_probe(uchar chip)
> >> +{
> >> +     int rc;
> >> +     uchar reg;
> >> +
> >> +     debug("i2c_probe: addr=0x%x\n", chip);
> >> +     reg = 0;
> >> +     rc = i2c_write_data(chip, &reg, 1);
> >> +     if (rc) {
> >> +             debug("Error probing 0x%x.\n", chip);
> >> +             return 1;
> >> +     }
> >> +     return 0;
> >> +}
> >> +
> >> +static int i2c_addr_ok(const uint addr, const int alen)
> >> +{
> >> +     if (alen < 0 || alen > sizeof(addr))
> >> +             return 0;
> >> +     if (alen != sizeof(addr)) {
> >> +             uint max_addr = (1 << (8 * alen)) - 1;
> >> +             if (addr > max_addr)
> >> +                     return 0;
> >> +     }
> >> +     return 1;
> >> +}
> >> +
> >> +/* Read bytes */
> >> +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
> >> +{
> >> +     uint offset;
> >> +     int i;
> >> +
> >> +     debug("i2c_read: chip=0x%x, addr=0x%x, len=0x%x\n",
> >> +                             chip, addr, len);
> >> +     if (!i2c_addr_ok(addr, alen)) {
> >> +             debug("i2c_read: Bad address %x.%d.\n", addr, alen);
> >> +             return 1;
> >> +     }
> >> +     for (offset = 0; offset < len; offset++) {
> >> +             if (alen) {
> >> +                     uchar data[alen];
> >> +                     for (i = 0; i < alen; i++) {
> >> +                             data[alen - i - 1] =
> >> +                                     (addr + offset) >> (8 * i);
> >> +                     }
> >> +                     if (i2c_write_data(chip, data, alen)) {
> >> +                             debug("i2c_read: error sending (0x%x)\n",
> >> +                                     addr);
> >> +                             return 1;
> >> +                     }
> >> +             }
> >> +             if (i2c_read_data(chip, buffer + offset, 1)) {
> >> +                     debug("i2c_read: error reading (0x%x)\n", addr);
> >> +                     return 1;
> >> +             }
> >> +     }
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +/* Write bytes */
> >> +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
> >> +{
> >> +     uint offset;
> >> +     int i;
> >> +
> >> +     debug("i2c_write: chip=0x%x, addr=0x%x, len=0x%x\n",
> >> +                             chip, addr, len);
> >> +     if (!i2c_addr_ok(addr, alen)) {
> >> +             debug("i2c_write: Bad address %x.%d.\n", addr, alen);
> >> +             return 1;
> >> +     }
> >> +     for (offset = 0; offset < len; offset++) {
> >> +             uchar data[alen + 1];
> >> +             for (i = 0; i < alen; i++)
> >> +                     data[alen - i - 1] = (addr + offset) >> (8 * i);
> >> +             data[alen] = buffer[offset];
> >> +             if (i2c_write_data(chip, data, alen + 1)) {
> >> +                     debug("i2c_write: error sending (0x%x)\n", addr);
> >> +                     return 1;
> >> +             }
> >> +     }
> >> +
> >> +     return 0;
> >> +}
> >> +
> >> +#if defined(CONFIG_I2C_MULTI_BUS)
> >> +/*
> >> + * Functions for multiple I2C bus handling
> >> + */
> >> +unsigned int i2c_get_bus_num(void)
> >> +{
> >> +     return i2c_bus_num;
> >> +}
> >> +
> >> +int i2c_set_bus_num(unsigned int bus)
> >> +{
> >> +     if (bus >= CONFIG_SYS_MAX_I2C_BUS)
> >> +             return -1;
> >> +     i2c_bus_num = bus;
> >> +
> >> +     return 0;
> >> +}
> >> +#endif
> > 
> > get/set_speed missing ?
> 
> This was done during init, so hoping we don't have to do it every time?

No, but you can use those functions to adjust the speed on the fly (there's i2c 
speed ... command in uboot)
> 
> >> diff --git a/include/fdtdec.h b/include/fdtdec.h
> >> index a8911b5..5547676 100644
> >> --- a/include/fdtdec.h
> >> +++ b/include/fdtdec.h
> >> @@ -58,6 +58,7 @@ struct fdt_memory {
> >>  enum fdt_compat_id {
> >>       COMPAT_UNKNOWN,
> >>       COMPAT_NVIDIA_TEGRA20_USB,      /* Tegra2 USB port */
> >> +     COMPAT_NVIDIA_TEGRA20_I2C,      /* Tegra2 i2c */
> >> 
> >>       COMPAT_COUNT,
> >>  };
> >> diff --git a/lib/fdtdec.c b/lib/fdtdec.c
> >> index 931b4ce..fb3d79d 100644
> >> --- a/lib/fdtdec.c
> >> +++ b/lib/fdtdec.c
> >> @@ -38,6 +38,7 @@ DECLARE_GLOBAL_DATA_PTR;
> >>  static const char * const compat_names[COMPAT_COUNT] = {
> >>       COMPAT(UNKNOWN, "<none>"),
> >>       COMPAT(NVIDIA_TEGRA20_USB, "nvidia,tegra20-ehci"),
> >> +     COMPAT(NVIDIA_TEGRA20_I2C, "nvidia,tegra20-i2c"),
> >>  };
> >> 
> >>  /**
> > 
> > M
> 
> Regards,
> Simon


More information about the U-Boot mailing list