diff options
| author | wdenk <wdenk> | 2003-03-06 21:55:29 +0000 | 
|---|---|---|
| committer | wdenk <wdenk> | 2003-03-06 21:55:29 +0000 | 
| commit | 1cb8e980c41e86760fa93de63f4e4cf643bef9d9 (patch) | |
| tree | e1993fba07dea51d92f1cec4c814a67173c1f8fb /drivers/s3c24x0_i2c.c | |
| parent | 500545cc6b83958209128bffa825b3c842a21a4e (diff) | |
| download | olio-uboot-2014.01-1cb8e980c41e86760fa93de63f4e4cf643bef9d9.tar.xz olio-uboot-2014.01-1cb8e980c41e86760fa93de63f4e4cf643bef9d9.zip | |
* Patches by David Müller, 31 Jan 2003:LABEL_2003_03_06_2255
  - minimal setup for CardBus bridges
  - add EEPROM read/write support in the CS8900 driver
  - add support for the builtin I2C controller in the Samsung s3c24x0 chips
  - add support for  MPL's VCMA9 (Samsung s3c2410 based) board
* Patch by Steven Scholz, 04 Feb 2003:
  add support for RTC DS1307
* Patch by Reinhard Meyer, 5 Feb 2003:
  fix PLPRCR/SCCR init sequence on 8xx to allow for
  changes of EBDF by software
* Patch by Vladimir Gurevich, 07 Feb 2003:
  "API-compatibility patch" for 4xx I2C driver
