diff options
-rw-r--r-- | arch/arm/cpu/armv7/tegra2/clock.c | 4 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-tegra2/clock.h | 9 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-tegra2/pinmux.h | 2 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-tegra2/tegra2.h | 4 | ||||
-rw-r--r-- | arch/arm/include/asm/arch-tegra2/tegra2_i2c.h | 166 | ||||
-rw-r--r-- | board/nvidia/common/board.c | 4 | ||||
-rw-r--r-- | drivers/i2c/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/tegra2_i2c.c | 547 |
8 files changed, 735 insertions, 2 deletions
diff --git a/arch/arm/cpu/armv7/tegra2/clock.c b/arch/arm/cpu/armv7/tegra2/clock.c index 1427585d825..0abd361af68 100644 --- a/arch/arm/cpu/armv7/tegra2/clock.c +++ b/arch/arm/cpu/armv7/tegra2/clock.c @@ -806,7 +806,9 @@ void clock_init(void) { pll_rate[CLOCK_ID_MEMORY] = get_clock_freq(CLOCK_ID_MEMORY); pll_rate[CLOCK_ID_PERIPH] = get_clock_freq(CLOCK_ID_PERIPH); - pll_rate[CLOCK_ID_OSC] = get_clock_freq(CLOCK_ID_PERIPH); + /* FIXME: I2C needs CLK_M for CLOCK_ID_OSC */ + /* pll_rate[CLOCK_ID_OSC] = get_clock_freq(CLOCK_ID_PERIPH); */ + pll_rate[CLOCK_ID_OSC] = osc_freq[clock_get_osc_freq()]; pll_rate[CLOCK_ID_SFROM32KHZ] = 32768; debug("PLLM = %d\n", pll_rate[CLOCK_ID_MEMORY]); debug("PLLP = %d\n", pll_rate[CLOCK_ID_PERIPH]); diff --git a/arch/arm/include/asm/arch-tegra2/clock.h b/arch/arm/include/asm/arch-tegra2/clock.h index 7b4152ba4e0..e2e44ed7ea3 100644 --- a/arch/arm/include/asm/arch-tegra2/clock.h +++ b/arch/arm/include/asm/arch-tegra2/clock.h @@ -224,7 +224,14 @@ unsigned long clock_start_pll(enum clock_id id, u32 divm, u32 divn, */ void clock_enable(enum periph_id clkid); -/** +/* + * Disable a clock + * + * @param id clock id + */ +void clock_disable(enum periph_id clkid); + +/* * Set whether a clock is enabled or disabled. * * @param id clock id diff --git a/arch/arm/include/asm/arch-tegra2/pinmux.h b/arch/arm/include/asm/arch-tegra2/pinmux.h index 84beec40e9b..c4be95cf08e 100644 --- a/arch/arm/include/asm/arch-tegra2/pinmux.h +++ b/arch/arm/include/asm/arch-tegra2/pinmux.h @@ -341,6 +341,8 @@ void pinmux_set_func(enum pmux_pingrp pin, enum pmux_func func); /* Set the complete configuration for a pin group */ void pinmux_config_pingroup(struct pingroup_config *config); +void pinmux_set_tristate(enum pmux_pingrp pin, int enable); + /** * Configuure a list of pin groups * diff --git a/arch/arm/include/asm/arch-tegra2/tegra2.h b/arch/arm/include/asm/arch-tegra2/tegra2.h index df9009c4362..eafcc50bdb4 100644 --- a/arch/arm/include/asm/arch-tegra2/tegra2.h +++ b/arch/arm/include/asm/arch-tegra2/tegra2.h @@ -38,6 +38,10 @@ #define NV_PA_APB_UARTC_BASE (NV_PA_APB_MISC_BASE + 0x6200) #define NV_PA_APB_UARTD_BASE (NV_PA_APB_MISC_BASE + 0x6300) #define NV_PA_APB_UARTE_BASE (NV_PA_APB_MISC_BASE + 0x6400) +#define TEGRA2_I2C1_BASE 0x7000C000 +#define TEGRA2_I2C2_BASE 0x7000C400 +#define TEGRA2_I2C3_BASE 0x7000C500 +#define TEGRA2_DVC_BASE 0x7000D000 #define NV_PA_PMC_BASE 0x7000E400 #define NV_PA_CSITE_BASE 0x70040000 #define NV_PA_USB1_BASE 0xC5000000 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 00000000000..bf42e948bde --- /dev/null +++ b/arch/arm/include/asm/arch-tegra2/tegra2_i2c.h @@ -0,0 +1,166 @@ +/* + * 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) + +/* Convert i2c slave address to be put on bus */ +#define I2C_ADDR_ON_BUS(chip) (chip << 1) + +enum { + I2CSPEED_KHZ = 100, /* in KHz */ + 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 + * 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_TYPE_RANGE 2 : 0 +#define PKT_HDR1_PROTOCOL_RANGE 7 : 4 +#define PKT_HDR1_CTLR_ID_RANGE 15 : 12 +#define PKT_HDR1_PKT_ID_RANGE 23 : 16 +#define PKT_HDR1_HDRSZ_RANGE 29 : 28 +#define PROTOCOL_TYPE_I2C 1 + +/* bit fields definitions for IO Packet Header 2 format */ +#define PKT_HDR2_PAYLOAD_SIZE_RANGE 11 : 0 + +/* bit fields definitions for IO Packet Header 3 format */ +#define PKT_HDR3_HS_MODE_RANGE 22 : 22 +#define PKT_HDR3_CONT_ON_NACK_RANGE 21 : 21 +#define PKT_HDR3_SEND_START_BYTE_RANGE 20 : 20 +#define PKT_HDR3_READ_MODE_RANGE 19 : 19 +#define PKT_HDR3_ADDR_MODE_RANGE 18 : 18 +#define PKT_HDR3_IE_RANGE 17 : 17 +#define PKT_HDR3_REPEAT_START_STOP_RANGE 16 : 16 +#define PKT_HDR3_HS_MASTER_ADDR_RANGE 14 : 12 +#define PKT_HDR3_SLAVE_ADDR_RANGE 9 : 0 + +#define DVC_CTRL_REG3_I2C_HW_SW_PROG_RANGE 26 : 26 + +/* pin_mux selections for I2Cs */ +#define I2CP_SEL_RANGE 9 : 8 +#define RM_SEL_RANGE 15 : 14 +#define PTA_SEL_RANGE 23 : 22 +#define DDC_SEL_RANGE 1 : 0 +#define DTF_SEL_RANGE 31 : 30 + +/* I2C_CNFG */ +#define I2C_CNFG_NEW_MASTER_FSM_RANGE 11 : 11 +#define I2C_CNFG_PACKET_MODE_RANGE 10 : 10 + +/* I2C_SL_CNFG */ +#define I2C_SL_CNFG_NEWSL_RANGE 2 : 2 + +/* I2C_FIFO_CONTROL */ +#define I2C_RX_FIFO_FLUSH_RANGE 0 : 0 +#define I2C_TX_FIFO_FLUSH_RANGE 1 : 1 + +/* I2C_FIFO_STATUS */ +#define RX_FIFO_FULL_CNT_RANGE 3 : 0 +#define TX_FIFO_EMPTY_CNT_RANGE 7 : 4 + +/* I2C_INTERRUPT_STATUS */ +#define I2C_INT_PACKET_XFER_COMPLETE_RANGE 7 : 7 +#define I2C_INT_NO_ACK_RANGE 3 : 3 +#define I2C_INT_ARBITRATION_LOST_RANGE 2 : 2 + +#endif diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c index 93dc39c84fd..b6960ae3d56 100644 --- a/board/nvidia/common/board.c +++ b/board/nvidia/common/board.c @@ -37,6 +37,7 @@ #include <asm/arch/pmc.h> #include <spi.h> #include <fdt_decode.h> +#include <i2c.h> #include "board.h" #ifdef CONFIG_TEGRA2_MMC @@ -209,6 +210,9 @@ int board_init(void) spi_init(); #endif power_det_init(); +#ifdef CONFIG_TEGRA2_I2C + i2c_init_board(); +#endif return 0; } diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index a48047a4f99..d3db8dc5e00 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -42,6 +42,7 @@ COBJS-$(CONFIG_SOFT_I2C) += soft_i2c.o COBJS-$(CONFIG_SPEAR_I2C) += spr_i2c.o COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o +COBJS-$(CONFIG_TEGRA2_I2C) += tegra2_i2c.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/i2c/tegra2_i2c.c b/drivers/i2c/tegra2_i2c.c new file mode 100644 index 00000000000..82c8d9aae3c --- /dev/null +++ b/drivers/i2c/tegra2_i2c.c @@ -0,0 +1,547 @@ +/* + * 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/bitfield.h> +#include <asm/arch/clk_rst.h> +#include <asm/arch/clock.h> +#include <asm/arch/gpio.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/tegra2_i2c.h> + +static unsigned int i2c_bus_num; + +struct i2c_bus { + int id; + enum periph_id periph_id; + int pinmux_config; + struct i2c_control *control; + struct i2c_ctlr *regs; +}; + +struct i2c_bus i2c_controllers[CONFIG_SYS_MAX_I2C_BUS]; + +static void i2c_pin_mux_select(struct i2c_bus *i2c_bus, int pinmux_config) +{ + switch (i2c_bus->periph_id) { + case PERIPH_ID_DVC_I2C: /* DVC I2C (I2CP) */ + /* there is only one selection, pinmux_config is ignored */ + pinmux_set_func(PINGRP_I2CP, PMUX_FUNC_I2C); + break; + case PERIPH_ID_I2C1: /* I2C1 */ + /* support pinmux_config of 1 for now, */ + pinmux_set_func(PINGRP_RM, PMUX_FUNC_I2C); + break; + case PERIPH_ID_I2C2: /* I2C2 */ + switch (pinmux_config) { + case 1: /* DDC pin group, select I2C2 */ + pinmux_set_func(PINGRP_DDC, PMUX_FUNC_I2C2); + /* PTA to HDMI */ + pinmux_set_func(PINGRP_PTA, PMUX_FUNC_HDMI); + break; + case 2: /* PTA pin group, select I2C2 */ + pinmux_set_func(PINGRP_PTA, PMUX_FUNC_I2C2); + /* set DDC_SEL to RSVDx (RSVD2 works for now) */ + pinmux_set_func(PINGRP_DDC, PMUX_FUNC_RSVD2); + break; + default: + printf("bad pinmux_config(%d): pinmux select ignored\n", + pinmux_config); + break; + } + break; + case PERIPH_ID_I2C3: /* I2C3 */ + /* support pinmux_config of 1 for now */ + pinmux_set_func(PINGRP_DTF, PMUX_FUNC_I2C3); + break; + default: + break; + } +} + +/** + * tristate : 0 - NORMAL + * 1 - TRISTATE + */ +static void i2c_pin_mux_tristate(struct i2c_bus *i2c_bus, int pinmux_config, + int tristate) +{ + enum pmux_pingrp pin; + + switch (i2c_bus->periph_id) { + case PERIPH_ID_DVC_I2C: /* DVC I2C (I2CP) */ + /* there is only one selection, pinmux_config is ignored */ + pin = PINGRP_I2CP; + break; + case PERIPH_ID_I2C1: /* I2C1 */ + /* support pinmux_config of 1 for now */ + pin = PINGRP_RM; + break; + case PERIPH_ID_I2C2: /* I2C2 */ + switch (pinmux_config) { + case 1: /* DDC pin group */ + pin = PINGRP_DDC; + break; + case 2: /* PTA pin group */ + pin = PINGRP_PTA; + break; + default: + printf("bad pinmux_config(%d): tristate ignored\n", + pinmux_config); + return; + } + break; + case PERIPH_ID_I2C3: /* I2C3 */ + /* support pinmux_config of 1 for now */ + pin = PINGRP_DTF; + break; + default: + printf("invalid controller(%d): tristate ignored\n", + i2c_bus->id); + return; + } + + pinmux_set_tristate(pin, tristate); +} + +static void set_packet_mode(struct i2c_bus *i2c_bus) +{ + u32 config; + + config = bf_pack(I2C_CNFG_NEW_MASTER_FSM, 1) | + bf_pack(I2C_CNFG_PACKET_MODE, 1); + + 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. + */ + bf_writel(I2C_SL_CNFG_NEWSL, 1, &i2c_bus->regs->sl_cnfg); + } +} + +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, u32 clock_khz) +{ + /* TODO: Fix bug which makes us need to do this */ + clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_OSC, + clock_khz * 1000 * (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; + + bf_writel(DVC_CTRL_REG3_I2C_HW_SW_PROG, 1, &dvc->ctrl3); + } + + i2c_pin_mux_select(i2c_bus, i2c_bus->pinmux_config); + i2c_pin_mux_tristate(i2c_bus, i2c_bus->pinmux_config, 0); +} + +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 = bf_pack(PKT_HDR1_PROTOCOL, PROTOCOL_TYPE_I2C) | + bf_pack(PKT_HDR1_PKT_ID, packet_id) | + bf_pack(PKT_HDR1_CTLR_ID, i2c_bus->id); + writel(data, &i2c_bus->control->tx_fifo); + debug("pkt header 1 sent (0x%x)\n", data); + + /* prepare header2 */ + data = bf_pack(PKT_HDR2_PAYLOAD_SIZE, (trans->num_bytes - 1)); + 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 = bf_pack(PKT_HDR3_SLAVE_ADDR, trans->address); + + /* Enable Read if it is not a write transaction */ + if (!(trans->flags & I2C_IS_WRITE)) + bf_update(PKT_HDR3_READ_MODE, data, 1); + + /* 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 = bf_readl(TX_FIFO_EMPTY_CNT, &control->fifo_status); + 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 = bf_readl(RX_FIFO_FULL_CNT, &control->fifo_status); + 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 (bf_unpack(I2C_INT_NO_ACK, int_status)) + return -int_status; + if (bf_unpack(I2C_INT_ARBITRATION_LOST, int_status)) + return -int_status; + if (bf_unpack(I2C_INT_PACKET_XFER_COMPLETE, int_status)) + 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; +} + +void i2c_init_board(void) +{ + struct i2c_bus *i2c_bus; + int i; + + enum periph_id i2c_periph_ids[CONFIG_SYS_MAX_I2C_BUS] = { + PERIPH_ID_DVC_I2C, + PERIPH_ID_I2C1, + PERIPH_ID_I2C2, + PERIPH_ID_I2C3 + }; + + 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 */ + 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 */ + }; + + /* 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; + 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]; + + 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, I2CSPEED_KHZ); + } +} + +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, ®, 1); + if (rc) { + debug("Error probing 0x%x.\n", chip); + return 1; + } + return 0; +} + +/* Read bytes */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int rc; + uchar *ptr = buffer; + + debug("i2c_read: chip=0x%x, addr=0x%x, len=0x%x\n", chip, addr, len); + + while (len) { + rc = i2c_write_data(chip, (uchar *)&addr, 1); + if (rc) { + debug("i2c_read: error sending (0x%x)\n", addr); + return 1; + } + + rc = i2c_read_data(chip, ptr, 1); + if (rc) { + debug("i2c_read: error reading (0x%x)\n", addr); + return 1; + } + ++addr; + ++ptr; + --len; + } + + return 0; +} + +/* Write bytes */ +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int rc; + uchar local_buffer[4]; + uchar *ptr = buffer; + + debug("i2c_write: chip=0x%x, addr=0x%x, len=0x%x\n", chip, addr, len); + + while (len) { + local_buffer[0] = addr & 0xFF; + local_buffer[1] = *ptr; + rc = i2c_write_data(chip, local_buffer, 2); + if (rc) { + debug("i2c_write: error sending (0x%x)\n", addr); + return 1; + } + ++addr; + ++ptr; + --len; + } + + 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 + |