Commit 79b2d0bb authored by Stefan Roese's avatar Stefan Roese
Browse files

[PATCH] PPC4xx: Add support for multiple I2C busses



This patch adds support for multiple I2C busses on the PPC4xx
platforms. Define CONFIG_I2C_MULTI_BUS in the board config file
to make use of this feature.

It also merges the 405 and 440 i2c header files into one common
file 4xx_i2c.h.

Also the 4xx i2c reset procedure is reworked since I experienced
some problems with the first access on the 440SPe Katmai board.
Signed-off-by: default avatarStefan Roese <sr@denx.de>
parent 7372ca68
......@@ -23,7 +23,7 @@
#include <common.h>
#include <asm/processor.h>
#include <405gp_i2c.h>
#include <4xx_i2c.h>
#include <command.h>
#include <rtc.h>
#include <post.h>
......
......@@ -25,7 +25,7 @@
#include "du405.h"
#include <asm/processor.h>
#include <ppc4xx.h>
#include <405gp_i2c.h>
#include <4xx_i2c.h>
#include <command.h>
DECLARE_GLOBAL_DATA_PTR;
......
......@@ -48,7 +48,7 @@ int testdram (void)
#include <common.h>
#include <asm/processor.h>
#include <405gp_i2c.h>
#include <4xx_i2c.h>
DECLARE_GLOBAL_DATA_PTR;
......
......@@ -65,7 +65,7 @@
#include <common.h>
#include "mip405.h"
#include <asm/processor.h>
#include <405gp_i2c.h>
#include <4xx_i2c.h>
#include <miiphy.h>
#include "../common/common_util.h"
#include <i2c.h>
......
......@@ -27,13 +27,8 @@
*/
#include <common.h>
#include <ppc4xx.h>
#if defined(CONFIG_440)
# include <440_i2c.h>
#else
# include <405gp_i2c.h>
#endif
#include <4xx_i2c.h>
#include <i2c.h>
#include <440_i2c.h>
#include <command.h>
#include "ppc440gx_i2c.h"
......
......@@ -27,11 +27,7 @@
*/
#include <common.h>
#include <ppc4xx.h>
#if defined(CONFIG_440)
# include <440_i2c.h>
#else
# include <405gp_i2c.h>
#endif
#include <4xx_i2c.h>
#include <i2c.h>
#ifdef CONFIG_HARD_I2C
......
/*****************************************************************************/
/* I2C Bus interface initialisation and I2C Commands */
/* for PPC405GP */
/* Author : AS HARNOIS */
/* Date : 13.Dec.00 */
/*****************************************************************************/
/*
* (C) Copyright 2007
* 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 <ppc4xx.h>
#if defined(CONFIG_440)
# include <440_i2c.h>
#else
# include <405gp_i2c.h>
#endif
#include <4xx_i2c.h>
#include <i2c.h>
#include <asm-ppc/io.h>
#ifdef CONFIG_HARD_I2C
DECLARE_GLOBAL_DATA_PTR;
#define IIC_OK 0
#define IIC_NOK 1
#define IIC_NOK_LA 2 /* Lost arbitration */
#define IIC_NOK_ICT 3 /* Incomplete transfer */
#define IIC_NOK_XFRA 4 /* Transfer aborted */
#define IIC_NOK_DATA 5 /* No data in buffer */
#define IIC_NOK_TOUT 6 /* Transfer timeout */
#define IIC_TIMEOUT 1 /* 1 seconde */
#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.
*/
#ifdef CFG_SPD_BUS_NUM
static unsigned int i2c_bus_num __attribute__ ((section ("data"))) = CFG_SPD_BUS_NUM;
#else
static unsigned int i2c_bus_num __attribute__ ((section ("data"))) = 0;
#endif
#endif /* CONFIG_I2C_MULTI_BUS */
static void _i2c_bus_reset (void)
static void _i2c_bus_reset(void)
{
int i, status;
int i;
u8 dc;
/* Reset status register */
/* write 1 in SCMP and IRQA to clear these fields */
out8 (IIC_STS, 0x0A);
out_8((u8 *)IIC_STS, 0x0A);
/* write 1 in IRQP IRQD LA ICT XFRA to clear these fields */
out8 (IIC_EXTSTS, 0x8F);
__asm__ volatile ("eieio");
/*
* Get current state, reset bus
* only if no transfers are pending.
*/
i = 10;
do {
/* Get status */
status = in8 (IIC_STS);
udelay (500); /* 500us */
i--;
} while ((status & IIC_STS_PT) && (i > 0));
/* Soft reset controller */
status = in8 (IIC_XTCNTLSS);
out8 (IIC_XTCNTLSS, (status | IIC_XTCNTLSS_SRST));
__asm__ volatile ("eieio");
/* make sure where in initial state, data hi, clock hi */
out8 (IIC_DIRECTCNTL, 0xC);
for (i = 0; i < 10; i++) {
if ((in8 (IIC_DIRECTCNTL) & 0x3) != 0x3) {
/* clock until we get to known state */
out8 (IIC_DIRECTCNTL, 0x8); /* clock lo */
udelay (100); /* 100us */
out8 (IIC_DIRECTCNTL, 0xC); /* clock hi */
udelay (100); /* 100us */
} else {
break;
out_8((u8 *)IIC_EXTSTS, 0x8F);
/* Place chip in the reset state */
out_8((u8 *)IIC_XTCNTLSS, IIC_XTCNTLSS_SRST);
/* Check if bus is free */
dc = in_8((u8 *)IIC_DIRECTCNTL);
if (!DIRCTNL_FREE(dc)){
/* Try to set bus free state */
out_8((u8 *)IIC_DIRECTCNTL, IIC_DIRCNTL_SDAC | IIC_DIRCNTL_SCC);
/* Wait until we regain bus control */
for (i = 0; i < 100; ++i) {
dc = in_8((u8 *)IIC_DIRECTCNTL);
if (DIRCTNL_FREE(dc))
break;
/* Toggle SCL line */
dc ^= IIC_DIRCNTL_SCC;
out_8((u8 *)IIC_DIRECTCNTL, dc);
udelay(10);
dc ^= IIC_DIRCNTL_SCC;
out_8((u8 *)IIC_DIRECTCNTL, dc);
}
}
/* send start condition */
out8 (IIC_DIRECTCNTL, 0x4);
udelay (1000); /* 1ms */
/* send stop condition */
out8 (IIC_DIRECTCNTL, 0xC);
udelay (1000); /* 1ms */
/* Unreset controller */
out8 (IIC_XTCNTLSS, (status & ~IIC_XTCNTLSS_SRST));
udelay (1000); /* 1ms */
/* Remove reset */
out_8((u8 *)IIC_XTCNTLSS, 0);
}
void i2c_init (int speed, int slaveadd)
void i2c_init(int speed, int slaveadd)
{
sys_info_t sysInfo;
unsigned long freqOPB;
int val, divisor;
int bus;
#ifdef CFG_I2C_INIT_BOARD
/* call board specific i2c bus reset routine before accessing the */
......@@ -94,101 +103,100 @@ void i2c_init (int speed, int slaveadd)
i2c_init_board();
#endif
/* Handle possible failed I2C state */
/* FIXME: put this into i2c_init_board()? */
_i2c_bus_reset ();
for (bus = 0; bus < CFG_MAX_I2C_BUS; bus++) {
I2C_SET_BUS(bus);
/* clear lo master address */
out8 (IIC_LMADR, 0);
/* Handle possible failed I2C state */
/* FIXME: put this into i2c_init_board()? */
_i2c_bus_reset();
/* clear hi master address */
out8 (IIC_HMADR, 0);
/* clear lo master address */
out_8((u8 *)IIC_LMADR, 0);
/* clear lo slave address */
out8 (IIC_LSADR, 0);
/* clear hi master address */
out_8((u8 *)IIC_HMADR, 0);
/* clear hi slave address */
out8 (IIC_HSADR, 0);
/* clear lo slave address */
out_8((u8 *)IIC_LSADR, 0);
/* Clock divide Register */
/* get OPB frequency */
get_sys_info (&sysInfo);
freqOPB = sysInfo.freqPLB / sysInfo.pllOpbDiv;
/* set divisor according to freqOPB */
divisor = (freqOPB - 1) / 10000000;
if (divisor == 0)
divisor = 1;
out8 (IIC_CLKDIV, divisor);
/* clear hi slave address */
out_8((u8 *)IIC_HSADR, 0);
/* no interrupts */
out8 (IIC_INTRMSK, 0);
/* Clock divide Register */
/* get OPB frequency */
get_sys_info(&sysInfo);
freqOPB = sysInfo.freqPLB / sysInfo.pllOpbDiv;
/* set divisor according to freqOPB */
divisor = (freqOPB - 1) / 10000000;
if (divisor == 0)
divisor = 1;
out_8((u8 *)IIC_CLKDIV, divisor);
/* clear transfer count */
out8 (IIC_XFRCNT, 0);
/* no interrupts */
out_8((u8 *)IIC_INTRMSK, 0);
/* clear extended control & stat */
/* write 1 in SRC SRS SWC SWS to clear these fields */
out8 (IIC_XTCNTLSS, 0xF0);
/* clear transfer count */
out_8((u8 *)IIC_XFRCNT, 0);
/* Mode Control Register
Flush Slave/Master data buffer */
out8 (IIC_MDCNTL, IIC_MDCNTL_FSDB | IIC_MDCNTL_FMDB);
__asm__ volatile ("eieio");
/* clear extended control & stat */
/* write 1 in SRC SRS SWC SWS to clear these fields */
out_8((u8 *)IIC_XTCNTLSS, 0xF0);
/* Mode Control Register
Flush Slave/Master data buffer */
out_8((u8 *)IIC_MDCNTL, IIC_MDCNTL_FSDB | IIC_MDCNTL_FMDB);
val = in8(IIC_MDCNTL);
__asm__ volatile ("eieio");
val = in_8((u8 *)IIC_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
*/
/* 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;
}
out8 (IIC_MDCNTL, val);
val |= IIC_MDCNTL_EUBS|IIC_MDCNTL_HSCL;
if (speed >= 400000)
val |= IIC_MDCNTL_FSM;
out_8((u8 *)IIC_MDCNTL, val);
/* clear control reg */
out8 (IIC_CNTL, 0x00);
__asm__ volatile ("eieio");
/* clear control reg */
out_8((u8 *)IIC_CNTL, 0x00);
}
/* set to SPD bus as default bus upon powerup */
I2C_SET_BUS(CFG_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 out8(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 )
* 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)
{
unsigned char* ptr;
int reading;
......@@ -198,97 +206,88 @@ int i2c_transfer(unsigned char cmd_type,
int i;
uchar creg;
if( data == 0 || data_len == 0 ){
/*Don't support data transfer of no length or to address 0*/
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 ){
if (addr && addr_len) {
ptr = addr;
cnt = addr_len;
reading = 0;
}else{
} else {
ptr = data;
cnt = data_len;
reading = cmd_type;
}
/*Clear Stop Complete Bit*/
out8(IIC_STS,IIC_STS_SCMP);
/* Clear Stop Complete Bit */
out_8((u8 *)IIC_STS, IIC_STS_SCMP);
/* Check init */
i=10;
i = 10;
do {
/* Get status */
status = in8(IIC_STS);
__asm__ volatile("eieio");
status = in_8((u8 *)IIC_STS);
i--;
} while ((status & IIC_STS_PT) && (i>0));
} while ((status & IIC_STS_PT) && (i > 0));
if (status & IIC_STS_PT) {
result = IIC_NOK_TOUT;
return(result);
}
/*flush the Master/Slave Databuffers*/
out8(IIC_MDCNTL, ((in8(IIC_MDCNTL))|IIC_MDCNTL_FMDB|IIC_MDCNTL_FSDB));
/*need to wait 4 OPB clocks? code below should take that long*/
/* flush the Master/Slave Databuffers */
out_8((u8 *)IIC_MDCNTL, ((in_8((u8 *)IIC_MDCNTL))|IIC_MDCNTL_FMDB|IIC_MDCNTL_FSDB));
/* need to wait 4 OPB clocks? code below should take that long */
/* 7-bit adressing */
out8(IIC_HMADR,0);
out8(IIC_LMADR, chip);
__asm__ volatile("eieio");
out_8((u8 *)IIC_HMADR, 0);
out_8((u8 *)IIC_LMADR, chip);
tran = 0;
result = IIC_OK;
creg = 0;
while ( tran != cnt && (result == IIC_OK)) {
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
*/
* 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) )
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++) {
else
for(j=0; j < bc; j++)
/* Set buffer */
out8(IIC_MDBUF,ptr[tran+j]);
__asm__ volatile("eieio");
}
}
out8(IIC_CNTL, creg );
__asm__ volatile("eieio");
out_8((u8 *)IIC_MDBUF, ptr[tran+j]);
out_8((u8 *)IIC_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;
* 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 = in8(IIC_STS);
__asm__ volatile("eieio");
udelay (10);
status = in_8((u8 *)IIC_STS);
udelay(10);
i--;
} while ((status & IIC_STS_PT) && !(status & IIC_STS_ERR)
&& (i>0));
} while ((status & IIC_STS_PT) && !(status & IIC_STS_ERR) && (i > 0));
if (status & IIC_STS_ERR) {
result = IIC_NOK;
status = in8 (IIC_EXTSTS);
status = in_8((u8 *)IIC_EXTSTS);
/* Lost arbitration? */
if (status & IIC_EXTSTS_LA)
result = IIC_NOK_LA;
......@@ -306,34 +305,32 @@ int i2c_transfer(unsigned char cmd_type,
/* 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] = in8(IIC_MDBUF);
__asm__ volatile("eieio");
}
* 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((u8 *)IIC_MDBUF);
} else
result = IIC_NOK_DATA;
}
creg = 0;
tran+=bc;
if( ptr == addr && tran == cnt ) {
tran += bc;
if (ptr == addr && tran == cnt) {
ptr = data;
cnt = data_len;
tran = 0;
reading = cmd_type;
if( reading )
if (reading)
creg = IIC_CNTL_RPST;
}
}
return (result);
}
int i2c_probe (uchar chip)
int i2c_probe(uchar chip)
{
uchar buf[1];
......@@ -344,21 +341,21 @@ int i2c_probe (uchar chip)
* 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);
return (i2c_transfer(1, chip << 1, 0,0, buf, 1) != 0);
}
int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len)
int i2c_read(uchar chip, uint addr, int alen, uchar * buffer, int len)
{
uchar xaddr[4];
int ret;
if ( alen > 4 ) {
if (alen > 4) {
printf ("I2C read: addr len %d not supported\n", alen);
return 1;
}
if ( alen > 0 ) {
if (alen > 0) {
xaddr[0] = (addr >> 24) & 0xFF;
xaddr[1] = (addr >> 16) & 0xFF;
xaddr[2] = (addr >> 8) & 0xFF;
......@@ -378,10 +375,10 @@ int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len)
* still be one byte because the extra address bits are
* hidden in the chip address.
*/
if( alen > 0 )
if (alen > 0)
chip |= ((addr >> (alen * 8)) & CFG_I2C_EEPROM_ADDR_OVERFLOW);
#endif
if( (ret = i2c_transfer( 1, chip<<1, &xaddr[4-alen], alen, buffer, len )) != 0) {
if ((ret = i2c_transfer(1, chip<<1, &xaddr[4-alen], alen, buffer, len)) != 0) {
if (gd->have_console)
printf( "I2c read: failed %d\n", ret);
</