Diffstat (limited to 'drivers/s3c24x0_i2c.c')
| -rw-r--r-- | drivers/s3c24x0_i2c.c | 409 | 
1 files changed, 409 insertions, 0 deletions
| diff --git a/drivers/s3c24x0_i2c.c b/drivers/s3c24x0_i2c.c new file mode 100644 index 000000000..bf435c983 --- /dev/null +++ b/drivers/s3c24x0_i2c.c @@ -0,0 +1,409 @@ +/* + * (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> + +#ifdef CONFIG_DRIVER_S3C24X0_I2C + +#if defined(CONFIG_S3C2400) +#include <s3c2400.h> +#elif defined(CONFIG_S3C2410) +#include <s3c2410.h> +#endif +#include <i2c.h> + +#ifdef CONFIG_HARD_I2C + +#define	IIC_WRITE	0 +#define IIC_READ	1 + +#define IIC_OK		0 +#define IIC_NOK		1 +#define IIC_NACK	2 +#define IIC_NOK_LA	3		/* Lost arbitration */ +#define IIC_NOK_TOUT	4		/* time out */ + +#define IICSTAT_BSY	0x20		/* Busy bit */ +#define IICSTAT_NACK	0x01		/* Nack bit */ +#define IICCON_IRPND	0x10		/* Interrupt pending bit */ +#define IIC_MODE_MT	0xC0		/* Master Transmit Mode */ +#define IIC_MODE_MR	0x80		/* Master Receive Mode */ +#define IIC_START_STOP	0x20		/* START / STOP */ +#define IIC_TXRX_ENA	0x10		/* I2C Tx/Rx enable */ + +#define IIC_TIMEOUT 1			/* 1 seconde */ + + +static int GetIICSDA(void) +{ +	return (rGPEDAT & 0x8000) >> 15; +} + +static void SetIICSDA(int x) +{ +	rGPEDAT = (rGPEDAT & ~0x8000) | (x&1) << 15; +} + +static void SetIICSCL(int x) +{ +	rGPEDAT = (rGPEDAT & ~0x4000) | (x&1) << 14; +} + + +static int WaitForXfer(void) +{ +    int i, status; + +    i = IIC_TIMEOUT * 1000; +    status = rIICCON; +    while ((i > 0) && !(status & IICCON_IRPND)) { +    	udelay(1000); +	status = rIICCON; +	i--; +    } + +    return(status & IICCON_IRPND) ? IIC_OK : IIC_NOK_TOUT; +} + +static int IsACK(void) +{ +    return(!(rIICSTAT & IICSTAT_NACK)); +} + +static void ReadWriteByte(void) +{ +    rIICCON &= ~IICCON_IRPND; +} + +void i2c_init (int speed, int slaveadd) +{ +    ulong freq, pres = 16, div; +    int i, status; + +    /* wait for some time to give previous transfer a chance to finish */ + +    i = IIC_TIMEOUT * 1000; +    status = rIICSTAT; +    while ((i > 0) && (status & IICSTAT_BSY)) { +	udelay(1000); +	status = rIICSTAT; +	i--; +    } + +    if ((status & IICSTAT_BSY) || GetIICSDA() == 0) { +	ulong old_gpecon = rGPECON; +	/* bus still busy probably by (most) previously interrupted transfer */ + +	/* set IICSDA and IICSCL (GPE15, GPE14) to GPIO */ +	rGPECON = (rGPECON & ~0xF0000000) | 0x10000000; + +	/* toggle IICSCL until bus idle */ +	SetIICSCL(0); udelay(1000); +	i = 10; +	while ((i > 0) && (GetIICSDA() != 1)) { +		SetIICSCL(1); udelay(1000); +		SetIICSCL(0); udelay(1000); +		i--; +	} +	SetIICSCL(1); udelay(1000); + +	/* restore pin functions */ +	rGPECON = old_gpecon; +    } + +    /* 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 */ +    rIICCON = (div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0); + +    /* init to SLAVE REVEIVE and set slaveaddr */ +    rIICSTAT = 0; +    rIICADD = slaveadd; +    /* program Master Transmit (and implicit STOP) */ +    rIICSTAT = IIC_MODE_MT | IIC_TXRX_ENA; + +} + +/* +  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) +{ +    int i, status, 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 IIC_NOK; +    } + +    //CheckDelay(); + +    /* Check I2C bus idle */ +    i = IIC_TIMEOUT * 1000; +    status = rIICSTAT; +    while ((i > 0) && (status & IICSTAT_BSY)) { +	udelay(1000); +	status = rIICSTAT; +	i--; +    } + + +    if (status & IICSTAT_BSY) { +	result = IIC_NOK_TOUT; +        return(result); +    } + +    rIICCON |= 0x80; + +    result = IIC_OK; + +    switch (cmd_type) { +	case IIC_WRITE: +	    if (addr && addr_len) { +		rIICDS = chip; +		/* send START */ +		rIICSTAT = IIC_MODE_MT | IIC_TXRX_ENA | IIC_START_STOP; +		i = 0; +		while ((i < addr_len) && (result == IIC_OK)) { +		    result = WaitForXfer(); +		    rIICDS = addr[i]; +		    ReadWriteByte(); +		    i++; +		} +		i = 0; +		while ((i < data_len) && (result == IIC_OK)) { +		    result = WaitForXfer(); +		    rIICDS = data[i]; +		    ReadWriteByte(); +		    i++; +		} +	    } else { +		rIICDS = chip; +		/* send START */ +		rIICSTAT = IIC_MODE_MT | IIC_TXRX_ENA | IIC_START_STOP; +		i = 0; +		while ((i < data_len) && (result = IIC_OK)) { +		    result = WaitForXfer(); +		    rIICDS = data[i]; +		    ReadWriteByte(); +		    i++; +		} +	    } + +	    if (result == IIC_OK) +	        result = WaitForXfer(); + +	    /* send STOP */ +	    rIICSTAT = IIC_MODE_MR | IIC_TXRX_ENA; +	    ReadWriteByte(); +	    break; + +	case IIC_READ: +	    if (addr && addr_len) { +		rIICSTAT = IIC_MODE_MT | IIC_TXRX_ENA; +		rIICDS = chip; +		/* send START */ +		rIICSTAT |= IIC_START_STOP; +		result = WaitForXfer(); +		if (IsACK()) { +		    i = 0; +		    while ((i < addr_len) && (result == IIC_OK)) { +			rIICDS = addr[i]; +			ReadWriteByte(); +			result = WaitForXfer(); +			i++; +		    } + +		    rIICDS = chip; +		    /* resend START */ +		    rIICSTAT = IIC_MODE_MR | IIC_TXRX_ENA | IIC_START_STOP; +		    ReadWriteByte(); +		    result = WaitForXfer(); +		    i = 0; +		    while ((i < data_len) && (result == IIC_OK)) { +			/* disable ACK for final READ */ +			if (i == data_len - 1) +			    rIICCON &= ~0x80; +			ReadWriteByte(); +			result = WaitForXfer(); +			data[i] = rIICDS; +			i++; +		    } +		} else { +		    result = IIC_NACK; +		} + +	    } else { +		rIICSTAT = IIC_MODE_MR | IIC_TXRX_ENA; +		rIICDS = chip; +		/* send START */ +		rIICSTAT |= IIC_START_STOP; +		result = WaitForXfer(); + +		if (IsACK()) { +		    i = 0; +		    while ((i < data_len) && (result == IIC_OK)) { +			/* disable ACK for final READ */ +			if (i == data_len - 1) +			    rIICCON &= ~0x80; +		        ReadWriteByte(); +			result = WaitForXfer(); +			data[i] = rIICDS; +			i++; +		    } +		} else { +		    result = IIC_NACK; +		} +	    } + +	    /* send STOP */ +	    rIICSTAT = IIC_MODE_MR | IIC_TXRX_ENA; +	    ReadWriteByte(); +	    break; + +	default: +	    printf( "i2c_transfer: bad call\n" ); +    	    result = IIC_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 (IIC_READ, chip << 1, 0, 0, buf, 1) != IIC_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 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. +     */ +    if( alen > 0 ) +	chip |= ((addr >> (alen * 8)) & CFG_I2C_EEPROM_ADDR_OVERFLOW); +#endif +    if( (ret = i2c_transfer(IIC_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 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. +     */ +    if( alen > 0 ) +        chip |= ((addr >> (alen * 8)) & CFG_I2C_EEPROM_ADDR_OVERFLOW); +#endif +    return (i2c_transfer(IIC_WRITE, chip<<1, &xaddr[4-alen], alen, buffer, len ) != 0); +} + +#endif	/* CONFIG_HARD_I2C */ + +#endif /* CONFIG_DRIVER_S3C24X0_I2C */ |