mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-26 21:54:37 +08:00
d4fc6012fd
Merge to two at45.c files into a common file, split to at45.c and spi.c Fix spelling error in DM9161 PHY Support. Initialize at91rm9200 board (and set LED). Add PIO control for at91rm9200dk LEDs and Mux. Change dataflash partition boundaries to be compatible with Linux 2.6. Signed-off-by: Peter Pearse <peter.pearse@arm.com> Signed-off-by: Ulf Samuelsson <ulf@atmel.com>
428 lines
9.5 KiB
C
428 lines
9.5 KiB
C
/*
|
|
* (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>
|
|
#endif
|
|
#ifdef CONFIG_AT91RM9200 /* need this for the at91rm9200 */
|
|
#include <asm/io.h>
|
|
#include <asm/arch/hardware.h>
|
|
#endif
|
|
#ifdef CONFIG_IXP425 /* only valid for IXP425 */
|
|
#include <asm/arch/ixp425.h>
|
|
#endif
|
|
#ifdef CONFIG_LPC2292
|
|
#include <asm/arch/hardware.h>
|
|
#endif
|
|
#include <i2c.h>
|
|
|
|
#if defined(CONFIG_SOFT_I2C)
|
|
|
|
/* #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 { \
|
|
if (gd->have_console) \
|
|
printf (fmt ,##args); \
|
|
} while (0)
|
|
#else
|
|
#define PRINTD(fmt,args...)
|
|
#endif
|
|
|
|
/*-----------------------------------------------------------------------
|
|
* Local functions
|
|
*/
|
|
static void send_reset (void);
|
|
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);
|
|
|
|
|
|
/*-----------------------------------------------------------------------
|
|
* 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)
|
|
{
|
|
#ifdef CONFIG_MPC8260
|
|
volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, I2C_PORT);
|
|
#endif
|
|
#ifdef CONFIG_8xx
|
|
volatile immap_t *immr = (immap_t *)CFG_IMMR;
|
|
#endif
|
|
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;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------
|
|
* START: High -> Low on SDA while SCL is High
|
|
*/
|
|
static void send_start(void)
|
|
{
|
|
#ifdef CONFIG_MPC8260
|
|
volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, I2C_PORT);
|
|
#endif
|
|
#ifdef CONFIG_8xx
|
|
volatile immap_t *immr = (immap_t *)CFG_IMMR;
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
#ifdef CONFIG_MPC8260
|
|
volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, I2C_PORT);
|
|
#endif
|
|
#ifdef CONFIG_8xx
|
|
volatile immap_t *immr = (immap_t *)CFG_IMMR;
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
#ifdef CONFIG_MPC8260
|
|
volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, I2C_PORT);
|
|
#endif
|
|
#ifdef CONFIG_8xx
|
|
volatile immap_t *immr = (immap_t *)CFG_IMMR;
|
|
#endif
|
|
|
|
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)
|
|
{
|
|
#ifdef CONFIG_MPC8260
|
|
volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, I2C_PORT);
|
|
#endif
|
|
#ifdef CONFIG_8xx
|
|
volatile immap_t *immr = (immap_t *)CFG_IMMR;
|
|
#endif
|
|
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 ack == I2C_ACK, ACK the byte so can continue reading, else
|
|
* send I2C_NOACK to end the read.
|
|
*/
|
|
static uchar read_byte(int ack)
|
|
{
|
|
#ifdef CONFIG_MPC8260
|
|
volatile ioport_t *iop = ioport_addr((immap_t *)CFG_IMMR, I2C_PORT);
|
|
#endif
|
|
#ifdef CONFIG_8xx
|
|
volatile immap_t *immr = (immap_t *)CFG_IMMR;
|
|
#endif
|
|
int data;
|
|
int j;
|
|
|
|
/*
|
|
* Read 8 bits, MSB first.
|
|
*/
|
|
I2C_TRISTATE;
|
|
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)
|
|
{
|
|
/*
|
|
* 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 ();
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------
|
|
* 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 CFG_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)) & CFG_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;
|
|
}
|
|
send_stop(); /* reportedly some chips need a full stop */
|
|
send_start();
|
|
}
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------
|
|
* Read a register
|
|
*/
|
|
uchar i2c_reg_read(uchar i2c_addr, uchar reg)
|
|
{
|
|
uchar buf;
|
|
|
|
i2c_read(i2c_addr, reg, 1, &buf, 1);
|
|
|
|
return(buf);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------
|
|
* Write a register
|
|
*/
|
|
void i2c_reg_write(uchar i2c_addr, uchar reg, uchar val)
|
|
{
|
|
i2c_write(i2c_addr, reg, 1, &val, 1);
|
|
}
|
|
|
|
|
|
#endif /* CONFIG_SOFT_I2C */
|