mirror of
https://github.com/Ysurac/openmptcprouter.git
synced 2025-03-09 15:40:20 +00:00
Fix for RUTX platform
This commit is contained in:
parent
ccdb64ad45
commit
59bc57d5d5
7254 changed files with 1810270 additions and 7 deletions
|
@ -0,0 +1,67 @@
|
|||
#
|
||||
# (C) Copyright 2000-2007
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
#
|
||||
# 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 $(TOPDIR)/config.mk
|
||||
|
||||
LIB := $(obj)libi2c.o
|
||||
|
||||
COBJS-$(CONFIG_BFIN_TWI_I2C) += bfin-twi_i2c.o
|
||||
COBJS-$(CONFIG_DRIVER_DAVINCI_I2C) += davinci_i2c.o
|
||||
COBJS-$(CONFIG_DW_I2C) += designware_i2c.o
|
||||
COBJS-$(CONFIG_FSL_I2C) += fsl_i2c.o
|
||||
COBJS-$(CONFIG_I2C_MVTWSI) += mvtwsi.o
|
||||
COBJS-$(CONFIG_I2C_MV) += mv_i2c.o
|
||||
COBJS-$(CONFIG_I2C_MXC) += mxc_i2c.o
|
||||
COBJS-$(CONFIG_I2C_MXS) += mxs_i2c.o
|
||||
COBJS-$(CONFIG_DRIVER_OMAP1510_I2C) += omap1510_i2c.o
|
||||
COBJS-$(CONFIG_DRIVER_OMAP24XX_I2C) += omap24xx_i2c.o
|
||||
COBJS-$(CONFIG_DRIVER_OMAP34XX_I2C) += omap24xx_i2c.o
|
||||
COBJS-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o
|
||||
COBJS-$(CONFIG_PPC4XX_I2C) += ppc4xx_i2c.o
|
||||
COBJS-$(CONFIG_DRIVER_S3C24X0_I2C) += s3c24x0_i2c.o
|
||||
COBJS-$(CONFIG_S3C44B0_I2C) += s3c44b0_i2c.o
|
||||
COBJS-$(CONFIG_SOFT_I2C) += soft_i2c.o
|
||||
COBJS-$(CONFIG_TEGRA_I2C) += tegra_i2c.o
|
||||
COBJS-$(CONFIG_TSI108_I2C) += tsi108_i2c.o
|
||||
COBJS-$(CONFIG_U8500_I2C) += u8500_i2c.o
|
||||
COBJS-$(CONFIG_SH_I2C) += sh_i2c.o
|
||||
COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o
|
||||
COBJS-$(CONFIG_IPQ806X_I2C) += ipq_i2c.o
|
||||
|
||||
COBJS := $(COBJS-y)
|
||||
SRCS := $(COBJS:.o=.c)
|
||||
OBJS := $(addprefix $(obj),$(COBJS))
|
||||
|
||||
all: $(LIB)
|
||||
|
||||
$(LIB): $(obj).depend $(OBJS)
|
||||
$(call cmd_link_o_target, $(OBJS))
|
||||
|
||||
#########################################################################
|
||||
|
||||
# defines $(obj).depend target
|
||||
include $(SRCTREE)/rules.mk
|
||||
|
||||
sinclude $(obj).depend
|
||||
|
||||
#########################################################################
|
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* i2c.c - driver for Blackfin on-chip TWI/I2C
|
||||
*
|
||||
* Copyright (c) 2006-2010 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
|
||||
#include <asm/blackfin.h>
|
||||
#include <asm/mach-common/bits/twi.h>
|
||||
|
||||
/* Every register is 32bit aligned, but only 16bits in size */
|
||||
#define ureg(name) u16 name; u16 __pad_##name;
|
||||
struct twi_regs {
|
||||
ureg(clkdiv);
|
||||
ureg(control);
|
||||
ureg(slave_ctl);
|
||||
ureg(slave_stat);
|
||||
ureg(slave_addr);
|
||||
ureg(master_ctl);
|
||||
ureg(master_stat);
|
||||
ureg(master_addr);
|
||||
ureg(int_stat);
|
||||
ureg(int_mask);
|
||||
ureg(fifo_ctl);
|
||||
ureg(fifo_stat);
|
||||
char __pad[0x50];
|
||||
ureg(xmt_data8);
|
||||
ureg(xmt_data16);
|
||||
ureg(rcv_data8);
|
||||
ureg(rcv_data16);
|
||||
};
|
||||
#undef ureg
|
||||
|
||||
/* U-Boot I2C framework allows only one active device at a time. */
|
||||
#ifdef TWI_CLKDIV
|
||||
#define TWI0_CLKDIV TWI_CLKDIV
|
||||
#endif
|
||||
static volatile struct twi_regs *twi = (void *)TWI0_CLKDIV;
|
||||
|
||||
#ifdef DEBUG
|
||||
# define dmemset(s, c, n) memset(s, c, n)
|
||||
#else
|
||||
# define dmemset(s, c, n)
|
||||
#endif
|
||||
#define debugi(fmt, args...) \
|
||||
debug( \
|
||||
"MSTAT:0x%03x FSTAT:0x%x ISTAT:0x%02x\t%-20s:%-3i: " fmt "\n", \
|
||||
twi->master_stat, twi->fifo_stat, twi->int_stat, \
|
||||
__func__, __LINE__, ## args)
|
||||
|
||||
#ifdef CONFIG_TWICLK_KHZ
|
||||
# error do not define CONFIG_TWICLK_KHZ ... use CONFIG_SYS_I2C_SPEED
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The way speed is changed into duty often results in integer truncation
|
||||
* with 50% duty, so we'll force rounding up to the next duty by adding 1
|
||||
* to the max. In practice this will get us a speed of something like
|
||||
* 385 KHz. The other limit is easy to handle as it is only 8 bits.
|
||||
*/
|
||||
#define I2C_SPEED_MAX 400000
|
||||
#define I2C_SPEED_TO_DUTY(speed) (5000000 / (speed))
|
||||
#define I2C_DUTY_MAX (I2C_SPEED_TO_DUTY(I2C_SPEED_MAX) + 1)
|
||||
#define I2C_DUTY_MIN 0xff /* 8 bit limited */
|
||||
#define SYS_I2C_DUTY I2C_SPEED_TO_DUTY(CONFIG_SYS_I2C_SPEED)
|
||||
/* Note: duty is inverse of speed, so the comparisons below are correct */
|
||||
#if SYS_I2C_DUTY < I2C_DUTY_MAX || SYS_I2C_DUTY > I2C_DUTY_MIN
|
||||
# error "The Blackfin I2C hardware can only operate 20KHz - 400KHz"
|
||||
#endif
|
||||
|
||||
/* All transfers are described by this data structure */
|
||||
struct i2c_msg {
|
||||
u8 flags;
|
||||
#define I2C_M_COMBO 0x4
|
||||
#define I2C_M_STOP 0x2
|
||||
#define I2C_M_READ 0x1
|
||||
int len; /* msg length */
|
||||
u8 *buf; /* pointer to msg data */
|
||||
int alen; /* addr length */
|
||||
u8 *abuf; /* addr buffer */
|
||||
};
|
||||
|
||||
/* Allow msec timeout per ~byte transfer */
|
||||
#define I2C_TIMEOUT 10
|
||||
|
||||
/**
|
||||
* wait_for_completion - manage the actual i2c transfer
|
||||
* @msg: the i2c msg
|
||||
*/
|
||||
static int wait_for_completion(struct i2c_msg *msg)
|
||||
{
|
||||
uint16_t int_stat;
|
||||
ulong timebase = get_timer(0);
|
||||
|
||||
do {
|
||||
int_stat = twi->int_stat;
|
||||
|
||||
if (int_stat & XMTSERV) {
|
||||
debugi("processing XMTSERV");
|
||||
twi->int_stat = XMTSERV;
|
||||
SSYNC();
|
||||
if (msg->alen) {
|
||||
twi->xmt_data8 = *(msg->abuf++);
|
||||
--msg->alen;
|
||||
} else if (!(msg->flags & I2C_M_COMBO) && msg->len) {
|
||||
twi->xmt_data8 = *(msg->buf++);
|
||||
--msg->len;
|
||||
} else {
|
||||
twi->master_ctl |= (msg->flags & I2C_M_COMBO) ? RSTART | MDIR : STOP;
|
||||
SSYNC();
|
||||
}
|
||||
}
|
||||
if (int_stat & RCVSERV) {
|
||||
debugi("processing RCVSERV");
|
||||
twi->int_stat = RCVSERV;
|
||||
SSYNC();
|
||||
if (msg->len) {
|
||||
*(msg->buf++) = twi->rcv_data8;
|
||||
--msg->len;
|
||||
} else if (msg->flags & I2C_M_STOP) {
|
||||
twi->master_ctl |= STOP;
|
||||
SSYNC();
|
||||
}
|
||||
}
|
||||
if (int_stat & MERR) {
|
||||
debugi("processing MERR");
|
||||
twi->int_stat = MERR;
|
||||
SSYNC();
|
||||
return msg->len;
|
||||
}
|
||||
if (int_stat & MCOMP) {
|
||||
debugi("processing MCOMP");
|
||||
twi->int_stat = MCOMP;
|
||||
SSYNC();
|
||||
if (msg->flags & I2C_M_COMBO && msg->len) {
|
||||
twi->master_ctl = (twi->master_ctl & ~RSTART) |
|
||||
(min(msg->len, 0xff) << 6) | MEN | MDIR;
|
||||
SSYNC();
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we were able to do something, reset timeout */
|
||||
if (int_stat)
|
||||
timebase = get_timer(0);
|
||||
|
||||
} while (get_timer(timebase) < I2C_TIMEOUT);
|
||||
|
||||
return msg->len;
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_transfer - setup an i2c transfer
|
||||
* @return: 0 if things worked, non-0 if things failed
|
||||
*
|
||||
* Here we just get the i2c stuff all prepped and ready, and then tail off
|
||||
* into wait_for_completion() for all the bits to go.
|
||||
*/
|
||||
static int i2c_transfer(uchar chip, uint addr, int alen, uchar *buffer, int len, u8 flags)
|
||||
{
|
||||
uchar addr_buffer[] = {
|
||||
(addr >> 0),
|
||||
(addr >> 8),
|
||||
(addr >> 16),
|
||||
};
|
||||
struct i2c_msg msg = {
|
||||
.flags = flags | (len >= 0xff ? I2C_M_STOP : 0),
|
||||
.buf = buffer,
|
||||
.len = len,
|
||||
.abuf = addr_buffer,
|
||||
.alen = alen,
|
||||
};
|
||||
int ret;
|
||||
|
||||
dmemset(buffer, 0xff, len);
|
||||
debugi("chip=0x%x addr=0x%02x alen=%i buf[0]=0x%02x len=%i flags=0x%02x[%s] ",
|
||||
chip, addr, alen, buffer[0], len, flags, (flags & I2C_M_READ ? "rd" : "wr"));
|
||||
|
||||
/* wait for things to settle */
|
||||
while (twi->master_stat & BUSBUSY)
|
||||
if (ctrlc())
|
||||
return 1;
|
||||
|
||||
/* Set Transmit device address */
|
||||
twi->master_addr = chip;
|
||||
|
||||
/* Clear the FIFO before starting things */
|
||||
twi->fifo_ctl = XMTFLUSH | RCVFLUSH;
|
||||
SSYNC();
|
||||
twi->fifo_ctl = 0;
|
||||
SSYNC();
|
||||
|
||||
/* prime the pump */
|
||||
if (msg.alen) {
|
||||
len = (msg.flags & I2C_M_COMBO) ? msg.alen : msg.alen + len;
|
||||
debugi("first byte=0x%02x", *msg.abuf);
|
||||
twi->xmt_data8 = *(msg.abuf++);
|
||||
--msg.alen;
|
||||
} else if (!(msg.flags & I2C_M_READ) && msg.len) {
|
||||
debugi("first byte=0x%02x", *msg.buf);
|
||||
twi->xmt_data8 = *(msg.buf++);
|
||||
--msg.len;
|
||||
}
|
||||
|
||||
/* clear int stat */
|
||||
twi->master_stat = -1;
|
||||
twi->int_stat = -1;
|
||||
twi->int_mask = 0;
|
||||
SSYNC();
|
||||
|
||||
/* Master enable */
|
||||
twi->master_ctl =
|
||||
(twi->master_ctl & FAST) |
|
||||
(min(len, 0xff) << 6) | MEN |
|
||||
((msg.flags & I2C_M_READ) ? MDIR : 0);
|
||||
SSYNC();
|
||||
debugi("CTL=0x%04x", twi->master_ctl);
|
||||
|
||||
/* process the rest */
|
||||
ret = wait_for_completion(&msg);
|
||||
debugi("ret=%d", ret);
|
||||
|
||||
if (ret) {
|
||||
twi->master_ctl &= ~MEN;
|
||||
twi->control &= ~TWI_ENA;
|
||||
SSYNC();
|
||||
twi->control |= TWI_ENA;
|
||||
SSYNC();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_set_bus_speed - set i2c bus speed
|
||||
* @speed: bus speed (in HZ)
|
||||
*/
|
||||
int i2c_set_bus_speed(unsigned int speed)
|
||||
{
|
||||
u16 clkdiv = I2C_SPEED_TO_DUTY(speed);
|
||||
|
||||
/* Set TWI interface clock */
|
||||
if (clkdiv < I2C_DUTY_MAX || clkdiv > I2C_DUTY_MIN)
|
||||
return -1;
|
||||
twi->clkdiv = (clkdiv << 8) | (clkdiv & 0xff);
|
||||
|
||||
/* Don't turn it on */
|
||||
twi->master_ctl = (speed > 100000 ? FAST : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_get_bus_speed - get i2c bus speed
|
||||
* @speed: bus speed (in HZ)
|
||||
*/
|
||||
unsigned int i2c_get_bus_speed(void)
|
||||
{
|
||||
/* 10 MHz / (2 * CLKDIV) -> 5 MHz / CLKDIV */
|
||||
return 5000000 / (twi->clkdiv & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_init - initialize the i2c bus
|
||||
* @speed: bus speed (in HZ)
|
||||
* @slaveaddr: address of device in slave mode (0 - not slave)
|
||||
*
|
||||
* Slave mode isn't actually implemented. It'll stay that way until
|
||||
* we get a real request for it.
|
||||
*/
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
uint8_t prescale = ((get_sclk() / 1024 / 1024 + 5) / 10) & 0x7F;
|
||||
|
||||
/* Set TWI internal clock as 10MHz */
|
||||
twi->control = prescale;
|
||||
|
||||
/* Set TWI interface clock as specified */
|
||||
i2c_set_bus_speed(speed);
|
||||
|
||||
/* Enable it */
|
||||
twi->control = TWI_ENA | prescale;
|
||||
SSYNC();
|
||||
|
||||
debugi("CONTROL:0x%04x CLKDIV:0x%04x", twi->control, twi->clkdiv);
|
||||
|
||||
#if CONFIG_SYS_I2C_SLAVE
|
||||
# error I2C slave support not tested/supported
|
||||
/* If they want us as a slave, do it */
|
||||
if (slaveaddr) {
|
||||
twi->slave_addr = slaveaddr;
|
||||
twi->slave_ctl = SEN;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_probe - test if a chip exists at a given i2c address
|
||||
* @chip: i2c chip addr to search for
|
||||
* @return: 0 if found, non-0 if not found
|
||||
*/
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
u8 byte;
|
||||
return i2c_read(chip, 0, 0, &byte, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_read - read data from an i2c device
|
||||
* @chip: i2c chip addr
|
||||
* @addr: memory (register) address in the chip
|
||||
* @alen: byte size of address
|
||||
* @buffer: buffer to store data read from chip
|
||||
* @len: how many bytes to read
|
||||
* @return: 0 on success, non-0 on failure
|
||||
*/
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
return i2c_transfer(chip, addr, alen, buffer, len, (alen ? I2C_M_COMBO : I2C_M_READ));
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_write - write data to an i2c device
|
||||
* @chip: i2c chip addr
|
||||
* @addr: memory (register) address in the chip
|
||||
* @alen: byte size of address
|
||||
* @buffer: buffer holding data to write to chip
|
||||
* @len: how many bytes to write
|
||||
* @return: 0 on success, non-0 on failure
|
||||
*/
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
return i2c_transfer(chip, addr, alen, buffer, len, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_set_bus_num - change active I2C bus
|
||||
* @bus: bus index, zero based
|
||||
* @returns: 0 on success, non-0 on failure
|
||||
*/
|
||||
int i2c_set_bus_num(unsigned int bus)
|
||||
{
|
||||
switch (bus) {
|
||||
#if CONFIG_SYS_MAX_I2C_BUS > 0
|
||||
case 0: twi = (void *)TWI0_CLKDIV; return 0;
|
||||
#endif
|
||||
#if CONFIG_SYS_MAX_I2C_BUS > 1
|
||||
case 1: twi = (void *)TWI1_CLKDIV; return 0;
|
||||
#endif
|
||||
#if CONFIG_SYS_MAX_I2C_BUS > 2
|
||||
case 2: twi = (void *)TWI2_CLKDIV; return 0;
|
||||
#endif
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_get_bus_num - returns index of active I2C bus
|
||||
*/
|
||||
unsigned int i2c_get_bus_num(void)
|
||||
{
|
||||
switch ((unsigned long)twi) {
|
||||
#if CONFIG_SYS_MAX_I2C_BUS > 0
|
||||
case TWI0_CLKDIV: return 0;
|
||||
#endif
|
||||
#if CONFIG_SYS_MAX_I2C_BUS > 1
|
||||
case TWI1_CLKDIV: return 1;
|
||||
#endif
|
||||
#if CONFIG_SYS_MAX_I2C_BUS > 2
|
||||
case TWI2_CLKDIV: return 2;
|
||||
#endif
|
||||
default: return -1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,332 @@
|
|||
/*
|
||||
* TI DaVinci (TMS320DM644x) I2C driver.
|
||||
*
|
||||
* Copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net>
|
||||
*
|
||||
* --------------------------------------------------------
|
||||
*
|
||||
* 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 <i2c.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/i2c_defs.h>
|
||||
|
||||
#define CHECK_NACK() \
|
||||
do {\
|
||||
if (tmp & (I2C_TIMEOUT | I2C_STAT_NACK)) {\
|
||||
REG(I2C_CON) = 0;\
|
||||
return(1);\
|
||||
}\
|
||||
} while (0)
|
||||
|
||||
|
||||
static int wait_for_bus(void)
|
||||
{
|
||||
int stat, timeout;
|
||||
|
||||
REG(I2C_STAT) = 0xffff;
|
||||
|
||||
for (timeout = 0; timeout < 10; timeout++) {
|
||||
if (!((stat = REG(I2C_STAT)) & I2C_STAT_BB)) {
|
||||
REG(I2C_STAT) = 0xffff;
|
||||
return(0);
|
||||
}
|
||||
|
||||
REG(I2C_STAT) = stat;
|
||||
udelay(50000);
|
||||
}
|
||||
|
||||
REG(I2C_STAT) = 0xffff;
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static int poll_i2c_irq(int mask)
|
||||
{
|
||||
int stat, timeout;
|
||||
|
||||
for (timeout = 0; timeout < 10; timeout++) {
|
||||
udelay(1000);
|
||||
stat = REG(I2C_STAT);
|
||||
if (stat & mask) {
|
||||
return(stat);
|
||||
}
|
||||
}
|
||||
|
||||
REG(I2C_STAT) = 0xffff;
|
||||
return(stat | I2C_TIMEOUT);
|
||||
}
|
||||
|
||||
|
||||
void flush_rx(void)
|
||||
{
|
||||
while (1) {
|
||||
if (!(REG(I2C_STAT) & I2C_STAT_RRDY))
|
||||
break;
|
||||
|
||||
REG(I2C_DRR);
|
||||
REG(I2C_STAT) = I2C_STAT_RRDY;
|
||||
udelay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void i2c_init(int speed, int slaveadd)
|
||||
{
|
||||
u_int32_t div, psc;
|
||||
|
||||
if (REG(I2C_CON) & I2C_CON_EN) {
|
||||
REG(I2C_CON) = 0;
|
||||
udelay (50000);
|
||||
}
|
||||
|
||||
psc = 2;
|
||||
div = (CONFIG_SYS_HZ_CLOCK / ((psc + 1) * speed)) - 10; /* SCLL + SCLH */
|
||||
REG(I2C_PSC) = psc; /* 27MHz / (2 + 1) = 9MHz */
|
||||
REG(I2C_SCLL) = (div * 50) / 100; /* 50% Duty */
|
||||
REG(I2C_SCLH) = div - REG(I2C_SCLL);
|
||||
|
||||
REG(I2C_OA) = slaveadd;
|
||||
REG(I2C_CNT) = 0;
|
||||
|
||||
/* Interrupts must be enabled or I2C module won't work */
|
||||
REG(I2C_IE) = I2C_IE_SCD_IE | I2C_IE_XRDY_IE |
|
||||
I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | I2C_IE_NACK_IE;
|
||||
|
||||
/* Now enable I2C controller (get it out of reset) */
|
||||
REG(I2C_CON) = I2C_CON_EN;
|
||||
|
||||
udelay(1000);
|
||||
}
|
||||
|
||||
int i2c_set_bus_speed(unsigned int speed)
|
||||
{
|
||||
i2c_init(speed, CONFIG_SYS_I2C_SLAVE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_probe(u_int8_t chip)
|
||||
{
|
||||
int rc = 1;
|
||||
|
||||
if (chip == REG(I2C_OA)) {
|
||||
return(rc);
|
||||
}
|
||||
|
||||
REG(I2C_CON) = 0;
|
||||
if (wait_for_bus()) {return(1);}
|
||||
|
||||
/* try to read one byte from current (or only) address */
|
||||
REG(I2C_CNT) = 1;
|
||||
REG(I2C_SA) = chip;
|
||||
REG(I2C_CON) = (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP);
|
||||
udelay (50000);
|
||||
|
||||
if (!(REG(I2C_STAT) & I2C_STAT_NACK)) {
|
||||
rc = 0;
|
||||
flush_rx();
|
||||
REG(I2C_STAT) = 0xffff;
|
||||
} else {
|
||||
REG(I2C_STAT) = 0xffff;
|
||||
REG(I2C_CON) |= I2C_CON_STP;
|
||||
udelay(20000);
|
||||
if (wait_for_bus()) {return(1);}
|
||||
}
|
||||
|
||||
flush_rx();
|
||||
REG(I2C_STAT) = 0xffff;
|
||||
REG(I2C_CNT) = 0;
|
||||
return(rc);
|
||||
}
|
||||
|
||||
|
||||
int i2c_read(u_int8_t chip, u_int32_t addr, int alen, u_int8_t *buf, int len)
|
||||
{
|
||||
u_int32_t tmp;
|
||||
int i;
|
||||
|
||||
if ((alen < 0) || (alen > 2)) {
|
||||
printf("%s(): bogus address length %x\n", __FUNCTION__, alen);
|
||||
return(1);
|
||||
}
|
||||
|
||||
if (wait_for_bus()) {return(1);}
|
||||
|
||||
if (alen != 0) {
|
||||
/* Start address phase */
|
||||
tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX;
|
||||
REG(I2C_CNT) = alen;
|
||||
REG(I2C_SA) = chip;
|
||||
REG(I2C_CON) = tmp;
|
||||
|
||||
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
switch (alen) {
|
||||
case 2:
|
||||
/* Send address MSByte */
|
||||
if (tmp & I2C_STAT_XRDY) {
|
||||
REG(I2C_DXR) = (addr >> 8) & 0xff;
|
||||
} else {
|
||||
REG(I2C_CON) = 0;
|
||||
return(1);
|
||||
}
|
||||
|
||||
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
/* No break, fall through */
|
||||
case 1:
|
||||
/* Send address LSByte */
|
||||
if (tmp & I2C_STAT_XRDY) {
|
||||
REG(I2C_DXR) = addr & 0xff;
|
||||
} else {
|
||||
REG(I2C_CON) = 0;
|
||||
return(1);
|
||||
}
|
||||
|
||||
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK | I2C_STAT_ARDY);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (!(tmp & I2C_STAT_ARDY)) {
|
||||
REG(I2C_CON) = 0;
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Address phase is over, now read 'len' bytes and stop */
|
||||
tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP;
|
||||
REG(I2C_CNT) = len & 0xffff;
|
||||
REG(I2C_SA) = chip;
|
||||
REG(I2C_CON) = tmp;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
tmp = poll_i2c_irq(I2C_STAT_RRDY | I2C_STAT_NACK | I2C_STAT_ROVR);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (tmp & I2C_STAT_RRDY) {
|
||||
buf[i] = REG(I2C_DRR);
|
||||
} else {
|
||||
REG(I2C_CON) = 0;
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
|
||||
tmp = poll_i2c_irq(I2C_STAT_SCD | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (!(tmp & I2C_STAT_SCD)) {
|
||||
REG(I2C_CON) = 0;
|
||||
return(1);
|
||||
}
|
||||
|
||||
flush_rx();
|
||||
REG(I2C_STAT) = 0xffff;
|
||||
REG(I2C_CNT) = 0;
|
||||
REG(I2C_CON) = 0;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
int i2c_write(u_int8_t chip, u_int32_t addr, int alen, u_int8_t *buf, int len)
|
||||
{
|
||||
u_int32_t tmp;
|
||||
int i;
|
||||
|
||||
if ((alen < 0) || (alen > 2)) {
|
||||
printf("%s(): bogus address length %x\n", __FUNCTION__, alen);
|
||||
return(1);
|
||||
}
|
||||
if (len < 0) {
|
||||
printf("%s(): bogus length %x\n", __FUNCTION__, len);
|
||||
return(1);
|
||||
}
|
||||
|
||||
if (wait_for_bus()) {return(1);}
|
||||
|
||||
/* Start address phase */
|
||||
tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | I2C_CON_STP;
|
||||
REG(I2C_CNT) = (alen == 0) ? len & 0xffff : (len & 0xffff) + alen;
|
||||
REG(I2C_SA) = chip;
|
||||
REG(I2C_CON) = tmp;
|
||||
|
||||
switch (alen) {
|
||||
case 2:
|
||||
/* Send address MSByte */
|
||||
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (tmp & I2C_STAT_XRDY) {
|
||||
REG(I2C_DXR) = (addr >> 8) & 0xff;
|
||||
} else {
|
||||
REG(I2C_CON) = 0;
|
||||
return(1);
|
||||
}
|
||||
/* No break, fall through */
|
||||
case 1:
|
||||
/* Send address LSByte */
|
||||
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (tmp & I2C_STAT_XRDY) {
|
||||
REG(I2C_DXR) = addr & 0xff;
|
||||
} else {
|
||||
REG(I2C_CON) = 0;
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
tmp = poll_i2c_irq(I2C_STAT_XRDY | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (tmp & I2C_STAT_XRDY) {
|
||||
REG(I2C_DXR) = buf[i];
|
||||
} else {
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
|
||||
tmp = poll_i2c_irq(I2C_STAT_SCD | I2C_STAT_NACK);
|
||||
|
||||
CHECK_NACK();
|
||||
|
||||
if (!(tmp & I2C_STAT_SCD)) {
|
||||
REG(I2C_CON) = 0;
|
||||
return(1);
|
||||
}
|
||||
|
||||
flush_rx();
|
||||
REG(I2C_STAT) = 0xffff;
|
||||
REG(I2C_CNT) = 0;
|
||||
REG(I2C_CON) = 0;
|
||||
|
||||
return(0);
|
||||
}
|
|
@ -0,0 +1,346 @@
|
|||
/*
|
||||
* (C) Copyright 2009
|
||||
* Vipin Kumar, ST Micoelectronics, vipin.kumar@st.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/hardware.h>
|
||||
#include "designware_i2c.h"
|
||||
|
||||
static struct i2c_regs *const i2c_regs_p =
|
||||
(struct i2c_regs *)CONFIG_SYS_I2C_BASE;
|
||||
|
||||
/*
|
||||
* set_speed - Set the i2c speed mode (standard, high, fast)
|
||||
* @i2c_spd: required i2c speed mode
|
||||
*
|
||||
* Set the i2c speed mode (standard, high, fast)
|
||||
*/
|
||||
static void set_speed(int i2c_spd)
|
||||
{
|
||||
unsigned int cntl;
|
||||
unsigned int hcnt, lcnt;
|
||||
unsigned int high, low;
|
||||
unsigned int enbl;
|
||||
|
||||
/* to set speed cltr must be disabled */
|
||||
enbl = readl(&i2c_regs_p->ic_enable);
|
||||
enbl &= ~IC_ENABLE_0B;
|
||||
writel(enbl, &i2c_regs_p->ic_enable);
|
||||
|
||||
|
||||
cntl = (readl(&i2c_regs_p->ic_con) & (~IC_CON_SPD_MSK));
|
||||
|
||||
switch (i2c_spd) {
|
||||
case IC_SPEED_MODE_MAX:
|
||||
cntl |= IC_CON_SPD_HS;
|
||||
high = MIN_HS_SCL_HIGHTIME;
|
||||
low = MIN_HS_SCL_LOWTIME;
|
||||
break;
|
||||
|
||||
case IC_SPEED_MODE_STANDARD:
|
||||
cntl |= IC_CON_SPD_SS;
|
||||
high = MIN_SS_SCL_HIGHTIME;
|
||||
low = MIN_SS_SCL_LOWTIME;
|
||||
break;
|
||||
|
||||
case IC_SPEED_MODE_FAST:
|
||||
default:
|
||||
cntl |= IC_CON_SPD_FS;
|
||||
high = MIN_FS_SCL_HIGHTIME;
|
||||
low = MIN_FS_SCL_LOWTIME;
|
||||
break;
|
||||
}
|
||||
|
||||
writel(cntl, &i2c_regs_p->ic_con);
|
||||
|
||||
hcnt = (IC_CLK * high) / NANO_TO_MICRO;
|
||||
writel(hcnt, &i2c_regs_p->ic_fs_scl_hcnt);
|
||||
|
||||
lcnt = (IC_CLK * low) / NANO_TO_MICRO;
|
||||
writel(lcnt, &i2c_regs_p->ic_fs_scl_lcnt);
|
||||
|
||||
/* re-enable i2c ctrl back now that speed is set */
|
||||
enbl |= IC_ENABLE_0B;
|
||||
writel(enbl, &i2c_regs_p->ic_enable);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_set_bus_speed - Set the i2c speed
|
||||
* @speed: required i2c speed
|
||||
*
|
||||
* Set the i2c speed.
|
||||
*/
|
||||
int i2c_set_bus_speed(int speed)
|
||||
{
|
||||
if (speed >= I2C_MAX_SPEED)
|
||||
set_speed(IC_SPEED_MODE_MAX);
|
||||
else if (speed >= I2C_FAST_SPEED)
|
||||
set_speed(IC_SPEED_MODE_FAST);
|
||||
else
|
||||
set_speed(IC_SPEED_MODE_STANDARD);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_get_bus_speed - Gets the i2c speed
|
||||
*
|
||||
* Gets the i2c speed.
|
||||
*/
|
||||
int i2c_get_bus_speed(void)
|
||||
{
|
||||
u32 cntl;
|
||||
|
||||
cntl = (readl(&i2c_regs_p->ic_con) & IC_CON_SPD_MSK);
|
||||
|
||||
if (cntl == IC_CON_SPD_HS)
|
||||
return I2C_MAX_SPEED;
|
||||
else if (cntl == IC_CON_SPD_FS)
|
||||
return I2C_FAST_SPEED;
|
||||
else if (cntl == IC_CON_SPD_SS)
|
||||
return I2C_STANDARD_SPEED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_init - Init function
|
||||
* @speed: required i2c speed
|
||||
* @slaveadd: slave address for the device
|
||||
*
|
||||
* Initialization function.
|
||||
*/
|
||||
void i2c_init(int speed, int slaveadd)
|
||||
{
|
||||
unsigned int enbl;
|
||||
|
||||
/* Disable i2c */
|
||||
enbl = readl(&i2c_regs_p->ic_enable);
|
||||
enbl &= ~IC_ENABLE_0B;
|
||||
writel(enbl, &i2c_regs_p->ic_enable);
|
||||
|
||||
writel((IC_CON_SD | IC_CON_SPD_FS | IC_CON_MM), &i2c_regs_p->ic_con);
|
||||
writel(IC_RX_TL, &i2c_regs_p->ic_rx_tl);
|
||||
writel(IC_TX_TL, &i2c_regs_p->ic_tx_tl);
|
||||
i2c_set_bus_speed(speed);
|
||||
writel(IC_STOP_DET, &i2c_regs_p->ic_intr_mask);
|
||||
writel(slaveadd, &i2c_regs_p->ic_sar);
|
||||
|
||||
/* Enable i2c */
|
||||
enbl = readl(&i2c_regs_p->ic_enable);
|
||||
enbl |= IC_ENABLE_0B;
|
||||
writel(enbl, &i2c_regs_p->ic_enable);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_setaddress - Sets the target slave address
|
||||
* @i2c_addr: target i2c address
|
||||
*
|
||||
* Sets the target slave address.
|
||||
*/
|
||||
static void i2c_setaddress(unsigned int i2c_addr)
|
||||
{
|
||||
writel(i2c_addr, &i2c_regs_p->ic_tar);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_flush_rxfifo - Flushes the i2c RX FIFO
|
||||
*
|
||||
* Flushes the i2c RX FIFO
|
||||
*/
|
||||
static void i2c_flush_rxfifo(void)
|
||||
{
|
||||
while (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE)
|
||||
readl(&i2c_regs_p->ic_cmd_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_wait_for_bb - Waits for bus busy
|
||||
*
|
||||
* Waits for bus busy
|
||||
*/
|
||||
static int i2c_wait_for_bb(void)
|
||||
{
|
||||
unsigned long start_time_bb = get_timer(0);
|
||||
|
||||
while ((readl(&i2c_regs_p->ic_status) & IC_STATUS_MA) ||
|
||||
!(readl(&i2c_regs_p->ic_status) & IC_STATUS_TFE)) {
|
||||
|
||||
/* Evaluate timeout */
|
||||
if (get_timer(start_time_bb) > (unsigned long)(I2C_BYTE_TO_BB))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check parameters for i2c_read and i2c_write */
|
||||
static int check_params(uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
if (buffer == NULL) {
|
||||
printf("Buffer is invalid\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (alen > 1) {
|
||||
printf("addr len %d not supported\n", alen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (addr + len > 256) {
|
||||
printf("address out of range\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_xfer_init(uchar chip, uint addr)
|
||||
{
|
||||
if (i2c_wait_for_bb())
|
||||
return 1;
|
||||
|
||||
i2c_setaddress(chip);
|
||||
writel(addr, &i2c_regs_p->ic_cmd_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_xfer_finish(void)
|
||||
{
|
||||
ulong start_stop_det = get_timer(0);
|
||||
|
||||
while (1) {
|
||||
if ((readl(&i2c_regs_p->ic_raw_intr_stat) & IC_STOP_DET)) {
|
||||
readl(&i2c_regs_p->ic_clr_stop_det);
|
||||
break;
|
||||
} else if (get_timer(start_stop_det) > I2C_STOPDET_TO) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i2c_wait_for_bb()) {
|
||||
printf("Timed out waiting for bus\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
i2c_flush_rxfifo();
|
||||
|
||||
/* Wait for read/write operation to complete on actual memory */
|
||||
udelay(10000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_read - Read from i2c memory
|
||||
* @chip: target i2c address
|
||||
* @addr: address to read from
|
||||
* @alen:
|
||||
* @buffer: buffer for read data
|
||||
* @len: no of bytes to be read
|
||||
*
|
||||
* Read from i2c memory.
|
||||
*/
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
unsigned long start_time_rx;
|
||||
|
||||
if (check_params(addr, alen, buffer, len))
|
||||
return 1;
|
||||
|
||||
if (i2c_xfer_init(chip, addr))
|
||||
return 1;
|
||||
|
||||
start_time_rx = get_timer(0);
|
||||
while (len) {
|
||||
writel(IC_CMD, &i2c_regs_p->ic_cmd_data);
|
||||
|
||||
if (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE) {
|
||||
*buffer++ = (uchar)readl(&i2c_regs_p->ic_cmd_data);
|
||||
len--;
|
||||
start_time_rx = get_timer(0);
|
||||
|
||||
} else if (get_timer(start_time_rx) > I2C_BYTE_TO) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return i2c_xfer_finish();
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_write - Write to i2c memory
|
||||
* @chip: target i2c address
|
||||
* @addr: address to read from
|
||||
* @alen:
|
||||
* @buffer: buffer for read data
|
||||
* @len: no of bytes to be read
|
||||
*
|
||||
* Write to i2c memory.
|
||||
*/
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int nb = len;
|
||||
unsigned long start_time_tx;
|
||||
|
||||
if (check_params(addr, alen, buffer, len))
|
||||
return 1;
|
||||
|
||||
if (i2c_xfer_init(chip, addr))
|
||||
return 1;
|
||||
|
||||
start_time_tx = get_timer(0);
|
||||
while (len) {
|
||||
if (readl(&i2c_regs_p->ic_status) & IC_STATUS_TFNF) {
|
||||
writel(*buffer, &i2c_regs_p->ic_cmd_data);
|
||||
buffer++;
|
||||
len--;
|
||||
start_time_tx = get_timer(0);
|
||||
|
||||
} else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) {
|
||||
printf("Timed out. i2c write Failed\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return i2c_xfer_finish();
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_probe - Probe the i2c chip
|
||||
*/
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Try to read the first location of the chip.
|
||||
*/
|
||||
ret = i2c_read(chip, 0, 1, (uchar *)&tmp, 1);
|
||||
if (ret)
|
||||
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* (C) Copyright 2009
|
||||
* Vipin Kumar, ST Micoelectronics, vipin.kumar@st.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
|
||||
*/
|
||||
|
||||
#ifndef __DW_I2C_H_
|
||||
#define __DW_I2C_H_
|
||||
|
||||
struct i2c_regs {
|
||||
u32 ic_con;
|
||||
u32 ic_tar;
|
||||
u32 ic_sar;
|
||||
u32 ic_hs_maddr;
|
||||
u32 ic_cmd_data;
|
||||
u32 ic_ss_scl_hcnt;
|
||||
u32 ic_ss_scl_lcnt;
|
||||
u32 ic_fs_scl_hcnt;
|
||||
u32 ic_fs_scl_lcnt;
|
||||
u32 ic_hs_scl_hcnt;
|
||||
u32 ic_hs_scl_lcnt;
|
||||
u32 ic_intr_stat;
|
||||
u32 ic_intr_mask;
|
||||
u32 ic_raw_intr_stat;
|
||||
u32 ic_rx_tl;
|
||||
u32 ic_tx_tl;
|
||||
u32 ic_clr_intr;
|
||||
u32 ic_clr_rx_under;
|
||||
u32 ic_clr_rx_over;
|
||||
u32 ic_clr_tx_over;
|
||||
u32 ic_clr_rd_req;
|
||||
u32 ic_clr_tx_abrt;
|
||||
u32 ic_clr_rx_done;
|
||||
u32 ic_clr_activity;
|
||||
u32 ic_clr_stop_det;
|
||||
u32 ic_clr_start_det;
|
||||
u32 ic_clr_gen_call;
|
||||
u32 ic_enable;
|
||||
u32 ic_status;
|
||||
u32 ic_txflr;
|
||||
u32 ix_rxflr;
|
||||
u32 reserved_1;
|
||||
u32 ic_tx_abrt_source;
|
||||
};
|
||||
|
||||
#define IC_CLK 166
|
||||
#define NANO_TO_MICRO 1000
|
||||
|
||||
/* High and low times in different speed modes (in ns) */
|
||||
#define MIN_SS_SCL_HIGHTIME 4000
|
||||
#define MIN_SS_SCL_LOWTIME 5000
|
||||
#define MIN_FS_SCL_HIGHTIME 800
|
||||
#define MIN_FS_SCL_LOWTIME 1700
|
||||
#define MIN_HS_SCL_HIGHTIME 60
|
||||
#define MIN_HS_SCL_LOWTIME 160
|
||||
|
||||
/* Worst case timeout for 1 byte is kept as 2ms */
|
||||
#define I2C_BYTE_TO (CONFIG_SYS_HZ/500)
|
||||
#define I2C_STOPDET_TO (CONFIG_SYS_HZ/500)
|
||||
#define I2C_BYTE_TO_BB (I2C_BYTE_TO * 16)
|
||||
|
||||
/* i2c control register definitions */
|
||||
#define IC_CON_SD 0x0040
|
||||
#define IC_CON_RE 0x0020
|
||||
#define IC_CON_10BITADDRMASTER 0x0010
|
||||
#define IC_CON_10BITADDR_SLAVE 0x0008
|
||||
#define IC_CON_SPD_MSK 0x0006
|
||||
#define IC_CON_SPD_SS 0x0002
|
||||
#define IC_CON_SPD_FS 0x0004
|
||||
#define IC_CON_SPD_HS 0x0006
|
||||
#define IC_CON_MM 0x0001
|
||||
|
||||
/* i2c target address register definitions */
|
||||
#define TAR_ADDR 0x0050
|
||||
|
||||
/* i2c slave address register definitions */
|
||||
#define IC_SLAVE_ADDR 0x0002
|
||||
|
||||
/* i2c data buffer and command register definitions */
|
||||
#define IC_CMD 0x0100
|
||||
|
||||
/* i2c interrupt status register definitions */
|
||||
#define IC_GEN_CALL 0x0800
|
||||
#define IC_START_DET 0x0400
|
||||
#define IC_STOP_DET 0x0200
|
||||
#define IC_ACTIVITY 0x0100
|
||||
#define IC_RX_DONE 0x0080
|
||||
#define IC_TX_ABRT 0x0040
|
||||
#define IC_RD_REQ 0x0020
|
||||
#define IC_TX_EMPTY 0x0010
|
||||
#define IC_TX_OVER 0x0008
|
||||
#define IC_RX_FULL 0x0004
|
||||
#define IC_RX_OVER 0x0002
|
||||
#define IC_RX_UNDER 0x0001
|
||||
|
||||
/* fifo threshold register definitions */
|
||||
#define IC_TL0 0x00
|
||||
#define IC_TL1 0x01
|
||||
#define IC_TL2 0x02
|
||||
#define IC_TL3 0x03
|
||||
#define IC_TL4 0x04
|
||||
#define IC_TL5 0x05
|
||||
#define IC_TL6 0x06
|
||||
#define IC_TL7 0x07
|
||||
#define IC_RX_TL IC_TL0
|
||||
#define IC_TX_TL IC_TL0
|
||||
|
||||
/* i2c enable register definitions */
|
||||
#define IC_ENABLE_0B 0x0001
|
||||
|
||||
/* i2c status register definitions */
|
||||
#define IC_STATUS_SA 0x0040
|
||||
#define IC_STATUS_MA 0x0020
|
||||
#define IC_STATUS_RFF 0x0010
|
||||
#define IC_STATUS_RFNE 0x0008
|
||||
#define IC_STATUS_TFE 0x0004
|
||||
#define IC_STATUS_TFNF 0x0002
|
||||
#define IC_STATUS_ACT 0x0001
|
||||
|
||||
/* Speed Selection */
|
||||
#define IC_SPEED_MODE_STANDARD 1
|
||||
#define IC_SPEED_MODE_FAST 2
|
||||
#define IC_SPEED_MODE_MAX 3
|
||||
|
||||
#define I2C_MAX_SPEED 3400000
|
||||
#define I2C_FAST_SPEED 400000
|
||||
#define I2C_STANDARD_SPEED 100000
|
||||
|
||||
#endif /* __DW_I2C_H_ */
|
495
root/package/utils/sysupgrade-helper/src/drivers/i2c/fsl_i2c.c
Normal file
495
root/package/utils/sysupgrade-helper/src/drivers/i2c/fsl_i2c.c
Normal file
|
@ -0,0 +1,495 @@
|
|||
/*
|
||||
* Copyright 2006,2009 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#ifdef CONFIG_HARD_I2C
|
||||
|
||||
#include <command.h>
|
||||
#include <i2c.h> /* Functional interface */
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/fsl_i2c.h> /* HW definitions */
|
||||
|
||||
/* The maximum number of microseconds we will wait until another master has
|
||||
* released the bus. If not defined in the board header file, then use a
|
||||
* generic value.
|
||||
*/
|
||||
#ifndef CONFIG_I2C_MBB_TIMEOUT
|
||||
#define CONFIG_I2C_MBB_TIMEOUT 100000
|
||||
#endif
|
||||
|
||||
/* The maximum number of microseconds we will wait for a read or write
|
||||
* operation to complete. If not defined in the board header file, then use a
|
||||
* generic value.
|
||||
*/
|
||||
#ifndef CONFIG_I2C_TIMEOUT
|
||||
#define CONFIG_I2C_TIMEOUT 10000
|
||||
#endif
|
||||
|
||||
#define I2C_READ_BIT 1
|
||||
#define I2C_WRITE_BIT 0
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* Initialize the bus pointer to whatever one the SPD EEPROM is on.
|
||||
* Default is bus 0. This is necessary because the DDR initialization
|
||||
* runs from ROM, and we can't switch buses because we can't modify
|
||||
* the global variables.
|
||||
*/
|
||||
#ifndef CONFIG_SYS_SPD_BUS_NUM
|
||||
#define CONFIG_SYS_SPD_BUS_NUM 0
|
||||
#endif
|
||||
static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = CONFIG_SYS_SPD_BUS_NUM;
|
||||
#if defined(CONFIG_I2C_MUX)
|
||||
static unsigned int i2c_bus_num_mux __attribute__ ((section ("data"))) = 0;
|
||||
#endif
|
||||
|
||||
static unsigned int i2c_bus_speed[2] = {CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SPEED};
|
||||
|
||||
static const struct fsl_i2c *i2c_dev[2] = {
|
||||
(struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C_OFFSET),
|
||||
#ifdef CONFIG_SYS_I2C2_OFFSET
|
||||
(struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C2_OFFSET)
|
||||
#endif
|
||||
};
|
||||
|
||||
/* I2C speed map for a DFSR value of 1 */
|
||||
|
||||
/*
|
||||
* Map I2C frequency dividers to FDR and DFSR values
|
||||
*
|
||||
* This structure is used to define the elements of a table that maps I2C
|
||||
* frequency divider (I2C clock rate divided by I2C bus speed) to a value to be
|
||||
* programmed into the Frequency Divider Ratio (FDR) and Digital Filter
|
||||
* Sampling Rate (DFSR) registers.
|
||||
*
|
||||
* The actual table should be defined in the board file, and it must be called
|
||||
* fsl_i2c_speed_map[].
|
||||
*
|
||||
* The last entry of the table must have a value of {-1, X}, where X is same
|
||||
* FDR/DFSR values as the second-to-last entry. This guarantees that any
|
||||
* search through the array will always find a match.
|
||||
*
|
||||
* The values of the divider must be in increasing numerical order, i.e.
|
||||
* fsl_i2c_speed_map[x+1].divider > fsl_i2c_speed_map[x].divider.
|
||||
*
|
||||
* For this table, the values are based on a value of 1 for the DFSR
|
||||
* register. See the application note AN2919 "Determining the I2C Frequency
|
||||
* Divider Ratio for SCL"
|
||||
*
|
||||
* ColdFire I2C frequency dividers for FDR values are different from
|
||||
* PowerPC. The protocol to use the I2C module is still the same.
|
||||
* A different table is defined and are based on MCF5xxx user manual.
|
||||
*
|
||||
*/
|
||||
static const struct {
|
||||
unsigned short divider;
|
||||
u8 fdr;
|
||||
} fsl_i2c_speed_map[] = {
|
||||
#ifdef __M68K__
|
||||
{20, 32}, {22, 33}, {24, 34}, {26, 35},
|
||||
{28, 0}, {28, 36}, {30, 1}, {32, 37},
|
||||
{34, 2}, {36, 38}, {40, 3}, {40, 39},
|
||||
{44, 4}, {48, 5}, {48, 40}, {56, 6},
|
||||
{56, 41}, {64, 42}, {68, 7}, {72, 43},
|
||||
{80, 8}, {80, 44}, {88, 9}, {96, 41},
|
||||
{104, 10}, {112, 42}, {128, 11}, {128, 43},
|
||||
{144, 12}, {160, 13}, {160, 48}, {192, 14},
|
||||
{192, 49}, {224, 50}, {240, 15}, {256, 51},
|
||||
{288, 16}, {320, 17}, {320, 52}, {384, 18},
|
||||
{384, 53}, {448, 54}, {480, 19}, {512, 55},
|
||||
{576, 20}, {640, 21}, {640, 56}, {768, 22},
|
||||
{768, 57}, {960, 23}, {896, 58}, {1024, 59},
|
||||
{1152, 24}, {1280, 25}, {1280, 60}, {1536, 26},
|
||||
{1536, 61}, {1792, 62}, {1920, 27}, {2048, 63},
|
||||
{2304, 28}, {2560, 29}, {3072, 30}, {3840, 31},
|
||||
{-1, 31}
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the I2C bus speed for a given I2C device
|
||||
*
|
||||
* @param dev: the I2C device
|
||||
* @i2c_clk: I2C bus clock frequency
|
||||
* @speed: the desired speed of the bus
|
||||
*
|
||||
* The I2C device must be stopped before calling this function.
|
||||
*
|
||||
* The return value is the actual bus speed that is set.
|
||||
*/
|
||||
static unsigned int set_i2c_bus_speed(const struct fsl_i2c *dev,
|
||||
unsigned int i2c_clk, unsigned int speed)
|
||||
{
|
||||
unsigned short divider = min(i2c_clk / speed, (unsigned short) -1);
|
||||
|
||||
/*
|
||||
* We want to choose an FDR/DFSR that generates an I2C bus speed that
|
||||
* is equal to or lower than the requested speed. That means that we
|
||||
* want the first divider that is equal to or greater than the
|
||||
* calculated divider.
|
||||
*/
|
||||
#ifdef __PPC__
|
||||
u8 dfsr, fdr = 0x31; /* Default if no FDR found */
|
||||
/* a, b and dfsr matches identifiers A,B and C respectively in AN2919 */
|
||||
unsigned short a, b, ga, gb;
|
||||
unsigned long c_div, est_div;
|
||||
|
||||
#ifdef CONFIG_FSL_I2C_CUSTOM_DFSR
|
||||
dfsr = CONFIG_FSL_I2C_CUSTOM_DFSR;
|
||||
#else
|
||||
/* Condition 1: dfsr <= 50/T */
|
||||
dfsr = (5 * (i2c_clk / 1000)) / 100000;
|
||||
#endif
|
||||
#ifdef CONFIG_FSL_I2C_CUSTOM_FDR
|
||||
fdr = CONFIG_FSL_I2C_CUSTOM_FDR;
|
||||
speed = i2c_clk / divider; /* Fake something */
|
||||
#else
|
||||
debug("Requested speed:%d, i2c_clk:%d\n", speed, i2c_clk);
|
||||
if (!dfsr)
|
||||
dfsr = 1;
|
||||
|
||||
est_div = ~0;
|
||||
for (ga = 0x4, a = 10; a <= 30; ga++, a += 2) {
|
||||
for (gb = 0; gb < 8; gb++) {
|
||||
b = 16 << gb;
|
||||
c_div = b * (a + ((3*dfsr)/b)*2);
|
||||
if ((c_div > divider) && (c_div < est_div)) {
|
||||
unsigned short bin_gb, bin_ga;
|
||||
|
||||
est_div = c_div;
|
||||
bin_gb = gb << 2;
|
||||
bin_ga = (ga & 0x3) | ((ga & 0x4) << 3);
|
||||
fdr = bin_gb | bin_ga;
|
||||
speed = i2c_clk / est_div;
|
||||
debug("FDR:0x%.2x, div:%ld, ga:0x%x, gb:0x%x, "
|
||||
"a:%d, b:%d, speed:%d\n",
|
||||
fdr, est_div, ga, gb, a, b, speed);
|
||||
/* Condition 2 not accounted for */
|
||||
debug("Tr <= %d ns\n",
|
||||
(b - 3 * dfsr) * 1000000 /
|
||||
(i2c_clk / 1000));
|
||||
}
|
||||
}
|
||||
if (a == 20)
|
||||
a += 2;
|
||||
if (a == 24)
|
||||
a += 4;
|
||||
}
|
||||
debug("divider:%d, est_div:%ld, DFSR:%d\n", divider, est_div, dfsr);
|
||||
debug("FDR:0x%.2x, speed:%d\n", fdr, speed);
|
||||
#endif
|
||||
writeb(dfsr, &dev->dfsrr); /* set default filter */
|
||||
writeb(fdr, &dev->fdr); /* set bus speed */
|
||||
#else
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(fsl_i2c_speed_map); i++)
|
||||
if (fsl_i2c_speed_map[i].divider >= divider) {
|
||||
u8 fdr;
|
||||
|
||||
fdr = fsl_i2c_speed_map[i].fdr;
|
||||
speed = i2c_clk / fsl_i2c_speed_map[i].divider;
|
||||
writeb(fdr, &dev->fdr); /* set bus speed */
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return speed;
|
||||
}
|
||||
|
||||
unsigned int get_i2c_clock(int bus)
|
||||
{
|
||||
if (bus)
|
||||
return gd->i2c2_clk; /* I2C2 clock */
|
||||
else
|
||||
return gd->i2c1_clk; /* I2C1 clock */
|
||||
}
|
||||
|
||||
void
|
||||
i2c_init(int speed, int slaveadd)
|
||||
{
|
||||
const struct fsl_i2c *dev;
|
||||
unsigned int temp;
|
||||
int bus_num, i;
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_INIT_BOARD
|
||||
/* Call board specific i2c bus reset routine before accessing the
|
||||
* environment, which might be in a chip on that bus. For details
|
||||
* about this problem see doc/I2C_Edge_Conditions.
|
||||
*/
|
||||
i2c_init_board();
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_I2C2_OFFSET
|
||||
bus_num = 2;
|
||||
#else
|
||||
bus_num = 1;
|
||||
#endif
|
||||
for (i = 0; i < bus_num; i++) {
|
||||
dev = i2c_dev[i];
|
||||
|
||||
writeb(0, &dev->cr); /* stop I2C controller */
|
||||
udelay(5); /* let it shutdown in peace */
|
||||
temp = set_i2c_bus_speed(dev, get_i2c_clock(i), speed);
|
||||
if (gd->flags & GD_FLG_RELOC)
|
||||
i2c_bus_speed[i] = temp;
|
||||
writeb(slaveadd << 1, &dev->adr);/* write slave address */
|
||||
writeb(0x0, &dev->sr); /* clear status register */
|
||||
writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT
|
||||
/* Call board specific i2c bus reset routine AFTER the bus has been
|
||||
* initialized. Use either this callpoint or i2c_init_board;
|
||||
* which is called before i2c_init operations.
|
||||
* For details about this problem see doc/I2C_Edge_Conditions.
|
||||
*/
|
||||
i2c_board_late_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_wait4bus(void)
|
||||
{
|
||||
unsigned long long timeval = get_ticks();
|
||||
const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT);
|
||||
|
||||
while (readb(&i2c_dev[i2c_bus_num]->sr) & I2C_SR_MBB) {
|
||||
if ((get_ticks() - timeval) > timeout)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __inline__ int
|
||||
i2c_wait(int write)
|
||||
{
|
||||
u32 csr;
|
||||
unsigned long long timeval = get_ticks();
|
||||
const unsigned long long timeout = usec2ticks(CONFIG_I2C_TIMEOUT);
|
||||
|
||||
do {
|
||||
csr = readb(&i2c_dev[i2c_bus_num]->sr);
|
||||
if (!(csr & I2C_SR_MIF))
|
||||
continue;
|
||||
/* Read again to allow register to stabilise */
|
||||
csr = readb(&i2c_dev[i2c_bus_num]->sr);
|
||||
|
||||
writeb(0x0, &i2c_dev[i2c_bus_num]->sr);
|
||||
|
||||
if (csr & I2C_SR_MAL) {
|
||||
debug("i2c_wait: MAL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(csr & I2C_SR_MCF)) {
|
||||
debug("i2c_wait: unfinished\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write == I2C_WRITE_BIT && (csr & I2C_SR_RXAK)) {
|
||||
debug("i2c_wait: No RXACK\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} while ((get_ticks() - timeval) < timeout);
|
||||
|
||||
debug("i2c_wait: timed out\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static __inline__ int
|
||||
i2c_write_addr (u8 dev, u8 dir, int rsta)
|
||||
{
|
||||
writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX
|
||||
| (rsta ? I2C_CR_RSTA : 0),
|
||||
&i2c_dev[i2c_bus_num]->cr);
|
||||
|
||||
writeb((dev << 1) | dir, &i2c_dev[i2c_bus_num]->dr);
|
||||
|
||||
if (i2c_wait(I2C_WRITE_BIT) < 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static __inline__ int
|
||||
__i2c_write(u8 *data, int length)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
writeb(data[i], &i2c_dev[i2c_bus_num]->dr);
|
||||
|
||||
if (i2c_wait(I2C_WRITE_BIT) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static __inline__ int
|
||||
__i2c_read(u8 *data, int length)
|
||||
{
|
||||
int i;
|
||||
|
||||
writeb(I2C_CR_MEN | I2C_CR_MSTA | ((length == 1) ? I2C_CR_TXAK : 0),
|
||||
&i2c_dev[i2c_bus_num]->cr);
|
||||
|
||||
/* dummy read */
|
||||
readb(&i2c_dev[i2c_bus_num]->dr);
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
if (i2c_wait(I2C_READ_BIT) < 0)
|
||||
break;
|
||||
|
||||
/* Generate ack on last next to last byte */
|
||||
if (i == length - 2)
|
||||
writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_TXAK,
|
||||
&i2c_dev[i2c_bus_num]->cr);
|
||||
|
||||
/* Do not generate stop on last byte */
|
||||
if (i == length - 1)
|
||||
writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX,
|
||||
&i2c_dev[i2c_bus_num]->cr);
|
||||
|
||||
data[i] = readb(&i2c_dev[i2c_bus_num]->dr);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int
|
||||
i2c_read(u8 dev, uint addr, int alen, u8 *data, int length)
|
||||
{
|
||||
int i = -1; /* signal error */
|
||||
u8 *a = (u8*)&addr;
|
||||
|
||||
if (i2c_wait4bus() >= 0
|
||||
&& i2c_write_addr(dev, I2C_WRITE_BIT, 0) != 0
|
||||
&& __i2c_write(&a[4 - alen], alen) == alen)
|
||||
i = 0; /* No error so far */
|
||||
|
||||
if (length
|
||||
&& i2c_write_addr(dev, I2C_READ_BIT, 1) != 0)
|
||||
i = __i2c_read(data, length);
|
||||
|
||||
writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr);
|
||||
|
||||
if (i2c_wait4bus()) /* Wait until STOP */
|
||||
debug("i2c_read: wait4bus timed out\n");
|
||||
|
||||
if (i == length)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
i2c_write(u8 dev, uint addr, int alen, u8 *data, int length)
|
||||
{
|
||||
int i = -1; /* signal error */
|
||||
u8 *a = (u8*)&addr;
|
||||
|
||||
if (i2c_wait4bus() >= 0
|
||||
&& i2c_write_addr(dev, I2C_WRITE_BIT, 0) != 0
|
||||
&& __i2c_write(&a[4 - alen], alen) == alen) {
|
||||
i = __i2c_write(data, length);
|
||||
}
|
||||
|
||||
writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr);
|
||||
if (i2c_wait4bus()) /* Wait until STOP */
|
||||
debug("i2c_write: wait4bus timed out\n");
|
||||
|
||||
if (i == length)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
i2c_probe(uchar chip)
|
||||
{
|
||||
/* For unknow reason the controller will ACK when
|
||||
* probing for a slave with the same address, so skip
|
||||
* it.
|
||||
*/
|
||||
if (chip == (readb(&i2c_dev[i2c_bus_num]->adr) >> 1))
|
||||
return -1;
|
||||
|
||||
return i2c_read(chip, 0, 0, NULL, 0);
|
||||
}
|
||||
|
||||
int i2c_set_bus_num(unsigned int bus)
|
||||
{
|
||||
#if defined(CONFIG_I2C_MUX)
|
||||
if (bus < CONFIG_SYS_MAX_I2C_BUS) {
|
||||
i2c_bus_num = bus;
|
||||
} else {
|
||||
int ret;
|
||||
|
||||
ret = i2x_mux_select_mux(bus);
|
||||
if (ret)
|
||||
return ret;
|
||||
i2c_bus_num = 0;
|
||||
}
|
||||
i2c_bus_num_mux = bus;
|
||||
#else
|
||||
#ifdef CONFIG_SYS_I2C2_OFFSET
|
||||
if (bus > 1) {
|
||||
#else
|
||||
if (bus > 0) {
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
i2c_bus_num = bus;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_set_bus_speed(unsigned int speed)
|
||||
{
|
||||
unsigned int i2c_clk = (i2c_bus_num == 1) ? gd->i2c2_clk : gd->i2c1_clk;
|
||||
|
||||
writeb(0, &i2c_dev[i2c_bus_num]->cr); /* stop controller */
|
||||
i2c_bus_speed[i2c_bus_num] =
|
||||
set_i2c_bus_speed(i2c_dev[i2c_bus_num], i2c_clk, speed);
|
||||
writeb(I2C_CR_MEN, &i2c_dev[i2c_bus_num]->cr); /* start controller */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int i2c_get_bus_num(void)
|
||||
{
|
||||
#if defined(CONFIG_I2C_MUX)
|
||||
return i2c_bus_num_mux;
|
||||
#else
|
||||
return i2c_bus_num;
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned int i2c_get_bus_speed(void)
|
||||
{
|
||||
return i2c_bus_speed[i2c_bus_num];
|
||||
}
|
||||
|
||||
#endif /* CONFIG_HARD_I2C */
|
414
root/package/utils/sysupgrade-helper/src/drivers/i2c/ipq_i2c.c
Normal file
414
root/package/utils/sysupgrade-helper/src/drivers/i2c/ipq_i2c.c
Normal file
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
* Copyright (c) 2014, 2016 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only 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.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include "ipq_i2c.h"
|
||||
#include <asm/io.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/arch-ipq806x/gsbi.h>
|
||||
#include <asm/arch-ipq806x/gpio.h>
|
||||
#include <asm/arch-ipq806x/iomap.h>
|
||||
#include <asm/arch-ipq806x/clock.h>
|
||||
|
||||
static int gsbi_port;
|
||||
static int gsbi_base_addr;
|
||||
static int i2c_hw_initialized;
|
||||
static int i2c_board_initialized;
|
||||
static uint32_t i2c_master_clk_ctl;
|
||||
|
||||
/*
|
||||
* Reset entire QUP and all mini cores
|
||||
*/
|
||||
static void i2c_reset(void)
|
||||
{
|
||||
writel(0x1, GSBI_QUP_BASE(gsbi_port) + QUP_SW_RESET_OFFSET);
|
||||
udelay(5);
|
||||
}
|
||||
|
||||
static int check_bit_state(uint32_t reg_addr, int bit_num, int val,
|
||||
int us_delay)
|
||||
{
|
||||
unsigned int count = TIMEOUT_CNT;
|
||||
unsigned int bit_val = ((readl(reg_addr) >> bit_num) & 0x01);
|
||||
|
||||
while (bit_val != val) {
|
||||
count--;
|
||||
if (count == 0) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
udelay(us_delay);
|
||||
bit_val = ((readl(reg_addr) >> bit_num) & 0x01);
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether GSBIn_QUP State is valid
|
||||
*/
|
||||
static int check_qup_state_valid(void)
|
||||
{
|
||||
return check_bit_state(GSBI_QUP_BASE(gsbi_port) + QUP_STATE_OFFSET,
|
||||
QUP_STATE_VALID_BIT,
|
||||
QUP_STATE_VALID, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure GSBIn Core state
|
||||
*/
|
||||
static int config_i2c_state(unsigned int state)
|
||||
{
|
||||
uint32_t val;
|
||||
int ret = SUCCESS;
|
||||
|
||||
ret = check_qup_state_valid();
|
||||
if (ret != SUCCESS)
|
||||
return ret;
|
||||
|
||||
/* Set the state */
|
||||
val = readl(GSBI_QUP_BASE(gsbi_port) + QUP_STATE_OFFSET);
|
||||
val = ((val & ~QUP_STATE_MASK) | state);
|
||||
writel(val, GSBI_QUP_BASE(gsbi_port) + QUP_STATE_OFFSET);
|
||||
ret = check_qup_state_valid();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure I2C IO Mode.
|
||||
*/
|
||||
void config_i2c_mode(void)
|
||||
{
|
||||
int cfg;
|
||||
|
||||
cfg = readl(GSBI_QUP_BASE(gsbi_port) + QUP_IO_MODES_OFFSET);
|
||||
cfg |= (INPUT_FIFO_MODE |
|
||||
OUTPUT_FIFO_MODE |
|
||||
OUTPUT_BIT_SHIFT_EN);
|
||||
writel(cfg, GSBI_QUP_BASE(gsbi_port) + QUP_IO_MODES_OFFSET);
|
||||
}
|
||||
|
||||
void i2c_ipq_board_init(void)
|
||||
{
|
||||
gsbi_base_addr = gboard_param->i2c_gsbi_base;
|
||||
gsbi_port = gboard_param->i2c_gsbi;
|
||||
|
||||
/*
|
||||
* Currently I2C GPIO lines (12(SDA), 13(SCLK)) are configured by
|
||||
* RPM firmware for PMIC. Trustzone firmware configures the TLMM
|
||||
* RPU in such a way that only RPM will be able to configure those
|
||||
* GPIOs. If Krait non secure (u-boot) tries to write to those GPIO
|
||||
* registers will lead to Master port L2 error.
|
||||
* GSBI4 uses GPIO 12 and 13 lines for I2C.
|
||||
* So for GSBI4 we will not update the GPIO configs. RPM would have
|
||||
* already configured this.`
|
||||
*/
|
||||
|
||||
/* Configure the GPIOs */
|
||||
if (gsbi_port != GSBI_4)
|
||||
ipq_configure_gpio(gboard_param->i2c_gpio, NO_OF_I2C_GPIOS);
|
||||
|
||||
/* Configure the I2C clock */
|
||||
i2c_clock_config(gboard_param->i2c_gsbi,
|
||||
gboard_param->i2c_mnd_value.m_value,
|
||||
gboard_param->i2c_mnd_value.n_value,
|
||||
gboard_param->i2c_mnd_value.d_value,
|
||||
gboard_param->clk_dummy);
|
||||
|
||||
i2c_hw_initialized = 0;
|
||||
i2c_board_initialized = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* GSBIn I2C Hardware Initialisation
|
||||
*/
|
||||
static int i2c_hw_init(void)
|
||||
{
|
||||
int ret, cfg;
|
||||
|
||||
/* GSBI module configuration */
|
||||
i2c_reset();
|
||||
|
||||
/* Set the GSBIn QUP state */
|
||||
ret = config_i2c_state(QUP_STATE_RESET);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Configure GSBI_CTRL register to set protocol_mode to I2C_UART:110 */
|
||||
writel(GSBI_PROTOCOL_CODE_I2C_UART <<
|
||||
GSBI_CTRL_REG_PROTOCOL_CODE_S,
|
||||
GSBI_CTRL_REG(gsbi_base_addr));
|
||||
|
||||
/* Configure Mini core to I2C core */
|
||||
cfg = readl(GSBI_QUP_BASE(gsbi_port) + QUP_CONFIG_OFFSET);
|
||||
cfg |= (QUP_CONFIG_MINI_CORE_I2C |
|
||||
I2C_BIT_WORD);
|
||||
writel(cfg, GSBI_QUP_BASE(gsbi_port) + QUP_CONFIG_OFFSET);
|
||||
|
||||
/* Configure I2C mode */
|
||||
config_i2c_mode();
|
||||
|
||||
/* Enable QUP Error Flags */
|
||||
writel(ERROR_FLAGS_EN,
|
||||
GSBI_QUP_BASE(gsbi_port) + QUP_ERROR_FLAGS_EN_OFFSET);
|
||||
|
||||
/* Clear the MASTER_CTL_STATUS */
|
||||
writel(I2C_MASTER_STATUS_CLEAR,
|
||||
GSBI_QUP_BASE(gsbi_port) + QUP_I2C_MASTER_STATUS_OFFSET);
|
||||
|
||||
/* Set to RUN STATE */
|
||||
ret = config_i2c_state(QUP_STATE_RUN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Configure the I2C Master clock */
|
||||
i2c_master_clk_ctl = ((QUP_INPUT_CLK / (CONFIG_I2C_CLK_FREQ * 2)) - 3) & 0xff;
|
||||
writel(i2c_master_clk_ctl, GSBI_QUP_BASE(gsbi_port) + QUP_I2C_MASTER_CLK_CTL_OFFSET);
|
||||
|
||||
i2c_hw_initialized = 1;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to check wheather Input or Output FIFO
|
||||
* has data to be serviced. For invalid slaves, this
|
||||
* flag will not be set.
|
||||
*/
|
||||
static int check_fifo_status(uint dir)
|
||||
{
|
||||
unsigned int count = TIMEOUT_CNT;
|
||||
unsigned int status_flag;
|
||||
unsigned int val;
|
||||
|
||||
if (dir == READ) {
|
||||
do {
|
||||
val = readl(GSBI_QUP_BASE(gsbi_port)
|
||||
+ QUP_OPERATIONAL_OFFSET);
|
||||
count--;
|
||||
if (count == 0)
|
||||
return -ETIMEDOUT;
|
||||
status_flag = val & INPUT_SERVICE_FLAG;
|
||||
udelay(10);
|
||||
} while (!status_flag);
|
||||
|
||||
} else if (dir == WRITE) {
|
||||
do {
|
||||
val = readl(GSBI_QUP_BASE(gsbi_port)
|
||||
+ QUP_OPERATIONAL_OFFSET);
|
||||
count--;
|
||||
if (count == 0)
|
||||
return -ETIMEDOUT;
|
||||
status_flag = val & OUTPUT_FIFO_FULL;
|
||||
udelay(10);
|
||||
} while (status_flag);
|
||||
|
||||
/*
|
||||
* Clear the flag and Acknowledge that the
|
||||
* software has or will write the data.
|
||||
*/
|
||||
if (readl(GSBI_QUP_BASE(gsbi_port) + QUP_OPERATIONAL_OFFSET)
|
||||
& OUTPUT_SERVICE_FLAG) {
|
||||
writel(OUTPUT_SERVICE_FLAG, GSBI_QUP_BASE(gsbi_port)
|
||||
+ QUP_OPERATIONAL_OFFSET);
|
||||
}
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the values in the OUTPUT FIFO are shifted out.
|
||||
*/
|
||||
static int check_write_done(void)
|
||||
{
|
||||
unsigned int count = TIMEOUT_CNT;
|
||||
unsigned int status_flag;
|
||||
unsigned int val;
|
||||
|
||||
do {
|
||||
val = readl(GSBI_QUP_BASE(gsbi_port)
|
||||
+ QUP_OPERATIONAL_OFFSET);
|
||||
count--;
|
||||
if (count == 0)
|
||||
return -ETIMEDOUT;
|
||||
status_flag = val & OUTPUT_FIFO_NOT_EMPTY;
|
||||
udelay(10);
|
||||
} while (status_flag);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int data = 0;
|
||||
|
||||
if (!i2c_board_initialized) {
|
||||
i2c_ipq_board_init();
|
||||
}
|
||||
|
||||
if(!i2c_hw_initialized) {
|
||||
i2c_hw_init();
|
||||
}
|
||||
|
||||
writel(0xFF, GSBI_QUP_BASE(gsbi_port)
|
||||
+ QUP_ERROR_FLAGS_OFFSET);
|
||||
|
||||
writel(0, GSBI_QUP_BASE(gsbi_port)
|
||||
+ QUP_I2C_MASTER_STATUS_OFFSET);
|
||||
|
||||
/* Set to RUN state */
|
||||
ret = config_i2c_state(QUP_STATE_RUN);
|
||||
if (ret != SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* Configure the I2C Master clock */
|
||||
writel(i2c_master_clk_ctl, GSBI_QUP_BASE(gsbi_port) + QUP_I2C_MASTER_CLK_CTL_OFFSET);
|
||||
|
||||
/* Send a write request to the chip */
|
||||
writel((QUP_I2C_START_SEQ | QUP_I2C_ADDR(chip)),
|
||||
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
|
||||
|
||||
writel((QUP_I2C_DATA_SEQ | QUP_I2C_DATA(addr)),
|
||||
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
|
||||
|
||||
ret = check_write_done();
|
||||
if (ret != SUCCESS)
|
||||
goto out;
|
||||
|
||||
ret = check_fifo_status(WRITE);
|
||||
if (ret != SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* Send read request */
|
||||
writel((QUP_I2C_START_SEQ |
|
||||
(QUP_I2C_ADDR(chip)|
|
||||
QUP_I2C_SLAVE_READ)),
|
||||
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
|
||||
|
||||
writel((QUP_I2C_RECV_SEQ | len),
|
||||
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
|
||||
|
||||
while (len--) {
|
||||
|
||||
ret = check_fifo_status(READ);
|
||||
if (ret != SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* Read the data from the FIFO */
|
||||
data = readl(GSBI_QUP_BASE(gsbi_port) + QUP_INPUT_FIFO_OFFSET);
|
||||
*buffer = QUP_I2C_DATA(data);
|
||||
|
||||
/*
|
||||
* Clear the flag and Acknowledge that the
|
||||
* software has or will read the data.
|
||||
*/
|
||||
writel(INPUT_SERVICE_FLAG,
|
||||
GSBI_QUP_BASE(gsbi_port) + QUP_OPERATIONAL_OFFSET);
|
||||
|
||||
buffer++;
|
||||
}
|
||||
|
||||
/* Set to PAUSE state */
|
||||
ret = config_i2c_state(QUP_STATE_PAUSE);
|
||||
if (ret != SUCCESS)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
/*
|
||||
* Put the I2C Core back in the Reset State to end the transfer.
|
||||
*/
|
||||
(void)config_i2c_state(QUP_STATE_RESET);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int ret = 0;
|
||||
int idx = 0;
|
||||
|
||||
if (!i2c_board_initialized) {
|
||||
i2c_ipq_board_init();
|
||||
}
|
||||
|
||||
if(!i2c_hw_initialized) {
|
||||
i2c_hw_init();
|
||||
}
|
||||
|
||||
/* Set to RUN state */
|
||||
ret = config_i2c_state(QUP_STATE_RUN);
|
||||
if (ret != SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* Configure the I2C Master clock */
|
||||
writel(i2c_master_clk_ctl, GSBI_QUP_BASE(gsbi_port) + QUP_I2C_MASTER_CLK_CTL_OFFSET);
|
||||
|
||||
/* Send the write request */
|
||||
writel((QUP_I2C_START_SEQ | QUP_I2C_ADDR(chip)),
|
||||
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
|
||||
writel((QUP_I2C_DATA_SEQ | QUP_I2C_DATA(addr)),
|
||||
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
|
||||
|
||||
while (len) {
|
||||
if (len == 1) {
|
||||
writel((QUP_I2C_STOP_SEQ | QUP_I2C_DATA(buffer[idx])),
|
||||
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
|
||||
} else {
|
||||
writel((QUP_I2C_DATA_SEQ | QUP_I2C_DATA(buffer[idx])),
|
||||
GSBI_QUP_BASE(gsbi_port) + QUP_OUTPUT_FIFO_OFFSET);
|
||||
}
|
||||
len--;
|
||||
idx++;
|
||||
|
||||
ret = check_fifo_status(WRITE);
|
||||
if (ret != SUCCESS)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = check_write_done();
|
||||
if (ret != SUCCESS)
|
||||
goto out;
|
||||
|
||||
/* Set to PAUSE state */
|
||||
ret = config_i2c_state(QUP_STATE_PAUSE);
|
||||
if (ret != SUCCESS)
|
||||
goto out;
|
||||
|
||||
return ret;
|
||||
out:
|
||||
/*
|
||||
* Put the I2C Core back in the Reset State to end the transfer.
|
||||
*/
|
||||
(void)config_i2c_state(QUP_STATE_RESET);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Probe the given I2C chip address.
|
||||
* Returns 0 if a chip responded.
|
||||
*/
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
uchar buf;
|
||||
|
||||
return i2c_read(chip, 0x0, 0x1, &buf, 0x1);
|
||||
}
|
||||
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr);
|
||||
}
|
||||
|
||||
|
155
root/package/utils/sysupgrade-helper/src/drivers/i2c/ipq_i2c.h
Normal file
155
root/package/utils/sysupgrade-helper/src/drivers/i2c/ipq_i2c.h
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright (c) 2014 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only 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.
|
||||
*/
|
||||
|
||||
static int gsbi_base[] = {
|
||||
0x12440000,
|
||||
0x12480000,
|
||||
0x16200000,
|
||||
0x16300000,
|
||||
0x1A200000,
|
||||
0x16500000,
|
||||
0x16600000
|
||||
};
|
||||
|
||||
/* GSBI QUP devices base address */
|
||||
static int qup_offset[] = {
|
||||
0x20000,
|
||||
0x20000,
|
||||
0x80000,
|
||||
0x80000,
|
||||
0x80000,
|
||||
0x80000,
|
||||
0x80000,
|
||||
};
|
||||
|
||||
/* QUP Registers Base Address */
|
||||
#define GSBI_QUP_BASE(x) (gsbi_base[x-1] + qup_offset[x-1])
|
||||
|
||||
#define GSBI_RESET(N) (0x009029DC + (32*(N-1)))
|
||||
|
||||
#define I2C_STATUS_ERROR_MASK 0x38000FC
|
||||
|
||||
|
||||
/* QUP Core register offsets */
|
||||
#define QUP_CONFIG_OFFSET 0x0
|
||||
#define QUP_STATE_OFFSET 0x4
|
||||
#define QUP_IO_MODES_OFFSET 0x8
|
||||
#define QUP_SW_RESET_OFFSET 0xc
|
||||
#define QUP_TIME_OUT_OFFSET 0x10
|
||||
#define QUP_TIME_OUT_CURRENT_OFFSET 0x14
|
||||
#define QUP_OPERATIONAL_OFFSET 0x18
|
||||
#define QUP_ERROR_FLAGS_OFFSET 0x1c
|
||||
#define QUP_ERROR_FLAGS_EN_OFFSET 0x20
|
||||
#define QUP_TEST_CTRL_OFFSET 0x24
|
||||
#define QUP_MX_OUTPUT_COUNT_OFFSET 0x100
|
||||
#define QUP_MX_OUTPUT_CNT_CURRENT_OFFSET 0x104
|
||||
#define QUP_OUTPUT_DEBUG_OFFSET 0x108
|
||||
#define QUP_OUTPUT_FIFO_WORD_CNT_OFFSET 0x10c
|
||||
#define QUP_OUTPUT_FIFO_OFFSET 0x110
|
||||
#define QUP_MX_WRITE_COUNT_OFFSET 0x150
|
||||
#define QUP_WRITE_CNT_CURRENT_OFFSET 0x154
|
||||
#define QUP_MX_INPUT_COUNT_OFFSET 0x200
|
||||
#define QUP_MX_READ_COUNT_OFFSET 0x208
|
||||
#define QUP_MX_READ_CNT_CURRENT_OFFSET 0x20c
|
||||
#define QUP_INPUT_DEBUG_OFFSET 0x210
|
||||
#define QUP_INPUT_FIFO_WORD_CNT_OFFSET 0x214
|
||||
#define QUP_INPUT_FIFO_OFFSET 0x218
|
||||
#define QUP_I2C_MASTER_CLK_CTL_OFFSET 0x400
|
||||
#define QUP_I2C_MASTER_STATUS_OFFSET 0x404
|
||||
#define QUP_MAX_OFFSET 0xfff
|
||||
|
||||
#define SUCCESS 0
|
||||
#define TIMEOUT_CNT 100
|
||||
|
||||
#define QUP_STATE_RESET 0x0
|
||||
#define QUP_STATE_RUN 0x1D
|
||||
#define QUP_STATE_PAUSE 0x1F
|
||||
#define QUP_STATE_VALID_BIT 2
|
||||
#define QUP_STATE_VALID 1
|
||||
#define QUP_STATE_MASK 0x3
|
||||
|
||||
#define QUP_CONFIG_MINI_CORE_I2C (2 << 8)
|
||||
#define I2C_BIT_WORD 0xF
|
||||
#define INPUT_FIFO_MODE (0x0 << 12)
|
||||
#define OUTPUT_FIFO_MODE (0x0 << 10)
|
||||
#define INPUT_BLOCK_MODE (0x01 << 12)
|
||||
#define OUTPUT_BLOCK_MODE (0x01 << 10)
|
||||
#define PACK_EN (0x01 << 15)
|
||||
#define UNPACK_EN (0x01 << 14)
|
||||
#define OUTPUT_BIT_SHIFT_EN (0x01 << 16)
|
||||
#define ERROR_FLAGS_EN 0x7C
|
||||
#define I2C_MASTER_STATUS_CLEAR 0xFFFFFC
|
||||
#define QUP_DATA_AVAILABLE_FOR_READ (1 << 5)
|
||||
#define QUP_OUTPUT_FIFO_NOT_EMPTY (1 << 4)
|
||||
#define OUTPUT_SERVICE_FLAG (1 << 8)
|
||||
#define INPUT_SERVICE_FLAG (1 << 9)
|
||||
#define QUP_OUTPUT_FIFO_FULL (1 << 6)
|
||||
#define MAX_OUTPUT_DONE_FLAG (1 << 10)
|
||||
#define MAX_INPUT_DONE_FLAG (1 << 11)
|
||||
#define QUP_DATA_AVAILABLE_FOR_READ (1 << 5)
|
||||
#define OUTPUT_FIFO_NOT_EMPTY (1 << 4)
|
||||
#define OUTPUT_FIFO_FULL (1 << 6)
|
||||
|
||||
#define QUP_I2C_START_SEQ (0x1 << 8)
|
||||
#define QUP_I2C_DATA_SEQ (0x2 << 8)
|
||||
#define QUP_I2C_STOP_SEQ (0x3 << 8)
|
||||
#define QUP_I2C_RECV_SEQ (0x4 << 8)
|
||||
|
||||
/* Tags for input FIFO */
|
||||
#define QUP_I2C_MIDATA_SEQ (0x5 << 8)
|
||||
#define QUP_I2C_MISTOP_SEQ (0x6 << 8)
|
||||
#define QUP_I2C_MINACK_SEQ (0x7 << 8)
|
||||
|
||||
#define QUP_I2C_ADDR(x) ((x & 0xFF) << 1)
|
||||
#define QUP_I2C_DATA(x) (x & 0xFF)
|
||||
#define QUP_I2C_MI_TAG(x) (x & 0xFF00)
|
||||
#define QUP_I2C_SLAVE_READ (0x1)
|
||||
|
||||
enum dir {
|
||||
READ,
|
||||
WRITE,
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
QUPI2C_NOP,
|
||||
QUPI2C_START,
|
||||
QUPI2C_MO_DATA,
|
||||
QUPI2C_MO_STOP,
|
||||
QUPI2C_MI_REC,
|
||||
QUPI2C_MI_DATA,
|
||||
QUPI2C_MI_STOP,
|
||||
QUPI2C_MI_NACK,
|
||||
QUPI2C_SO_DATA,
|
||||
QUPI2C_SI_START,
|
||||
QUPI2C_SI_DATA
|
||||
} qupi2c_TagType;
|
||||
|
||||
#define qupi2c_NumberOfNops(c) ((QUPI2C_NOP << 8) | c)
|
||||
#define qupi2c_StartWrite(c) ((QUPI2C_START << 8) | (c << 1) | 0)
|
||||
#define qupi2c_StartRead(c) ((QUPI2C_START << 8) | (c << 1) | 1)
|
||||
#define qupi2c_ByteWrite(c) ((QUPI2C_MO_DATA << 8) | c)
|
||||
#define qupi2c_LastByteWrite(c) ((QUPI2C_MO_STOP << 8) | c)
|
||||
#define qupi2c_NumberofReads(c) ((QUPI2C_MI_REC << 8) | c)
|
||||
|
||||
/* I2C some pre-defined frequencies */
|
||||
#define I2C_CLK_1KHZ 1
|
||||
#define I2C_CLK_100KHZ 100
|
||||
#define I2C_CLK_400KHZ 400
|
||||
#define I2C_CLK_1MHZ 1000
|
||||
#define QUP_INPUT_CLK_TCXO 19200
|
||||
#define QUP_INPUT_CLK_PLL8 24000
|
||||
#define I2C_INPUT_CLK_TCXO_DIV4 ((I2C_INPUT_CLK_TCXO)/4)
|
||||
#define QUP_INPUT_CLK QUP_INPUT_CLK_PLL8
|
||||
#define CONFIG_I2C_CLK_FREQ I2C_CLK_100KHZ
|
||||
|
487
root/package/utils/sysupgrade-helper/src/drivers/i2c/mv_i2c.c
Normal file
487
root/package/utils/sysupgrade-helper/src/drivers/i2c/mv_i2c.c
Normal file
|
@ -0,0 +1,487 @@
|
|||
/*
|
||||
* (C) Copyright 2000
|
||||
* Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it
|
||||
*
|
||||
* (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
|
||||
* Marius Groeger <mgroeger@sysgo.de>
|
||||
*
|
||||
* (C) Copyright 2003 Pengutronix e.K.
|
||||
* Robert Schwebel <r.schwebel@pengutronix.de>
|
||||
*
|
||||
* (C) Copyright 2011 Marvell Inc.
|
||||
* Lei Wen <leiwen@marvell.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
|
||||
*
|
||||
* Back ported to the 8xx platform (from the 8260 platform) by
|
||||
* Murray.Jensen@cmst.csiro.au, 27-Jan-01.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#ifdef CONFIG_HARD_I2C
|
||||
#include <i2c.h>
|
||||
#include "mv_i2c.h"
|
||||
|
||||
#ifdef DEBUG_I2C
|
||||
#define PRINTD(x) printf x
|
||||
#else
|
||||
#define PRINTD(x)
|
||||
#endif
|
||||
|
||||
/* All transfers are described by this data structure */
|
||||
struct i2c_msg {
|
||||
u8 condition;
|
||||
u8 acknack;
|
||||
u8 direction;
|
||||
u8 data;
|
||||
};
|
||||
|
||||
struct mv_i2c {
|
||||
u32 ibmr;
|
||||
u32 pad0;
|
||||
u32 idbr;
|
||||
u32 pad1;
|
||||
u32 icr;
|
||||
u32 pad2;
|
||||
u32 isr;
|
||||
u32 pad3;
|
||||
u32 isar;
|
||||
};
|
||||
|
||||
static struct mv_i2c *base;
|
||||
static void i2c_board_init(struct mv_i2c *base)
|
||||
{
|
||||
#ifdef CONFIG_SYS_I2C_INIT_BOARD
|
||||
u32 icr;
|
||||
/*
|
||||
* call board specific i2c bus reset routine before accessing the
|
||||
* environment, which might be in a chip on that bus. For details
|
||||
* about this problem see doc/I2C_Edge_Conditions.
|
||||
*
|
||||
* disable I2C controller first, otherwhise it thinks we want to
|
||||
* talk to the slave port...
|
||||
*/
|
||||
icr = readl(&base->icr);
|
||||
writel(readl(&base->icr) & ~(ICR_SCLE | ICR_IUE), &base->icr);
|
||||
|
||||
i2c_init_board();
|
||||
|
||||
writel(icr, &base->icr);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_I2C_MULTI_BUS
|
||||
static u32 i2c_regs[CONFIG_MV_I2C_NUM] = CONFIG_MV_I2C_REG;
|
||||
static unsigned int bus_initialized[CONFIG_MV_I2C_NUM];
|
||||
static unsigned int current_bus;
|
||||
|
||||
int i2c_set_bus_num(unsigned int bus)
|
||||
{
|
||||
if ((bus < 0) || (bus >= CONFIG_MV_I2C_NUM)) {
|
||||
printf("Bad bus: %d\n", bus);
|
||||
return -1;
|
||||
}
|
||||
|
||||
base = (struct mv_i2c *)i2c_regs[bus];
|
||||
current_bus = bus;
|
||||
|
||||
if (!bus_initialized[current_bus]) {
|
||||
i2c_board_init(base);
|
||||
bus_initialized[current_bus] = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int i2c_get_bus_num(void)
|
||||
{
|
||||
return current_bus;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* i2c_reset: - reset the host controller
|
||||
*
|
||||
*/
|
||||
static void i2c_reset(void)
|
||||
{
|
||||
writel(readl(&base->icr) & ~ICR_IUE, &base->icr); /* disable unit */
|
||||
writel(readl(&base->icr) | ICR_UR, &base->icr); /* reset the unit */
|
||||
udelay(100);
|
||||
writel(readl(&base->icr) & ~ICR_IUE, &base->icr); /* disable unit */
|
||||
|
||||
i2c_clk_enable();
|
||||
|
||||
writel(CONFIG_SYS_I2C_SLAVE, &base->isar); /* set our slave address */
|
||||
writel(I2C_ICR_INIT, &base->icr); /* set control reg values */
|
||||
writel(I2C_ISR_INIT, &base->isr); /* set clear interrupt bits */
|
||||
writel(readl(&base->icr) | ICR_IUE, &base->icr); /* enable unit */
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_isr_set_cleared: - wait until certain bits of the I2C status register
|
||||
* are set and cleared
|
||||
*
|
||||
* @return: 1 in case of success, 0 means timeout (no match within 10 ms).
|
||||
*/
|
||||
static int i2c_isr_set_cleared(unsigned long set_mask,
|
||||
unsigned long cleared_mask)
|
||||
{
|
||||
int timeout = 1000, isr;
|
||||
|
||||
do {
|
||||
isr = readl(&base->isr);
|
||||
udelay(10);
|
||||
if (timeout-- < 0)
|
||||
return 0;
|
||||
} while (((isr & set_mask) != set_mask)
|
||||
|| ((isr & cleared_mask) != 0));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_transfer: - Transfer one byte over the i2c bus
|
||||
*
|
||||
* This function can tranfer a byte over the i2c bus in both directions.
|
||||
* It is used by the public API functions.
|
||||
*
|
||||
* @return: 0: transfer successful
|
||||
* -1: message is empty
|
||||
* -2: transmit timeout
|
||||
* -3: ACK missing
|
||||
* -4: receive timeout
|
||||
* -5: illegal parameters
|
||||
* -6: bus is busy and couldn't be aquired
|
||||
*/
|
||||
int i2c_transfer(struct i2c_msg *msg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!msg)
|
||||
goto transfer_error_msg_empty;
|
||||
|
||||
switch (msg->direction) {
|
||||
case I2C_WRITE:
|
||||
/* check if bus is not busy */
|
||||
if (!i2c_isr_set_cleared(0, ISR_IBB))
|
||||
goto transfer_error_bus_busy;
|
||||
|
||||
/* start transmission */
|
||||
writel(readl(&base->icr) & ~ICR_START, &base->icr);
|
||||
writel(readl(&base->icr) & ~ICR_STOP, &base->icr);
|
||||
writel(msg->data, &base->idbr);
|
||||
if (msg->condition == I2C_COND_START)
|
||||
writel(readl(&base->icr) | ICR_START, &base->icr);
|
||||
if (msg->condition == I2C_COND_STOP)
|
||||
writel(readl(&base->icr) | ICR_STOP, &base->icr);
|
||||
if (msg->acknack == I2C_ACKNAK_SENDNAK)
|
||||
writel(readl(&base->icr) | ICR_ACKNAK, &base->icr);
|
||||
if (msg->acknack == I2C_ACKNAK_SENDACK)
|
||||
writel(readl(&base->icr) & ~ICR_ACKNAK, &base->icr);
|
||||
writel(readl(&base->icr) & ~ICR_ALDIE, &base->icr);
|
||||
writel(readl(&base->icr) | ICR_TB, &base->icr);
|
||||
|
||||
/* transmit register empty? */
|
||||
if (!i2c_isr_set_cleared(ISR_ITE, 0))
|
||||
goto transfer_error_transmit_timeout;
|
||||
|
||||
/* clear 'transmit empty' state */
|
||||
writel(readl(&base->isr) | ISR_ITE, &base->isr);
|
||||
|
||||
/* wait for ACK from slave */
|
||||
if (msg->acknack == I2C_ACKNAK_WAITACK)
|
||||
if (!i2c_isr_set_cleared(0, ISR_ACKNAK))
|
||||
goto transfer_error_ack_missing;
|
||||
break;
|
||||
|
||||
case I2C_READ:
|
||||
|
||||
/* check if bus is not busy */
|
||||
if (!i2c_isr_set_cleared(0, ISR_IBB))
|
||||
goto transfer_error_bus_busy;
|
||||
|
||||
/* start receive */
|
||||
writel(readl(&base->icr) & ~ICR_START, &base->icr);
|
||||
writel(readl(&base->icr) & ~ICR_STOP, &base->icr);
|
||||
if (msg->condition == I2C_COND_START)
|
||||
writel(readl(&base->icr) | ICR_START, &base->icr);
|
||||
if (msg->condition == I2C_COND_STOP)
|
||||
writel(readl(&base->icr) | ICR_STOP, &base->icr);
|
||||
if (msg->acknack == I2C_ACKNAK_SENDNAK)
|
||||
writel(readl(&base->icr) | ICR_ACKNAK, &base->icr);
|
||||
if (msg->acknack == I2C_ACKNAK_SENDACK)
|
||||
writel(readl(&base->icr) & ~ICR_ACKNAK, &base->icr);
|
||||
writel(readl(&base->icr) & ~ICR_ALDIE, &base->icr);
|
||||
writel(readl(&base->icr) | ICR_TB, &base->icr);
|
||||
|
||||
/* receive register full? */
|
||||
if (!i2c_isr_set_cleared(ISR_IRF, 0))
|
||||
goto transfer_error_receive_timeout;
|
||||
|
||||
msg->data = readl(&base->idbr);
|
||||
|
||||
/* clear 'receive empty' state */
|
||||
writel(readl(&base->isr) | ISR_IRF, &base->isr);
|
||||
break;
|
||||
default:
|
||||
goto transfer_error_illegal_param;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
transfer_error_msg_empty:
|
||||
PRINTD(("i2c_transfer: error: 'msg' is empty\n"));
|
||||
ret = -1; goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_transmit_timeout:
|
||||
PRINTD(("i2c_transfer: error: transmit timeout\n"));
|
||||
ret = -2; goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_ack_missing:
|
||||
PRINTD(("i2c_transfer: error: ACK missing\n"));
|
||||
ret = -3; goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_receive_timeout:
|
||||
PRINTD(("i2c_transfer: error: receive timeout\n"));
|
||||
ret = -4; goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_illegal_param:
|
||||
PRINTD(("i2c_transfer: error: illegal parameters\n"));
|
||||
ret = -5; goto i2c_transfer_finish;
|
||||
|
||||
transfer_error_bus_busy:
|
||||
PRINTD(("i2c_transfer: error: bus is busy\n"));
|
||||
ret = -6; goto i2c_transfer_finish;
|
||||
|
||||
i2c_transfer_finish:
|
||||
PRINTD(("i2c_transfer: ISR: 0x%04x\n", readl(&base->isr)));
|
||||
i2c_reset();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* API Functions */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
#ifdef CONFIG_I2C_MULTI_BUS
|
||||
current_bus = 0;
|
||||
base = (struct mv_i2c *)i2c_regs[current_bus];
|
||||
#else
|
||||
base = (struct mv_i2c *)CONFIG_MV_I2C_REG;
|
||||
#endif
|
||||
|
||||
i2c_board_init(base);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_probe: - Test if a chip answers for a given i2c address
|
||||
*
|
||||
* @chip: address of the chip which is searched for
|
||||
* @return: 0 if a chip was found, -1 otherwhise
|
||||
*/
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
struct i2c_msg msg;
|
||||
|
||||
i2c_reset();
|
||||
|
||||
msg.condition = I2C_COND_START;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = (chip << 1) + 1;
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
|
||||
msg.condition = I2C_COND_STOP;
|
||||
msg.acknack = I2C_ACKNAK_SENDNAK;
|
||||
msg.direction = I2C_READ;
|
||||
msg.data = 0x00;
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_read: - Read multiple bytes from an i2c device
|
||||
*
|
||||
* The higher level routines take into account that this function is only
|
||||
* called with len < page length of the device (see configuration file)
|
||||
*
|
||||
* @chip: address of the chip which is to be read
|
||||
* @addr: i2c data address within the chip
|
||||
* @alen: length of the i2c data address (1..2 bytes)
|
||||
* @buffer: where to write the data
|
||||
* @len: how much byte do we want to read
|
||||
* @return: 0 in case of success
|
||||
*/
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
struct i2c_msg msg;
|
||||
u8 addr_bytes[3]; /* lowest...highest byte of data address */
|
||||
|
||||
PRINTD(("i2c_read(chip=0x%02x, addr=0x%02x, alen=0x%02x, "
|
||||
"len=0x%02x)\n", chip, addr, alen, len));
|
||||
|
||||
i2c_reset();
|
||||
|
||||
/* dummy chip address write */
|
||||
PRINTD(("i2c_read: dummy chip address write\n"));
|
||||
msg.condition = I2C_COND_START;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = (chip << 1);
|
||||
msg.data &= 0xFE;
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* send memory address bytes;
|
||||
* alen defines how much bytes we have to send.
|
||||
*/
|
||||
/*addr &= ((1 << CONFIG_SYS_EEPROM_PAGE_WRITE_BITS)-1); */
|
||||
addr_bytes[0] = (u8)((addr >> 0) & 0x000000FF);
|
||||
addr_bytes[1] = (u8)((addr >> 8) & 0x000000FF);
|
||||
addr_bytes[2] = (u8)((addr >> 16) & 0x000000FF);
|
||||
|
||||
while (--alen >= 0) {
|
||||
PRINTD(("i2c_read: send memory word address byte %1d\n", alen));
|
||||
msg.condition = I2C_COND_NORMAL;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = addr_bytes[alen];
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* start read sequence */
|
||||
PRINTD(("i2c_read: start read sequence\n"));
|
||||
msg.condition = I2C_COND_START;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = (chip << 1);
|
||||
msg.data |= 0x01;
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
|
||||
/* read bytes; send NACK at last byte */
|
||||
while (len--) {
|
||||
if (len == 0) {
|
||||
msg.condition = I2C_COND_STOP;
|
||||
msg.acknack = I2C_ACKNAK_SENDNAK;
|
||||
} else {
|
||||
msg.condition = I2C_COND_NORMAL;
|
||||
msg.acknack = I2C_ACKNAK_SENDACK;
|
||||
}
|
||||
|
||||
msg.direction = I2C_READ;
|
||||
msg.data = 0x00;
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
|
||||
*buffer = msg.data;
|
||||
PRINTD(("i2c_read: reading byte (0x%08x)=0x%02x\n",
|
||||
(unsigned int)buffer, *buffer));
|
||||
buffer++;
|
||||
}
|
||||
|
||||
i2c_reset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_write: - Write multiple bytes to an i2c device
|
||||
*
|
||||
* The higher level routines take into account that this function is only
|
||||
* called with len < page length of the device (see configuration file)
|
||||
*
|
||||
* @chip: address of the chip which is to be written
|
||||
* @addr: i2c data address within the chip
|
||||
* @alen: length of the i2c data address (1..2 bytes)
|
||||
* @buffer: where to find the data to be written
|
||||
* @len: how much byte do we want to read
|
||||
* @return: 0 in case of success
|
||||
*/
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
struct i2c_msg msg;
|
||||
u8 addr_bytes[3]; /* lowest...highest byte of data address */
|
||||
|
||||
PRINTD(("i2c_write(chip=0x%02x, addr=0x%02x, alen=0x%02x, "
|
||||
"len=0x%02x)\n", chip, addr, alen, len));
|
||||
|
||||
i2c_reset();
|
||||
|
||||
/* chip address write */
|
||||
PRINTD(("i2c_write: chip address write\n"));
|
||||
msg.condition = I2C_COND_START;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = (chip << 1);
|
||||
msg.data &= 0xFE;
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* send memory address bytes;
|
||||
* alen defines how much bytes we have to send.
|
||||
*/
|
||||
addr_bytes[0] = (u8)((addr >> 0) & 0x000000FF);
|
||||
addr_bytes[1] = (u8)((addr >> 8) & 0x000000FF);
|
||||
addr_bytes[2] = (u8)((addr >> 16) & 0x000000FF);
|
||||
|
||||
while (--alen >= 0) {
|
||||
PRINTD(("i2c_write: send memory word address\n"));
|
||||
msg.condition = I2C_COND_NORMAL;
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = addr_bytes[alen];
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* write bytes; send NACK at last byte */
|
||||
while (len--) {
|
||||
PRINTD(("i2c_write: writing byte (0x%08x)=0x%02x\n",
|
||||
(unsigned int)buffer, *buffer));
|
||||
|
||||
if (len == 0)
|
||||
msg.condition = I2C_COND_STOP;
|
||||
else
|
||||
msg.condition = I2C_COND_NORMAL;
|
||||
|
||||
msg.acknack = I2C_ACKNAK_WAITACK;
|
||||
msg.direction = I2C_WRITE;
|
||||
msg.data = *(buffer++);
|
||||
|
||||
if (i2c_transfer(&msg))
|
||||
return -1;
|
||||
}
|
||||
|
||||
i2c_reset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_HARD_I2C */
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* (C) Copyright 2011
|
||||
* Marvell Inc, <www.marvell.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
|
||||
*/
|
||||
|
||||
#ifndef _MV_I2C_H_
|
||||
#define _MV_I2C_H_
|
||||
extern void i2c_clk_enable(void);
|
||||
|
||||
/* Shall the current transfer have a start/stop condition? */
|
||||
#define I2C_COND_NORMAL 0
|
||||
#define I2C_COND_START 1
|
||||
#define I2C_COND_STOP 2
|
||||
|
||||
/* Shall the current transfer be ack/nacked or being waited for it? */
|
||||
#define I2C_ACKNAK_WAITACK 1
|
||||
#define I2C_ACKNAK_SENDACK 2
|
||||
#define I2C_ACKNAK_SENDNAK 4
|
||||
|
||||
/* Specify who shall transfer the data (master or slave) */
|
||||
#define I2C_READ 0
|
||||
#define I2C_WRITE 1
|
||||
|
||||
#if (CONFIG_SYS_I2C_SPEED == 400000)
|
||||
#define I2C_ICR_INIT (ICR_FM | ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD \
|
||||
| ICR_SCLE)
|
||||
#else
|
||||
#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE)
|
||||
#endif
|
||||
|
||||
#define I2C_ISR_INIT 0x7FF
|
||||
/* ----- Control register bits ---------------------------------------- */
|
||||
|
||||
#define ICR_START 0x1 /* start bit */
|
||||
#define ICR_STOP 0x2 /* stop bit */
|
||||
#define ICR_ACKNAK 0x4 /* send ACK(0) or NAK(1) */
|
||||
#define ICR_TB 0x8 /* transfer byte bit */
|
||||
#define ICR_MA 0x10 /* master abort */
|
||||
#define ICR_SCLE 0x20 /* master clock enable, mona SCLEA */
|
||||
#define ICR_IUE 0x40 /* unit enable */
|
||||
#define ICR_GCD 0x80 /* general call disable */
|
||||
#define ICR_ITEIE 0x100 /* enable tx interrupts */
|
||||
#define ICR_IRFIE 0x200 /* enable rx interrupts, mona: DRFIE */
|
||||
#define ICR_BEIE 0x400 /* enable bus error ints */
|
||||
#define ICR_SSDIE 0x800 /* slave STOP detected int enable */
|
||||
#define ICR_ALDIE 0x1000 /* enable arbitration interrupt */
|
||||
#define ICR_SADIE 0x2000 /* slave address detected int enable */
|
||||
#define ICR_UR 0x4000 /* unit reset */
|
||||
#define ICR_FM 0x8000 /* Fast Mode */
|
||||
|
||||
/* ----- Status register bits ----------------------------------------- */
|
||||
|
||||
#define ISR_RWM 0x1 /* read/write mode */
|
||||
#define ISR_ACKNAK 0x2 /* ack/nak status */
|
||||
#define ISR_UB 0x4 /* unit busy */
|
||||
#define ISR_IBB 0x8 /* bus busy */
|
||||
#define ISR_SSD 0x10 /* slave stop detected */
|
||||
#define ISR_ALD 0x20 /* arbitration loss detected */
|
||||
#define ISR_ITE 0x40 /* tx buffer empty */
|
||||
#define ISR_IRF 0x80 /* rx buffer full */
|
||||
#define ISR_GCAD 0x100 /* general call address detected */
|
||||
#define ISR_SAD 0x200 /* slave address detected */
|
||||
#define ISR_BED 0x400 /* bus error no ACK/NAK */
|
||||
|
||||
#endif
|
428
root/package/utils/sysupgrade-helper/src/drivers/i2c/mvtwsi.c
Normal file
428
root/package/utils/sysupgrade-helper/src/drivers/i2c/mvtwsi.c
Normal file
|
@ -0,0 +1,428 @@
|
|||
/*
|
||||
* Driver for the TWSI (i2c) controller found on the Marvell
|
||||
* orion5x and kirkwood SoC families.
|
||||
*
|
||||
* Author: Albert Aribaud <albert.u.boot@aribaud.net>
|
||||
* Copyright (c) 2010 Albert Aribaud.
|
||||
*
|
||||
* 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., 51 Franklin Street, Fifth Floor, Boston,
|
||||
* MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/*
|
||||
* include a file that will provide CONFIG_I2C_MVTWSI_BASE
|
||||
* and possibly other settings
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_ORION5X)
|
||||
#include <asm/arch/orion5x.h>
|
||||
#elif defined(CONFIG_KIRKWOOD)
|
||||
#include <asm/arch/kirkwood.h>
|
||||
#else
|
||||
#error Driver mvtwsi not supported by SoC or board
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TWSI register structure
|
||||
*/
|
||||
|
||||
struct mvtwsi_registers {
|
||||
u32 slave_address;
|
||||
u32 data;
|
||||
u32 control;
|
||||
union {
|
||||
u32 status; /* when reading */
|
||||
u32 baudrate; /* when writing */
|
||||
};
|
||||
u32 xtnd_slave_addr;
|
||||
u32 reserved[2];
|
||||
u32 soft_reset;
|
||||
};
|
||||
|
||||
/*
|
||||
* Control register fields
|
||||
*/
|
||||
|
||||
#define MVTWSI_CONTROL_ACK 0x00000004
|
||||
#define MVTWSI_CONTROL_IFLG 0x00000008
|
||||
#define MVTWSI_CONTROL_STOP 0x00000010
|
||||
#define MVTWSI_CONTROL_START 0x00000020
|
||||
#define MVTWSI_CONTROL_TWSIEN 0x00000040
|
||||
#define MVTWSI_CONTROL_INTEN 0x00000080
|
||||
|
||||
/*
|
||||
* Status register values -- only those expected in normal master
|
||||
* operation on non-10-bit-address devices; whatever status we don't
|
||||
* expect in nominal conditions (bus errors, arbitration losses,
|
||||
* missing ACKs...) we just pass back to the caller as an error
|
||||
* code.
|
||||
*/
|
||||
|
||||
#define MVTWSI_STATUS_START 0x08
|
||||
#define MVTWSI_STATUS_REPEATED_START 0x10
|
||||
#define MVTWSI_STATUS_ADDR_W_ACK 0x18
|
||||
#define MVTWSI_STATUS_DATA_W_ACK 0x28
|
||||
#define MVTWSI_STATUS_ADDR_R_ACK 0x40
|
||||
#define MVTWSI_STATUS_ADDR_R_NAK 0x48
|
||||
#define MVTWSI_STATUS_DATA_R_ACK 0x50
|
||||
#define MVTWSI_STATUS_DATA_R_NAK 0x58
|
||||
#define MVTWSI_STATUS_IDLE 0xF8
|
||||
|
||||
/*
|
||||
* The single instance of the controller we'll be dealing with
|
||||
*/
|
||||
|
||||
static struct mvtwsi_registers *twsi =
|
||||
(struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE;
|
||||
|
||||
/*
|
||||
* Returned statuses are 0 for success and nonzero otherwise.
|
||||
* Currently, cmd_i2c and cmd_eeprom do not interpret an error status.
|
||||
* Thus to ease debugging, the return status contains some debug info:
|
||||
* - bits 31..24 are error class: 1 is timeout, 2 is 'status mismatch'.
|
||||
* - bits 23..16 are the last value of the control register.
|
||||
* - bits 15..8 are the last value of the status register.
|
||||
* - bits 7..0 are the expected value of the status register.
|
||||
*/
|
||||
|
||||
#define MVTWSI_ERROR_WRONG_STATUS 0x01
|
||||
#define MVTWSI_ERROR_TIMEOUT 0x02
|
||||
|
||||
#define MVTWSI_ERROR(ec, lc, ls, es) (((ec << 24) & 0xFF000000) | \
|
||||
((lc << 16) & 0x00FF0000) | ((ls<<8) & 0x0000FF00) | (es & 0xFF))
|
||||
|
||||
/*
|
||||
* Wait for IFLG to raise, or return 'timeout'; then if status is as expected,
|
||||
* return 0 (ok) or return 'wrong status'.
|
||||
*/
|
||||
static int twsi_wait(int expected_status)
|
||||
{
|
||||
int control, status;
|
||||
int timeout = 1000;
|
||||
|
||||
do {
|
||||
control = readl(&twsi->control);
|
||||
if (control & MVTWSI_CONTROL_IFLG) {
|
||||
status = readl(&twsi->status);
|
||||
if (status == expected_status)
|
||||
return 0;
|
||||
else
|
||||
return MVTWSI_ERROR(
|
||||
MVTWSI_ERROR_WRONG_STATUS,
|
||||
control, status, expected_status);
|
||||
}
|
||||
udelay(10); /* one clock cycle at 100 kHz */
|
||||
} while (timeout--);
|
||||
status = readl(&twsi->status);
|
||||
return MVTWSI_ERROR(
|
||||
MVTWSI_ERROR_TIMEOUT, control, status, expected_status);
|
||||
}
|
||||
|
||||
/*
|
||||
* These flags are ORed to any write to the control register
|
||||
* They allow global setting of TWSIEN and ACK.
|
||||
* By default none are set.
|
||||
* twsi_start() sets TWSIEN (in case the controller was disabled)
|
||||
* twsi_recv() sets ACK or resets it depending on expected status.
|
||||
*/
|
||||
static u8 twsi_control_flags = MVTWSI_CONTROL_TWSIEN;
|
||||
|
||||
/*
|
||||
* Assert the START condition, either in a single I2C transaction
|
||||
* or inside back-to-back ones (repeated starts).
|
||||
*/
|
||||
static int twsi_start(int expected_status)
|
||||
{
|
||||
/* globally set TWSIEN in case it was not */
|
||||
twsi_control_flags |= MVTWSI_CONTROL_TWSIEN;
|
||||
/* assert START */
|
||||
writel(twsi_control_flags | MVTWSI_CONTROL_START, &twsi->control);
|
||||
/* wait for controller to process START */
|
||||
return twsi_wait(expected_status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a byte (i2c address or data).
|
||||
*/
|
||||
static int twsi_send(u8 byte, int expected_status)
|
||||
{
|
||||
/* put byte in data register for sending */
|
||||
writel(byte, &twsi->data);
|
||||
/* clear any pending interrupt -- that'll cause sending */
|
||||
writel(twsi_control_flags, &twsi->control);
|
||||
/* wait for controller to receive byte and check ACK */
|
||||
return twsi_wait(expected_status);
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive a byte.
|
||||
* Global mvtwsi_control_flags variable says if we should ack or nak.
|
||||
*/
|
||||
static int twsi_recv(u8 *byte)
|
||||
{
|
||||
int expected_status, status;
|
||||
|
||||
/* compute expected status based on ACK bit in global control flags */
|
||||
if (twsi_control_flags & MVTWSI_CONTROL_ACK)
|
||||
expected_status = MVTWSI_STATUS_DATA_R_ACK;
|
||||
else
|
||||
expected_status = MVTWSI_STATUS_DATA_R_NAK;
|
||||
/* acknowledge *previous state* and launch receive */
|
||||
writel(twsi_control_flags, &twsi->control);
|
||||
/* wait for controller to receive byte and assert ACK or NAK */
|
||||
status = twsi_wait(expected_status);
|
||||
/* if we did receive expected byte then store it */
|
||||
if (status == 0)
|
||||
*byte = readl(&twsi->data);
|
||||
/* return status */
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assert the STOP condition.
|
||||
* This is also used to force the bus back in idle (SDA=SCL=1).
|
||||
*/
|
||||
static int twsi_stop(int status)
|
||||
{
|
||||
int control, stop_status;
|
||||
int timeout = 1000;
|
||||
|
||||
/* assert STOP */
|
||||
control = MVTWSI_CONTROL_TWSIEN | MVTWSI_CONTROL_STOP;
|
||||
writel(control, &twsi->control);
|
||||
/* wait for IDLE; IFLG won't rise so twsi_wait() is no use. */
|
||||
do {
|
||||
stop_status = readl(&twsi->status);
|
||||
if (stop_status == MVTWSI_STATUS_IDLE)
|
||||
break;
|
||||
udelay(10); /* one clock cycle at 100 kHz */
|
||||
} while (timeout--);
|
||||
control = readl(&twsi->control);
|
||||
if (stop_status != MVTWSI_STATUS_IDLE)
|
||||
if (status == 0)
|
||||
status = MVTWSI_ERROR(
|
||||
MVTWSI_ERROR_TIMEOUT,
|
||||
control, status, MVTWSI_STATUS_IDLE);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ugly formula to convert m and n values to a frequency comes from
|
||||
* TWSI specifications
|
||||
*/
|
||||
|
||||
#define TWSI_FREQUENCY(m, n) \
|
||||
((u8) (CONFIG_SYS_TCLK / (10 * (m + 1) * 2 * (1 << n))))
|
||||
|
||||
/*
|
||||
* These are required to be reprogrammed before enabling the controller
|
||||
* because a reset loses them.
|
||||
* Default values come from the spec, but a twsi_reset will change them.
|
||||
* twsi_slave_address left uninitialized lest checkpatch.pl complains.
|
||||
*/
|
||||
|
||||
/* Baudrate generator: m (bits 7..4) =4, n (bits 3..0) =4 */
|
||||
static u8 twsi_baud_rate = 0x44; /* baudrate at controller reset */
|
||||
/* Default frequency corresponding to default m=4, n=4 */
|
||||
static u8 twsi_actual_speed = TWSI_FREQUENCY(4, 4);
|
||||
/* Default slave address is 0 (so is an uninitialized static) */
|
||||
static u8 twsi_slave_address;
|
||||
|
||||
/*
|
||||
* Reset controller.
|
||||
* Called at end of i2c_init unsuccessful i2c transactions.
|
||||
* Controller reset also resets the baud rate and slave address, so
|
||||
* re-establish them.
|
||||
*/
|
||||
static void twsi_reset(void)
|
||||
{
|
||||
/* ensure controller will be enabled by any twsi*() function */
|
||||
twsi_control_flags = MVTWSI_CONTROL_TWSIEN;
|
||||
/* reset controller */
|
||||
writel(0, &twsi->soft_reset);
|
||||
/* wait 2 ms -- this is what the Marvell LSP does */
|
||||
udelay(20000);
|
||||
/* set baud rate */
|
||||
writel(twsi_baud_rate, &twsi->baudrate);
|
||||
/* set slave address even though we don't use it */
|
||||
writel(twsi_slave_address, &twsi->slave_address);
|
||||
writel(0, &twsi->xtnd_slave_addr);
|
||||
/* assert STOP but don't care for the result */
|
||||
(void) twsi_stop(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C init called by cmd_i2c when doing 'i2c reset'.
|
||||
* Sets baud to the highest possible value not exceeding requested one.
|
||||
*/
|
||||
void i2c_init(int requested_speed, int slaveadd)
|
||||
{
|
||||
int tmp_speed, highest_speed, n, m;
|
||||
int baud = 0x44; /* baudrate at controller reset */
|
||||
|
||||
/* use actual speed to collect progressively higher values */
|
||||
highest_speed = 0;
|
||||
/* compute m, n setting for highest speed not above requested speed */
|
||||
for (n = 0; n < 8; n++) {
|
||||
for (m = 0; m < 16; m++) {
|
||||
tmp_speed = TWSI_FREQUENCY(m, n);
|
||||
if ((tmp_speed <= requested_speed)
|
||||
&& (tmp_speed > highest_speed)) {
|
||||
highest_speed = tmp_speed;
|
||||
baud = (m << 3) | n;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* save baud rate and slave for later calls to twsi_reset */
|
||||
twsi_baud_rate = baud;
|
||||
twsi_actual_speed = highest_speed;
|
||||
twsi_slave_address = slaveadd;
|
||||
/* reset controller */
|
||||
twsi_reset();
|
||||
}
|
||||
|
||||
/*
|
||||
* Begin I2C transaction with expected start status, at given address.
|
||||
* Common to i2c_probe, i2c_read and i2c_write.
|
||||
* Expected address status will derive from direction bit (bit 0) in addr.
|
||||
*/
|
||||
static int i2c_begin(int expected_start_status, u8 addr)
|
||||
{
|
||||
int status, expected_addr_status;
|
||||
|
||||
/* compute expected address status from direction bit in addr */
|
||||
if (addr & 1) /* reading */
|
||||
expected_addr_status = MVTWSI_STATUS_ADDR_R_ACK;
|
||||
else /* writing */
|
||||
expected_addr_status = MVTWSI_STATUS_ADDR_W_ACK;
|
||||
/* assert START */
|
||||
status = twsi_start(expected_start_status);
|
||||
/* send out the address if the start went well */
|
||||
if (status == 0)
|
||||
status = twsi_send(addr, expected_addr_status);
|
||||
/* return ok or status of first failure to caller */
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C probe called by cmd_i2c when doing 'i2c probe'.
|
||||
* Begin read, nak data byte, end.
|
||||
*/
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
u8 dummy_byte;
|
||||
int status;
|
||||
|
||||
/* begin i2c read */
|
||||
status = i2c_begin(MVTWSI_STATUS_START, (chip << 1) | 1);
|
||||
/* dummy read was accepted: receive byte but NAK it. */
|
||||
if (status == 0)
|
||||
status = twsi_recv(&dummy_byte);
|
||||
/* Stop transaction */
|
||||
twsi_stop(0);
|
||||
/* return 0 or status of first failure */
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c
|
||||
* Begin write, send address byte(s), begin read, receive data bytes, end.
|
||||
*
|
||||
* NOTE: some EEPROMS want a stop right before the second start, while
|
||||
* some will choke if it is there. Deciding which we should do is eeprom
|
||||
* stuff, not i2c, but at the moment the APIs won't let us put it in
|
||||
* cmd_eeprom, so we have to choose here, and for the moment that'll be
|
||||
* a repeated start without a preceding stop.
|
||||
*/
|
||||
int i2c_read(u8 dev, uint addr, int alen, u8 *data, int length)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* begin i2c write to send the address bytes */
|
||||
status = i2c_begin(MVTWSI_STATUS_START, (dev << 1));
|
||||
/* send addr bytes */
|
||||
while ((status == 0) && alen--)
|
||||
status = twsi_send(addr >> (8*alen),
|
||||
MVTWSI_STATUS_DATA_W_ACK);
|
||||
/* begin i2c read to receive eeprom data bytes */
|
||||
if (status == 0)
|
||||
status = i2c_begin(
|
||||
MVTWSI_STATUS_REPEATED_START, (dev << 1) | 1);
|
||||
/* prepare ACK if at least one byte must be received */
|
||||
if (length > 0)
|
||||
twsi_control_flags |= MVTWSI_CONTROL_ACK;
|
||||
/* now receive actual bytes */
|
||||
while ((status == 0) && length--) {
|
||||
/* reset NAK if we if no more to read now */
|
||||
if (length == 0)
|
||||
twsi_control_flags &= ~MVTWSI_CONTROL_ACK;
|
||||
/* read current byte */
|
||||
status = twsi_recv(data++);
|
||||
}
|
||||
/* Stop transaction */
|
||||
status = twsi_stop(status);
|
||||
/* return 0 or status of first failure */
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c
|
||||
* Begin write, send address byte(s), send data bytes, end.
|
||||
*/
|
||||
int i2c_write(u8 dev, uint addr, int alen, u8 *data, int length)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* begin i2c write to send the eeprom adress bytes then data bytes */
|
||||
status = i2c_begin(MVTWSI_STATUS_START, (dev << 1));
|
||||
/* send addr bytes */
|
||||
while ((status == 0) && alen--)
|
||||
status = twsi_send(addr >> (8*alen),
|
||||
MVTWSI_STATUS_DATA_W_ACK);
|
||||
/* send data bytes */
|
||||
while ((status == 0) && (length-- > 0))
|
||||
status = twsi_send(*(data++), MVTWSI_STATUS_DATA_W_ACK);
|
||||
/* Stop transaction */
|
||||
status = twsi_stop(status);
|
||||
/* return 0 or status of first failure */
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bus set routine: we only support bus 0.
|
||||
*/
|
||||
int i2c_set_bus_num(unsigned int bus)
|
||||
{
|
||||
if (bus > 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bus get routine: hard-return bus 0.
|
||||
*/
|
||||
unsigned int i2c_get_bus_num(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
449
root/package/utils/sysupgrade-helper/src/drivers/i2c/mxc_i2c.c
Normal file
449
root/package/utils/sysupgrade-helper/src/drivers/i2c/mxc_i2c.c
Normal file
|
@ -0,0 +1,449 @@
|
|||
/*
|
||||
* i2c driver for Freescale i.MX series
|
||||
*
|
||||
* (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
|
||||
* (c) 2011 Marek Vasut <marek.vasut@gmail.com>
|
||||
*
|
||||
* Based on i2c-imx.c from linux kernel:
|
||||
* 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>
|
||||
*
|
||||
*
|
||||
* 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>
|
||||
|
||||
#if defined(CONFIG_HARD_I2C)
|
||||
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <i2c.h>
|
||||
|
||||
struct mxc_i2c_regs {
|
||||
uint32_t iadr;
|
||||
uint32_t ifdr;
|
||||
uint32_t i2cr;
|
||||
uint32_t i2sr;
|
||||
uint32_t i2dr;
|
||||
};
|
||||
|
||||
#define I2CR_IEN (1 << 7)
|
||||
#define I2CR_IIEN (1 << 6)
|
||||
#define I2CR_MSTA (1 << 5)
|
||||
#define I2CR_MTX (1 << 4)
|
||||
#define I2CR_TX_NO_AK (1 << 3)
|
||||
#define I2CR_RSTA (1 << 2)
|
||||
|
||||
#define I2SR_ICF (1 << 7)
|
||||
#define I2SR_IBB (1 << 5)
|
||||
#define I2SR_IIF (1 << 1)
|
||||
#define I2SR_RX_NO_AK (1 << 0)
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_BASE
|
||||
#define I2C_BASE CONFIG_SYS_I2C_BASE
|
||||
#else
|
||||
#error "define CONFIG_SYS_I2C_BASE to use the mxc_i2c driver"
|
||||
#endif
|
||||
|
||||
#define I2C_MAX_TIMEOUT 10000
|
||||
|
||||
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 }
|
||||
};
|
||||
|
||||
/*
|
||||
* Calculate and set proper clock divider
|
||||
*/
|
||||
static uint8_t i2c_imx_get_clk(unsigned int rate)
|
||||
{
|
||||
unsigned int i2c_clk_rate;
|
||||
unsigned int div;
|
||||
u8 clk_div;
|
||||
|
||||
#if defined(CONFIG_MX31)
|
||||
struct clock_control_regs *sc_regs =
|
||||
(struct clock_control_regs *)CCM_BASE;
|
||||
|
||||
/* start the required I2C clock */
|
||||
writel(readl(&sc_regs->cgr0) | (3 << CONFIG_SYS_I2C_CLK_OFFSET),
|
||||
&sc_regs->cgr0);
|
||||
#endif
|
||||
|
||||
/* Divider value calculation */
|
||||
i2c_clk_rate = mxc_get_clock(MXC_IPG_PERCLK);
|
||||
div = (i2c_clk_rate + rate - 1) / rate;
|
||||
if (div < i2c_clk_div[0][0])
|
||||
clk_div = 0;
|
||||
else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0])
|
||||
clk_div = ARRAY_SIZE(i2c_clk_div) - 1;
|
||||
else
|
||||
for (clk_div = 0; i2c_clk_div[clk_div][0] < div; clk_div++)
|
||||
;
|
||||
|
||||
/* Store divider value */
|
||||
return clk_div;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset I2C Controller
|
||||
*/
|
||||
void i2c_reset(void)
|
||||
{
|
||||
struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
|
||||
|
||||
writeb(0, &i2c_regs->i2cr); /* Reset module */
|
||||
writeb(0, &i2c_regs->i2sr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Init I2C Bus
|
||||
*/
|
||||
void i2c_init(int speed, int unused)
|
||||
{
|
||||
struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
|
||||
u8 clk_idx = i2c_imx_get_clk(speed);
|
||||
u8 idx = i2c_clk_div[clk_idx][1];
|
||||
|
||||
/* Store divider value */
|
||||
writeb(idx, &i2c_regs->ifdr);
|
||||
|
||||
i2c_reset();
|
||||
}
|
||||
|
||||
/*
|
||||
* Set I2C Speed
|
||||
*/
|
||||
int i2c_set_bus_speed(unsigned int speed)
|
||||
{
|
||||
i2c_init(speed, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get I2C Speed
|
||||
*/
|
||||
unsigned int i2c_get_bus_speed(void)
|
||||
{
|
||||
struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
|
||||
u8 clk_idx = readb(&i2c_regs->ifdr);
|
||||
u8 clk_div;
|
||||
|
||||
for (clk_div = 0; i2c_clk_div[clk_div][1] != clk_idx; clk_div++)
|
||||
;
|
||||
|
||||
return mxc_get_clock(MXC_IPG_PERCLK) / i2c_clk_div[clk_div][0];
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for bus to be busy (or free if for_busy = 0)
|
||||
*
|
||||
* for_busy = 1: Wait for IBB to be asserted
|
||||
* for_busy = 0: Wait for IBB to be de-asserted
|
||||
*/
|
||||
int i2c_imx_bus_busy(int for_busy)
|
||||
{
|
||||
struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
|
||||
unsigned int temp;
|
||||
|
||||
int timeout = I2C_MAX_TIMEOUT;
|
||||
|
||||
while (timeout--) {
|
||||
temp = readb(&i2c_regs->i2sr);
|
||||
|
||||
if (for_busy && (temp & I2SR_IBB))
|
||||
return 0;
|
||||
if (!for_busy && !(temp & I2SR_IBB))
|
||||
return 0;
|
||||
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for transaction to complete
|
||||
*/
|
||||
int i2c_imx_trx_complete(void)
|
||||
{
|
||||
struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
|
||||
int timeout = I2C_MAX_TIMEOUT;
|
||||
|
||||
while (timeout--) {
|
||||
if (readb(&i2c_regs->i2sr) & I2SR_IIF) {
|
||||
writeb(0, &i2c_regs->i2sr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the transaction was ACKed
|
||||
*/
|
||||
int i2c_imx_acked(void)
|
||||
{
|
||||
struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
|
||||
|
||||
return readb(&i2c_regs->i2sr) & I2SR_RX_NO_AK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the controller
|
||||
*/
|
||||
int i2c_imx_start(void)
|
||||
{
|
||||
struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
|
||||
unsigned int temp = 0;
|
||||
int result;
|
||||
|
||||
/* Enable I2C controller */
|
||||
writeb(0, &i2c_regs->i2sr);
|
||||
writeb(I2CR_IEN, &i2c_regs->i2cr);
|
||||
|
||||
/* Wait controller to be stable */
|
||||
udelay(50);
|
||||
|
||||
/* Start I2C transaction */
|
||||
temp = readb(&i2c_regs->i2cr);
|
||||
temp |= I2CR_MSTA;
|
||||
writeb(temp, &i2c_regs->i2cr);
|
||||
|
||||
result = i2c_imx_bus_busy(1);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
temp |= I2CR_MTX | I2CR_TX_NO_AK;
|
||||
writeb(temp, &i2c_regs->i2cr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stop the controller
|
||||
*/
|
||||
void i2c_imx_stop(void)
|
||||
{
|
||||
struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
|
||||
unsigned int temp = 0;
|
||||
|
||||
/* Stop I2C transaction */
|
||||
temp = readb(&i2c_regs->i2cr);
|
||||
temp |= ~(I2CR_MSTA | I2CR_MTX);
|
||||
writeb(temp, &i2c_regs->i2cr);
|
||||
|
||||
i2c_imx_bus_busy(0);
|
||||
|
||||
/* Disable I2C controller */
|
||||
writeb(0, &i2c_regs->i2cr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set chip address and access mode
|
||||
*
|
||||
* read = 1: READ access
|
||||
* read = 0: WRITE access
|
||||
*/
|
||||
int i2c_imx_set_chip_addr(uchar chip, int read)
|
||||
{
|
||||
struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
|
||||
int ret;
|
||||
|
||||
writeb((chip << 1) | read, &i2c_regs->i2dr);
|
||||
|
||||
ret = i2c_imx_trx_complete();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = i2c_imx_acked();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write register address
|
||||
*/
|
||||
int i2c_imx_set_reg_addr(uint addr, int alen)
|
||||
{
|
||||
struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
|
||||
int ret = 0;
|
||||
|
||||
while (alen--) {
|
||||
writeb((addr >> (alen * 8)) & 0xff, &i2c_regs->i2dr);
|
||||
|
||||
ret = i2c_imx_trx_complete();
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = i2c_imx_acked();
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try if a chip add given address responds (probe the chip)
|
||||
*/
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_imx_start();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = i2c_imx_set_chip_addr(chip, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i2c_imx_stop();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read data from I2C device
|
||||
*/
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len)
|
||||
{
|
||||
struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
|
||||
int ret;
|
||||
unsigned int temp;
|
||||
int i;
|
||||
|
||||
ret = i2c_imx_start();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* write slave address */
|
||||
ret = i2c_imx_set_chip_addr(chip, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = i2c_imx_set_reg_addr(addr, alen);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
temp = readb(&i2c_regs->i2cr);
|
||||
temp |= I2CR_RSTA;
|
||||
writeb(temp, &i2c_regs->i2cr);
|
||||
|
||||
ret = i2c_imx_set_chip_addr(chip, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* setup bus to read data */
|
||||
temp = readb(&i2c_regs->i2cr);
|
||||
temp &= ~(I2CR_MTX | I2CR_TX_NO_AK);
|
||||
if (len == 1)
|
||||
temp |= I2CR_TX_NO_AK;
|
||||
writeb(temp, &i2c_regs->i2cr);
|
||||
readb(&i2c_regs->i2dr);
|
||||
|
||||
/* read data */
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = i2c_imx_trx_complete();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* It must generate STOP before read I2DR to prevent
|
||||
* controller from generating another clock cycle
|
||||
*/
|
||||
if (i == (len - 1)) {
|
||||
temp = readb(&i2c_regs->i2cr);
|
||||
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
||||
writeb(temp, &i2c_regs->i2cr);
|
||||
i2c_imx_bus_busy(0);
|
||||
} else if (i == (len - 2)) {
|
||||
temp = readb(&i2c_regs->i2cr);
|
||||
temp |= I2CR_TX_NO_AK;
|
||||
writeb(temp, &i2c_regs->i2cr);
|
||||
}
|
||||
|
||||
buf[i] = readb(&i2c_regs->i2dr);
|
||||
}
|
||||
|
||||
i2c_imx_stop();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write data to I2C device
|
||||
*/
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len)
|
||||
{
|
||||
struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)I2C_BASE;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = i2c_imx_start();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* write slave address */
|
||||
ret = i2c_imx_set_chip_addr(chip, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = i2c_imx_set_reg_addr(addr, alen);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
writeb(buf[i], &i2c_regs->i2dr);
|
||||
|
||||
ret = i2c_imx_trx_complete();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = i2c_imx_acked();
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_imx_stop();
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_HARD_I2C */
|
246
root/package/utils/sysupgrade-helper/src/drivers/i2c/mxs_i2c.c
Normal file
246
root/package/utils/sysupgrade-helper/src/drivers/i2c/mxs_i2c.c
Normal file
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* Freescale i.MX28 I2C Driver
|
||||
*
|
||||
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
|
||||
* on behalf of DENX Software Engineering GmbH
|
||||
*
|
||||
* Partly based on Linux kernel i2c-mxs.c driver:
|
||||
* Copyright (C) 2011 Wolfram Sang, Pengutronix e.K.
|
||||
*
|
||||
* Which was based on a (non-working) driver which was:
|
||||
* Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
*
|
||||
* 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 <malloc.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/imx-regs.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
|
||||
#define MXS_I2C_MAX_TIMEOUT 1000000
|
||||
|
||||
void mxs_i2c_reset(void)
|
||||
{
|
||||
struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE;
|
||||
int ret;
|
||||
|
||||
ret = mx28_reset_block(&i2c_regs->hw_i2c_ctrl0_reg);
|
||||
if (ret) {
|
||||
debug("MXS I2C: Block reset timeout\n");
|
||||
return;
|
||||
}
|
||||
|
||||
writel(I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ | I2C_CTRL1_NO_SLAVE_ACK_IRQ |
|
||||
I2C_CTRL1_EARLY_TERM_IRQ | I2C_CTRL1_MASTER_LOSS_IRQ |
|
||||
I2C_CTRL1_SLAVE_STOP_IRQ | I2C_CTRL1_SLAVE_IRQ,
|
||||
&i2c_regs->hw_i2c_ctrl1_clr);
|
||||
|
||||
writel(I2C_QUEUECTRL_PIO_QUEUE_MODE, &i2c_regs->hw_i2c_queuectrl_set);
|
||||
}
|
||||
|
||||
void mxs_i2c_setup_read(uint8_t chip, int len)
|
||||
{
|
||||
struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE;
|
||||
|
||||
writel(I2C_QUEUECMD_RETAIN_CLOCK | I2C_QUEUECMD_PRE_SEND_START |
|
||||
I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION |
|
||||
(1 << I2C_QUEUECMD_XFER_COUNT_OFFSET),
|
||||
&i2c_regs->hw_i2c_queuecmd);
|
||||
|
||||
writel((chip << 1) | 1, &i2c_regs->hw_i2c_data);
|
||||
|
||||
writel(I2C_QUEUECMD_SEND_NAK_ON_LAST | I2C_QUEUECMD_MASTER_MODE |
|
||||
(len << I2C_QUEUECMD_XFER_COUNT_OFFSET) |
|
||||
I2C_QUEUECMD_POST_SEND_STOP, &i2c_regs->hw_i2c_queuecmd);
|
||||
|
||||
writel(I2C_QUEUECTRL_QUEUE_RUN, &i2c_regs->hw_i2c_queuectrl_set);
|
||||
}
|
||||
|
||||
void mxs_i2c_write(uchar chip, uint addr, int alen,
|
||||
uchar *buf, int blen, int stop)
|
||||
{
|
||||
struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE;
|
||||
uint32_t data;
|
||||
int i, remain, off;
|
||||
|
||||
if ((alen > 4) || (alen == 0)) {
|
||||
debug("MXS I2C: Invalid address length\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (stop)
|
||||
stop = I2C_QUEUECMD_POST_SEND_STOP;
|
||||
|
||||
writel(I2C_QUEUECMD_PRE_SEND_START |
|
||||
I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION |
|
||||
((blen + alen + 1) << I2C_QUEUECMD_XFER_COUNT_OFFSET) | stop,
|
||||
&i2c_regs->hw_i2c_queuecmd);
|
||||
|
||||
data = (chip << 1) << 24;
|
||||
|
||||
for (i = 0; i < alen; i++) {
|
||||
data >>= 8;
|
||||
data |= ((char *)&addr)[alen - i - 1] << 24;
|
||||
if ((i & 3) == 2)
|
||||
writel(data, &i2c_regs->hw_i2c_data);
|
||||
}
|
||||
|
||||
off = i;
|
||||
for (; i < off + blen; i++) {
|
||||
data >>= 8;
|
||||
data |= buf[i - off] << 24;
|
||||
if ((i & 3) == 2)
|
||||
writel(data, &i2c_regs->hw_i2c_data);
|
||||
}
|
||||
|
||||
remain = 24 - ((i & 3) * 8);
|
||||
if (remain)
|
||||
writel(data >> remain, &i2c_regs->hw_i2c_data);
|
||||
|
||||
writel(I2C_QUEUECTRL_QUEUE_RUN, &i2c_regs->hw_i2c_queuectrl_set);
|
||||
}
|
||||
|
||||
int mxs_i2c_wait_for_ack(void)
|
||||
{
|
||||
struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE;
|
||||
uint32_t tmp;
|
||||
int timeout = MXS_I2C_MAX_TIMEOUT;
|
||||
|
||||
for (;;) {
|
||||
tmp = readl(&i2c_regs->hw_i2c_ctrl1);
|
||||
if (tmp & I2C_CTRL1_NO_SLAVE_ACK_IRQ) {
|
||||
debug("MXS I2C: No slave ACK\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (tmp & (
|
||||
I2C_CTRL1_EARLY_TERM_IRQ | I2C_CTRL1_MASTER_LOSS_IRQ |
|
||||
I2C_CTRL1_SLAVE_STOP_IRQ | I2C_CTRL1_SLAVE_IRQ)) {
|
||||
debug("MXS I2C: Error (CTRL1 = %08x)\n", tmp);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (tmp & I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ)
|
||||
break;
|
||||
|
||||
if (!timeout--) {
|
||||
debug("MXS I2C: Operation timed out\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
mxs_i2c_reset();
|
||||
return 1;
|
||||
}
|
||||
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE;
|
||||
uint32_t tmp = 0;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
mxs_i2c_write(chip, addr, alen, NULL, 0, 0);
|
||||
ret = mxs_i2c_wait_for_ack();
|
||||
if (ret) {
|
||||
debug("MXS I2C: Failed writing address\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
mxs_i2c_setup_read(chip, len);
|
||||
ret = mxs_i2c_wait_for_ack();
|
||||
if (ret) {
|
||||
debug("MXS I2C: Failed reading address\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!(i & 3)) {
|
||||
while (readl(&i2c_regs->hw_i2c_queuestat) &
|
||||
I2C_QUEUESTAT_RD_QUEUE_EMPTY)
|
||||
;
|
||||
tmp = readl(&i2c_regs->hw_i2c_queuedata);
|
||||
}
|
||||
buffer[i] = tmp & 0xff;
|
||||
tmp >>= 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int ret;
|
||||
mxs_i2c_write(chip, addr, alen, buffer, len, 1);
|
||||
ret = mxs_i2c_wait_for_ack();
|
||||
if (ret)
|
||||
debug("MXS I2C: Failed writing address\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
int ret;
|
||||
mxs_i2c_write(chip, 0, 1, NULL, 0, 1);
|
||||
ret = mxs_i2c_wait_for_ack();
|
||||
mxs_i2c_reset();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void i2c_init(int speed, int slaveadd)
|
||||
{
|
||||
struct mx28_i2c_regs *i2c_regs = (struct mx28_i2c_regs *)MXS_I2C0_BASE;
|
||||
|
||||
mxs_i2c_reset();
|
||||
|
||||
switch (speed) {
|
||||
case 100000:
|
||||
writel((0x0078 << I2C_TIMING0_HIGH_COUNT_OFFSET) |
|
||||
(0x0030 << I2C_TIMING0_RCV_COUNT_OFFSET),
|
||||
&i2c_regs->hw_i2c_timing0);
|
||||
writel((0x0080 << I2C_TIMING1_LOW_COUNT_OFFSET) |
|
||||
(0x0030 << I2C_TIMING1_XMIT_COUNT_OFFSET),
|
||||
&i2c_regs->hw_i2c_timing1);
|
||||
break;
|
||||
case 400000:
|
||||
writel((0x000f << I2C_TIMING0_HIGH_COUNT_OFFSET) |
|
||||
(0x0007 << I2C_TIMING0_RCV_COUNT_OFFSET),
|
||||
&i2c_regs->hw_i2c_timing0);
|
||||
writel((0x001f << I2C_TIMING1_LOW_COUNT_OFFSET) |
|
||||
(0x000f << I2C_TIMING1_XMIT_COUNT_OFFSET),
|
||||
&i2c_regs->hw_i2c_timing1);
|
||||
break;
|
||||
default:
|
||||
printf("MXS I2C: Invalid speed selected (%d Hz)\n", speed);
|
||||
return;
|
||||
}
|
||||
|
||||
writel((0x0015 << I2C_TIMING2_BUS_FREE_OFFSET) |
|
||||
(0x000d << I2C_TIMING2_LEADIN_COUNT_OFFSET),
|
||||
&i2c_regs->hw_i2c_timing2);
|
||||
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* Basic I2C functions
|
||||
*
|
||||
* Copyright (c) 2003 Texas Instruments
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the license found in the file
|
||||
* named COPYING that should have accompanied this file.
|
||||
*
|
||||
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* Author: Jian Zhang jzhang@ti.com, Texas Instruments
|
||||
*
|
||||
* Copyright (c) 2003 Wolfgang Denk, wd@denx.de
|
||||
* Rewritten to fit into the current U-Boot framework
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
static void wait_for_bb (void);
|
||||
static u16 wait_for_pin (void);
|
||||
|
||||
void i2c_init (int speed, int slaveadd)
|
||||
{
|
||||
u16 scl;
|
||||
|
||||
if (inw (I2C_CON) & I2C_CON_EN) {
|
||||
outw (0, I2C_CON);
|
||||
udelay (5000);
|
||||
}
|
||||
|
||||
/* 12MHz I2C module clock */
|
||||
outw (0, I2C_PSC);
|
||||
outw (I2C_CON_EN, I2C_CON);
|
||||
outw (0, I2C_SYSTEST);
|
||||
/* have to enable intrrupts or OMAP i2c module doesn't work */
|
||||
outw (I2C_IE_XRDY_IE | I2C_IE_RRDY_IE | I2C_IE_ARDY_IE |
|
||||
I2C_IE_NACK_IE | I2C_IE_AL_IE, I2C_IE);
|
||||
scl = (12000000 / 2) / speed - 6;
|
||||
outw (scl, I2C_SCLL);
|
||||
outw (scl, I2C_SCLH);
|
||||
/* own address */
|
||||
outw (slaveadd, I2C_OA);
|
||||
outw (0, I2C_CNT);
|
||||
udelay (1000);
|
||||
}
|
||||
|
||||
static int i2c_read_byte (u8 devaddr, u8 regoffset, u8 * value)
|
||||
{
|
||||
int i2c_error = 0;
|
||||
u16 status;
|
||||
|
||||
/* wait until bus not busy */
|
||||
wait_for_bb ();
|
||||
|
||||
/* one byte only */
|
||||
outw (1, I2C_CNT);
|
||||
/* set slave address */
|
||||
outw (devaddr, I2C_SA);
|
||||
/* no stop bit needed here */
|
||||
outw (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX, I2C_CON);
|
||||
|
||||
status = wait_for_pin ();
|
||||
|
||||
if (status & I2C_STAT_XRDY) {
|
||||
/* Important: have to use byte access */
|
||||
*(volatile u8 *) (I2C_DATA) = regoffset;
|
||||
udelay (20000);
|
||||
if (inw (I2C_STAT) & I2C_STAT_NACK) {
|
||||
i2c_error = 1;
|
||||
}
|
||||
} else {
|
||||
i2c_error = 1;
|
||||
}
|
||||
|
||||
if (!i2c_error) {
|
||||
/* free bus, otherwise we can't use a combined transction */
|
||||
outw (0, I2C_CON);
|
||||
while (inw (I2C_STAT) || (inw (I2C_CON) & I2C_CON_MST)) {
|
||||
udelay (10000);
|
||||
/* Have to clear pending interrupt to clear I2C_STAT */
|
||||
inw (I2C_IV);
|
||||
}
|
||||
|
||||
wait_for_bb ();
|
||||
/* set slave address */
|
||||
outw (devaddr, I2C_SA);
|
||||
/* read one byte from slave */
|
||||
outw (1, I2C_CNT);
|
||||
/* need stop bit here */
|
||||
outw (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP,
|
||||
I2C_CON);
|
||||
|
||||
status = wait_for_pin ();
|
||||
if (status & I2C_STAT_RRDY) {
|
||||
*value = inw (I2C_DATA);
|
||||
udelay (20000);
|
||||
} else {
|
||||
i2c_error = 1;
|
||||
}
|
||||
|
||||
if (!i2c_error) {
|
||||
outw (I2C_CON_EN, I2C_CON);
|
||||
while (inw (I2C_STAT)
|
||||
|| (inw (I2C_CON) & I2C_CON_MST)) {
|
||||
udelay (10000);
|
||||
inw (I2C_IV);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i2c_error;
|
||||
}
|
||||
|
||||
static int i2c_write_byte (u8 devaddr, u8 regoffset, u8 value)
|
||||
{
|
||||
int i2c_error = 0;
|
||||
u16 status;
|
||||
|
||||
/* wait until bus not busy */
|
||||
wait_for_bb ();
|
||||
|
||||
/* two bytes */
|
||||
outw (2, I2C_CNT);
|
||||
/* set slave address */
|
||||
outw (devaddr, I2C_SA);
|
||||
/* stop bit needed here */
|
||||
outw (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX |
|
||||
I2C_CON_STP, I2C_CON);
|
||||
|
||||
/* wait until state change */
|
||||
status = wait_for_pin ();
|
||||
|
||||
if (status & I2C_STAT_XRDY) {
|
||||
/* send out two bytes */
|
||||
outw ((value << 8) + regoffset, I2C_DATA);
|
||||
/* must have enough delay to allow BB bit to go low */
|
||||
udelay (30000);
|
||||
if (inw (I2C_STAT) & I2C_STAT_NACK) {
|
||||
i2c_error = 1;
|
||||
}
|
||||
} else {
|
||||
i2c_error = 1;
|
||||
}
|
||||
|
||||
if (!i2c_error) {
|
||||
outw (I2C_CON_EN, I2C_CON);
|
||||
while (inw (I2C_STAT) || (inw (I2C_CON) & I2C_CON_MST)) {
|
||||
udelay (1000);
|
||||
/* have to read to clear intrrupt */
|
||||
inw (I2C_IV);
|
||||
}
|
||||
}
|
||||
|
||||
return i2c_error;
|
||||
}
|
||||
|
||||
int i2c_probe (uchar chip)
|
||||
{
|
||||
int res = 1;
|
||||
|
||||
if (chip == inw (I2C_OA)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
/* wait until bus not busy */
|
||||
wait_for_bb ();
|
||||
|
||||
/* try to read one byte */
|
||||
outw (1, I2C_CNT);
|
||||
/* set slave address */
|
||||
outw (chip, I2C_SA);
|
||||
/* stop bit needed here */
|
||||
outw (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, I2C_CON);
|
||||
/* enough delay for the NACK bit set */
|
||||
udelay (2000);
|
||||
if (!(inw (I2C_STAT) & I2C_STAT_NACK)) {
|
||||
res = 0;
|
||||
} else {
|
||||
outw (inw (I2C_CON) | I2C_CON_STP, I2C_CON);
|
||||
udelay (20);
|
||||
wait_for_bb ();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (alen > 1) {
|
||||
printf ("I2C read: addr len %d not supported\n", alen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (addr + len > 256) {
|
||||
printf ("I2C read: address out of range\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i2c_read_byte (chip, addr + i, &buffer[i])) {
|
||||
printf ("I2C read: I/O error\n");
|
||||
i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (alen > 1) {
|
||||
printf ("I2C read: addr len %d not supported\n", alen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (addr + len > 256) {
|
||||
printf ("I2C read: address out of range\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i2c_write_byte (chip, addr + i, buffer[i])) {
|
||||
printf ("I2C read: I/O error\n");
|
||||
i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wait_for_bb (void)
|
||||
{
|
||||
int timeout = 10;
|
||||
|
||||
while ((inw (I2C_STAT) & I2C_STAT_BB) && timeout--) {
|
||||
inw (I2C_IV);
|
||||
udelay (1000);
|
||||
}
|
||||
|
||||
if (timeout <= 0) {
|
||||
printf ("timed out in wait_for_bb: I2C_STAT=%x\n",
|
||||
inw (I2C_STAT));
|
||||
}
|
||||
}
|
||||
|
||||
static u16 wait_for_pin (void)
|
||||
{
|
||||
u16 status, iv;
|
||||
int timeout = 10;
|
||||
|
||||
do {
|
||||
udelay (1000);
|
||||
status = inw (I2C_STAT);
|
||||
iv = inw (I2C_IV);
|
||||
} while (!iv &&
|
||||
!(status &
|
||||
(I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY |
|
||||
I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK |
|
||||
I2C_STAT_AL)) && timeout--);
|
||||
|
||||
if (timeout <= 0) {
|
||||
printf ("timed out in wait_for_pin: I2C_STAT=%x\n",
|
||||
inw (I2C_STAT));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
|
@ -0,0 +1,472 @@
|
|||
/*
|
||||
* Basic I2C functions
|
||||
*
|
||||
* Copyright (c) 2004 Texas Instruments
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the license found in the file
|
||||
* named COPYING that should have accompanied this file.
|
||||
*
|
||||
* THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* Author: Jian Zhang jzhang@ti.com, Texas Instruments
|
||||
*
|
||||
* Copyright (c) 2003 Wolfgang Denk, wd@denx.de
|
||||
* Rewritten to fit into the current U-Boot framework
|
||||
*
|
||||
* Adapted for OMAP2420 I2C, r-woodruff2@ti.com
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#include <asm/arch/i2c.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "omap24xx_i2c.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define I2C_TIMEOUT 1000
|
||||
|
||||
static void wait_for_bb(void);
|
||||
static u16 wait_for_pin(void);
|
||||
static void flush_fifo(void);
|
||||
|
||||
/*
|
||||
* For SPL boot some boards need i2c before SDRAM is initialised so force
|
||||
* variables to live in SRAM
|
||||
*/
|
||||
static struct i2c __attribute__((section (".data"))) *i2c_base =
|
||||
(struct i2c *)I2C_DEFAULT_BASE;
|
||||
static unsigned int __attribute__((section (".data"))) bus_initialized[I2C_BUS_MAX] =
|
||||
{ [0 ... (I2C_BUS_MAX-1)] = 0 };
|
||||
static unsigned int __attribute__((section (".data"))) current_bus = 0;
|
||||
|
||||
void i2c_init(int speed, int slaveadd)
|
||||
{
|
||||
int psc, fsscll, fssclh;
|
||||
int hsscll = 0, hssclh = 0;
|
||||
u32 scll, sclh;
|
||||
int timeout = I2C_TIMEOUT;
|
||||
|
||||
/* Only handle standard, fast and high speeds */
|
||||
if ((speed != OMAP_I2C_STANDARD) &&
|
||||
(speed != OMAP_I2C_FAST_MODE) &&
|
||||
(speed != OMAP_I2C_HIGH_SPEED)) {
|
||||
printf("Error : I2C unsupported speed %d\n", speed);
|
||||
return;
|
||||
}
|
||||
|
||||
psc = I2C_IP_CLK / I2C_INTERNAL_SAMPLING_CLK;
|
||||
psc -= 1;
|
||||
if (psc < I2C_PSC_MIN) {
|
||||
printf("Error : I2C unsupported prescalar %d\n", psc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (speed == OMAP_I2C_HIGH_SPEED) {
|
||||
/* High speed */
|
||||
|
||||
/* For first phase of HS mode */
|
||||
fsscll = fssclh = I2C_INTERNAL_SAMPLING_CLK /
|
||||
(2 * OMAP_I2C_FAST_MODE);
|
||||
|
||||
fsscll -= I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM;
|
||||
fssclh -= I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM;
|
||||
if (((fsscll < 0) || (fssclh < 0)) ||
|
||||
((fsscll > 255) || (fssclh > 255))) {
|
||||
puts("Error : I2C initializing first phase clock\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* For second phase of HS mode */
|
||||
hsscll = hssclh = I2C_INTERNAL_SAMPLING_CLK / (2 * speed);
|
||||
|
||||
hsscll -= I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM;
|
||||
hssclh -= I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM;
|
||||
if (((fsscll < 0) || (fssclh < 0)) ||
|
||||
((fsscll > 255) || (fssclh > 255))) {
|
||||
puts("Error : I2C initializing second phase clock\n");
|
||||
return;
|
||||
}
|
||||
|
||||
scll = (unsigned int)hsscll << 8 | (unsigned int)fsscll;
|
||||
sclh = (unsigned int)hssclh << 8 | (unsigned int)fssclh;
|
||||
|
||||
} else {
|
||||
/* Standard and fast speed */
|
||||
fsscll = fssclh = I2C_INTERNAL_SAMPLING_CLK / (2 * speed);
|
||||
|
||||
fsscll -= I2C_FASTSPEED_SCLL_TRIM;
|
||||
fssclh -= I2C_FASTSPEED_SCLH_TRIM;
|
||||
if (((fsscll < 0) || (fssclh < 0)) ||
|
||||
((fsscll > 255) || (fssclh > 255))) {
|
||||
puts("Error : I2C initializing clock\n");
|
||||
return;
|
||||
}
|
||||
|
||||
scll = (unsigned int)fsscll;
|
||||
sclh = (unsigned int)fssclh;
|
||||
}
|
||||
|
||||
if (readw(&i2c_base->con) & I2C_CON_EN) {
|
||||
writew(0, &i2c_base->con);
|
||||
udelay(50000);
|
||||
}
|
||||
|
||||
writew(0x2, &i2c_base->sysc); /* for ES2 after soft reset */
|
||||
udelay(1000);
|
||||
|
||||
writew(I2C_CON_EN, &i2c_base->con);
|
||||
while (!(readw(&i2c_base->syss) & I2C_SYSS_RDONE) && timeout--) {
|
||||
if (timeout <= 0) {
|
||||
puts("ERROR: Timeout in soft-reset\n");
|
||||
return;
|
||||
}
|
||||
udelay(1000);
|
||||
}
|
||||
|
||||
writew(0, &i2c_base->con);
|
||||
writew(psc, &i2c_base->psc);
|
||||
writew(scll, &i2c_base->scll);
|
||||
writew(sclh, &i2c_base->sclh);
|
||||
|
||||
/* own address */
|
||||
writew(slaveadd, &i2c_base->oa);
|
||||
writew(I2C_CON_EN, &i2c_base->con);
|
||||
|
||||
/* have to enable intrrupts or OMAP i2c module doesn't work */
|
||||
writew(I2C_IE_XRDY_IE | I2C_IE_RRDY_IE | I2C_IE_ARDY_IE |
|
||||
I2C_IE_NACK_IE | I2C_IE_AL_IE, &i2c_base->ie);
|
||||
udelay(1000);
|
||||
flush_fifo();
|
||||
writew(0xFFFF, &i2c_base->stat);
|
||||
writew(0, &i2c_base->cnt);
|
||||
|
||||
if (gd->flags & GD_FLG_RELOC)
|
||||
bus_initialized[current_bus] = 1;
|
||||
}
|
||||
|
||||
static int i2c_read_byte(u8 devaddr, u8 regoffset, u8 *value)
|
||||
{
|
||||
int i2c_error = 0;
|
||||
u16 status;
|
||||
|
||||
/* wait until bus not busy */
|
||||
wait_for_bb();
|
||||
|
||||
/* one byte only */
|
||||
writew(1, &i2c_base->cnt);
|
||||
/* set slave address */
|
||||
writew(devaddr, &i2c_base->sa);
|
||||
/* no stop bit needed here */
|
||||
writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT |
|
||||
I2C_CON_TRX, &i2c_base->con);
|
||||
|
||||
/* send register offset */
|
||||
while (1) {
|
||||
status = wait_for_pin();
|
||||
if (status == 0 || status & I2C_STAT_NACK) {
|
||||
i2c_error = 1;
|
||||
goto read_exit;
|
||||
}
|
||||
if (status & I2C_STAT_XRDY) {
|
||||
/* Important: have to use byte access */
|
||||
writeb(regoffset, &i2c_base->data);
|
||||
writew(I2C_STAT_XRDY, &i2c_base->stat);
|
||||
}
|
||||
if (status & I2C_STAT_ARDY) {
|
||||
writew(I2C_STAT_ARDY, &i2c_base->stat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* set slave address */
|
||||
writew(devaddr, &i2c_base->sa);
|
||||
/* read one byte from slave */
|
||||
writew(1, &i2c_base->cnt);
|
||||
/* need stop bit here */
|
||||
writew(I2C_CON_EN | I2C_CON_MST |
|
||||
I2C_CON_STT | I2C_CON_STP,
|
||||
&i2c_base->con);
|
||||
|
||||
/* receive data */
|
||||
while (1) {
|
||||
status = wait_for_pin();
|
||||
if (status == 0 || status & I2C_STAT_NACK) {
|
||||
i2c_error = 1;
|
||||
goto read_exit;
|
||||
}
|
||||
if (status & I2C_STAT_RRDY) {
|
||||
#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \
|
||||
defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX)
|
||||
*value = readb(&i2c_base->data);
|
||||
#else
|
||||
*value = readw(&i2c_base->data);
|
||||
#endif
|
||||
writew(I2C_STAT_RRDY, &i2c_base->stat);
|
||||
}
|
||||
if (status & I2C_STAT_ARDY) {
|
||||
writew(I2C_STAT_ARDY, &i2c_base->stat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
read_exit:
|
||||
flush_fifo();
|
||||
writew(0xFFFF, &i2c_base->stat);
|
||||
writew(0, &i2c_base->cnt);
|
||||
return i2c_error;
|
||||
}
|
||||
|
||||
static void flush_fifo(void)
|
||||
{ u16 stat;
|
||||
|
||||
/* note: if you try and read data when its not there or ready
|
||||
* you get a bus error
|
||||
*/
|
||||
while (1) {
|
||||
stat = readw(&i2c_base->stat);
|
||||
if (stat == I2C_STAT_RRDY) {
|
||||
#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \
|
||||
defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX)
|
||||
readb(&i2c_base->data);
|
||||
#else
|
||||
readw(&i2c_base->data);
|
||||
#endif
|
||||
writew(I2C_STAT_RRDY, &i2c_base->stat);
|
||||
udelay(1000);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
u16 status;
|
||||
int res = 1; /* default = fail */
|
||||
|
||||
if (chip == readw(&i2c_base->oa))
|
||||
return res;
|
||||
|
||||
/* wait until bus not busy */
|
||||
wait_for_bb();
|
||||
|
||||
/* try to read one byte */
|
||||
writew(1, &i2c_base->cnt);
|
||||
/* set slave address */
|
||||
writew(chip, &i2c_base->sa);
|
||||
/* stop bit needed here */
|
||||
writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, &i2c_base->con);
|
||||
|
||||
while (1) {
|
||||
status = wait_for_pin();
|
||||
if (status == 0 || status & I2C_STAT_AL) {
|
||||
res = 1;
|
||||
goto probe_exit;
|
||||
}
|
||||
if (status & I2C_STAT_NACK) {
|
||||
res = 1;
|
||||
writew(0xff, &i2c_base->stat);
|
||||
writew (readw (&i2c_base->con) | I2C_CON_STP, &i2c_base->con);
|
||||
wait_for_bb ();
|
||||
break;
|
||||
}
|
||||
if (status & I2C_STAT_ARDY) {
|
||||
writew(I2C_STAT_ARDY, &i2c_base->stat);
|
||||
break;
|
||||
}
|
||||
if (status & I2C_STAT_RRDY) {
|
||||
res = 0;
|
||||
#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \
|
||||
defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX)
|
||||
readb(&i2c_base->data);
|
||||
#else
|
||||
readw(&i2c_base->data);
|
||||
#endif
|
||||
writew(I2C_STAT_RRDY, &i2c_base->stat);
|
||||
}
|
||||
}
|
||||
|
||||
probe_exit:
|
||||
flush_fifo();
|
||||
/* don't allow any more data in... we don't want it. */
|
||||
writew(0, &i2c_base->cnt);
|
||||
writew(0xFFFF, &i2c_base->stat);
|
||||
return res;
|
||||
}
|
||||
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (alen > 1) {
|
||||
printf("I2C read: addr len %d not supported\n", alen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (addr + len > 256) {
|
||||
puts("I2C read: address out of range\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (i2c_read_byte(chip, addr + i, &buffer[i])) {
|
||||
puts("I2C read: I/O error\n");
|
||||
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int i;
|
||||
u16 status;
|
||||
int i2c_error = 0;
|
||||
|
||||
if (alen > 1) {
|
||||
printf("I2C write: addr len %d not supported\n", alen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (addr + len > 256) {
|
||||
printf("I2C write: address 0x%x + 0x%x out of range\n",
|
||||
addr, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* wait until bus not busy */
|
||||
wait_for_bb();
|
||||
|
||||
/* start address phase - will write regoffset + len bytes data */
|
||||
/* TODO consider case when !CONFIG_OMAP243X/34XX/44XX */
|
||||
writew(alen + len, &i2c_base->cnt);
|
||||
/* set slave address */
|
||||
writew(chip, &i2c_base->sa);
|
||||
/* stop bit needed here */
|
||||
writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX |
|
||||
I2C_CON_STP, &i2c_base->con);
|
||||
|
||||
/* Send address byte */
|
||||
status = wait_for_pin();
|
||||
|
||||
if (status == 0 || status & I2C_STAT_NACK) {
|
||||
i2c_error = 1;
|
||||
printf("error waiting for i2c address ACK (status=0x%x)\n",
|
||||
status);
|
||||
goto write_exit;
|
||||
}
|
||||
|
||||
if (status & I2C_STAT_XRDY) {
|
||||
writeb(addr & 0xFF, &i2c_base->data);
|
||||
writew(I2C_STAT_XRDY, &i2c_base->stat);
|
||||
} else {
|
||||
i2c_error = 1;
|
||||
printf("i2c bus not ready for transmit (status=0x%x)\n",
|
||||
status);
|
||||
goto write_exit;
|
||||
}
|
||||
|
||||
/* address phase is over, now write data */
|
||||
for (i = 0; i < len; i++) {
|
||||
status = wait_for_pin();
|
||||
|
||||
if (status == 0 || status & I2C_STAT_NACK) {
|
||||
i2c_error = 1;
|
||||
printf("i2c error waiting for data ACK (status=0x%x)\n",
|
||||
status);
|
||||
goto write_exit;
|
||||
}
|
||||
|
||||
if (status & I2C_STAT_XRDY) {
|
||||
writeb(buffer[i], &i2c_base->data);
|
||||
writew(I2C_STAT_XRDY, &i2c_base->stat);
|
||||
} else {
|
||||
i2c_error = 1;
|
||||
printf("i2c bus not ready for Tx (i=%d)\n", i);
|
||||
goto write_exit;
|
||||
}
|
||||
}
|
||||
|
||||
write_exit:
|
||||
flush_fifo();
|
||||
writew(0xFFFF, &i2c_base->stat);
|
||||
return i2c_error;
|
||||
}
|
||||
|
||||
static void wait_for_bb(void)
|
||||
{
|
||||
int timeout = I2C_TIMEOUT;
|
||||
u16 stat;
|
||||
|
||||
writew(0xFFFF, &i2c_base->stat); /* clear current interrupts...*/
|
||||
while ((stat = readw(&i2c_base->stat) & I2C_STAT_BB) && timeout--) {
|
||||
writew(stat, &i2c_base->stat);
|
||||
udelay(1000);
|
||||
}
|
||||
|
||||
if (timeout <= 0) {
|
||||
printf("timed out in wait_for_bb: I2C_STAT=%x\n",
|
||||
readw(&i2c_base->stat));
|
||||
}
|
||||
writew(0xFFFF, &i2c_base->stat); /* clear delayed stuff*/
|
||||
}
|
||||
|
||||
static u16 wait_for_pin(void)
|
||||
{
|
||||
u16 status;
|
||||
int timeout = I2C_TIMEOUT;
|
||||
|
||||
do {
|
||||
udelay(1000);
|
||||
status = readw(&i2c_base->stat);
|
||||
} while (!(status &
|
||||
(I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY |
|
||||
I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK |
|
||||
I2C_STAT_AL)) && timeout--);
|
||||
|
||||
if (timeout <= 0) {
|
||||
printf("timed out in wait_for_pin: I2C_STAT=%x\n",
|
||||
readw(&i2c_base->stat));
|
||||
writew(0xFFFF, &i2c_base->stat);
|
||||
status = 0;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int i2c_set_bus_num(unsigned int bus)
|
||||
{
|
||||
if ((bus < 0) || (bus >= I2C_BUS_MAX)) {
|
||||
printf("Bad bus: %d\n", bus);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if I2C_BUS_MAX == 3
|
||||
if (bus == 2)
|
||||
i2c_base = (struct i2c *)I2C_BASE3;
|
||||
else
|
||||
#endif
|
||||
if (bus == 1)
|
||||
i2c_base = (struct i2c *)I2C_BASE2;
|
||||
else
|
||||
i2c_base = (struct i2c *)I2C_BASE1;
|
||||
|
||||
current_bus = bus;
|
||||
|
||||
if (!bus_initialized[current_bus])
|
||||
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_get_bus_num(void)
|
||||
{
|
||||
return (int) current_bus;
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* (C) Copyright 2004-2010
|
||||
* Texas Instruments, <www.ti.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
|
||||
*/
|
||||
#ifndef _OMAP2PLUS_I2C_H_
|
||||
#define _OMAP2PLUS_I2C_H_
|
||||
|
||||
/* I2C masks */
|
||||
|
||||
/* I2C Interrupt Enable Register (I2C_IE): */
|
||||
#define I2C_IE_GC_IE (1 << 5)
|
||||
#define I2C_IE_XRDY_IE (1 << 4) /* Transmit data ready interrupt enable */
|
||||
#define I2C_IE_RRDY_IE (1 << 3) /* Receive data ready interrupt enable */
|
||||
#define I2C_IE_ARDY_IE (1 << 2) /* Register access ready interrupt enable */
|
||||
#define I2C_IE_NACK_IE (1 << 1) /* No acknowledgment interrupt enable */
|
||||
#define I2C_IE_AL_IE (1 << 0) /* Arbitration lost interrupt enable */
|
||||
|
||||
/* I2C Status Register (I2C_STAT): */
|
||||
|
||||
#define I2C_STAT_SBD (1 << 15) /* Single byte data */
|
||||
#define I2C_STAT_BB (1 << 12) /* Bus busy */
|
||||
#define I2C_STAT_ROVR (1 << 11) /* Receive overrun */
|
||||
#define I2C_STAT_XUDF (1 << 10) /* Transmit underflow */
|
||||
#define I2C_STAT_AAS (1 << 9) /* Address as slave */
|
||||
#define I2C_STAT_GC (1 << 5)
|
||||
#define I2C_STAT_XRDY (1 << 4) /* Transmit data ready */
|
||||
#define I2C_STAT_RRDY (1 << 3) /* Receive data ready */
|
||||
#define I2C_STAT_ARDY (1 << 2) /* Register access ready */
|
||||
#define I2C_STAT_NACK (1 << 1) /* No acknowledgment interrupt enable */
|
||||
#define I2C_STAT_AL (1 << 0) /* Arbitration lost interrupt enable */
|
||||
|
||||
/* I2C Interrupt Code Register (I2C_INTCODE): */
|
||||
|
||||
#define I2C_INTCODE_MASK 7
|
||||
#define I2C_INTCODE_NONE 0
|
||||
#define I2C_INTCODE_AL 1 /* Arbitration lost */
|
||||
#define I2C_INTCODE_NAK 2 /* No acknowledgement/general call */
|
||||
#define I2C_INTCODE_ARDY 3 /* Register access ready */
|
||||
#define I2C_INTCODE_RRDY 4 /* Rcv data ready */
|
||||
#define I2C_INTCODE_XRDY 5 /* Xmit data ready */
|
||||
|
||||
/* I2C Buffer Configuration Register (I2C_BUF): */
|
||||
|
||||
#define I2C_BUF_RDMA_EN (1 << 15) /* Receive DMA channel enable */
|
||||
#define I2C_BUF_XDMA_EN (1 << 7) /* Transmit DMA channel enable */
|
||||
|
||||
/* I2C Configuration Register (I2C_CON): */
|
||||
|
||||
#define I2C_CON_EN (1 << 15) /* I2C module enable */
|
||||
#define I2C_CON_BE (1 << 14) /* Big endian mode */
|
||||
#define I2C_CON_STB (1 << 11) /* Start byte mode (master mode only) */
|
||||
#define I2C_CON_MST (1 << 10) /* Master/slave mode */
|
||||
#define I2C_CON_TRX (1 << 9) /* Transmitter/receiver mode */
|
||||
/* (master mode only) */
|
||||
#define I2C_CON_XA (1 << 8) /* Expand address */
|
||||
#define I2C_CON_STP (1 << 1) /* Stop condition (master mode only) */
|
||||
#define I2C_CON_STT (1 << 0) /* Start condition (master mode only) */
|
||||
|
||||
/* I2C System Test Register (I2C_SYSTEST): */
|
||||
|
||||
#define I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */
|
||||
#define I2C_SYSTEST_FREE (1 << 14) /* Free running mode, on brkpoint) */
|
||||
#define I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */
|
||||
#define I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */
|
||||
#define I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense input value */
|
||||
#define I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive output value */
|
||||
#define I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense input value */
|
||||
#define I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive output value */
|
||||
|
||||
/* I2C System Status Register (I2C_SYSS): */
|
||||
|
||||
#define I2C_SYSS_RDONE (1 << 0) /* Internel reset monitoring */
|
||||
|
||||
#define I2C_SCLL_SCLL 0
|
||||
#define I2C_SCLL_SCLL_M 0xFF
|
||||
#define I2C_SCLL_HSSCLL 8
|
||||
#define I2C_SCLH_HSSCLL_M 0xFF
|
||||
#define I2C_SCLH_SCLH 0
|
||||
#define I2C_SCLH_SCLH_M 0xFF
|
||||
#define I2C_SCLH_HSSCLH 8
|
||||
#define I2C_SCLH_HSSCLH_M 0xFF
|
||||
|
||||
#define OMAP_I2C_STANDARD 100000
|
||||
#define OMAP_I2C_FAST_MODE 400000
|
||||
#define OMAP_I2C_HIGH_SPEED 3400000
|
||||
|
||||
#define SYSTEM_CLOCK_12 12000000
|
||||
#define SYSTEM_CLOCK_13 13000000
|
||||
#define SYSTEM_CLOCK_192 19200000
|
||||
#define SYSTEM_CLOCK_96 96000000
|
||||
|
||||
/* Use the reference value of 96MHz if not explicitly set by the board */
|
||||
#ifndef I2C_IP_CLK
|
||||
#define I2C_IP_CLK SYSTEM_CLOCK_96
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The reference minimum clock for high speed is 19.2MHz.
|
||||
* The linux 2.6.30 kernel uses this value.
|
||||
* The reference minimum clock for fast mode is 9.6MHz
|
||||
* The reference minimum clock for standard mode is 4MHz
|
||||
* In TRM, the value of 12MHz is used.
|
||||
*/
|
||||
#ifndef I2C_INTERNAL_SAMPLING_CLK
|
||||
#define I2C_INTERNAL_SAMPLING_CLK 19200000
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The equation for the low and high time is
|
||||
* tlow = scll + scll_trim = (sampling clock * tlow_duty) / speed
|
||||
* thigh = sclh + sclh_trim = (sampling clock * (1 - tlow_duty)) / speed
|
||||
*
|
||||
* If the duty cycle is 50%
|
||||
*
|
||||
* tlow = scll + scll_trim = sampling clock / (2 * speed)
|
||||
* thigh = sclh + sclh_trim = sampling clock / (2 * speed)
|
||||
*
|
||||
* In TRM
|
||||
* scll_trim = 7
|
||||
* sclh_trim = 5
|
||||
*
|
||||
* The linux 2.6.30 kernel uses
|
||||
* scll_trim = 6
|
||||
* sclh_trim = 6
|
||||
*
|
||||
* These are the trim values for standard and fast speed
|
||||
*/
|
||||
#ifndef I2C_FASTSPEED_SCLL_TRIM
|
||||
#define I2C_FASTSPEED_SCLL_TRIM 6
|
||||
#endif
|
||||
#ifndef I2C_FASTSPEED_SCLH_TRIM
|
||||
#define I2C_FASTSPEED_SCLH_TRIM 6
|
||||
#endif
|
||||
|
||||
/* These are the trim values for high speed */
|
||||
#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM
|
||||
#define I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM
|
||||
#endif
|
||||
#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM
|
||||
#define I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM
|
||||
#endif
|
||||
#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM
|
||||
#define I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM
|
||||
#endif
|
||||
#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM
|
||||
#define I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM
|
||||
#endif
|
||||
|
||||
#define I2C_PSC_MAX 0x0f
|
||||
#define I2C_PSC_MIN 0x00
|
||||
|
||||
#endif /* _OMAP24XX_I2C_H_ */
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* File: drivers/i2c/pca9564.c
|
||||
* Based on: drivers/i2c/s3c44b0_i2c.c
|
||||
* Author:
|
||||
*
|
||||
* Created: 2009-06-23
|
||||
* Description: PCA9564 i2c bridge driver
|
||||
*
|
||||
* Modified:
|
||||
* Copyright 2009 CJSC "NII STT", http://www.niistt.ru/
|
||||
*
|
||||
* Bugs:
|
||||
*
|
||||
* 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, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
#include <pca9564.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define PCA_STA (CONFIG_PCA9564_BASE + 0)
|
||||
#define PCA_TO (CONFIG_PCA9564_BASE + 0)
|
||||
#define PCA_DAT (CONFIG_PCA9564_BASE + (1 << 2))
|
||||
#define PCA_ADR (CONFIG_PCA9564_BASE + (2 << 2))
|
||||
#define PCA_CON (CONFIG_PCA9564_BASE + (3 << 2))
|
||||
|
||||
static unsigned char pca_read_reg(unsigned int reg)
|
||||
{
|
||||
return readb((void *)reg);
|
||||
}
|
||||
|
||||
static void pca_write_reg(unsigned int reg, unsigned char value)
|
||||
{
|
||||
writeb(value, (void *)reg);
|
||||
}
|
||||
|
||||
static int pca_wait_busy(void)
|
||||
{
|
||||
unsigned int timeout = 10000;
|
||||
|
||||
while (!(pca_read_reg(PCA_CON) & PCA_CON_SI) && --timeout)
|
||||
udelay(1);
|
||||
|
||||
if (timeout == 0)
|
||||
debug("I2C timeout!\n");
|
||||
|
||||
debug("CON = 0x%02x, STA = 0x%02x\n", pca_read_reg(PCA_CON),
|
||||
pca_read_reg(PCA_STA));
|
||||
|
||||
return timeout ? 0 : 1;
|
||||
}
|
||||
|
||||
/*=====================================================================*/
|
||||
/* Public Functions */
|
||||
/*=====================================================================*/
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Initialization
|
||||
*/
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | speed);
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe the given I2C chip address. Returns 0 if a chip responded,
|
||||
* not 0 on failure.
|
||||
*/
|
||||
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
unsigned char res;
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_STA | PCA_CON_ENSIO);
|
||||
pca_wait_busy();
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_STA | PCA_CON_ENSIO);
|
||||
|
||||
pca_write_reg(PCA_DAT, (chip << 1) | 1);
|
||||
res = pca_wait_busy();
|
||||
|
||||
if ((res == 0) && (pca_read_reg(PCA_STA) == 0x48))
|
||||
res = 1;
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_STO | PCA_CON_ENSIO);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read/Write interface:
|
||||
* chip: I2C chip address, range 0..127
|
||||
* addr: Memory (register) address within the chip
|
||||
* alen: Number of bytes to use for addr (typically 1, 2 for larger
|
||||
* memories, 0 for register type devices with only one
|
||||
* register)
|
||||
* buffer: Where to read/write the data
|
||||
* len: How many bytes to read/write
|
||||
*
|
||||
* Returns: 0 on success, not 0 on failure
|
||||
*/
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA);
|
||||
pca_wait_busy();
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
|
||||
pca_write_reg(PCA_DAT, (chip << 1));
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
|
||||
if (alen > 0) {
|
||||
pca_write_reg(PCA_DAT, addr);
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
}
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STO);
|
||||
|
||||
udelay(500);
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA);
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
|
||||
pca_write_reg(PCA_DAT, (chip << 1) | 1);
|
||||
pca_wait_busy();
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (i == len - 1)
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
else
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_AA);
|
||||
|
||||
pca_wait_busy();
|
||||
buffer[i] = pca_read_reg(PCA_DAT);
|
||||
|
||||
}
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STO);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA);
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
|
||||
pca_write_reg(PCA_DAT, chip << 1);
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
|
||||
if (alen > 0) {
|
||||
pca_write_reg(PCA_DAT, addr);
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
}
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
pca_write_reg(PCA_DAT, buffer[i]);
|
||||
pca_wait_busy();
|
||||
pca_write_reg(PCA_CON, PCA_CON_ENSIO);
|
||||
}
|
||||
|
||||
pca_write_reg(PCA_CON, PCA_CON_STO | PCA_CON_ENSIO);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,436 @@
|
|||
/*
|
||||
* (C) Copyright 2007-2009
|
||||
* Stefan Roese, DENX Software Engineering, sr@denx.de.
|
||||
*
|
||||
* based on work by Anne Sophie Harnois <anne-sophie.harnois@nextream.fr>
|
||||
*
|
||||
* (C) Copyright 2001
|
||||
* Bill Hunter, Wave 7 Optics, williamhunter@mediaone.net
|
||||
*
|
||||
* 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/ppc4xx.h>
|
||||
#include <asm/ppc4xx-i2c.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#ifdef CONFIG_HARD_I2C
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#if defined(CONFIG_I2C_MULTI_BUS)
|
||||
/*
|
||||
* Initialize the bus pointer to whatever one the SPD EEPROM is on.
|
||||
* Default is bus 0. This is necessary because the DDR initialization
|
||||
* runs from ROM, and we can't switch buses because we can't modify
|
||||
* the global variables.
|
||||
*/
|
||||
#ifndef CONFIG_SYS_SPD_BUS_NUM
|
||||
#define CONFIG_SYS_SPD_BUS_NUM 0
|
||||
#endif
|
||||
static unsigned int i2c_bus_num __attribute__ ((section (".data"))) =
|
||||
CONFIG_SYS_SPD_BUS_NUM;
|
||||
#endif /* CONFIG_I2C_MULTI_BUS */
|
||||
|
||||
static void _i2c_bus_reset(void)
|
||||
{
|
||||
struct ppc4xx_i2c *i2c = (struct ppc4xx_i2c *)I2C_BASE_ADDR;
|
||||
int i;
|
||||
u8 dc;
|
||||
|
||||
/* Reset status register */
|
||||
/* write 1 in SCMP and IRQA to clear these fields */
|
||||
out_8(&i2c->sts, 0x0A);
|
||||
|
||||
/* write 1 in IRQP IRQD LA ICT XFRA to clear these fields */
|
||||
out_8(&i2c->extsts, 0x8F);
|
||||
|
||||
/* Place chip in the reset state */
|
||||
out_8(&i2c->xtcntlss, IIC_XTCNTLSS_SRST);
|
||||
|
||||
/* Check if bus is free */
|
||||
dc = in_8(&i2c->directcntl);
|
||||
if (!DIRCTNL_FREE(dc)){
|
||||
/* Try to set bus free state */
|
||||
out_8(&i2c->directcntl, IIC_DIRCNTL_SDAC | IIC_DIRCNTL_SCC);
|
||||
|
||||
/* Wait until we regain bus control */
|
||||
for (i = 0; i < 100; ++i) {
|
||||
dc = in_8(&i2c->directcntl);
|
||||
if (DIRCTNL_FREE(dc))
|
||||
break;
|
||||
|
||||
/* Toggle SCL line */
|
||||
dc ^= IIC_DIRCNTL_SCC;
|
||||
out_8(&i2c->directcntl, dc);
|
||||
udelay(10);
|
||||
dc ^= IIC_DIRCNTL_SCC;
|
||||
out_8(&i2c->directcntl, dc);
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove reset */
|
||||
out_8(&i2c->xtcntlss, 0);
|
||||
}
|
||||
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
struct ppc4xx_i2c *i2c;
|
||||
int val, divisor;
|
||||
int bus;
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_INIT_BOARD
|
||||
/*
|
||||
* Call board specific i2c bus reset routine before accessing the
|
||||
* environment, which might be in a chip on that bus. For details
|
||||
* about this problem see doc/I2C_Edge_Conditions.
|
||||
*/
|
||||
i2c_init_board();
|
||||
#endif
|
||||
|
||||
for (bus = 0; bus < CONFIG_SYS_MAX_I2C_BUS; bus++) {
|
||||
I2C_SET_BUS(bus);
|
||||
|
||||
/* Set i2c pointer after calling I2C_SET_BUS() */
|
||||
i2c = (struct ppc4xx_i2c *)I2C_BASE_ADDR;
|
||||
|
||||
/* Handle possible failed I2C state */
|
||||
/* FIXME: put this into i2c_init_board()? */
|
||||
_i2c_bus_reset();
|
||||
|
||||
/* clear lo master address */
|
||||
out_8(&i2c->lmadr, 0);
|
||||
|
||||
/* clear hi master address */
|
||||
out_8(&i2c->hmadr, 0);
|
||||
|
||||
/* clear lo slave address */
|
||||
out_8(&i2c->lsadr, 0);
|
||||
|
||||
/* clear hi slave address */
|
||||
out_8(&i2c->hsadr, 0);
|
||||
|
||||
/* Clock divide Register */
|
||||
/* set divisor according to freq_opb */
|
||||
divisor = (get_OPB_freq() - 1) / 10000000;
|
||||
if (divisor == 0)
|
||||
divisor = 1;
|
||||
out_8(&i2c->clkdiv, divisor);
|
||||
|
||||
/* no interrupts */
|
||||
out_8(&i2c->intrmsk, 0);
|
||||
|
||||
/* clear transfer count */
|
||||
out_8(&i2c->xfrcnt, 0);
|
||||
|
||||
/* clear extended control & stat */
|
||||
/* write 1 in SRC SRS SWC SWS to clear these fields */
|
||||
out_8(&i2c->xtcntlss, 0xF0);
|
||||
|
||||
/* Mode Control Register
|
||||
Flush Slave/Master data buffer */
|
||||
out_8(&i2c->mdcntl, IIC_MDCNTL_FSDB | IIC_MDCNTL_FMDB);
|
||||
|
||||
val = in_8(&i2c->mdcntl);
|
||||
|
||||
/* Ignore General Call, slave transfers are ignored,
|
||||
* disable interrupts, exit unknown bus state, enable hold
|
||||
* SCL 100kHz normaly or FastMode for 400kHz and above
|
||||
*/
|
||||
|
||||
val |= IIC_MDCNTL_EUBS | IIC_MDCNTL_HSCL;
|
||||
if (speed >= 400000)
|
||||
val |= IIC_MDCNTL_FSM;
|
||||
out_8(&i2c->mdcntl, val);
|
||||
|
||||
/* clear control reg */
|
||||
out_8(&i2c->cntl, 0x00);
|
||||
}
|
||||
|
||||
/* set to SPD bus as default bus upon powerup */
|
||||
I2C_SET_BUS(CONFIG_SYS_SPD_BUS_NUM);
|
||||
}
|
||||
|
||||
/*
|
||||
* This code tries to use the features of the 405GP i2c
|
||||
* controller. It will transfer up to 4 bytes in one pass
|
||||
* on the loop. It only does out_8((u8 *)lbz) to the buffer when it
|
||||
* is possible to do out16(lhz) transfers.
|
||||
*
|
||||
* cmd_type is 0 for write 1 for read.
|
||||
*
|
||||
* addr_len can take any value from 0-255, it is only limited
|
||||
* by the char, we could make it larger if needed. If it is
|
||||
* 0 we skip the address write cycle.
|
||||
*
|
||||
* Typical case is a Write of an addr followd by a Read. The
|
||||
* IBM FAQ does not cover this. On the last byte of the write
|
||||
* we don't set the creg CHT bit, and on the first bytes of the
|
||||
* read we set the RPST bit.
|
||||
*
|
||||
* It does not support address only transfers, there must be
|
||||
* a data part. If you want to write the address yourself, put
|
||||
* it in the data pointer.
|
||||
*
|
||||
* It does not support transfer to/from address 0.
|
||||
*
|
||||
* It does not check XFRCNT.
|
||||
*/
|
||||
static int i2c_transfer(unsigned char cmd_type,
|
||||
unsigned char chip,
|
||||
unsigned char addr[],
|
||||
unsigned char addr_len,
|
||||
unsigned char data[],
|
||||
unsigned short data_len)
|
||||
{
|
||||
struct ppc4xx_i2c *i2c = (struct ppc4xx_i2c *)I2C_BASE_ADDR;
|
||||
u8 *ptr;
|
||||
int reading;
|
||||
int tran, cnt;
|
||||
int result;
|
||||
int status;
|
||||
int i;
|
||||
u8 creg;
|
||||
|
||||
if (data == 0 || data_len == 0) {
|
||||
/* Don't support data transfer of no length or to address 0 */
|
||||
printf( "i2c_transfer: bad call\n" );
|
||||
return IIC_NOK;
|
||||
}
|
||||
if (addr && addr_len) {
|
||||
ptr = addr;
|
||||
cnt = addr_len;
|
||||
reading = 0;
|
||||
} else {
|
||||
ptr = data;
|
||||
cnt = data_len;
|
||||
reading = cmd_type;
|
||||
}
|
||||
|
||||
/* Clear Stop Complete Bit */
|
||||
out_8(&i2c->sts, IIC_STS_SCMP);
|
||||
|
||||
/* Check init */
|
||||
i = 10;
|
||||
do {
|
||||
/* Get status */
|
||||
status = in_8(&i2c->sts);
|
||||
i--;
|
||||
} while ((status & IIC_STS_PT) && (i > 0));
|
||||
|
||||
if (status & IIC_STS_PT) {
|
||||
result = IIC_NOK_TOUT;
|
||||
return(result);
|
||||
}
|
||||
|
||||
/* flush the Master/Slave Databuffers */
|
||||
out_8(&i2c->mdcntl, in_8(&i2c->mdcntl) |
|
||||
IIC_MDCNTL_FMDB | IIC_MDCNTL_FSDB);
|
||||
|
||||
/* need to wait 4 OPB clocks? code below should take that long */
|
||||
|
||||
/* 7-bit adressing */
|
||||
out_8(&i2c->hmadr, 0);
|
||||
out_8(&i2c->lmadr, chip);
|
||||
|
||||
tran = 0;
|
||||
result = IIC_OK;
|
||||
creg = 0;
|
||||
|
||||
while (tran != cnt && (result == IIC_OK)) {
|
||||
int bc,j;
|
||||
|
||||
/*
|
||||
* Control register =
|
||||
* Normal transfer, 7-bits adressing, Transfer up to
|
||||
* bc bytes, Normal start, Transfer is a sequence of transfers
|
||||
*/
|
||||
creg |= IIC_CNTL_PT;
|
||||
|
||||
bc = (cnt - tran) > 4 ? 4 : cnt - tran;
|
||||
creg |= (bc - 1) << 4;
|
||||
/* if the real cmd type is write continue trans */
|
||||
if ((!cmd_type && (ptr == addr)) || ((tran + bc) != cnt))
|
||||
creg |= IIC_CNTL_CHT;
|
||||
|
||||
if (reading) {
|
||||
creg |= IIC_CNTL_READ;
|
||||
} else {
|
||||
for(j = 0; j < bc; j++) {
|
||||
/* Set buffer */
|
||||
out_8(&i2c->mdbuf, ptr[tran + j]);
|
||||
}
|
||||
}
|
||||
out_8(&i2c->cntl, creg);
|
||||
|
||||
/*
|
||||
* Transfer is in progress
|
||||
* we have to wait for upto 5 bytes of data
|
||||
* 1 byte chip address+r/w bit then bc bytes
|
||||
* of data.
|
||||
* udelay(10) is 1 bit time at 100khz
|
||||
* Doubled for slop. 20 is too small.
|
||||
*/
|
||||
i = 2 * 5 * 8;
|
||||
do {
|
||||
/* Get status */
|
||||
status = in_8(&i2c->sts);
|
||||
udelay(10);
|
||||
i--;
|
||||
} while ((status & IIC_STS_PT) && !(status & IIC_STS_ERR) &&
|
||||
(i > 0));
|
||||
|
||||
if (status & IIC_STS_ERR) {
|
||||
result = IIC_NOK;
|
||||
status = in_8(&i2c->extsts);
|
||||
/* Lost arbitration? */
|
||||
if (status & IIC_EXTSTS_LA)
|
||||
result = IIC_NOK_LA;
|
||||
/* Incomplete transfer? */
|
||||
if (status & IIC_EXTSTS_ICT)
|
||||
result = IIC_NOK_ICT;
|
||||
/* Transfer aborted? */
|
||||
if (status & IIC_EXTSTS_XFRA)
|
||||
result = IIC_NOK_XFRA;
|
||||
} else if ( status & IIC_STS_PT) {
|
||||
result = IIC_NOK_TOUT;
|
||||
}
|
||||
|
||||
/* Command is reading => get buffer */
|
||||
if ((reading) && (result == IIC_OK)) {
|
||||
/* Are there data in buffer */
|
||||
if (status & IIC_STS_MDBS) {
|
||||
/*
|
||||
* even if we have data we have to wait 4OPB
|
||||
* clocks for it to hit the front of the FIFO,
|
||||
* after that we can just read. We should check
|
||||
* XFCNT here and if the FIFO is full there is
|
||||
* no need to wait.
|
||||
*/
|
||||
udelay(1);
|
||||
for (j = 0; j < bc; j++)
|
||||
ptr[tran + j] = in_8(&i2c->mdbuf);
|
||||
} else
|
||||
result = IIC_NOK_DATA;
|
||||
}
|
||||
creg = 0;
|
||||
tran += bc;
|
||||
if (ptr == addr && tran == cnt) {
|
||||
ptr = data;
|
||||
cnt = data_len;
|
||||
tran = 0;
|
||||
reading = cmd_type;
|
||||
if (reading)
|
||||
creg = IIC_CNTL_RPST;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
uchar buf[1];
|
||||
|
||||
buf[0] = 0;
|
||||
|
||||
/*
|
||||
* What is needed is to send the chip address and verify that the
|
||||
* address was <ACK>ed (i.e. there was a chip at that address which
|
||||
* drove the data line low).
|
||||
*/
|
||||
return (i2c_transfer(1, chip << 1, 0, 0, buf, 1) != 0);
|
||||
}
|
||||
|
||||
static int ppc4xx_i2c_transfer(uchar chip, uint addr, int alen, uchar *buffer,
|
||||
int len, int read)
|
||||
{
|
||||
uchar xaddr[4];
|
||||
int ret;
|
||||
|
||||
if (alen > 4) {
|
||||
printf("I2C: addr len %d not supported\n", alen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (alen > 0) {
|
||||
xaddr[0] = (addr >> 24) & 0xFF;
|
||||
xaddr[1] = (addr >> 16) & 0xFF;
|
||||
xaddr[2] = (addr >> 8) & 0xFF;
|
||||
xaddr[3] = addr & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
|
||||
/*
|
||||
* EEPROM chips that implement "address overflow" are ones
|
||||
* like Catalyst 24WC04/08/16 which has 9/10/11 bits of
|
||||
* address and the extra bits end up in the "chip address"
|
||||
* bit slots. This makes a 24WC08 (1Kbyte) chip look like
|
||||
* four 256 byte chips.
|
||||
*
|
||||
* Note that we consider the length of the address field to
|
||||
* still be one byte because the extra address bits are
|
||||
* hidden in the chip address.
|
||||
*/
|
||||
if (alen > 0)
|
||||
chip |= ((addr >> (alen * 8)) &
|
||||
CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
|
||||
#endif
|
||||
if ((ret = i2c_transfer(read, chip << 1, &xaddr[4 - alen], alen,
|
||||
buffer, len)) != 0) {
|
||||
printf("I2C %s: failed %d\n", read ? "read" : "write", ret);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar * buffer, int len)
|
||||
{
|
||||
return ppc4xx_i2c_transfer(chip, addr, alen, buffer, len, 1);
|
||||
}
|
||||
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar * buffer, int len)
|
||||
{
|
||||
return ppc4xx_i2c_transfer(chip, addr, alen, buffer, len, 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 /* CONFIG_I2C_MULTI_BUS */
|
||||
#endif /* CONFIG_HARD_I2C */
|
|
@ -0,0 +1,443 @@
|
|||
/*
|
||||
* (C) Copyright 2002
|
||||
* David Mueller, ELSOFT AG, d.mueller@elsoft.ch
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/* This code should work for both the S3C2400 and the S3C2410
|
||||
* as they seem to have the same I2C controller inside.
|
||||
* The different address mapping is handled by the s3c24xx.h files below.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/arch/s3c24x0_cpu.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <i2c.h>
|
||||
|
||||
#ifdef CONFIG_HARD_I2C
|
||||
|
||||
#define I2C_WRITE 0
|
||||
#define I2C_READ 1
|
||||
|
||||
#define I2C_OK 0
|
||||
#define I2C_NOK 1
|
||||
#define I2C_NACK 2
|
||||
#define I2C_NOK_LA 3 /* Lost arbitration */
|
||||
#define I2C_NOK_TOUT 4 /* time out */
|
||||
|
||||
#define I2CSTAT_BSY 0x20 /* Busy bit */
|
||||
#define I2CSTAT_NACK 0x01 /* Nack bit */
|
||||
#define I2CCON_IRPND 0x10 /* Interrupt pending bit */
|
||||
#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */
|
||||
#define I2C_MODE_MR 0x80 /* Master Receive Mode */
|
||||
#define I2C_START_STOP 0x20 /* START / STOP */
|
||||
#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */
|
||||
|
||||
#define I2C_TIMEOUT 1 /* 1 second */
|
||||
|
||||
static int GetI2CSDA(void)
|
||||
{
|
||||
struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio();
|
||||
|
||||
#ifdef CONFIG_S3C2410
|
||||
return (readl(&gpio->gpedat) & 0x8000) >> 15;
|
||||
#endif
|
||||
#ifdef CONFIG_S3C2400
|
||||
return (readl(&gpio->pgdat) & 0x0020) >> 5;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void SetI2CSDA(int x)
|
||||
{
|
||||
rGPEDAT = (rGPEDAT & ~0x8000) | (x & 1) << 15;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void SetI2CSCL(int x)
|
||||
{
|
||||
struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio();
|
||||
|
||||
#ifdef CONFIG_S3C2410
|
||||
writel((readl(&gpio->gpedat) & ~0x4000) | (x & 1) << 14, &gpio->gpedat);
|
||||
#endif
|
||||
#ifdef CONFIG_S3C2400
|
||||
writel((readl(&gpio->pgdat) & ~0x0040) | (x & 1) << 6, &gpio->pgdat);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int WaitForXfer(void)
|
||||
{
|
||||
struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c();
|
||||
int i;
|
||||
|
||||
i = I2C_TIMEOUT * 10000;
|
||||
while (!(readl(&i2c->iiccon) & I2CCON_IRPND) && (i > 0)) {
|
||||
udelay(100);
|
||||
i--;
|
||||
}
|
||||
|
||||
return (readl(&i2c->iiccon) & I2CCON_IRPND) ? I2C_OK : I2C_NOK_TOUT;
|
||||
}
|
||||
|
||||
static int IsACK(void)
|
||||
{
|
||||
struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c();
|
||||
|
||||
return !(readl(&i2c->iicstat) & I2CSTAT_NACK);
|
||||
}
|
||||
|
||||
static void ReadWriteByte(void)
|
||||
{
|
||||
struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c();
|
||||
|
||||
writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon);
|
||||
}
|
||||
|
||||
void i2c_init(int speed, int slaveadd)
|
||||
{
|
||||
struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c();
|
||||
struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio();
|
||||
ulong freq, pres = 16, div;
|
||||
int i;
|
||||
|
||||
/* wait for some time to give previous transfer a chance to finish */
|
||||
|
||||
i = I2C_TIMEOUT * 1000;
|
||||
while ((readl(&i2c->iicstat) && I2CSTAT_BSY) && (i > 0)) {
|
||||
udelay(1000);
|
||||
i--;
|
||||
}
|
||||
|
||||
if ((readl(&i2c->iicstat) & I2CSTAT_BSY) || GetI2CSDA() == 0) {
|
||||
#ifdef CONFIG_S3C2410
|
||||
ulong old_gpecon = readl(&gpio->gpecon);
|
||||
#endif
|
||||
#ifdef CONFIG_S3C2400
|
||||
ulong old_gpecon = readl(&gpio->pgcon);
|
||||
#endif
|
||||
/* bus still busy probably by (most) previously interrupted
|
||||
transfer */
|
||||
|
||||
#ifdef CONFIG_S3C2410
|
||||
/* set I2CSDA and I2CSCL (GPE15, GPE14) to GPIO */
|
||||
writel((readl(&gpio->gpecon) & ~0xF0000000) | 0x10000000,
|
||||
&gpio->gpecon);
|
||||
#endif
|
||||
#ifdef CONFIG_S3C2400
|
||||
/* set I2CSDA and I2CSCL (PG5, PG6) to GPIO */
|
||||
writel((readl(&gpio->pgcon) & ~0x00003c00) | 0x00001000,
|
||||
&gpio->pgcon);
|
||||
#endif
|
||||
|
||||
/* toggle I2CSCL until bus idle */
|
||||
SetI2CSCL(0);
|
||||
udelay(1000);
|
||||
i = 10;
|
||||
while ((i > 0) && (GetI2CSDA() != 1)) {
|
||||
SetI2CSCL(1);
|
||||
udelay(1000);
|
||||
SetI2CSCL(0);
|
||||
udelay(1000);
|
||||
i--;
|
||||
}
|
||||
SetI2CSCL(1);
|
||||
udelay(1000);
|
||||
|
||||
/* restore pin functions */
|
||||
#ifdef CONFIG_S3C2410
|
||||
writel(old_gpecon, &gpio->gpecon);
|
||||
#endif
|
||||
#ifdef CONFIG_S3C2400
|
||||
writel(old_gpecon, &gpio->pgcon);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* calculate prescaler and divisor values */
|
||||
freq = get_PCLK();
|
||||
if ((freq / pres / (16 + 1)) > speed)
|
||||
/* set prescaler to 512 */
|
||||
pres = 512;
|
||||
|
||||
div = 0;
|
||||
while ((freq / pres / (div + 1)) > speed)
|
||||
div++;
|
||||
|
||||
/* set prescaler, divisor according to freq, also set
|
||||
* ACKGEN, IRQ */
|
||||
writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon);
|
||||
|
||||
/* init to SLAVE REVEIVE and set slaveaddr */
|
||||
writel(0, &i2c->iicstat);
|
||||
writel(slaveadd, &i2c->iicadd);
|
||||
/* program Master Transmit (and implicit STOP) */
|
||||
writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* cmd_type is 0 for write, 1 for read.
|
||||
*
|
||||
* addr_len can take any value from 0-255, it is only limited
|
||||
* by the char, we could make it larger if needed. If it is
|
||||
* 0 we skip the address write cycle.
|
||||
*/
|
||||
static
|
||||
int i2c_transfer(unsigned char cmd_type,
|
||||
unsigned char chip,
|
||||
unsigned char addr[],
|
||||
unsigned char addr_len,
|
||||
unsigned char data[], unsigned short data_len)
|
||||
{
|
||||
struct s3c24x0_i2c *i2c = s3c24x0_get_base_i2c();
|
||||
int i, result;
|
||||
|
||||
if (data == 0 || data_len == 0) {
|
||||
/*Don't support data transfer of no length or to address 0 */
|
||||
printf("i2c_transfer: bad call\n");
|
||||
return I2C_NOK;
|
||||
}
|
||||
|
||||
/* Check I2C bus idle */
|
||||
i = I2C_TIMEOUT * 1000;
|
||||
while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) {
|
||||
udelay(1000);
|
||||
i--;
|
||||
}
|
||||
|
||||
if (readl(&i2c->iicstat) & I2CSTAT_BSY)
|
||||
return I2C_NOK_TOUT;
|
||||
|
||||
writel(readl(&i2c->iiccon) | 0x80, &i2c->iiccon);
|
||||
result = I2C_OK;
|
||||
|
||||
switch (cmd_type) {
|
||||
case I2C_WRITE:
|
||||
if (addr && addr_len) {
|
||||
writel(chip, &i2c->iicds);
|
||||
/* send START */
|
||||
writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
|
||||
&i2c->iicstat);
|
||||
i = 0;
|
||||
while ((i < addr_len) && (result == I2C_OK)) {
|
||||
result = WaitForXfer();
|
||||
writel(addr[i], &i2c->iicds);
|
||||
ReadWriteByte();
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
while ((i < data_len) && (result == I2C_OK)) {
|
||||
result = WaitForXfer();
|
||||
writel(data[i], &i2c->iicds);
|
||||
ReadWriteByte();
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
writel(chip, &i2c->iicds);
|
||||
/* send START */
|
||||
writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
|
||||
&i2c->iicstat);
|
||||
i = 0;
|
||||
while ((i < data_len) && (result = I2C_OK)) {
|
||||
result = WaitForXfer();
|
||||
writel(data[i], &i2c->iicds);
|
||||
ReadWriteByte();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (result == I2C_OK)
|
||||
result = WaitForXfer();
|
||||
|
||||
/* send STOP */
|
||||
writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
|
||||
ReadWriteByte();
|
||||
break;
|
||||
|
||||
case I2C_READ:
|
||||
if (addr && addr_len) {
|
||||
writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
|
||||
writel(chip, &i2c->iicds);
|
||||
/* send START */
|
||||
writel(readl(&i2c->iicstat) | I2C_START_STOP,
|
||||
&i2c->iicstat);
|
||||
result = WaitForXfer();
|
||||
if (IsACK()) {
|
||||
i = 0;
|
||||
while ((i < addr_len) && (result == I2C_OK)) {
|
||||
writel(addr[i], &i2c->iicds);
|
||||
ReadWriteByte();
|
||||
result = WaitForXfer();
|
||||
i++;
|
||||
}
|
||||
|
||||
writel(chip, &i2c->iicds);
|
||||
/* resend START */
|
||||
writel(I2C_MODE_MR | I2C_TXRX_ENA |
|
||||
I2C_START_STOP, &i2c->iicstat);
|
||||
ReadWriteByte();
|
||||
result = WaitForXfer();
|
||||
i = 0;
|
||||
while ((i < data_len) && (result == I2C_OK)) {
|
||||
/* disable ACK for final READ */
|
||||
if (i == data_len - 1)
|
||||
writel(readl(&i2c->iiccon)
|
||||
& ~0x80, &i2c->iiccon);
|
||||
ReadWriteByte();
|
||||
result = WaitForXfer();
|
||||
data[i] = readl(&i2c->iicds);
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
result = I2C_NACK;
|
||||
}
|
||||
|
||||
} else {
|
||||
writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
|
||||
writel(chip, &i2c->iicds);
|
||||
/* send START */
|
||||
writel(readl(&i2c->iicstat) | I2C_START_STOP,
|
||||
&i2c->iicstat);
|
||||
result = WaitForXfer();
|
||||
|
||||
if (IsACK()) {
|
||||
i = 0;
|
||||
while ((i < data_len) && (result == I2C_OK)) {
|
||||
/* disable ACK for final READ */
|
||||
if (i == data_len - 1)
|
||||
writel(readl(&i2c->iiccon) &
|
||||
~0x80, &i2c->iiccon);
|
||||
ReadWriteByte();
|
||||
result = WaitForXfer();
|
||||
data[i] = readl(&i2c->iicds);
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
result = I2C_NACK;
|
||||
}
|
||||
}
|
||||
|
||||
/* send STOP */
|
||||
writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat);
|
||||
ReadWriteByte();
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("i2c_transfer: bad call\n");
|
||||
result = I2C_NOK;
|
||||
break;
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
uchar buf[1];
|
||||
|
||||
buf[0] = 0;
|
||||
|
||||
/*
|
||||
* What is needed is to send the chip address and verify that the
|
||||
* address was <ACK>ed (i.e. there was a chip at that address which
|
||||
* drove the data line low).
|
||||
*/
|
||||
return i2c_transfer(I2C_READ, chip << 1, 0, 0, buf, 1) != I2C_OK;
|
||||
}
|
||||
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
uchar xaddr[4];
|
||||
int ret;
|
||||
|
||||
if (alen > 4) {
|
||||
printf("I2C read: addr len %d not supported\n", alen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (alen > 0) {
|
||||
xaddr[0] = (addr >> 24) & 0xFF;
|
||||
xaddr[1] = (addr >> 16) & 0xFF;
|
||||
xaddr[2] = (addr >> 8) & 0xFF;
|
||||
xaddr[3] = addr & 0xFF;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
|
||||
/*
|
||||
* EEPROM chips that implement "address overflow" are ones
|
||||
* like Catalyst 24WC04/08/16 which has 9/10/11 bits of
|
||||
* address and the extra bits end up in the "chip address"
|
||||
* bit slots. This makes a 24WC08 (1Kbyte) chip look like
|
||||
* four 256 byte chips.
|
||||
*
|
||||
* Note that we consider the length of the address field to
|
||||
* still be one byte because the extra address bits are
|
||||
* hidden in the chip address.
|
||||
*/
|
||||
if (alen > 0)
|
||||
chip |= ((addr >> (alen * 8)) &
|
||||
CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
|
||||
#endif
|
||||
if ((ret =
|
||||
i2c_transfer(I2C_READ, chip << 1, &xaddr[4 - alen], alen,
|
||||
buffer, len)) != 0) {
|
||||
printf("I2c read: failed %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
uchar xaddr[4];
|
||||
|
||||
if (alen > 4) {
|
||||
printf("I2C write: addr len %d not supported\n", alen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (alen > 0) {
|
||||
xaddr[0] = (addr >> 24) & 0xFF;
|
||||
xaddr[1] = (addr >> 16) & 0xFF;
|
||||
xaddr[2] = (addr >> 8) & 0xFF;
|
||||
xaddr[3] = addr & 0xFF;
|
||||
}
|
||||
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
|
||||
/*
|
||||
* EEPROM chips that implement "address overflow" are ones
|
||||
* like Catalyst 24WC04/08/16 which has 9/10/11 bits of
|
||||
* address and the extra bits end up in the "chip address"
|
||||
* bit slots. This makes a 24WC08 (1Kbyte) chip look like
|
||||
* four 256 byte chips.
|
||||
*
|
||||
* Note that we consider the length of the address field to
|
||||
* still be one byte because the extra address bits are
|
||||
* hidden in the chip address.
|
||||
*/
|
||||
if (alen > 0)
|
||||
chip |= ((addr >> (alen * 8)) &
|
||||
CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
|
||||
#endif
|
||||
return (i2c_transfer
|
||||
(I2C_WRITE, chip << 1, &xaddr[4 - alen], alen, buffer,
|
||||
len) != 0);
|
||||
}
|
||||
#endif /* CONFIG_HARD_I2C */
|
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* (C) Copyright 2004
|
||||
* DAVE Srl
|
||||
* http://www.dave-tech.it
|
||||
* http://www.wawnet.biz
|
||||
* mailto:info@wawnet.biz
|
||||
*
|
||||
* 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 <command.h>
|
||||
#include <asm/hardware.h>
|
||||
|
||||
/*
|
||||
* Initialization, must be called once on start up, may be called
|
||||
* repeatedly to change the speed and slave addresses.
|
||||
*/
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
/*
|
||||
setting up I2C support
|
||||
*/
|
||||
unsigned int save_F,save_PF,rIICCON,rPCONA,rPDATA,rPCONF,rPUPF;
|
||||
|
||||
save_F = PCONF;
|
||||
save_PF = PUPF;
|
||||
|
||||
rPCONF = ((save_F & ~(0xF))| 0xa);
|
||||
rPUPF = (save_PF | 0x3);
|
||||
PCONF = rPCONF; /*PF0:IICSCL, PF1:IICSDA*/
|
||||
PUPF = rPUPF; /* Disable pull-up */
|
||||
|
||||
/* Configuring pin for WC pin of EEprom */
|
||||
rPCONA = PCONA;
|
||||
rPCONA &= ~(1<<9);
|
||||
PCONA = rPCONA;
|
||||
|
||||
rPDATA = PDATA;
|
||||
rPDATA &= ~(1<<9);
|
||||
PDATA = rPDATA;
|
||||
|
||||
/*
|
||||
Enable ACK, IICCLK=MCLK/16, enable interrupt
|
||||
75MHz/16/(12+1) = 390625 Hz
|
||||
*/
|
||||
rIICCON=(1<<7)|(0<<6)|(1<<5)|(0xC);
|
||||
IICCON = rIICCON;
|
||||
|
||||
IICADD = slaveaddr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe the given I2C chip address. Returns 0 if a chip responded,
|
||||
* not 0 on failure.
|
||||
*/
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
/*
|
||||
not implemented
|
||||
*/
|
||||
|
||||
printf("i2c_probe chip %d\n", (int) chip);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read/Write interface:
|
||||
* chip: I2C chip address, range 0..127
|
||||
* addr: Memory (register) address within the chip
|
||||
* alen: Number of bytes to use for addr (typically 1, 2 for larger
|
||||
* memories, 0 for register type devices with only one
|
||||
* register)
|
||||
* buffer: Where to read/write the data
|
||||
* len: How many bytes to read/write
|
||||
*
|
||||
* Returns: 0 on success, not 0 on failure
|
||||
*/
|
||||
|
||||
#define S3C44B0X_rIIC_INTPEND (1<<4)
|
||||
#define S3C44B0X_rIIC_LAST_RECEIV_BIT (1<<0)
|
||||
#define S3C44B0X_rIIC_INTERRUPT_ENABLE (1<<5)
|
||||
#define S3C44B0_IIC_TIMEOUT 100
|
||||
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
|
||||
int k, j, temp;
|
||||
u32 rIICSTAT;
|
||||
|
||||
/*
|
||||
send the device offset
|
||||
*/
|
||||
|
||||
rIICSTAT = 0xD0;
|
||||
IICSTAT = rIICSTAT;
|
||||
|
||||
IICDS = chip; /* this is a write operation... */
|
||||
|
||||
rIICSTAT |= (1<<5);
|
||||
IICSTAT = rIICSTAT;
|
||||
|
||||
for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) {
|
||||
temp = IICCON;
|
||||
if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND)
|
||||
break;
|
||||
udelay(2000);
|
||||
}
|
||||
if (k==S3C44B0_IIC_TIMEOUT)
|
||||
return -1;
|
||||
|
||||
/* wait and check ACK */
|
||||
temp = IICSTAT;
|
||||
if ((temp & S3C44B0X_rIIC_LAST_RECEIV_BIT) == S3C44B0X_rIIC_LAST_RECEIV_BIT )
|
||||
return -1;
|
||||
|
||||
IICDS = addr;
|
||||
IICCON = IICCON & ~(S3C44B0X_rIIC_INTPEND);
|
||||
|
||||
/* wait and check ACK */
|
||||
for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) {
|
||||
temp = IICCON;
|
||||
if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND)
|
||||
break;
|
||||
udelay(2000);
|
||||
}
|
||||
if (k==S3C44B0_IIC_TIMEOUT)
|
||||
return -1;
|
||||
|
||||
temp = IICSTAT;
|
||||
if ((temp & S3C44B0X_rIIC_LAST_RECEIV_BIT) == S3C44B0X_rIIC_LAST_RECEIV_BIT )
|
||||
return -1;
|
||||
|
||||
/*
|
||||
now we can start with the read operation...
|
||||
*/
|
||||
|
||||
IICDS = chip | 0x01; /* this is a read operation... */
|
||||
|
||||
rIICSTAT = 0x90; /*master recv*/
|
||||
rIICSTAT |= (1<<5);
|
||||
IICSTAT = rIICSTAT;
|
||||
|
||||
IICCON = IICCON & ~(S3C44B0X_rIIC_INTPEND);
|
||||
|
||||
/* wait and check ACK */
|
||||
for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) {
|
||||
temp = IICCON;
|
||||
if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND)
|
||||
break;
|
||||
udelay(2000);
|
||||
}
|
||||
if (k==S3C44B0_IIC_TIMEOUT)
|
||||
return -1;
|
||||
|
||||
temp = IICSTAT;
|
||||
if ((temp & S3C44B0X_rIIC_LAST_RECEIV_BIT) == S3C44B0X_rIIC_LAST_RECEIV_BIT )
|
||||
return -1;
|
||||
|
||||
for (j=0; j<len-1; j++) {
|
||||
|
||||
/*clear pending bit to resume */
|
||||
|
||||
temp = IICCON & ~(S3C44B0X_rIIC_INTPEND);
|
||||
IICCON = temp;
|
||||
|
||||
/* wait and check ACK */
|
||||
for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) {
|
||||
temp = IICCON;
|
||||
if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND)
|
||||
break;
|
||||
udelay(2000);
|
||||
}
|
||||
if (k==S3C44B0_IIC_TIMEOUT)
|
||||
return -1;
|
||||
|
||||
|
||||
buffer[j] = IICDS; /*save readed data*/
|
||||
|
||||
} /*end for(j)*/
|
||||
|
||||
/*
|
||||
reading the last data
|
||||
unset ACK generation
|
||||
*/
|
||||
temp = IICCON & ~(S3C44B0X_rIIC_INTPEND | (1<<7));
|
||||
IICCON = temp;
|
||||
|
||||
/* wait but NOT check ACK */
|
||||
for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) {
|
||||
temp = IICCON;
|
||||
if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND)
|
||||
break;
|
||||
udelay(2000);
|
||||
}
|
||||
if (k==S3C44B0_IIC_TIMEOUT)
|
||||
return -1;
|
||||
|
||||
buffer[j] = IICDS; /*save readed data*/
|
||||
|
||||
rIICSTAT = 0x90; /*master recv*/
|
||||
|
||||
/* Write operation Terminate sending STOP */
|
||||
IICSTAT = rIICSTAT;
|
||||
/*Clear Int Pending Bit to RESUME*/
|
||||
temp = IICCON;
|
||||
IICCON = temp & (~S3C44B0X_rIIC_INTPEND);
|
||||
|
||||
IICCON = IICCON | (1<<7); /*restore ACK generation*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int j, k;
|
||||
u32 rIICSTAT, temp;
|
||||
|
||||
|
||||
/*
|
||||
send the device offset
|
||||
*/
|
||||
|
||||
rIICSTAT = 0xD0;
|
||||
IICSTAT = rIICSTAT;
|
||||
|
||||
IICDS = chip; /* this is a write operation... */
|
||||
|
||||
rIICSTAT |= (1<<5);
|
||||
IICSTAT = rIICSTAT;
|
||||
|
||||
IICCON = IICCON & ~(S3C44B0X_rIIC_INTPEND);
|
||||
|
||||
/* wait and check ACK */
|
||||
for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) {
|
||||
temp = IICCON;
|
||||
if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND)
|
||||
break;
|
||||
udelay(2000);
|
||||
}
|
||||
if (k==S3C44B0_IIC_TIMEOUT)
|
||||
return -1;
|
||||
|
||||
temp = IICSTAT;
|
||||
if ((temp & S3C44B0X_rIIC_LAST_RECEIV_BIT) == S3C44B0X_rIIC_LAST_RECEIV_BIT )
|
||||
return -1;
|
||||
|
||||
IICDS = addr;
|
||||
IICCON = IICCON & ~(S3C44B0X_rIIC_INTPEND);
|
||||
|
||||
/* wait and check ACK */
|
||||
for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) {
|
||||
temp = IICCON;
|
||||
if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND)
|
||||
break;
|
||||
udelay(2000);
|
||||
}
|
||||
if (k==S3C44B0_IIC_TIMEOUT)
|
||||
return -1;
|
||||
|
||||
temp = IICSTAT;
|
||||
if ((temp & S3C44B0X_rIIC_LAST_RECEIV_BIT) == S3C44B0X_rIIC_LAST_RECEIV_BIT )
|
||||
return -1;
|
||||
|
||||
/*
|
||||
now we can start with the read write operation
|
||||
*/
|
||||
for (j=0; j<len; j++) {
|
||||
|
||||
IICDS = buffer[j]; /*prerare data to write*/
|
||||
|
||||
/*clear pending bit to resume*/
|
||||
|
||||
temp = IICCON & ~(S3C44B0X_rIIC_INTPEND);
|
||||
IICCON = temp;
|
||||
|
||||
/* wait but NOT check ACK */
|
||||
for(k=0; k<S3C44B0_IIC_TIMEOUT; k++) {
|
||||
temp = IICCON;
|
||||
if( (temp & S3C44B0X_rIIC_INTPEND) == S3C44B0X_rIIC_INTPEND)
|
||||
break;
|
||||
|
||||
udelay(2000);
|
||||
}
|
||||
|
||||
if (k==S3C44B0_IIC_TIMEOUT)
|
||||
return -1;
|
||||
|
||||
} /* end for(j) */
|
||||
|
||||
/* sending stop to terminate */
|
||||
rIICSTAT = 0xD0; /*master send*/
|
||||
IICSTAT = rIICSTAT;
|
||||
/*Clear Int Pending Bit to RESUME*/
|
||||
temp = IICCON;
|
||||
IICCON = temp & (~S3C44B0X_rIIC_INTPEND);
|
||||
|
||||
return 0;
|
||||
}
|
292
root/package/utils/sysupgrade-helper/src/drivers/i2c/sh_i2c.c
Normal file
292
root/package/utils/sysupgrade-helper/src/drivers/i2c/sh_i2c.c
Normal file
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Renesas Solutions Corp.
|
||||
* Copyright (C) 2011 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.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., 59 Temple Place, Suite 330, Boston,
|
||||
* MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/* Every register is 32bit aligned, but only 8bits in size */
|
||||
#define ureg(name) u8 name; u8 __pad_##name##0; u16 __pad_##name##1;
|
||||
struct sh_i2c {
|
||||
ureg(icdr);
|
||||
ureg(iccr);
|
||||
ureg(icsr);
|
||||
ureg(icic);
|
||||
ureg(iccl);
|
||||
ureg(icch);
|
||||
};
|
||||
#undef ureg
|
||||
|
||||
static struct sh_i2c *base;
|
||||
|
||||
/* ICCR */
|
||||
#define SH_I2C_ICCR_ICE (1 << 7)
|
||||
#define SH_I2C_ICCR_RACK (1 << 6)
|
||||
#define SH_I2C_ICCR_RTS (1 << 4)
|
||||
#define SH_I2C_ICCR_BUSY (1 << 2)
|
||||
#define SH_I2C_ICCR_SCP (1 << 0)
|
||||
|
||||
/* ICSR / ICIC */
|
||||
#define SH_IC_BUSY (1 << 3)
|
||||
#define SH_IC_TACK (1 << 2)
|
||||
#define SH_IC_WAIT (1 << 1)
|
||||
#define SH_IC_DTE (1 << 0)
|
||||
|
||||
static u8 iccl, icch;
|
||||
|
||||
#define IRQ_WAIT 1000
|
||||
|
||||
static void irq_wait(struct sh_i2c *base)
|
||||
{
|
||||
int i;
|
||||
u8 status;
|
||||
|
||||
for (i = 0 ; i < IRQ_WAIT ; i++) {
|
||||
status = readb(&base->icsr);
|
||||
if (SH_IC_WAIT & status)
|
||||
break;
|
||||
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
writeb(status & ~SH_IC_WAIT, &base->icsr);
|
||||
}
|
||||
|
||||
static void irq_dte(struct sh_i2c *base)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < IRQ_WAIT ; i++) {
|
||||
if (SH_IC_DTE & readb(&base->icsr))
|
||||
break;
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void irq_busy(struct sh_i2c *base)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < IRQ_WAIT ; i++) {
|
||||
if (!(SH_IC_BUSY & readb(&base->icsr)))
|
||||
break;
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void i2c_set_addr(struct sh_i2c *base, u8 id, u8 reg, int stop)
|
||||
{
|
||||
writeb(readb(&base->iccr) & ~SH_I2C_ICCR_ICE, &base->iccr);
|
||||
writeb(readb(&base->iccr) | SH_I2C_ICCR_ICE, &base->iccr);
|
||||
|
||||
writeb(iccl, &base->iccl);
|
||||
writeb(icch, &base->icch);
|
||||
writeb(0, &base->icic);
|
||||
|
||||
writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RTS|SH_I2C_ICCR_BUSY), &base->iccr);
|
||||
irq_dte(base);
|
||||
|
||||
writeb(id << 1, &base->icdr);
|
||||
irq_dte(base);
|
||||
|
||||
writeb(reg, &base->icdr);
|
||||
if (stop)
|
||||
writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RTS), &base->iccr);
|
||||
|
||||
irq_dte(base);
|
||||
}
|
||||
|
||||
static void i2c_finish(struct sh_i2c *base)
|
||||
{
|
||||
writeb(0, &base->icsr);
|
||||
writeb(readb(&base->iccr) & ~SH_I2C_ICCR_ICE, &base->iccr);
|
||||
}
|
||||
|
||||
static void i2c_raw_write(struct sh_i2c *base, u8 id, u8 reg, u8 val)
|
||||
{
|
||||
i2c_set_addr(base, id, reg, 0);
|
||||
udelay(10);
|
||||
|
||||
writeb(val, &base->icdr);
|
||||
irq_dte(base);
|
||||
|
||||
writeb((SH_I2C_ICCR_ICE | SH_I2C_ICCR_RTS), &base->iccr);
|
||||
irq_dte(base);
|
||||
irq_busy(base);
|
||||
|
||||
i2c_finish(base);
|
||||
}
|
||||
|
||||
static u8 i2c_raw_read(struct sh_i2c *base, u8 id, u8 reg)
|
||||
{
|
||||
u8 ret;
|
||||
|
||||
i2c_set_addr(base, id, reg, 1);
|
||||
udelay(100);
|
||||
|
||||
writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RTS|SH_I2C_ICCR_BUSY), &base->iccr);
|
||||
irq_dte(base);
|
||||
|
||||
writeb(id << 1 | 0x01, &base->icdr);
|
||||
irq_dte(base);
|
||||
|
||||
writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_SCP), &base->iccr);
|
||||
irq_dte(base);
|
||||
|
||||
ret = readb(&base->icdr);
|
||||
|
||||
writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RACK), &base->iccr);
|
||||
readb(&base->icdr); /* Dummy read */
|
||||
irq_busy(base);
|
||||
|
||||
i2c_finish(base);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_I2C_MULTI_BUS
|
||||
static unsigned int current_bus;
|
||||
|
||||
/**
|
||||
* i2c_set_bus_num - change active I2C bus
|
||||
* @bus: bus index, zero based
|
||||
* @returns: 0 on success, non-0 on failure
|
||||
*/
|
||||
int i2c_set_bus_num(unsigned int bus)
|
||||
{
|
||||
if ((bus < 0) || (bus >= CONFIG_SYS_MAX_I2C_BUS)) {
|
||||
printf("Bad bus: %d\n", bus);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (bus) {
|
||||
case 0:
|
||||
base = (void *)CONFIG_SH_I2C_BASE0;
|
||||
break;
|
||||
case 1:
|
||||
base = (void *)CONFIG_SH_I2C_BASE1;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
current_bus = bus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_get_bus_num - returns index of active I2C bus
|
||||
*/
|
||||
unsigned int i2c_get_bus_num(void)
|
||||
{
|
||||
return current_bus;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SH_I2C_ICCL_CALC(clk, date, t_low, t_high) \
|
||||
((clk / rate) * (t_low / t_low + t_high))
|
||||
#define SH_I2C_ICCH_CALC(clk, date, t_low, t_high) \
|
||||
((clk / rate) * (t_high / t_low + t_high))
|
||||
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
int num, denom, tmp;
|
||||
|
||||
#ifdef CONFIG_I2C_MULTI_BUS
|
||||
current_bus = 0;
|
||||
#endif
|
||||
base = (struct sh_i2c *)CONFIG_SH_I2C_BASE0;
|
||||
|
||||
/*
|
||||
* Calculate the value for iccl. From the data sheet:
|
||||
* iccl = (p-clock / transfer-rate) * (L / (L + H))
|
||||
* where L and H are the SCL low and high ratio.
|
||||
*/
|
||||
num = CONFIG_SH_I2C_CLOCK * CONFIG_SH_I2C_DATA_LOW;
|
||||
denom = speed * (CONFIG_SH_I2C_DATA_HIGH + CONFIG_SH_I2C_DATA_LOW);
|
||||
tmp = num * 10 / denom;
|
||||
if (tmp % 10 >= 5)
|
||||
iccl = (u8)((num/denom) + 1);
|
||||
else
|
||||
iccl = (u8)(num/denom);
|
||||
|
||||
/* Calculate the value for icch. From the data sheet:
|
||||
icch = (p clock / transfer rate) * (H / (L + H)) */
|
||||
num = CONFIG_SH_I2C_CLOCK * CONFIG_SH_I2C_DATA_HIGH;
|
||||
tmp = num * 10 / denom;
|
||||
if (tmp % 10 >= 5)
|
||||
icch = (u8)((num/denom) + 1);
|
||||
else
|
||||
icch = (u8)(num/denom);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_read: - Read multiple bytes from an i2c device
|
||||
*
|
||||
* The higher level routines take into account that this function is only
|
||||
* called with len < page length of the device (see configuration file)
|
||||
*
|
||||
* @chip: address of the chip which is to be read
|
||||
* @addr: i2c data address within the chip
|
||||
* @alen: length of the i2c data address (1..2 bytes)
|
||||
* @buffer: where to write the data
|
||||
* @len: how much byte do we want to read
|
||||
* @return: 0 in case of success
|
||||
*/
|
||||
int i2c_read(u8 chip, u32 addr, int alen, u8 *buffer, int len)
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0 ; i < len ; i++)
|
||||
buffer[i] = i2c_raw_read(base, chip, addr + i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_write: - Write multiple bytes to an i2c device
|
||||
*
|
||||
* The higher level routines take into account that this function is only
|
||||
* called with len < page length of the device (see configuration file)
|
||||
*
|
||||
* @chip: address of the chip which is to be written
|
||||
* @addr: i2c data address within the chip
|
||||
* @alen: length of the i2c data address (1..2 bytes)
|
||||
* @buffer: where to find the data to be written
|
||||
* @len: how much byte do we want to read
|
||||
* @return: 0 in case of success
|
||||
*/
|
||||
int i2c_write(u8 chip, u32 addr, int alen, u8 *buffer, int len)
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0; i < len ; i++)
|
||||
i2c_raw_write(base, chip, addr + i, buffer[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_probe: - Test if a chip answers for a given i2c address
|
||||
*
|
||||
* @chip: address of the chip which is searched for
|
||||
* @return: 0 if a chip was found, -1 otherwhise
|
||||
*/
|
||||
int i2c_probe(u8 chip)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,387 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
|
||||
* Copyright (C) 2012 Renesas Solutions Corp.
|
||||
*
|
||||
* 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 <i2c.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
struct sh_i2c {
|
||||
u8 iccr1;
|
||||
u8 iccr2;
|
||||
u8 icmr;
|
||||
u8 icier;
|
||||
u8 icsr;
|
||||
u8 sar;
|
||||
u8 icdrt;
|
||||
u8 icdrr;
|
||||
u8 nf2cyc;
|
||||
u8 __pad0;
|
||||
u8 __pad1;
|
||||
};
|
||||
|
||||
static struct sh_i2c *base;
|
||||
static u8 iccr1_cks, nf2cyc;
|
||||
|
||||
/* ICCR1 */
|
||||
#define SH_I2C_ICCR1_ICE (1 << 7)
|
||||
#define SH_I2C_ICCR1_RCVD (1 << 6)
|
||||
#define SH_I2C_ICCR1_MST (1 << 5)
|
||||
#define SH_I2C_ICCR1_TRS (1 << 4)
|
||||
#define SH_I2C_ICCR1_MTRS \
|
||||
(SH_I2C_ICCR1_MST | SH_I2C_ICCR1_TRS)
|
||||
|
||||
/* ICCR1 */
|
||||
#define SH_I2C_ICCR2_BBSY (1 << 7)
|
||||
#define SH_I2C_ICCR2_SCP (1 << 6)
|
||||
#define SH_I2C_ICCR2_SDAO (1 << 5)
|
||||
#define SH_I2C_ICCR2_SDAOP (1 << 4)
|
||||
#define SH_I2C_ICCR2_SCLO (1 << 3)
|
||||
#define SH_I2C_ICCR2_IICRST (1 << 1)
|
||||
|
||||
#define SH_I2C_ICIER_TIE (1 << 7)
|
||||
#define SH_I2C_ICIER_TEIE (1 << 6)
|
||||
#define SH_I2C_ICIER_RIE (1 << 5)
|
||||
#define SH_I2C_ICIER_NAKIE (1 << 4)
|
||||
#define SH_I2C_ICIER_STIE (1 << 3)
|
||||
#define SH_I2C_ICIER_ACKE (1 << 2)
|
||||
#define SH_I2C_ICIER_ACKBR (1 << 1)
|
||||
#define SH_I2C_ICIER_ACKBT (1 << 0)
|
||||
|
||||
#define SH_I2C_ICSR_TDRE (1 << 7)
|
||||
#define SH_I2C_ICSR_TEND (1 << 6)
|
||||
#define SH_I2C_ICSR_RDRF (1 << 5)
|
||||
#define SH_I2C_ICSR_NACKF (1 << 4)
|
||||
#define SH_I2C_ICSR_STOP (1 << 3)
|
||||
#define SH_I2C_ICSR_ALOVE (1 << 2)
|
||||
#define SH_I2C_ICSR_AAS (1 << 1)
|
||||
#define SH_I2C_ICSR_ADZ (1 << 0)
|
||||
|
||||
#define IRQ_WAIT 1000
|
||||
|
||||
static void sh_i2c_send_stop(struct sh_i2c *base)
|
||||
{
|
||||
clrbits_8(&base->iccr2, SH_I2C_ICCR2_BBSY | SH_I2C_ICCR2_SCP);
|
||||
}
|
||||
|
||||
static int check_icsr_bits(struct sh_i2c *base, u8 bits)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IRQ_WAIT; i++) {
|
||||
if (bits & readb(&base->icsr))
|
||||
return 0;
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int check_stop(struct sh_i2c *base)
|
||||
{
|
||||
int ret = check_icsr_bits(base, SH_I2C_ICSR_STOP);
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_STOP);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_tend(struct sh_i2c *base, int stop)
|
||||
{
|
||||
int ret = check_icsr_bits(base, SH_I2C_ICSR_TEND);
|
||||
|
||||
if (stop) {
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_STOP);
|
||||
sh_i2c_send_stop(base);
|
||||
}
|
||||
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_TEND);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int check_tdre(struct sh_i2c *base)
|
||||
{
|
||||
return check_icsr_bits(base, SH_I2C_ICSR_TDRE);
|
||||
}
|
||||
|
||||
static int check_rdrf(struct sh_i2c *base)
|
||||
{
|
||||
return check_icsr_bits(base, SH_I2C_ICSR_RDRF);
|
||||
}
|
||||
|
||||
static int check_bbsy(struct sh_i2c *base)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < IRQ_WAIT ; i++) {
|
||||
if (!(SH_I2C_ICCR2_BBSY & readb(&base->iccr2)))
|
||||
return 0;
|
||||
udelay(10);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int check_ackbr(struct sh_i2c *base)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < IRQ_WAIT ; i++) {
|
||||
if (!(SH_I2C_ICIER_ACKBR & readb(&base->icier)))
|
||||
return 0;
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void sh_i2c_reset(struct sh_i2c *base)
|
||||
{
|
||||
setbits_8(&base->iccr2, SH_I2C_ICCR2_IICRST);
|
||||
|
||||
udelay(100);
|
||||
|
||||
clrbits_8(&base->iccr2, SH_I2C_ICCR2_IICRST);
|
||||
}
|
||||
|
||||
static int i2c_set_addr(struct sh_i2c *base, u8 id, u8 reg)
|
||||
{
|
||||
if (check_bbsy(base)) {
|
||||
puts("i2c bus busy\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
setbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS);
|
||||
clrsetbits_8(&base->iccr2, SH_I2C_ICCR2_SCP, SH_I2C_ICCR2_BBSY);
|
||||
|
||||
writeb((id << 1), &base->icdrt);
|
||||
|
||||
if (check_tend(base, 0)) {
|
||||
puts("TEND check fail...\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (check_ackbr(base)) {
|
||||
check_tend(base, 0);
|
||||
sh_i2c_send_stop(base);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
writeb(reg, &base->icdrt);
|
||||
|
||||
if (check_tdre(base)) {
|
||||
puts("TDRE check fail...\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (check_tend(base, 0)) {
|
||||
puts("TEND check fail...\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
i2c_raw_write(struct sh_i2c *base, u8 id, u8 reg, u8 *val, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (i2c_set_addr(base, id, reg)) {
|
||||
puts("Fail set slave address\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
writeb(val[i], &base->icdrt);
|
||||
check_tdre(base);
|
||||
}
|
||||
|
||||
check_tend(base, 1);
|
||||
check_stop(base);
|
||||
|
||||
udelay(100);
|
||||
|
||||
clrbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS);
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE);
|
||||
sh_i2c_reset(base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 i2c_raw_read(struct sh_i2c *base, u8 id, u8 reg)
|
||||
{
|
||||
u8 ret = 0;
|
||||
|
||||
if (i2c_set_addr(base, id, reg)) {
|
||||
puts("Fail set slave address\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
clrsetbits_8(&base->iccr2, SH_I2C_ICCR2_SCP, SH_I2C_ICCR2_BBSY);
|
||||
writeb((id << 1) | 1, &base->icdrt);
|
||||
|
||||
if (check_tend(base, 0))
|
||||
puts("TDRE check fail...\n");
|
||||
|
||||
clrsetbits_8(&base->iccr1, SH_I2C_ICCR1_TRS, SH_I2C_ICCR1_MST);
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE);
|
||||
setbits_8(&base->icier, SH_I2C_ICIER_ACKBT);
|
||||
setbits_8(&base->iccr1, SH_I2C_ICCR1_RCVD);
|
||||
|
||||
/* read data (dummy) */
|
||||
ret = readb(&base->icdrr);
|
||||
|
||||
if (check_rdrf(base)) {
|
||||
puts("check RDRF error\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_STOP);
|
||||
udelay(1000);
|
||||
|
||||
sh_i2c_send_stop(base);
|
||||
|
||||
if (check_stop(base)) {
|
||||
puts("check STOP error\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
clrbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS);
|
||||
clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE);
|
||||
|
||||
/* data read */
|
||||
ret = readb(&base->icdrr);
|
||||
|
||||
fail:
|
||||
clrbits_8(&base->iccr1, SH_I2C_ICCR1_RCVD);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_I2C_MULTI_BUS
|
||||
static unsigned int current_bus;
|
||||
|
||||
/**
|
||||
* i2c_set_bus_num - change active I2C bus
|
||||
* @bus: bus index, zero based
|
||||
* @returns: 0 on success, non-0 on failure
|
||||
*/
|
||||
int i2c_set_bus_num(unsigned int bus)
|
||||
{
|
||||
switch (bus) {
|
||||
case 0:
|
||||
base = (void *)CONFIG_SH_I2C_BASE0;
|
||||
break;
|
||||
case 1:
|
||||
base = (void *)CONFIG_SH_I2C_BASE1;
|
||||
break;
|
||||
default:
|
||||
printf("Bad bus: %d\n", bus);
|
||||
return -1;
|
||||
}
|
||||
|
||||
current_bus = bus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* i2c_get_bus_num - returns index of active I2C bus
|
||||
*/
|
||||
unsigned int i2c_get_bus_num(void)
|
||||
{
|
||||
return current_bus;
|
||||
}
|
||||
#endif
|
||||
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
#ifdef CONFIG_I2C_MULTI_BUS
|
||||
current_bus = 0;
|
||||
#endif
|
||||
base = (struct sh_i2c *)CONFIG_SH_I2C_BASE0;
|
||||
|
||||
if (speed == 400000)
|
||||
iccr1_cks = 0x07;
|
||||
else
|
||||
iccr1_cks = 0x0F;
|
||||
|
||||
nf2cyc = 1;
|
||||
|
||||
/* Reset */
|
||||
sh_i2c_reset(base);
|
||||
|
||||
/* ICE enable and set clock */
|
||||
writeb(SH_I2C_ICCR1_ICE | iccr1_cks, &base->iccr1);
|
||||
writeb(nf2cyc, &base->nf2cyc);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_read: - Read multiple bytes from an i2c device
|
||||
*
|
||||
* The higher level routines take into account that this function is only
|
||||
* called with len < page length of the device (see configuration file)
|
||||
*
|
||||
* @chip: address of the chip which is to be read
|
||||
* @addr: i2c data address within the chip
|
||||
* @alen: length of the i2c data address (1..2 bytes)
|
||||
* @buffer: where to write the data
|
||||
* @len: how much byte do we want to read
|
||||
* @return: 0 in case of success
|
||||
*/
|
||||
int i2c_read(u8 chip, u32 addr, int alen, u8 *buffer, int len)
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0; i < len; i++)
|
||||
buffer[i] = i2c_raw_read(base, chip, addr + i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_write: - Write multiple bytes to an i2c device
|
||||
*
|
||||
* The higher level routines take into account that this function is only
|
||||
* called with len < page length of the device (see configuration file)
|
||||
*
|
||||
* @chip: address of the chip which is to be written
|
||||
* @addr: i2c data address within the chip
|
||||
* @alen: length of the i2c data address (1..2 bytes)
|
||||
* @buffer: where to find the data to be written
|
||||
* @len: how much byte do we want to read
|
||||
* @return: 0 in case of success
|
||||
*/
|
||||
int i2c_write(u8 chip, u32 addr, int alen, u8 *buffer, int len)
|
||||
{
|
||||
return i2c_raw_write(base, chip, addr, buffer, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_probe: - Test if a chip answers for a given i2c address
|
||||
*
|
||||
* @chip: address of the chip which is searched for
|
||||
* @return: 0 if a chip was found, -1 otherwhise
|
||||
*/
|
||||
int i2c_probe(u8 chip)
|
||||
{
|
||||
u8 byte;
|
||||
return i2c_read(chip, 0, 0, &byte, 1);
|
||||
}
|
484
root/package/utils/sysupgrade-helper/src/drivers/i2c/soft_i2c.c
Normal file
484
root/package/utils/sysupgrade-helper/src/drivers/i2c/soft_i2c.c
Normal file
|
@ -0,0 +1,484 @@
|
|||
/*
|
||||
* (C) Copyright 2001, 2002
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* This has been changed substantially by Gerald Van Baren, Custom IDEAS,
|
||||
* vanbaren@cideas.com. It was heavily influenced by LiMon, written by
|
||||
* Neil Russell.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#ifdef CONFIG_MPC8260 /* only valid for MPC8260 */
|
||||
#include <ioports.h>
|
||||
#include <asm/io.h>
|
||||
#endif
|
||||
#if defined(CONFIG_AT91FAMILY)
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/at91_pio.h>
|
||||
#ifdef CONFIG_AT91_LEGACY
|
||||
#include <asm/arch/gpio.h>
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_IXP425 /* only valid for IXP425 */
|
||||
#include <asm/arch/ixp425.h>
|
||||
#endif
|
||||
#ifdef CONFIG_LPC2292
|
||||
#include <asm/arch/hardware.h>
|
||||
#endif
|
||||
#if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866)
|
||||
#include <asm/io.h>
|
||||
#endif
|
||||
#include <i2c.h>
|
||||
|
||||
#if defined(CONFIG_SOFT_I2C_GPIO_SCL)
|
||||
# include <asm/gpio.h>
|
||||
|
||||
# ifndef I2C_GPIO_SYNC
|
||||
# define I2C_GPIO_SYNC
|
||||
# endif
|
||||
|
||||
# ifndef I2C_INIT
|
||||
# define I2C_INIT \
|
||||
do { \
|
||||
gpio_request(CONFIG_SOFT_I2C_GPIO_SCL, "soft_i2c"); \
|
||||
gpio_request(CONFIG_SOFT_I2C_GPIO_SDA, "soft_i2c"); \
|
||||
} while (0)
|
||||
# endif
|
||||
|
||||
# ifndef I2C_ACTIVE
|
||||
# define I2C_ACTIVE do { } while (0)
|
||||
# endif
|
||||
|
||||
# ifndef I2C_TRISTATE
|
||||
# define I2C_TRISTATE do { } while (0)
|
||||
# endif
|
||||
|
||||
# ifndef I2C_READ
|
||||
# define I2C_READ gpio_get_value(CONFIG_SOFT_I2C_GPIO_SDA)
|
||||
# endif
|
||||
|
||||
# ifndef I2C_SDA
|
||||
# define I2C_SDA(bit) \
|
||||
do { \
|
||||
if (bit) \
|
||||
gpio_direction_input(CONFIG_SOFT_I2C_GPIO_SDA); \
|
||||
else \
|
||||
gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SDA, 0); \
|
||||
I2C_GPIO_SYNC; \
|
||||
} while (0)
|
||||
# endif
|
||||
|
||||
# ifndef I2C_SCL
|
||||
# define I2C_SCL(bit) \
|
||||
do { \
|
||||
gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SCL, bit); \
|
||||
I2C_GPIO_SYNC; \
|
||||
} while (0)
|
||||
# endif
|
||||
|
||||
# ifndef I2C_DELAY
|
||||
# define I2C_DELAY udelay(5) /* 1/4 I2C clock duration */
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
/* #define DEBUG_I2C */
|
||||
|
||||
#ifdef DEBUG_I2C
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Definitions
|
||||
*/
|
||||
|
||||
#define RETRIES 0
|
||||
|
||||
#define I2C_ACK 0 /* PD_SDA level to ack a byte */
|
||||
#define I2C_NOACK 1 /* PD_SDA level to noack a byte */
|
||||
|
||||
|
||||
#ifdef DEBUG_I2C
|
||||
#define PRINTD(fmt,args...) do { \
|
||||
printf (fmt ,##args); \
|
||||
} while (0)
|
||||
#else
|
||||
#define PRINTD(fmt,args...)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_I2C_MULTI_BUS)
|
||||
static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = 0;
|
||||
#endif /* CONFIG_I2C_MULTI_BUS */
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Local functions
|
||||
*/
|
||||
#if !defined(CONFIG_SYS_I2C_INIT_BOARD)
|
||||
static void send_reset (void);
|
||||
#endif
|
||||
static void send_start (void);
|
||||
static void send_stop (void);
|
||||
static void send_ack (int);
|
||||
static int write_byte (uchar byte);
|
||||
static uchar read_byte (int);
|
||||
|
||||
#if !defined(CONFIG_SYS_I2C_INIT_BOARD)
|
||||
/*-----------------------------------------------------------------------
|
||||
* Send a reset sequence consisting of 9 clocks with the data signal high
|
||||
* to clock any confused device back into an idle state. Also send a
|
||||
* <stop> at the end of the sequence for belts & suspenders.
|
||||
*/
|
||||
static void send_reset(void)
|
||||
{
|
||||
I2C_SOFT_DECLARATIONS /* intentional without ';' */
|
||||
int j;
|
||||
|
||||
I2C_SCL(1);
|
||||
I2C_SDA(1);
|
||||
#ifdef I2C_INIT
|
||||
I2C_INIT;
|
||||
#endif
|
||||
I2C_TRISTATE;
|
||||
for(j = 0; j < 9; j++) {
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
I2C_DELAY;
|
||||
}
|
||||
send_stop();
|
||||
I2C_TRISTATE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* START: High -> Low on SDA while SCL is High
|
||||
*/
|
||||
static void send_start(void)
|
||||
{
|
||||
I2C_SOFT_DECLARATIONS /* intentional without ';' */
|
||||
|
||||
I2C_DELAY;
|
||||
I2C_SDA(1);
|
||||
I2C_ACTIVE;
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
I2C_SDA(0);
|
||||
I2C_DELAY;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* STOP: Low -> High on SDA while SCL is High
|
||||
*/
|
||||
static void send_stop(void)
|
||||
{
|
||||
I2C_SOFT_DECLARATIONS /* intentional without ';' */
|
||||
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_SDA(0);
|
||||
I2C_ACTIVE;
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
I2C_SDA(1);
|
||||
I2C_DELAY;
|
||||
I2C_TRISTATE;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* ack should be I2C_ACK or I2C_NOACK
|
||||
*/
|
||||
static void send_ack(int ack)
|
||||
{
|
||||
I2C_SOFT_DECLARATIONS /* intentional without ';' */
|
||||
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_ACTIVE;
|
||||
I2C_SDA(ack);
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
I2C_DELAY;
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Send 8 bits and look for an acknowledgement.
|
||||
*/
|
||||
static int write_byte(uchar data)
|
||||
{
|
||||
I2C_SOFT_DECLARATIONS /* intentional without ';' */
|
||||
int j;
|
||||
int nack;
|
||||
|
||||
I2C_ACTIVE;
|
||||
for(j = 0; j < 8; j++) {
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_SDA(data & 0x80);
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
I2C_DELAY;
|
||||
|
||||
data <<= 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look for an <ACK>(negative logic) and return it.
|
||||
*/
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_SDA(1);
|
||||
I2C_TRISTATE;
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
I2C_DELAY;
|
||||
nack = I2C_READ;
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_ACTIVE;
|
||||
|
||||
return(nack); /* not a nack is an ack */
|
||||
}
|
||||
|
||||
#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 defined(CONFIG_I2C_MUX)
|
||||
if (bus < CONFIG_SYS_MAX_I2C_BUS) {
|
||||
i2c_bus_num = bus;
|
||||
} else {
|
||||
int ret;
|
||||
|
||||
ret = i2x_mux_select_mux(bus);
|
||||
i2c_init_board();
|
||||
if (ret == 0)
|
||||
i2c_bus_num = bus;
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
if (bus >= CONFIG_SYS_MAX_I2C_BUS)
|
||||
return -1;
|
||||
i2c_bus_num = bus;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* if ack == I2C_ACK, ACK the byte so can continue reading, else
|
||||
* send I2C_NOACK to end the read.
|
||||
*/
|
||||
static uchar read_byte(int ack)
|
||||
{
|
||||
I2C_SOFT_DECLARATIONS /* intentional without ';' */
|
||||
int data;
|
||||
int j;
|
||||
|
||||
/*
|
||||
* Read 8 bits, MSB first.
|
||||
*/
|
||||
I2C_TRISTATE;
|
||||
I2C_SDA(1);
|
||||
data = 0;
|
||||
for(j = 0; j < 8; j++) {
|
||||
I2C_SCL(0);
|
||||
I2C_DELAY;
|
||||
I2C_SCL(1);
|
||||
I2C_DELAY;
|
||||
data <<= 1;
|
||||
data |= I2C_READ;
|
||||
I2C_DELAY;
|
||||
}
|
||||
send_ack(ack);
|
||||
|
||||
return(data);
|
||||
}
|
||||
|
||||
/*=====================================================================*/
|
||||
/* Public Functions */
|
||||
/*=====================================================================*/
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Initialization
|
||||
*/
|
||||
void i2c_init (int speed, int slaveaddr)
|
||||
{
|
||||
#if defined(CONFIG_SYS_I2C_INIT_BOARD)
|
||||
/* call board specific i2c bus reset routine before accessing the */
|
||||
/* environment, which might be in a chip on that bus. For details */
|
||||
/* about this problem see doc/I2C_Edge_Conditions. */
|
||||
i2c_init_board();
|
||||
#else
|
||||
/*
|
||||
* WARNING: Do NOT save speed in a static variable: if the
|
||||
* I2C routines are called before RAM is initialized (to read
|
||||
* the DIMM SPD, for instance), RAM won't be usable and your
|
||||
* system will crash.
|
||||
*/
|
||||
send_reset ();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Probe to see if a chip is present. Also good for checking for the
|
||||
* completion of EEPROM writes since the chip stops responding until
|
||||
* the write completes (typically 10mSec).
|
||||
*/
|
||||
int i2c_probe(uchar addr)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* perform 1 byte write transaction with just address byte
|
||||
* (fake write)
|
||||
*/
|
||||
send_start();
|
||||
rc = write_byte ((addr << 1) | 0);
|
||||
send_stop();
|
||||
|
||||
return (rc ? 1 : 0);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Read bytes
|
||||
*/
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int shift;
|
||||
PRINTD("i2c_read: chip %02X addr %02X alen %d buffer %p len %d\n",
|
||||
chip, addr, alen, buffer, len);
|
||||
|
||||
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
|
||||
/*
|
||||
* EEPROM chips that implement "address overflow" are ones
|
||||
* like Catalyst 24WC04/08/16 which has 9/10/11 bits of
|
||||
* address and the extra bits end up in the "chip address"
|
||||
* bit slots. This makes a 24WC08 (1Kbyte) chip look like
|
||||
* four 256 byte chips.
|
||||
*
|
||||
* Note that we consider the length of the address field to
|
||||
* still be one byte because the extra address bits are
|
||||
* hidden in the chip address.
|
||||
*/
|
||||
chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
|
||||
|
||||
PRINTD("i2c_read: fix addr_overflow: chip %02X addr %02X\n",
|
||||
chip, addr);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Do the addressing portion of a write cycle to set the
|
||||
* chip's address pointer. If the address length is zero,
|
||||
* don't do the normal write cycle to set the address pointer,
|
||||
* there is no address pointer in this chip.
|
||||
*/
|
||||
send_start();
|
||||
if(alen > 0) {
|
||||
if(write_byte(chip << 1)) { /* write cycle */
|
||||
send_stop();
|
||||
PRINTD("i2c_read, no chip responded %02X\n", chip);
|
||||
return(1);
|
||||
}
|
||||
shift = (alen-1) * 8;
|
||||
while(alen-- > 0) {
|
||||
if(write_byte(addr >> shift)) {
|
||||
PRINTD("i2c_read, address not <ACK>ed\n");
|
||||
return(1);
|
||||
}
|
||||
shift -= 8;
|
||||
}
|
||||
|
||||
/* Some I2C chips need a stop/start sequence here,
|
||||
* other chips don't work with a full stop and need
|
||||
* only a start. Default behaviour is to send the
|
||||
* stop/start sequence.
|
||||
*/
|
||||
#ifdef CONFIG_SOFT_I2C_READ_REPEATED_START
|
||||
send_start();
|
||||
#else
|
||||
send_stop();
|
||||
send_start();
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
* Send the chip address again, this time for a read cycle.
|
||||
* Then read the data. On the last byte, we do a NACK instead
|
||||
* of an ACK(len == 0) to terminate the read.
|
||||
*/
|
||||
write_byte((chip << 1) | 1); /* read cycle */
|
||||
while(len-- > 0) {
|
||||
*buffer++ = read_byte(len == 0);
|
||||
}
|
||||
send_stop();
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*-----------------------------------------------------------------------
|
||||
* Write bytes
|
||||
*/
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int shift, failures = 0;
|
||||
|
||||
PRINTD("i2c_write: chip %02X addr %02X alen %d buffer %p len %d\n",
|
||||
chip, addr, alen, buffer, len);
|
||||
|
||||
send_start();
|
||||
if(write_byte(chip << 1)) { /* write cycle */
|
||||
send_stop();
|
||||
PRINTD("i2c_write, no chip responded %02X\n", chip);
|
||||
return(1);
|
||||
}
|
||||
shift = (alen-1) * 8;
|
||||
while(alen-- > 0) {
|
||||
if(write_byte(addr >> shift)) {
|
||||
PRINTD("i2c_write, address not <ACK>ed\n");
|
||||
return(1);
|
||||
}
|
||||
shift -= 8;
|
||||
}
|
||||
|
||||
while(len-- > 0) {
|
||||
if(write_byte(*buffer++)) {
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
send_stop();
|
||||
return(failures);
|
||||
}
|
583
root/package/utils/sysupgrade-helper/src/drivers/i2c/tegra_i2c.c
Normal file
583
root/package/utils/sysupgrade-helper/src/drivers/i2c/tegra_i2c.c
Normal file
|
@ -0,0 +1,583 @@
|
|||
/*
|
||||
* Copyright (c) 2012 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 <fdtdec.h>
|
||||
#include <i2c.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/tegra_i2c.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;
|
||||
int is_dvc; /* DVC type, rather than I2C */
|
||||
int inited; /* bus is inited */
|
||||
};
|
||||
|
||||
static struct i2c_bus i2c_controllers[TEGRA_I2C_NUM_CONTROLLERS];
|
||||
|
||||
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->is_dvc) {
|
||||
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)
|
||||
{
|
||||
/*
|
||||
* Use PLLP - DP-04508-001_v06 datasheet indicates a divisor of 8
|
||||
* here, in section 23.3.1, but in fact we seem to need a factor of
|
||||
* 16 to get the right frequency.
|
||||
*/
|
||||
clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH,
|
||||
i2c_bus->speed * 2 * 8);
|
||||
|
||||
/* Reset I2C controller. */
|
||||
i2c_reset_controller(i2c_bus);
|
||||
|
||||
/* Configure I2C controller. */
|
||||
if (i2c_bus->is_dvc) { /* 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 = DIV_ROUND_UP(trans->num_bytes, 4);
|
||||
last_bytes = trans->num_bytes & 3;
|
||||
dptr = trans->buf;
|
||||
|
||||
while (words) {
|
||||
u32 *wptr = (u32 *)dptr;
|
||||
|
||||
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(*wptr, &control->tx_fifo);
|
||||
debug("pkt data sent (0x%x)\n", *wptr);
|
||||
}
|
||||
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
|
||||
*wptr = 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
|
||||
#error "Please enable device tree support to use this driver"
|
||||
#endif
|
||||
|
||||
unsigned int i2c_get_bus_speed(void)
|
||||
{
|
||||
return i2c_controllers[i2c_bus_num].speed;
|
||||
}
|
||||
|
||||
int i2c_set_bus_speed(unsigned int speed)
|
||||
{
|
||||
struct i2c_bus *i2c_bus;
|
||||
|
||||
i2c_bus = &i2c_controllers[i2c_bus_num];
|
||||
i2c_bus->speed = speed;
|
||||
i2c_init_controller(i2c_bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus)
|
||||
{
|
||||
i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node, "reg");
|
||||
|
||||
/*
|
||||
* We don't have a binding for pinmux yet. Leave it out for now. So
|
||||
* far no one needs anything other than the default.
|
||||
*/
|
||||
i2c_bus->pinmux_config = FUNCMUX_DEFAULT;
|
||||
i2c_bus->speed = fdtdec_get_int(blob, node, "clock-frequency", 0);
|
||||
i2c_bus->periph_id = clock_decode_periph_id(blob, node);
|
||||
|
||||
/*
|
||||
* We can't specify the pinmux config in the fdt, so I2C2 will not
|
||||
* work on Seaboard. It normally has no devices on it anyway.
|
||||
* You could add in this little hack if you need to use it.
|
||||
* The correct solution is a pinmux binding in the fdt.
|
||||
*
|
||||
* if (i2c_bus->periph_id == PERIPH_ID_I2C2)
|
||||
* i2c_bus->pinmux_config = FUNCMUX_I2C2_PTA;
|
||||
*/
|
||||
if (i2c_bus->periph_id == -1)
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a list of nodes, adding them to our list of I2C ports.
|
||||
*
|
||||
* @param blob fdt blob
|
||||
* @param node_list list of nodes to process (any <=0 are ignored)
|
||||
* @param count number of nodes to process
|
||||
* @param is_dvc 1 if these are DVC ports, 0 if standard I2C
|
||||
* @return 0 if ok, -1 on error
|
||||
*/
|
||||
static int process_nodes(const void *blob, int node_list[], int count,
|
||||
int is_dvc)
|
||||
{
|
||||
struct i2c_bus *i2c_bus;
|
||||
int i;
|
||||
|
||||
/* build the i2c_controllers[] for each controller */
|
||||
for (i = 0; i < count; i++) {
|
||||
int node = node_list[i];
|
||||
|
||||
if (node <= 0)
|
||||
continue;
|
||||
|
||||
i2c_bus = &i2c_controllers[i];
|
||||
i2c_bus->id = i;
|
||||
|
||||
if (i2c_get_config(blob, node, i2c_bus)) {
|
||||
printf("i2c_init_board: failed to decode bus %d\n", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
i2c_bus->is_dvc = is_dvc;
|
||||
if (is_dvc) {
|
||||
i2c_bus->control =
|
||||
&((struct dvc_ctlr *)i2c_bus->regs)->control;
|
||||
} else {
|
||||
i2c_bus->control = &i2c_bus->regs->control;
|
||||
}
|
||||
debug("%s: controller bus %d at %p, periph_id %d, speed %d: ",
|
||||
is_dvc ? "dvc" : "i2c", i, i2c_bus->regs,
|
||||
i2c_bus->periph_id, i2c_bus->speed);
|
||||
i2c_init_controller(i2c_bus);
|
||||
debug("ok\n");
|
||||
i2c_bus->inited = 1;
|
||||
|
||||
/* Mark position as used */
|
||||
node_list[i] = -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sadly there is no error return from this function */
|
||||
void i2c_init_board(void)
|
||||
{
|
||||
int node_list[TEGRA_I2C_NUM_CONTROLLERS];
|
||||
const void *blob = gd->fdt_blob;
|
||||
int count;
|
||||
|
||||
/* First get the normal i2c ports */
|
||||
count = fdtdec_find_aliases_for_id(blob, "i2c",
|
||||
COMPAT_NVIDIA_TEGRA20_I2C, node_list,
|
||||
TEGRA_I2C_NUM_CONTROLLERS);
|
||||
if (process_nodes(blob, node_list, count, 0))
|
||||
return;
|
||||
|
||||
/* Now look for dvc ports */
|
||||
count = fdtdec_add_aliases_for_id(blob, "i2c",
|
||||
COMPAT_NVIDIA_TEGRA20_DVC, node_list,
|
||||
TEGRA_I2C_NUM_CONTROLLERS);
|
||||
if (process_nodes(blob, node_list, count, 1))
|
||||
return;
|
||||
}
|
||||
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
/* This will override the speed selected in the fdt for that port */
|
||||
debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr);
|
||||
i2c_set_bus_speed(speed);
|
||||
}
|
||||
|
||||
/* 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");
|
||||
|
||||
/* Shift 7-bit address over for lower-level i2c functions */
|
||||
rc = tegra2_i2c_write_data(chip << 1, 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");
|
||||
/* Shift 7-bit address over for lower-level i2c functions */
|
||||
rc = tegra2_i2c_read_data(chip << 1, 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;
|
||||
}
|
||||
|
||||
static int i2c_addr_ok(const uint addr, const int alen)
|
||||
{
|
||||
/* We support 7 or 10 bit addresses, so one or two bytes each */
|
||||
return alen == 1 || alen == 2;
|
||||
}
|
||||
|
||||
/* 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 >= TEGRA_I2C_NUM_CONTROLLERS || !i2c_controllers[bus].inited)
|
||||
return -1;
|
||||
i2c_bus_num = bus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int tegra_i2c_get_dvc_bus_num(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CONFIG_SYS_MAX_I2C_BUS; i++) {
|
||||
struct i2c_bus *bus = &i2c_controllers[i];
|
||||
|
||||
if (bus->inited && bus->is_dvc)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* (C) Copyright 2004 Tundra Semiconductor Corp.
|
||||
* Author: Alex Bounine
|
||||
*
|
||||
* 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 <config.h>
|
||||
#include <common.h>
|
||||
|
||||
#include <tsi108.h>
|
||||
|
||||
#if defined(CONFIG_CMD_I2C)
|
||||
|
||||
#define I2C_DELAY 100000
|
||||
#undef DEBUG_I2C
|
||||
|
||||
#ifdef DEBUG_I2C
|
||||
#define DPRINT(x) printf (x)
|
||||
#else
|
||||
#define DPRINT(x)
|
||||
#endif
|
||||
|
||||
/* All functions assume that Tsi108 I2C block is the only master on the bus */
|
||||
/* I2C read helper function */
|
||||
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
/*
|
||||
* The TSI108 has a fixed I2C clock rate and doesn't support slave
|
||||
* operation. This function only exists as a stub to fit into the
|
||||
* U-Boot I2C API.
|
||||
*/
|
||||
}
|
||||
|
||||
static int i2c_read_byte (
|
||||
uint i2c_chan, /* I2C channel number: 0 - main, 1 - SDC SPD */
|
||||
uchar chip_addr,/* I2C device address on the bus */
|
||||
uint byte_addr, /* Byte address within I2C device */
|
||||
uchar * buffer /* pointer to data buffer */
|
||||
)
|
||||
{
|
||||
u32 temp;
|
||||
u32 to_count = I2C_DELAY;
|
||||
u32 op_status = TSI108_I2C_TIMEOUT_ERR;
|
||||
u32 chan_offset = TSI108_I2C_OFFSET;
|
||||
|
||||
DPRINT (("I2C read_byte() %d 0x%02x 0x%02x\n",
|
||||
i2c_chan, chip_addr, byte_addr));
|
||||
|
||||
if (0 != i2c_chan)
|
||||
chan_offset = TSI108_I2C_SDRAM_OFFSET;
|
||||
|
||||
/* Check if I2C operation is in progress */
|
||||
temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2);
|
||||
|
||||
if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS |
|
||||
I2C_CNTRL2_START))) {
|
||||
/* Set device address and operation (read = 0) */
|
||||
temp = (byte_addr << 16) | ((chip_addr & 0x07) << 8) |
|
||||
((chip_addr >> 3) & 0x0F);
|
||||
*(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL1) =
|
||||
temp;
|
||||
|
||||
/* Issue the read command
|
||||
* (at this moment all other parameters are 0
|
||||
* (size = 1 byte, lane = 0)
|
||||
*/
|
||||
|
||||
*(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2) =
|
||||
(I2C_CNTRL2_START);
|
||||
|
||||
/* Wait until operation completed */
|
||||
do {
|
||||
/* Read I2C operation status */
|
||||
temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2);
|
||||
|
||||
if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_START))) {
|
||||
if (0 == (temp &
|
||||
(I2C_CNTRL2_I2C_CFGERR |
|
||||
I2C_CNTRL2_I2C_TO_ERR))
|
||||
) {
|
||||
op_status = TSI108_I2C_SUCCESS;
|
||||
|
||||
temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE +
|
||||
chan_offset +
|
||||
I2C_RD_DATA);
|
||||
|
||||
*buffer = (u8) (temp & 0xFF);
|
||||
} else {
|
||||
/* report HW error */
|
||||
op_status = TSI108_I2C_IF_ERROR;
|
||||
|
||||
DPRINT (("I2C HW error reported: 0x%02x\n", temp));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} while (to_count--);
|
||||
} else {
|
||||
op_status = TSI108_I2C_IF_BUSY;
|
||||
|
||||
DPRINT (("I2C Transaction start failed: 0x%02x\n", temp));
|
||||
}
|
||||
|
||||
DPRINT (("I2C read_byte() status: 0x%02x\n", op_status));
|
||||
return op_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C Read interface as defined in "include/i2c.h" :
|
||||
* chip_addr: I2C chip address, range 0..127
|
||||
* (to read from SPD channel EEPROM use (0xD0 ... 0xD7)
|
||||
* NOTE: The bit 7 in the chip_addr serves as a channel select.
|
||||
* This hack is for enabling "i2c sdram" command on Tsi108 boards
|
||||
* without changes to common code. Used for I2C reads only.
|
||||
* byte_addr: Memory or register address within the chip
|
||||
* alen: Number of bytes to use for addr (typically 1, 2 for larger
|
||||
* memories, 0 for register type devices with only one
|
||||
* register)
|
||||
* buffer: Pointer to destination buffer for data to be read
|
||||
* len: How many bytes to read
|
||||
*
|
||||
* Returns: 0 on success, not 0 on failure
|
||||
*/
|
||||
|
||||
int i2c_read (uchar chip_addr, uint byte_addr, int alen,
|
||||
uchar * buffer, int len)
|
||||
{
|
||||
u32 op_status = TSI108_I2C_PARAM_ERR;
|
||||
u32 i2c_if = 0;
|
||||
|
||||
/* Hack to support second (SPD) I2C controller (SPD EEPROM read only).*/
|
||||
if (0xD0 == (chip_addr & ~0x07)) {
|
||||
i2c_if = 1;
|
||||
chip_addr &= 0x7F;
|
||||
}
|
||||
/* Check for valid I2C address */
|
||||
if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) {
|
||||
while (len--) {
|
||||
op_status = i2c_read_byte(i2c_if, chip_addr, byte_addr++, buffer++);
|
||||
|
||||
if (TSI108_I2C_SUCCESS != op_status) {
|
||||
DPRINT (("I2C read_byte() failed: 0x%02x (%d left)\n", op_status, len));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DPRINT (("I2C read() status: 0x%02x\n", op_status));
|
||||
return op_status;
|
||||
}
|
||||
|
||||
/* I2C write helper function */
|
||||
|
||||
static int i2c_write_byte (uchar chip_addr,/* I2C device address on the bus */
|
||||
uint byte_addr, /* Byte address within I2C device */
|
||||
uchar * buffer /* pointer to data buffer */
|
||||
)
|
||||
{
|
||||
u32 temp;
|
||||
u32 to_count = I2C_DELAY;
|
||||
u32 op_status = TSI108_I2C_TIMEOUT_ERR;
|
||||
|
||||
/* Check if I2C operation is in progress */
|
||||
temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + I2C_CNTRL2);
|
||||
|
||||
if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) {
|
||||
/* Place data into the I2C Tx Register */
|
||||
*(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET +
|
||||
I2C_TX_DATA) = (u32) * buffer;
|
||||
|
||||
/* Set device address and operation */
|
||||
temp =
|
||||
I2C_CNTRL1_I2CWRITE | (byte_addr << 16) |
|
||||
((chip_addr & 0x07) << 8) | ((chip_addr >> 3) & 0x0F);
|
||||
*(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET +
|
||||
I2C_CNTRL1) = temp;
|
||||
|
||||
/* Issue the write command (at this moment all other parameters
|
||||
* are 0 (size = 1 byte, lane = 0)
|
||||
*/
|
||||
|
||||
*(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET +
|
||||
I2C_CNTRL2) = (I2C_CNTRL2_START);
|
||||
|
||||
op_status = TSI108_I2C_TIMEOUT_ERR;
|
||||
|
||||
/* Wait until operation completed */
|
||||
do {
|
||||
/* Read I2C operation status */
|
||||
temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + I2C_CNTRL2);
|
||||
|
||||
if (0 == (temp & (I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) {
|
||||
if (0 == (temp &
|
||||
(I2C_CNTRL2_I2C_CFGERR |
|
||||
I2C_CNTRL2_I2C_TO_ERR))) {
|
||||
op_status = TSI108_I2C_SUCCESS;
|
||||
} else {
|
||||
/* report detected HW error */
|
||||
op_status = TSI108_I2C_IF_ERROR;
|
||||
|
||||
DPRINT (("I2C HW error reported: 0x%02x\n", temp));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
} while (to_count--);
|
||||
} else {
|
||||
op_status = TSI108_I2C_IF_BUSY;
|
||||
|
||||
DPRINT (("I2C Transaction start failed: 0x%02x\n", temp));
|
||||
}
|
||||
|
||||
return op_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C Write interface as defined in "include/i2c.h" :
|
||||
* chip_addr: I2C chip address, range 0..127
|
||||
* byte_addr: Memory or register address within the chip
|
||||
* alen: Number of bytes to use for addr (typically 1, 2 for larger
|
||||
* memories, 0 for register type devices with only one
|
||||
* register)
|
||||
* buffer: Pointer to data to be written
|
||||
* len: How many bytes to write
|
||||
*
|
||||
* Returns: 0 on success, not 0 on failure
|
||||
*/
|
||||
|
||||
int i2c_write (uchar chip_addr, uint byte_addr, int alen, uchar * buffer,
|
||||
int len)
|
||||
{
|
||||
u32 op_status = TSI108_I2C_PARAM_ERR;
|
||||
|
||||
/* Check for valid I2C address */
|
||||
if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) {
|
||||
while (len--) {
|
||||
op_status =
|
||||
i2c_write_byte (chip_addr, byte_addr++, buffer++);
|
||||
|
||||
if (TSI108_I2C_SUCCESS != op_status) {
|
||||
DPRINT (("I2C write_byte() failed: 0x%02x (%d left)\n", op_status, len));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return op_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* I2C interface function as defined in "include/i2c.h".
|
||||
* Probe the given I2C chip address by reading single byte from offset 0.
|
||||
* Returns 0 if a chip responded, not 0 on failure.
|
||||
*/
|
||||
|
||||
int i2c_probe (uchar chip)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
/*
|
||||
* Try to read the first location of the chip.
|
||||
* The Tsi108 HW doesn't support sending just the chip address
|
||||
* and checkong for an <ACK> back.
|
||||
*/
|
||||
return i2c_read (chip, 0, 1, (uchar *)&tmp, 1);
|
||||
}
|
||||
|
||||
#endif
|
613
root/package/utils/sysupgrade-helper/src/drivers/i2c/u8500_i2c.c
Normal file
613
root/package/utils/sysupgrade-helper/src/drivers/i2c/u8500_i2c.c
Normal file
|
@ -0,0 +1,613 @@
|
|||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Basic U-Boot I2C interface for STn8500/DB8500
|
||||
* Author: Michael Brandt <Michael.Brandt@stericsson.com> for ST-Ericsson
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Only 7-bit I2C device addresses are supported.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <i2c.h>
|
||||
|
||||
#include "u8500_i2c.h"
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/clock.h>
|
||||
|
||||
#define U8500_I2C_ENDAD_COUNTER (CONFIG_SYS_HZ/100) /* I2C bus timeout */
|
||||
#define U8500_I2C_FIFO_FLUSH_COUNTER 500000 /* flush "timeout" */
|
||||
#define U8500_I2C_SCL_FREQ 100000 /* I2C bus clock freq */
|
||||
#define U8500_I2C_INPUT_FREQ 48000000 /* Input clock freq */
|
||||
#define TX_FIFO_THRESHOLD 0x4
|
||||
#define RX_FIFO_THRESHOLD 0x4
|
||||
#define SLAVE_SETUP_TIME 14 /* Slave data setup time, 250ns for 48MHz i2c_clk */
|
||||
|
||||
#define WRITE_FIELD(var, mask, shift, value) \
|
||||
(var = ((var & ~(mask)) | ((value) << (shift))))
|
||||
|
||||
static unsigned int bus_initialized[CONFIG_SYS_U8500_I2C_BUS_MAX];
|
||||
static unsigned int i2c_bus_num;
|
||||
static unsigned int i2c_bus_speed[] = {
|
||||
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SPEED,
|
||||
CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SPEED
|
||||
};
|
||||
static struct u8500_i2c_regs *i2c_dev[] = {
|
||||
(struct u8500_i2c_regs *)CONFIG_SYS_U8500_I2C0_BASE,
|
||||
(struct u8500_i2c_regs *)CONFIG_SYS_U8500_I2C1_BASE,
|
||||
(struct u8500_i2c_regs *)CONFIG_SYS_U8500_I2C2_BASE,
|
||||
(struct u8500_i2c_regs *)CONFIG_SYS_U8500_I2C3_BASE,
|
||||
};
|
||||
|
||||
static struct {
|
||||
int periph;
|
||||
int pcken;
|
||||
int kcken;
|
||||
} i2c_clock_bits[] = {
|
||||
{3, 3, 3}, /* I2C0 */
|
||||
{1, 2, 2}, /* I2C1 */
|
||||
{1, 6, 6}, /* I2C2 */
|
||||
{2, 0, 0}, /* I2C3 */
|
||||
};
|
||||
|
||||
static void i2c_set_bit(void *reg, u32 mask)
|
||||
{
|
||||
writel(readl(reg) | mask, reg);
|
||||
}
|
||||
|
||||
static void i2c_clr_bit(void *reg, u32 mask)
|
||||
{
|
||||
writel(readl(reg) & ~mask, reg);
|
||||
}
|
||||
|
||||
static void i2c_write_field(void *reg, u32 mask, uint shift, u32 value)
|
||||
{
|
||||
writel((readl(reg) & ~mask) | (value << shift), reg);
|
||||
}
|
||||
|
||||
static int __i2c_set_bus_speed(unsigned int speed)
|
||||
{
|
||||
u32 value;
|
||||
struct u8500_i2c_regs *i2c_regs;
|
||||
|
||||
i2c_regs = i2c_dev[i2c_bus_num];
|
||||
|
||||
/* Select standard (100 kbps) speed mode */
|
||||
i2c_write_field(&i2c_regs->cr, U8500_I2C_CR_SM,
|
||||
U8500_I2C_CR_SHIFT_SM, 0x0);
|
||||
|
||||
/*
|
||||
* Set the Baud Rate Counter 2 value
|
||||
* Baud rate (standard) = fi2cclk / ( (BRCNT2 x 2) + Foncycle )
|
||||
* Foncycle = 0 (no digital filtering)
|
||||
*/
|
||||
value = (u32) (U8500_I2C_INPUT_FREQ / (speed * 2));
|
||||
i2c_write_field(&i2c_regs->brcr, U8500_I2C_BRCR_BRCNT2,
|
||||
U8500_I2C_BRCR_SHIFT_BRCNT2, value);
|
||||
|
||||
/* ensure that BRCNT value is zero */
|
||||
i2c_write_field(&i2c_regs->brcr, U8500_I2C_BRCR_BRCNT1,
|
||||
U8500_I2C_BRCR_SHIFT_BRCNT1, 0);
|
||||
|
||||
return U8500_I2C_INPUT_FREQ/(value * 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* i2c_init - initialize the i2c bus
|
||||
*
|
||||
* speed: bus speed (in HZ)
|
||||
* slaveaddr: address of device in slave mode
|
||||
*
|
||||
* Slave mode is not implemented.
|
||||
*/
|
||||
void i2c_init(int speed, int slaveaddr)
|
||||
{
|
||||
struct u8500_i2c_regs *i2c_regs;
|
||||
|
||||
debug("i2c_init bus %d, speed %d\n", i2c_bus_num, speed);
|
||||
|
||||
u8500_clock_enable(i2c_clock_bits[i2c_bus_num].periph,
|
||||
i2c_clock_bits[i2c_bus_num].pcken,
|
||||
i2c_clock_bits[i2c_bus_num].kcken);
|
||||
|
||||
i2c_regs = i2c_dev[i2c_bus_num];
|
||||
|
||||
/* Disable the controller */
|
||||
i2c_clr_bit(&i2c_regs->cr, U8500_I2C_CR_PE);
|
||||
|
||||
/* Clear registers */
|
||||
writel(0, &i2c_regs->cr);
|
||||
writel(0, &i2c_regs->scr);
|
||||
writel(0, &i2c_regs->hsmcr);
|
||||
writel(0, &i2c_regs->tftr);
|
||||
writel(0, &i2c_regs->rftr);
|
||||
writel(0, &i2c_regs->dmar);
|
||||
|
||||
i2c_bus_speed[i2c_bus_num] = __i2c_set_bus_speed(speed);
|
||||
|
||||
/*
|
||||
* Set our own address.
|
||||
* Set slave address mode to 7 bit addressing mode
|
||||
*/
|
||||
i2c_clr_bit(&i2c_regs->cr, U8500_I2C_CR_SAM);
|
||||
i2c_write_field(&i2c_regs->scr, U8500_I2C_SCR_ADDR,
|
||||
U8500_I2C_SCR_SHIFT_ADDR, slaveaddr);
|
||||
/* Slave Data Set up Time */
|
||||
i2c_write_field(&i2c_regs->scr, U8500_I2C_SCR_DATA_SETUP_TIME,
|
||||
U8500_I2C_SCR_SHIFT_DATA_SETUP_TIME, SLAVE_SETUP_TIME);
|
||||
|
||||
/* Disable the DMA sync logic */
|
||||
i2c_write_field(&i2c_regs->cr, U8500_I2C_CR_DMA_SLE,
|
||||
U8500_I2C_CR_SHIFT_DMA_SLE, 0);
|
||||
|
||||
/* Disable interrupts */
|
||||
writel(0, &i2c_regs->imscr);
|
||||
|
||||
/* Configure bus master mode */
|
||||
i2c_write_field(&i2c_regs->cr, U8500_I2C_CR_OM, U8500_I2C_CR_SHIFT_OM,
|
||||
U8500_I2C_BUS_MASTER_MODE);
|
||||
/* Set FIFO threshold values */
|
||||
writel(TX_FIFO_THRESHOLD, &i2c_regs->tftr);
|
||||
writel(RX_FIFO_THRESHOLD, &i2c_regs->rftr);
|
||||
|
||||
/* Enable the I2C Controller */
|
||||
i2c_set_bit(&i2c_regs->cr, U8500_I2C_CR_PE);
|
||||
|
||||
bus_initialized[i2c_bus_num] = 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* loop_till_bit_clear - polls on a bit till it clears
|
||||
* ioreg: register where you want to check status
|
||||
* mask: bit mask for the bit you wish to check
|
||||
* timeout: timeout in ticks/s
|
||||
*/
|
||||
static int loop_till_bit_clear(void *io_reg, u32 mask, unsigned long timeout)
|
||||
{
|
||||
unsigned long timebase = get_timer(0);
|
||||
|
||||
do {
|
||||
if ((readl(io_reg) & mask) == 0x0UL)
|
||||
return 0;
|
||||
} while (get_timer(timebase) < timeout);
|
||||
|
||||
debug("loop_till_bit_clear timed out\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* loop_till_bit_set - polls on a bit till it is set.
|
||||
* ioreg: register where you want to check status
|
||||
* mask: bit mask for the bit you wish to check
|
||||
* timeout: timeout in ticks/s
|
||||
*/
|
||||
static int loop_till_bit_set(void *io_reg, u32 mask, unsigned long timeout)
|
||||
{
|
||||
unsigned long timebase = get_timer(0);
|
||||
|
||||
do {
|
||||
if ((readl(io_reg) & mask) != 0x0UL)
|
||||
return 0;
|
||||
} while (get_timer(timebase) < timeout);
|
||||
|
||||
debug("loop_till_bit_set timed out\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* flush_fifo - flush the I2C TX and RX FIFOs
|
||||
*/
|
||||
static void flush_fifo(struct u8500_i2c_regs *i2c_regs)
|
||||
{
|
||||
int counter = U8500_I2C_FIFO_FLUSH_COUNTER;
|
||||
|
||||
/* Flush Tx FIFO */
|
||||
i2c_set_bit(&i2c_regs->cr, U8500_I2C_CR_FTX);
|
||||
/* Flush Rx FIFO */
|
||||
i2c_set_bit(&i2c_regs->cr, U8500_I2C_CR_FRX);
|
||||
while (counter--) {
|
||||
if (!(readl(&i2c_regs->cr) &
|
||||
(U8500_I2C_CR_FTX | U8500_I2C_CR_FRX)))
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void print_abort_reason(struct u8500_i2c_regs *i2c_regs)
|
||||
{
|
||||
int cause;
|
||||
|
||||
printf("abort: risr %08x, sr %08x\n", i2c_regs->risr, i2c_regs->sr);
|
||||
cause = (readl(&i2c_regs->sr) & U8500_I2C_SR_CAUSE) >>
|
||||
U8500_I2C_SR_SHIFT_CAUSE;
|
||||
switch (cause) {
|
||||
case U8500_I2C_NACK_ADDR:
|
||||
printf("No Ack received after Slave Address xmission\n");
|
||||
break;
|
||||
case U8500_I2C_NACK_DATA:
|
||||
printf("Valid for MASTER_WRITE: No Ack received "
|
||||
"during data phase\n");
|
||||
break;
|
||||
case U8500_I2C_ACK_MCODE:
|
||||
printf("Master recv ack after xmission of master code"
|
||||
"in hs mode\n");
|
||||
break;
|
||||
case U8500_I2C_ARB_LOST:
|
||||
printf("Master Lost arbitration\n");
|
||||
break;
|
||||
case U8500_I2C_BERR_START:
|
||||
printf("Slave restarts\n");
|
||||
break;
|
||||
case U8500_I2C_BERR_STOP:
|
||||
printf("Slave reset\n");
|
||||
break;
|
||||
case U8500_I2C_OVFL:
|
||||
printf("Overflow\n");
|
||||
break;
|
||||
default:
|
||||
printf("Unknown error type\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* i2c_abort - called when a I2C transaction failed
|
||||
*/
|
||||
static void i2c_abort(struct u8500_i2c_regs *i2c_regs)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
print_abort_reason(i2c_regs);
|
||||
#endif
|
||||
/* flush RX and TX fifos */
|
||||
flush_fifo(i2c_regs);
|
||||
|
||||
/* Acknowledge the Master Transaction Done */
|
||||
i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTD);
|
||||
|
||||
/* Acknowledge the Master Transaction Done Without Stop */
|
||||
i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS);
|
||||
|
||||
i2c_init(i2c_bus_speed[i2c_bus_num], CONFIG_SYS_I2C_SLAVE);
|
||||
}
|
||||
|
||||
/*
|
||||
* write addr, alias index, to I2C bus.
|
||||
*/
|
||||
static int i2c_write_addr(struct u8500_i2c_regs *i2c_regs, uint addr, int alen)
|
||||
{
|
||||
while (alen--) {
|
||||
/* Wait until the Tx Fifo is not full */
|
||||
if (loop_till_bit_clear((void *)&i2c_regs->risr,
|
||||
U8500_I2C_INT_TXFF,
|
||||
U8500_I2C_ENDAD_COUNTER)) {
|
||||
i2c_abort(i2c_regs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* MSB first */
|
||||
writeb((addr >> (alen * 8)) & 0xff, &i2c_regs->tfr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal simplified read function:
|
||||
* i2c_regs: Pointer to I2C registers for current bus
|
||||
* chip: I2C chip address, range 0..127
|
||||
* addr: Memory (register) address within the chip
|
||||
* alen: Number of bytes to use for addr (typically 1, 2 for larger
|
||||
* memories, 0 for register type devices with only one register)
|
||||
* value: Where to put the data
|
||||
*
|
||||
* Returns: 0 on success, not 0 on failure
|
||||
*/
|
||||
static int i2c_read_byte(struct u8500_i2c_regs *i2c_regs, uchar chip,
|
||||
uint addr, int alen, uchar *value)
|
||||
{
|
||||
u32 mcr = 0;
|
||||
|
||||
/* Set the address mode to 7 bit */
|
||||
WRITE_FIELD(mcr, U8500_I2C_MCR_AM, U8500_I2C_MCR_SHIFT_AM, 1);
|
||||
|
||||
/* Store the slave address in the master control register */
|
||||
WRITE_FIELD(mcr, U8500_I2C_MCR_A7, U8500_I2C_MCR_SHIFT_A7, chip);
|
||||
|
||||
if (alen != 0) {
|
||||
/* Master write operation */
|
||||
mcr &= ~(U8500_I2C_MCR_OP);
|
||||
|
||||
/* Configure the Frame length to one byte */
|
||||
WRITE_FIELD(mcr, U8500_I2C_MCR_LENGTH,
|
||||
U8500_I2C_MCR_SHIFT_LENGTH, 1);
|
||||
|
||||
/* Repeated start, no stop */
|
||||
mcr &= ~(U8500_I2C_MCR_STOP);
|
||||
|
||||
/* Write Master Control Register */
|
||||
writel(mcr, &i2c_regs->mcr);
|
||||
|
||||
/* send addr/index */
|
||||
if (i2c_write_addr(i2c_regs, addr, alen) != 0)
|
||||
return -1;
|
||||
|
||||
/* Check for the Master Transaction Done Without Stop */
|
||||
if (loop_till_bit_set((void *)&i2c_regs->risr,
|
||||
U8500_I2C_INT_MTDWS,
|
||||
U8500_I2C_ENDAD_COUNTER)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Acknowledge the Master Transaction Done Without Stop */
|
||||
i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS);
|
||||
}
|
||||
|
||||
/* Master control configuration for read operation */
|
||||
mcr |= U8500_I2C_MCR_OP;
|
||||
|
||||
/* Configure the STOP condition, we read only one byte */
|
||||
mcr |= U8500_I2C_MCR_STOP;
|
||||
|
||||
/* Set the frame length to one byte, we support only 1 byte reads */
|
||||
WRITE_FIELD(mcr, U8500_I2C_MCR_LENGTH, U8500_I2C_MCR_SHIFT_LENGTH, 1);
|
||||
|
||||
i2c_write_field(&i2c_regs->mcr, U8500_I2C_MCR_LENGTH_STOP_OP,
|
||||
U8500_I2C_MCR_SHIFT_LENGTH_STOP_OP, mcr);
|
||||
|
||||
/*
|
||||
* receive_data_polling
|
||||
*/
|
||||
|
||||
/* Wait until the Rx FIFO is not empty */
|
||||
if (loop_till_bit_clear((void *)&i2c_regs->risr,
|
||||
U8500_I2C_INT_RXFE,
|
||||
U8500_I2C_ENDAD_COUNTER))
|
||||
return -1;
|
||||
|
||||
/* Read the data byte from Rx FIFO */
|
||||
*value = readb(&i2c_regs->rfr);
|
||||
|
||||
/* Wait until the work is done */
|
||||
if (loop_till_bit_set((void *)&i2c_regs->risr, U8500_I2C_INT_MTD,
|
||||
U8500_I2C_ENDAD_COUNTER))
|
||||
return -1;
|
||||
|
||||
/* Acknowledge the Master Transaction Done */
|
||||
i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTD);
|
||||
|
||||
/* If MTD is set, Master Transaction Done Without Stop is set too */
|
||||
i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal simplified write function:
|
||||
* i2c_regs: Pointer to I2C registers for current bus
|
||||
* chip: I2C chip address, range 0..127
|
||||
* addr: Memory (register) address within the chip
|
||||
* alen: Number of bytes to use for addr (typically 1, 2 for larger
|
||||
* memories, 0 for register type devices with only one register)
|
||||
* data: Where to read the data
|
||||
* len: How many bytes to write
|
||||
*
|
||||
* Returns: 0 on success, not 0 on failure
|
||||
*/
|
||||
static int __i2c_write(struct u8500_i2c_regs *i2c_regs, u8 chip, uint addr,
|
||||
int alen, u8 *data, int len)
|
||||
{
|
||||
int i;
|
||||
u32 mcr = 0;
|
||||
|
||||
/* Set the address mode to 7 bit */
|
||||
WRITE_FIELD(mcr, U8500_I2C_MCR_AM, U8500_I2C_MCR_SHIFT_AM, 1);
|
||||
|
||||
/* Store the slave address in the master control register */
|
||||
WRITE_FIELD(mcr, U8500_I2C_MCR_A7, U8500_I2C_MCR_SHIFT_A7, chip);
|
||||
|
||||
/* Write operation */
|
||||
mcr &= ~(U8500_I2C_MCR_OP);
|
||||
|
||||
/* Current transaction is terminated by STOP condition */
|
||||
mcr |= U8500_I2C_MCR_STOP;
|
||||
|
||||
/* Frame length: addr byte + len */
|
||||
WRITE_FIELD(mcr, U8500_I2C_MCR_LENGTH, U8500_I2C_MCR_SHIFT_LENGTH,
|
||||
(alen + len));
|
||||
|
||||
/* Write MCR register */
|
||||
writel(mcr, &i2c_regs->mcr);
|
||||
|
||||
if (i2c_write_addr(i2c_regs, addr, alen) != 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
/* Wait until the Tx FIFO is not full */
|
||||
if (loop_till_bit_clear((void *)&i2c_regs->risr,
|
||||
U8500_I2C_INT_TXFF,
|
||||
U8500_I2C_ENDAD_COUNTER))
|
||||
return -1;
|
||||
|
||||
/* it is a 32 bit register with upper 24 reserved R/O */
|
||||
writeb(data[i], &i2c_regs->tfr);
|
||||
}
|
||||
|
||||
/* Check for Master Transaction Done */
|
||||
if (loop_till_bit_set((void *)&i2c_regs->risr,
|
||||
U8500_I2C_INT_MTD,
|
||||
U8500_I2C_ENDAD_COUNTER)) {
|
||||
printf("i2c_write_byte error2: risr %08x\n",
|
||||
i2c_regs->risr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Acknowledge Master Transaction Done */
|
||||
i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTD);
|
||||
|
||||
/* Acknowledge Master Transaction Done Without Stop */
|
||||
i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe the given I2C chip address. Returns 0 if a chip responded,
|
||||
* not 0 on failure.
|
||||
*/
|
||||
int i2c_probe(uchar chip)
|
||||
{
|
||||
u32 mcr = 0;
|
||||
struct u8500_i2c_regs *i2c_regs;
|
||||
|
||||
if (chip == CONFIG_SYS_I2C_SLAVE)
|
||||
return 1;
|
||||
|
||||
i2c_regs = i2c_dev[i2c_bus_num];
|
||||
|
||||
/* Set the address mode to 7 bit */
|
||||
WRITE_FIELD(mcr, U8500_I2C_MCR_AM, U8500_I2C_MCR_SHIFT_AM, 1);
|
||||
|
||||
/* Store the slave address in the master control register */
|
||||
WRITE_FIELD(mcr, U8500_I2C_MCR_A10, U8500_I2C_MCR_SHIFT_A7, chip);
|
||||
|
||||
/* Read operation */
|
||||
mcr |= U8500_I2C_MCR_OP;
|
||||
|
||||
/* Set the frame length to one byte */
|
||||
WRITE_FIELD(mcr, U8500_I2C_MCR_LENGTH, U8500_I2C_MCR_SHIFT_LENGTH, 1);
|
||||
|
||||
/* Current transaction is terminated by STOP condition */
|
||||
mcr |= U8500_I2C_MCR_STOP;
|
||||
|
||||
/* Write MCR register */
|
||||
writel(mcr, &i2c_regs->mcr);
|
||||
|
||||
/* Wait until the Rx Fifo is not empty */
|
||||
if (loop_till_bit_clear((void *)&i2c_regs->risr,
|
||||
U8500_I2C_INT_RXFE,
|
||||
U8500_I2C_ENDAD_COUNTER)) {
|
||||
i2c_abort(i2c_regs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
flush_fifo(i2c_regs);
|
||||
|
||||
/* Acknowledge the Master Transaction Done */
|
||||
i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTD);
|
||||
|
||||
/* Acknowledge the Master Transaction Done Without Stop */
|
||||
i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read/Write interface:
|
||||
* chip: I2C chip address, range 0..127
|
||||
* addr: Memory (register) address within the chip
|
||||
* alen: Number of bytes to use for addr (typically 1, 2 for larger
|
||||
* memories, 0 for register type devices with only one
|
||||
* register)
|
||||
* buffer: Where to read/write the data
|
||||
* len: How many bytes to read/write
|
||||
*
|
||||
* Returns: 0 on success, not 0 on failure
|
||||
*/
|
||||
int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
struct u8500_i2c_regs *i2c_regs;
|
||||
|
||||
if (alen > 2) {
|
||||
debug("I2C read: addr len %d not supported\n", alen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
i2c_regs = i2c_dev[i2c_bus_num];
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
rc = i2c_read_byte(i2c_regs, chip, addr + i, alen, &buffer[i]);
|
||||
if (rc != 0) {
|
||||
debug("I2C read: I/O error: %d\n", rc);
|
||||
i2c_abort(i2c_regs);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
|
||||
{
|
||||
int rc;
|
||||
struct u8500_i2c_regs *i2c_regs;
|
||||
i2c_regs = i2c_dev[i2c_bus_num];
|
||||
|
||||
rc = __i2c_write(i2c_regs, chip, addr, alen, buffer,
|
||||
len);
|
||||
if (rc != 0) {
|
||||
debug("I2C write: I/O error\n");
|
||||
i2c_abort(i2c_regs);
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_set_bus_num(unsigned int bus)
|
||||
{
|
||||
if (bus > ARRAY_SIZE(i2c_dev) - 1) {
|
||||
debug("i2c_set_bus_num: only up to bus %d supported\n",
|
||||
ARRAY_SIZE(i2c_dev)-1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
i2c_bus_num = bus;
|
||||
|
||||
if (!bus_initialized[i2c_bus_num])
|
||||
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_set_bus_speed(unsigned int speed)
|
||||
{
|
||||
|
||||
if (speed > U8500_I2C_MAX_STANDARD_SCL) {
|
||||
debug("i2c_set_bus_speed: only up to %d supported\n",
|
||||
U8500_I2C_MAX_STANDARD_SCL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* sets as side effect i2c_bus_speed[i2c_bus_num] */
|
||||
i2c_init(speed, CONFIG_SYS_I2C_SLAVE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int i2c_get_bus_num(void)
|
||||
{
|
||||
return i2c_bus_num;
|
||||
}
|
||||
|
||||
unsigned int i2c_get_bus_speed(void)
|
||||
{
|
||||
return i2c_bus_speed[i2c_bus_num];
|
||||
}
|
194
root/package/utils/sysupgrade-helper/src/drivers/i2c/u8500_i2c.h
Normal file
194
root/package/utils/sysupgrade-helper/src/drivers/i2c/u8500_i2c.h
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2009
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#ifndef _U8500_I2C_H_
|
||||
#define _U8500_I2C_H_
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/arch/u8500.h>
|
||||
|
||||
struct u8500_i2c_regs {
|
||||
u32 cr; /* Control Register 0x00 */
|
||||
u32 scr; /* Slave Address Register 0x04 */
|
||||
u32 hsmcr; /* HS Master code Register 0x08 */
|
||||
u32 mcr; /* Master Control Register 0x0C */
|
||||
u32 tfr; /* Transmit Fifo Register 0x10 */
|
||||
u32 sr; /* Status Register 0x14 */
|
||||
u32 rfr; /* Receiver Fifo Register 0x18 */
|
||||
u32 tftr; /* Transmit Fifo Threshold Register 0x1C */
|
||||
u32 rftr; /* Receiver Fifo Threshold Register 0x20 */
|
||||
u32 dmar; /* DMA register 0x24 */
|
||||
u32 brcr; /* Baud Rate Counter Register 0x28 */
|
||||
u32 imscr; /* Interrupt Mask Set and Clear Register 0x2C */
|
||||
u32 risr; /* Raw interrupt status register 0x30 */
|
||||
u32 misr; /* Masked interrupt status register 0x34 */
|
||||
u32 icr; /* Interrupt Set and Clear Register 0x38 */
|
||||
u32 reserved_1[(0xFE0 - 0x3c) >> 2]; /* Reserved 0x03C to 0xFE0 */
|
||||
u32 periph_id_0; /* peripheral ID 0 0xFE0 */
|
||||
u32 periph_id_1; /* peripheral ID 1 0xFE4 */
|
||||
u32 periph_id_2; /* peripheral ID 2 0xFE8 */
|
||||
u32 periph_id_3; /* peripheral ID 3 0xFEC */
|
||||
u32 cell_id_0; /* I2C cell ID 0 0xFF0 */
|
||||
u32 cell_id_1; /* I2C cell ID 1 0xFF4 */
|
||||
u32 cell_id_2; /* I2C cell ID 2 0xFF8 */
|
||||
u32 cell_id_3; /* I2C cell ID 3 0xFFC */
|
||||
};
|
||||
|
||||
|
||||
/* Control Register */
|
||||
|
||||
/* Mask values for control register mask */
|
||||
#define U8500_I2C_CR_PE 0x0001 /* Peripheral enable */
|
||||
#define U8500_I2C_CR_OM 0x0006 /* Operation mode */
|
||||
#define U8500_I2C_CR_SAM 0x0008 /* Slave Addressing mode */
|
||||
#define U8500_I2C_CR_SM 0x0030 /* Speed mode */
|
||||
#define U8500_I2C_CR_SGCM 0x0040 /* Slave General call mode */
|
||||
#define U8500_I2C_CR_FTX 0x0080 /* Flush Transmit */
|
||||
#define U8500_I2C_CR_FRX 0x0100 /* Flush Receive */
|
||||
#define U8500_I2C_CR_DMA_TX_EN 0x0200 /* DMA TX Enable */
|
||||
#define U8500_I2C_CR_DMA_RX_EN 0x0400 /* DMA Rx Enable */
|
||||
#define U8500_I2C_CR_DMA_SLE 0x0800 /* DMA Synchronization Logic enable */
|
||||
#define U8500_I2C_CR_LM 0x1000 /* Loop back mode */
|
||||
#define U8500_I2C_CR_FON 0x6000 /* Filtering On */
|
||||
|
||||
/* shift valus for control register bit fields */
|
||||
#define U8500_I2C_CR_SHIFT_PE 0 /* Peripheral enable */
|
||||
#define U8500_I2C_CR_SHIFT_OM 1 /* Operation mode */
|
||||
#define U8500_I2C_CR_SHIFT_SAM 3 /* Slave Addressing mode */
|
||||
#define U8500_I2C_CR_SHIFT_SM 4 /* Speed mode */
|
||||
#define U8500_I2C_CR_SHIFT_SGCM 6 /* Slave General call mode */
|
||||
#define U8500_I2C_CR_SHIFT_FTX 7 /* Flush Transmit */
|
||||
#define U8500_I2C_CR_SHIFT_FRX 8 /* Flush Receive */
|
||||
#define U8500_I2C_CR_SHIFT_DMA_TX_EN 9 /* DMA TX Enable */
|
||||
#define U8500_I2C_CR_SHIFT_DMA_RX_EN 10 /* DMA Rx Enable */
|
||||
#define U8500_I2C_CR_SHIFT_DMA_SLE 11 /* DMA Synch Logic enable */
|
||||
#define U8500_I2C_CR_SHIFT_LM 12 /* Loop back mode */
|
||||
#define U8500_I2C_CR_SHIFT_FON 13 /* Filtering On */
|
||||
|
||||
/* bus operation modes */
|
||||
#define U8500_I2C_BUS_SLAVE_MODE 0
|
||||
#define U8500_I2C_BUS_MASTER_MODE 1
|
||||
#define U8500_I2C_BUS_MASTER_SLAVE_MODE 2
|
||||
|
||||
|
||||
/* Slave control register*/
|
||||
|
||||
/* Mask values slave control register */
|
||||
#define U8500_I2C_SCR_ADDR 0x3FF
|
||||
#define U8500_I2C_SCR_DATA_SETUP_TIME 0xFFFF0000
|
||||
|
||||
/* Shift values for Slave control register */
|
||||
#define U8500_I2C_SCR_SHIFT_ADDR 0
|
||||
#define U8500_I2C_SCR_SHIFT_DATA_SETUP_TIME 16
|
||||
|
||||
|
||||
/* Master Control Register */
|
||||
|
||||
/* Mask values for Master control register */
|
||||
#define U8500_I2C_MCR_OP 0x00000001 /* Operation */
|
||||
#define U8500_I2C_MCR_A7 0x000000FE /* LSB bits of Address */
|
||||
#define U8500_I2C_MCR_EA10 0x00000700 /* Extended Address */
|
||||
#define U8500_I2C_MCR_SB 0x00000800 /* Start byte procedure */
|
||||
#define U8500_I2C_MCR_AM 0x00003000 /* Address type */
|
||||
#define U8500_I2C_MCR_STOP 0x00004000 /* stop condition */
|
||||
#define U8500_I2C_MCR_LENGTH 0x03FF8000 /* Frame length */
|
||||
#define U8500_I2C_MCR_A10 0x000007FE /* Enable 10 bit address */
|
||||
/* mask for length field,stop and operation */
|
||||
#define U8500_I2C_MCR_LENGTH_STOP_OP 0x3FFC001
|
||||
|
||||
/* Shift values for Master control values */
|
||||
#define U8500_I2C_MCR_SHIFT_OP 0 /* Operation */
|
||||
#define U8500_I2C_MCR_SHIFT_A7 1 /* LSB bits of Address */
|
||||
#define U8500_I2C_MCR_SHIFT_EA10 8 /* Extended Address */
|
||||
#define U8500_I2C_MCR_SHIFT_SB 11 /* Start byte procedure */
|
||||
#define U8500_I2C_MCR_SHIFT_AM 12 /* Address type */
|
||||
#define U8500_I2C_MCR_SHIFT_STOP 14 /* stop condition */
|
||||
#define U8500_I2C_MCR_SHIFT_LENGTH 15 /* Frame length */
|
||||
#define U8500_I2C_MCR_SHIFT_A10 1 /* Enable 10 bit address */
|
||||
|
||||
#define U8500_I2C_MCR_SHIFT_LENGTH_STOP_OP 0
|
||||
|
||||
|
||||
/* Status Register */
|
||||
|
||||
/* Mask values for Status register */
|
||||
#define U8500_I2C_SR_OP 0x00000003 /* Operation */
|
||||
#define U8500_I2C_SR_STATUS 0x0000000C /* Controller Status */
|
||||
#define U8500_I2C_SR_CAUSE 0x00000070 /* Abort Cause */
|
||||
#define U8500_I2C_SR_TYPE 0x00000180 /* Receive Type */
|
||||
#define U8500_I2C_SR_LENGTH 0x000FF700 /* Transfer length */
|
||||
|
||||
/* Shift values for Status register */
|
||||
#define U8500_I2C_SR_SHIFT_OP 0 /* Operation */
|
||||
#define U8500_I2C_SR_SHIFT_STATUS 2 /* Controller Status */
|
||||
#define U8500_I2C_SR_SHIFT_CAUSE 4 /* Abort Cause */
|
||||
#define U8500_I2C_SR_SHIFT_TYPE 7 /* Receive Type */
|
||||
#define U8500_I2C_SR_SHIFT_LENGTH 9 /* Transfer length */
|
||||
|
||||
/* abort cause */
|
||||
#define U8500_I2C_NACK_ADDR 0
|
||||
#define U8500_I2C_NACK_DATA 1
|
||||
#define U8500_I2C_ACK_MCODE 2
|
||||
#define U8500_I2C_ARB_LOST 3
|
||||
#define U8500_I2C_BERR_START 4
|
||||
#define U8500_I2C_BERR_STOP 5
|
||||
#define U8500_I2C_OVFL 6
|
||||
|
||||
|
||||
/* Baud rate counter registers */
|
||||
|
||||
/* Mask values for Baud rate counter register */
|
||||
#define U8500_I2C_BRCR_BRCNT2 0xFFFF /* Baud Rate Cntr BRCR for HS */
|
||||
#define U8500_I2C_BRCR_BRCNT1 0xFFFF0000 /* BRCR for Standard and Fast */
|
||||
|
||||
/* Shift values for the Baud rate counter register */
|
||||
#define U8500_I2C_BRCR_SHIFT_BRCNT2 0
|
||||
#define U8500_I2C_BRCR_SHIFT_BRCNT1 16
|
||||
|
||||
|
||||
/* Interrupt Register */
|
||||
|
||||
/* Mask values for Interrupt registers */
|
||||
#define U8500_I2C_INT_TXFE 0x00000001 /* Tx fifo empty */
|
||||
#define U8500_I2C_INT_TXFNE 0x00000002 /* Tx Fifo nearly empty */
|
||||
#define U8500_I2C_INT_TXFF 0x00000004 /* Tx Fifo Full */
|
||||
#define U8500_I2C_INT_TXFOVR 0x00000008 /* Tx Fifo over run */
|
||||
#define U8500_I2C_INT_RXFE 0x00000010 /* Rx Fifo Empty */
|
||||
#define U8500_I2C_INT_RXFNF 0x00000020 /* Rx Fifo nearly empty */
|
||||
#define U8500_I2C_INT_RXFF 0x00000040 /* Rx Fifo Full */
|
||||
#define U8500_I2C_INT_RFSR 0x00010000 /* Read From slave request */
|
||||
#define U8500_I2C_INT_RFSE 0x00020000 /* Read from slave empty */
|
||||
#define U8500_I2C_INT_WTSR 0x00040000 /* Write to Slave request */
|
||||
#define U8500_I2C_INT_MTD 0x00080000 /* Master Transcation Done*/
|
||||
#define U8500_I2C_INT_STD 0x00100000 /* Slave Transaction Done */
|
||||
#define U8500_I2C_INT_MAL 0x01000000 /* Master Arbitation Lost */
|
||||
#define U8500_I2C_INT_BERR 0x02000000 /* Bus Error */
|
||||
#define U8500_I2C_INT_MTDWS 0x10000000 /* Master Tran Done wo/ Stop */
|
||||
|
||||
/* Max clocks (Hz) */
|
||||
#define U8500_I2C_MAX_STANDARD_SCL 100000
|
||||
#define U8500_I2C_MAX_FAST_SCL 400000
|
||||
#define U8500_I2C_MAX_HIGH_SPEED_SCL 3400000
|
||||
|
||||
#endif /* _U8500_I2C_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue